@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.
- package/README.md +53 -9
- package/bin/server-cmd.js +1 -0
- package/bin/swarmclaw.js +76 -16
- package/next.config.ts +11 -1
- package/package.json +5 -2
- package/scripts/postinstall.mjs +18 -0
- package/src/app/api/canvas/[sessionId]/route.ts +31 -0
- package/src/app/api/chatrooms/[id]/chat/route.ts +284 -0
- package/src/app/api/chatrooms/[id]/members/route.ts +82 -0
- package/src/app/api/chatrooms/[id]/pins/route.ts +39 -0
- package/src/app/api/chatrooms/[id]/reactions/route.ts +42 -0
- package/src/app/api/chatrooms/[id]/route.ts +84 -0
- package/src/app/api/chatrooms/route.ts +50 -0
- package/src/app/api/connectors/[id]/route.ts +1 -0
- package/src/app/api/connectors/route.ts +2 -1
- package/src/app/api/credentials/route.ts +2 -3
- package/src/app/api/files/open/route.ts +43 -0
- package/src/app/api/knowledge/[id]/route.ts +13 -2
- package/src/app/api/knowledge/route.ts +8 -1
- package/src/app/api/memory/route.ts +8 -0
- package/src/app/api/notifications/route.ts +4 -0
- package/src/app/api/orchestrator/run/route.ts +1 -1
- package/src/app/api/plugins/install/route.ts +2 -2
- package/src/app/api/search/route.ts +53 -1
- package/src/app/api/sessions/[id]/chat/route.ts +2 -0
- package/src/app/api/sessions/[id]/edit-resend/route.ts +1 -1
- package/src/app/api/sessions/[id]/fork/route.ts +1 -1
- package/src/app/api/sessions/[id]/messages/route.ts +70 -2
- package/src/app/api/sessions/[id]/route.ts +4 -0
- package/src/app/api/sessions/route.ts +3 -3
- package/src/app/api/settings/route.ts +9 -0
- package/src/app/api/setup/check-provider/route.ts +3 -16
- package/src/app/api/skills/[id]/route.ts +6 -0
- package/src/app/api/skills/route.ts +6 -0
- package/src/app/api/tasks/[id]/route.ts +12 -0
- package/src/app/api/tasks/bulk/route.ts +100 -0
- package/src/app/api/tasks/metrics/route.ts +101 -0
- package/src/app/api/tasks/route.ts +18 -2
- package/src/app/api/tts/route.ts +3 -2
- package/src/app/api/tts/stream/route.ts +3 -2
- package/src/app/api/uploads/[filename]/route.ts +19 -34
- package/src/app/api/uploads/route.ts +94 -0
- package/src/app/api/webhooks/[id]/route.ts +15 -1
- package/src/app/globals.css +63 -15
- package/src/app/page.tsx +142 -13
- package/src/cli/index.js +40 -1
- package/src/cli/index.test.js +30 -0
- package/src/cli/spec.js +42 -0
- package/src/components/agents/agent-avatar.tsx +57 -10
- package/src/components/agents/agent-card.tsx +50 -17
- package/src/components/agents/agent-chat-list.tsx +148 -12
- package/src/components/agents/agent-list.tsx +50 -19
- package/src/components/agents/agent-sheet.tsx +120 -65
- package/src/components/agents/inspector-panel.tsx +81 -6
- package/src/components/agents/openclaw-skills-panel.tsx +32 -3
- package/src/components/agents/personality-builder.tsx +42 -14
- package/src/components/agents/soul-library-picker.tsx +89 -0
- package/src/components/auth/access-key-gate.tsx +10 -3
- package/src/components/auth/setup-wizard.tsx +2 -2
- package/src/components/auth/user-picker.tsx +31 -3
- package/src/components/canvas/canvas-panel.tsx +96 -0
- package/src/components/chat/activity-moment.tsx +173 -0
- package/src/components/chat/chat-area.tsx +46 -22
- package/src/components/chat/chat-header.tsx +457 -286
- package/src/components/chat/chat-preview-panel.tsx +1 -2
- package/src/components/chat/chat-tool-toggles.tsx +1 -1
- package/src/components/chat/delegation-banner.tsx +371 -0
- package/src/components/chat/file-path-chip.tsx +146 -0
- package/src/components/chat/heartbeat-history-panel.tsx +269 -0
- package/src/components/chat/markdown-utils.ts +9 -0
- package/src/components/chat/message-bubble.tsx +356 -315
- package/src/components/chat/message-list.tsx +230 -8
- package/src/components/chat/streaming-bubble.tsx +104 -47
- package/src/components/chat/suggestions-bar.tsx +1 -1
- package/src/components/chat/thinking-indicator.tsx +72 -10
- package/src/components/chat/tool-call-bubble.tsx +111 -73
- package/src/components/chat/tool-request-banner.tsx +31 -7
- package/src/components/chat/transfer-agent-picker.tsx +63 -0
- package/src/components/chatrooms/agent-hover-card.tsx +124 -0
- package/src/components/chatrooms/chatroom-input.tsx +320 -0
- package/src/components/chatrooms/chatroom-list.tsx +130 -0
- package/src/components/chatrooms/chatroom-message.tsx +432 -0
- package/src/components/chatrooms/chatroom-sheet.tsx +215 -0
- package/src/components/chatrooms/chatroom-tool-request-banner.tsx +134 -0
- package/src/components/chatrooms/chatroom-typing-bar.tsx +88 -0
- package/src/components/chatrooms/chatroom-view.tsx +344 -0
- package/src/components/chatrooms/reaction-picker.tsx +273 -0
- package/src/components/connectors/connector-list.tsx +168 -90
- package/src/components/connectors/connector-sheet.tsx +95 -56
- package/src/components/home/home-view.tsx +501 -0
- package/src/components/input/chat-input.tsx +107 -43
- package/src/components/knowledge/knowledge-list.tsx +31 -1
- package/src/components/knowledge/knowledge-sheet.tsx +83 -2
- package/src/components/layout/app-layout.tsx +194 -97
- package/src/components/layout/update-banner.tsx +2 -2
- package/src/components/logs/log-list.tsx +2 -2
- package/src/components/mcp-servers/mcp-server-sheet.tsx +1 -1
- package/src/components/memory/memory-agent-list.tsx +143 -0
- package/src/components/memory/memory-browser.tsx +205 -0
- package/src/components/memory/memory-card.tsx +34 -7
- package/src/components/memory/memory-detail.tsx +359 -120
- package/src/components/memory/memory-sheet.tsx +157 -23
- package/src/components/plugins/plugin-list.tsx +1 -1
- package/src/components/plugins/plugin-sheet.tsx +1 -1
- package/src/components/projects/project-detail.tsx +509 -0
- package/src/components/projects/project-list.tsx +195 -59
- package/src/components/providers/provider-list.tsx +2 -2
- package/src/components/providers/provider-sheet.tsx +3 -3
- package/src/components/schedules/schedule-card.tsx +1 -1
- package/src/components/schedules/schedule-list.tsx +1 -1
- package/src/components/schedules/schedule-sheet.tsx +259 -126
- package/src/components/secrets/secret-sheet.tsx +47 -24
- package/src/components/secrets/secrets-list.tsx +18 -8
- package/src/components/sessions/new-session-sheet.tsx +33 -65
- package/src/components/sessions/session-card.tsx +45 -14
- package/src/components/sessions/session-list.tsx +35 -18
- package/src/components/settings/gateway-disconnect-overlay.tsx +80 -0
- package/src/components/shared/agent-picker-list.tsx +90 -0
- package/src/components/shared/agent-switch-dialog.tsx +156 -0
- package/src/components/shared/attachment-chip.tsx +165 -0
- package/src/components/shared/avatar.tsx +10 -1
- package/src/components/shared/chatroom-picker-list.tsx +61 -0
- package/src/components/shared/check-icon.tsx +12 -0
- package/src/components/shared/confirm-dialog.tsx +1 -1
- package/src/components/shared/connector-platform-icon.tsx +51 -4
- package/src/components/shared/empty-state.tsx +32 -0
- package/src/components/shared/file-preview.tsx +34 -0
- package/src/components/shared/form-styles.ts +2 -0
- package/src/components/shared/icon-button.tsx +16 -2
- package/src/components/shared/keyboard-shortcuts-dialog.tsx +116 -0
- package/src/components/shared/notification-center.tsx +44 -6
- package/src/components/shared/profile-sheet.tsx +115 -0
- package/src/components/shared/reply-quote.tsx +26 -0
- package/src/components/shared/search-dialog.tsx +31 -15
- package/src/components/shared/section-label.tsx +12 -0
- package/src/components/shared/settings/plugin-manager.tsx +1 -1
- package/src/components/shared/settings/section-embedding.tsx +48 -13
- package/src/components/shared/settings/section-orchestrator.tsx +46 -15
- package/src/components/shared/settings/section-providers.tsx +1 -1
- package/src/components/shared/settings/section-secrets.tsx +1 -1
- package/src/components/shared/settings/section-storage.tsx +206 -0
- package/src/components/shared/settings/section-theme.tsx +95 -0
- package/src/components/shared/settings/section-user-preferences.tsx +57 -0
- package/src/components/shared/settings/section-voice.tsx +42 -21
- package/src/components/shared/settings/section-web-search.tsx +30 -6
- package/src/components/shared/settings/settings-page.tsx +182 -27
- package/src/components/shared/settings/settings-sheet.tsx +9 -73
- package/src/components/shared/settings/storage-browser.tsx +259 -0
- package/src/components/shared/sheet-footer.tsx +33 -0
- package/src/components/skills/skill-list.tsx +61 -30
- package/src/components/skills/skill-sheet.tsx +81 -2
- package/src/components/tasks/task-board.tsx +448 -26
- package/src/components/tasks/task-card.tsx +59 -9
- package/src/components/tasks/task-column.tsx +62 -3
- package/src/components/tasks/task-list.tsx +12 -4
- package/src/components/tasks/task-sheet.tsx +416 -74
- package/src/components/ui/hover-card.tsx +52 -0
- package/src/components/usage/metrics-dashboard.tsx +90 -6
- package/src/components/usage/usage-list.tsx +1 -1
- package/src/components/webhooks/webhook-sheet.tsx +1 -1
- package/src/hooks/use-continuous-speech.ts +10 -4
- package/src/hooks/use-view-router.ts +69 -19
- package/src/hooks/use-voice-conversation.ts +53 -10
- package/src/hooks/use-ws.ts +4 -2
- package/src/instrumentation.ts +15 -1
- package/src/lib/chat.ts +2 -0
- package/src/lib/memory.ts +3 -0
- package/src/lib/providers/anthropic.ts +13 -7
- package/src/lib/providers/index.ts +1 -0
- package/src/lib/providers/openai.ts +13 -7
- package/src/lib/server/chat-execution.ts +75 -15
- package/src/lib/server/chatroom-helpers.ts +146 -0
- package/src/lib/server/connectors/manager.ts +229 -7
- package/src/lib/server/context-manager.ts +225 -13
- package/src/lib/server/create-notification.ts +14 -2
- package/src/lib/server/daemon-state.ts +157 -10
- package/src/lib/server/execution-log.ts +1 -0
- package/src/lib/server/heartbeat-service.ts +48 -6
- package/src/lib/server/heartbeat-wake.ts +110 -0
- package/src/lib/server/langgraph-checkpoint.ts +1 -0
- package/src/lib/server/main-agent-loop.ts +1 -1
- package/src/lib/server/memory-consolidation.ts +105 -0
- package/src/lib/server/memory-db.ts +183 -10
- package/src/lib/server/mime.ts +51 -0
- package/src/lib/server/openclaw-gateway.ts +9 -1
- package/src/lib/server/orchestrator-lg.ts +2 -0
- package/src/lib/server/orchestrator.ts +5 -2
- package/src/lib/server/playwright-proxy.mjs +2 -3
- package/src/lib/server/prompt-runtime-context.ts +53 -0
- package/src/lib/server/provider-health.ts +125 -0
- package/src/lib/server/queue.ts +56 -10
- package/src/lib/server/scheduler.ts +8 -0
- package/src/lib/server/session-run-manager.ts +4 -0
- package/src/lib/server/session-tools/canvas.ts +67 -0
- package/src/lib/server/session-tools/chatroom.ts +136 -0
- package/src/lib/server/session-tools/connector.ts +83 -9
- package/src/lib/server/session-tools/context-mgmt.ts +36 -18
- package/src/lib/server/session-tools/crud.ts +21 -0
- package/src/lib/server/session-tools/delegate.ts +68 -4
- package/src/lib/server/session-tools/git.ts +71 -0
- package/src/lib/server/session-tools/http.ts +57 -0
- package/src/lib/server/session-tools/index.ts +10 -0
- package/src/lib/server/session-tools/memory.ts +7 -1
- package/src/lib/server/session-tools/search-providers.ts +16 -8
- package/src/lib/server/session-tools/subagent.ts +106 -0
- package/src/lib/server/session-tools/web.ts +115 -4
- package/src/lib/server/storage.ts +53 -29
- package/src/lib/server/stream-agent-chat.ts +185 -57
- package/src/lib/server/system-events.ts +49 -0
- package/src/lib/server/task-mention.ts +41 -0
- package/src/lib/server/ws-hub.ts +11 -0
- package/src/lib/sessions.ts +10 -0
- package/src/lib/soul-library.ts +103 -0
- package/src/lib/soul-suggestions.ts +109 -0
- package/src/lib/task-dedupe.ts +26 -0
- package/src/lib/tasks.ts +4 -1
- package/src/lib/tool-definitions.ts +2 -0
- package/src/lib/tts.ts +2 -2
- package/src/lib/view-routes.ts +36 -1
- package/src/lib/ws-client.ts +14 -4
- package/src/stores/use-app-store.ts +41 -3
- package/src/stores/use-chat-store.ts +113 -5
- package/src/stores/use-chatroom-store.ts +276 -0
- package/src/types/index.ts +88 -4
|
@@ -2,20 +2,45 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect, useMemo, useState } from 'react'
|
|
4
4
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
|
+
import type { Agent, BoardTask, Schedule } from '@/types'
|
|
6
|
+
|
|
7
|
+
function relativeDate(ts: number): string {
|
|
8
|
+
const diff = Date.now() - ts
|
|
9
|
+
if (diff < 60_000) return 'just now'
|
|
10
|
+
if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m ago`
|
|
11
|
+
if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`
|
|
12
|
+
if (diff < 604_800_000) return `${Math.floor(diff / 86_400_000)}d ago`
|
|
13
|
+
return new Date(ts).toLocaleDateString(undefined, { month: 'short', day: 'numeric' })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ProjectStats {
|
|
17
|
+
agents: number
|
|
18
|
+
tasks: number
|
|
19
|
+
completedTasks: number
|
|
20
|
+
schedules: number
|
|
21
|
+
lastActivity: number
|
|
22
|
+
}
|
|
5
23
|
|
|
6
24
|
export function ProjectList() {
|
|
7
25
|
const projects = useAppStore((s) => s.projects)
|
|
8
26
|
const loadProjects = useAppStore((s) => s.loadProjects)
|
|
9
|
-
const agents = useAppStore((s) => s.agents)
|
|
10
|
-
const tasks = useAppStore((s) => s.tasks)
|
|
27
|
+
const agents = useAppStore((s) => s.agents) as Record<string, Agent>
|
|
28
|
+
const tasks = useAppStore((s) => s.tasks) as Record<string, BoardTask>
|
|
29
|
+
const schedules = useAppStore((s) => s.schedules) as Record<string, Schedule>
|
|
11
30
|
const setProjectSheetOpen = useAppStore((s) => s.setProjectSheetOpen)
|
|
12
31
|
const setEditingProjectId = useAppStore((s) => s.setEditingProjectId)
|
|
13
32
|
const activeProjectFilter = useAppStore((s) => s.activeProjectFilter)
|
|
14
33
|
const setActiveProjectFilter = useAppStore((s) => s.setActiveProjectFilter)
|
|
34
|
+
const loadTasks = useAppStore((s) => s.loadTasks)
|
|
35
|
+
const loadSchedules = useAppStore((s) => s.loadSchedules)
|
|
15
36
|
const [search, setSearch] = useState('')
|
|
16
37
|
|
|
17
|
-
|
|
18
|
-
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
loadProjects()
|
|
40
|
+
loadTasks()
|
|
41
|
+
loadSchedules()
|
|
42
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
43
|
+
}, [])
|
|
19
44
|
|
|
20
45
|
const filtered = useMemo(() => {
|
|
21
46
|
return Object.values(projects)
|
|
@@ -26,97 +51,208 @@ export function ProjectList() {
|
|
|
26
51
|
.sort((a, b) => b.updatedAt - a.updatedAt)
|
|
27
52
|
}, [projects, search])
|
|
28
53
|
|
|
29
|
-
const
|
|
30
|
-
const
|
|
54
|
+
const statsMap = useMemo(() => {
|
|
55
|
+
const map: Record<string, ProjectStats> = {}
|
|
31
56
|
for (const p of Object.values(projects)) {
|
|
32
|
-
|
|
57
|
+
map[p.id] = { agents: 0, tasks: 0, completedTasks: 0, schedules: 0, lastActivity: p.updatedAt }
|
|
33
58
|
}
|
|
34
59
|
for (const a of Object.values(agents)) {
|
|
35
|
-
if (a.projectId &&
|
|
60
|
+
if (a.projectId && map[a.projectId]) {
|
|
61
|
+
map[a.projectId].agents++
|
|
62
|
+
if (a.updatedAt && a.updatedAt > map[a.projectId].lastActivity) {
|
|
63
|
+
map[a.projectId].lastActivity = a.updatedAt
|
|
64
|
+
}
|
|
65
|
+
}
|
|
36
66
|
}
|
|
37
67
|
for (const t of Object.values(tasks)) {
|
|
38
|
-
if (t.projectId &&
|
|
68
|
+
if (t.projectId && map[t.projectId]) {
|
|
69
|
+
map[t.projectId].tasks++
|
|
70
|
+
if (t.status === 'completed') map[t.projectId].completedTasks++
|
|
71
|
+
if (t.updatedAt && t.updatedAt > map[t.projectId].lastActivity) {
|
|
72
|
+
map[t.projectId].lastActivity = t.updatedAt
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (const s of Object.values(schedules)) {
|
|
77
|
+
if (s.projectId && map[s.projectId]) {
|
|
78
|
+
map[s.projectId].schedules++
|
|
79
|
+
}
|
|
39
80
|
}
|
|
40
|
-
return
|
|
41
|
-
}, [projects, agents, tasks])
|
|
81
|
+
return map
|
|
82
|
+
}, [projects, agents, tasks, schedules])
|
|
83
|
+
|
|
84
|
+
// Summary stats
|
|
85
|
+
const totalProjects = Object.keys(projects).length
|
|
86
|
+
const totalTasks = Object.values(tasks).filter((t) => t.projectId).length
|
|
87
|
+
const totalCompleted = Object.values(tasks).filter((t) => t.projectId && t.status === 'completed').length
|
|
42
88
|
|
|
43
89
|
if (!filtered.length && !search) {
|
|
44
90
|
return (
|
|
45
91
|
<div className="flex-1 flex flex-col items-center justify-center gap-4 text-text-3 p-8 text-center">
|
|
46
|
-
<div className="w-
|
|
47
|
-
<svg width="
|
|
92
|
+
<div className="w-14 h-14 rounded-[16px] bg-accent-soft flex items-center justify-center mb-1">
|
|
93
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" className="text-accent-bright">
|
|
48
94
|
<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
95
|
<path d="M14 2v7h7" />
|
|
50
96
|
</svg>
|
|
51
97
|
</div>
|
|
52
|
-
<p className="font-display text-[
|
|
53
|
-
<p className="text-[13px] text-text-3/
|
|
98
|
+
<p className="font-display text-[16px] font-600 text-text-2">No projects yet</p>
|
|
99
|
+
<p className="text-[13px] text-text-3/60 max-w-[280px]">
|
|
100
|
+
Projects group your agents, tasks, and schedules together. Create one to get organized.
|
|
101
|
+
</p>
|
|
54
102
|
<button
|
|
55
103
|
onClick={() => { setEditingProjectId(null); setProjectSheetOpen(true) }}
|
|
56
|
-
className="inline-flex items-center gap-1.5 px-
|
|
104
|
+
className="inline-flex items-center gap-1.5 px-5 py-2.5 text-[13px] font-600 text-white bg-accent-bright rounded-[10px] hover:brightness-110 transition-all cursor-pointer border-none"
|
|
105
|
+
style={{ fontFamily: 'inherit' }}
|
|
57
106
|
>
|
|
58
|
-
<
|
|
107
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
108
|
+
<line x1="12" y1="5" x2="12" y2="19" />
|
|
109
|
+
<line x1="5" y1="12" x2="19" y2="12" />
|
|
110
|
+
</svg>
|
|
111
|
+
New Project
|
|
59
112
|
</button>
|
|
60
113
|
</div>
|
|
61
114
|
)
|
|
62
115
|
}
|
|
63
116
|
|
|
64
117
|
return (
|
|
65
|
-
<div className="flex-1 flex flex-col h-full overflow-
|
|
66
|
-
|
|
67
|
-
|
|
118
|
+
<div className="flex-1 flex flex-col h-full overflow-hidden">
|
|
119
|
+
{/* Header with search and new button */}
|
|
120
|
+
<div className="px-5 pt-5 pb-3 shrink-0">
|
|
121
|
+
<div className="flex items-center justify-between mb-4">
|
|
122
|
+
<div>
|
|
123
|
+
<h2 className="font-display text-[20px] font-700 text-text tracking-[-0.02em]">Projects</h2>
|
|
124
|
+
<p className="text-[12px] text-text-3/60 mt-0.5">
|
|
125
|
+
{totalProjects} project{totalProjects !== 1 ? 's' : ''}
|
|
126
|
+
{totalTasks > 0 && <> · {totalCompleted}/{totalTasks} tasks done</>}
|
|
127
|
+
</p>
|
|
128
|
+
</div>
|
|
129
|
+
<button
|
|
130
|
+
onClick={() => { setEditingProjectId(null); setProjectSheetOpen(true) }}
|
|
131
|
+
className="inline-flex items-center gap-1.5 px-3.5 py-2 text-[12px] font-600 text-white bg-accent-bright rounded-[10px] hover:brightness-110 transition-all cursor-pointer border-none"
|
|
132
|
+
style={{ fontFamily: 'inherit' }}
|
|
133
|
+
>
|
|
134
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
|
|
135
|
+
<line x1="12" y1="5" x2="12" y2="19" />
|
|
136
|
+
<line x1="5" y1="12" x2="19" y2="12" />
|
|
137
|
+
</svg>
|
|
138
|
+
New
|
|
139
|
+
</button>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
{/* Search */}
|
|
143
|
+
<div className="relative">
|
|
144
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="absolute left-3 top-1/2 -translate-y-1/2 text-text-3/50">
|
|
145
|
+
<circle cx="11" cy="11" r="8" />
|
|
146
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
147
|
+
</svg>
|
|
68
148
|
<input
|
|
69
149
|
type="text"
|
|
70
150
|
value={search}
|
|
71
151
|
onChange={(e) => setSearch(e.target.value)}
|
|
72
152
|
placeholder="Search projects..."
|
|
73
|
-
className="
|
|
153
|
+
className="w-full pl-9 pr-3 py-2.5 rounded-[10px] bg-white/[0.04] border border-white/[0.06] text-[13px] text-text placeholder:text-text-3/40 focus:outline-none focus:border-accent-bright/30 transition-colors"
|
|
74
154
|
style={{ fontFamily: 'inherit' }}
|
|
75
155
|
/>
|
|
76
156
|
</div>
|
|
77
157
|
</div>
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
158
|
+
|
|
159
|
+
{/* Project cards */}
|
|
160
|
+
<div className="flex-1 overflow-y-auto px-5 pb-5">
|
|
161
|
+
<div className="grid gap-3">
|
|
162
|
+
{filtered.map((project) => {
|
|
163
|
+
const stats = statsMap[project.id] || { agents: 0, tasks: 0, completedTasks: 0, schedules: 0, lastActivity: project.updatedAt }
|
|
164
|
+
const isActive = activeProjectFilter === project.id
|
|
165
|
+
const progressPct = stats.tasks > 0 ? Math.round((stats.completedTasks / stats.tasks) * 100) : 0
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<div
|
|
169
|
+
key={project.id}
|
|
170
|
+
className={`group relative rounded-[14px] border transition-all duration-200 cursor-pointer overflow-hidden
|
|
171
|
+
${isActive
|
|
172
|
+
? 'bg-white/[0.06] border-accent-bright/30 shadow-[0_0_20px_rgba(99,102,241,0.08)]'
|
|
173
|
+
: 'bg-white/[0.02] border-white/[0.06] hover:bg-white/[0.05] hover:border-white/[0.1]'}`}
|
|
174
|
+
onClick={() => setActiveProjectFilter(isActive ? null : project.id)}
|
|
175
|
+
>
|
|
176
|
+
{/* Color accent stripe */}
|
|
177
|
+
<div className="absolute left-0 top-0 bottom-0 w-1 rounded-l-[14px]" style={{ backgroundColor: project.color || '#6B7280' }} />
|
|
178
|
+
|
|
179
|
+
<div className="pl-5 pr-4 py-4">
|
|
180
|
+
<div className="flex items-start justify-between gap-3">
|
|
181
|
+
<div className="min-w-0 flex-1">
|
|
182
|
+
<div className="flex items-center gap-2">
|
|
183
|
+
<h3 className="font-display text-[14px] font-600 text-text truncate">{project.name}</h3>
|
|
184
|
+
{isActive && (
|
|
185
|
+
<span className="shrink-0 text-[9px] font-700 uppercase tracking-wider text-accent-bright bg-accent-soft px-1.5 py-0.5 rounded-[5px]">
|
|
186
|
+
active filter
|
|
187
|
+
</span>
|
|
188
|
+
)}
|
|
189
|
+
</div>
|
|
190
|
+
{project.description && (
|
|
191
|
+
<p className="text-[12px] text-text-3/60 mt-1 line-clamp-2 leading-relaxed">{project.description}</p>
|
|
192
|
+
)}
|
|
193
|
+
</div>
|
|
194
|
+
<button
|
|
195
|
+
onClick={(e) => { e.stopPropagation(); setEditingProjectId(project.id); setProjectSheetOpen(true) }}
|
|
196
|
+
className="opacity-0 group-hover:opacity-100 p-1.5 rounded-[8px] hover:bg-white/[0.08] transition-all text-text-3/50 hover:text-text-2 cursor-pointer bg-transparent border-none shrink-0"
|
|
197
|
+
>
|
|
198
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
199
|
+
<path d="M17 3a2.83 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" />
|
|
200
|
+
</svg>
|
|
201
|
+
</button>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
{/* Stats row */}
|
|
205
|
+
<div className="flex items-center gap-4 mt-3 text-[11px] text-text-3/50">
|
|
206
|
+
<span className="flex items-center gap-1.5">
|
|
207
|
+
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
208
|
+
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
|
209
|
+
<circle cx="9" cy="7" r="4" />
|
|
210
|
+
</svg>
|
|
211
|
+
{stats.agents} agent{stats.agents !== 1 ? 's' : ''}
|
|
212
|
+
</span>
|
|
213
|
+
<span className="flex items-center gap-1.5">
|
|
214
|
+
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
215
|
+
<path d="M9 11l3 3L22 4" />
|
|
216
|
+
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11" />
|
|
217
|
+
</svg>
|
|
218
|
+
{stats.completedTasks}/{stats.tasks} task{stats.tasks !== 1 ? 's' : ''}
|
|
219
|
+
</span>
|
|
220
|
+
{stats.schedules > 0 && (
|
|
221
|
+
<span className="flex items-center gap-1.5">
|
|
222
|
+
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
223
|
+
<circle cx="12" cy="12" r="10" />
|
|
224
|
+
<polyline points="12 6 12 12 16 14" />
|
|
225
|
+
</svg>
|
|
226
|
+
{stats.schedules} schedule{stats.schedules !== 1 ? 's' : ''}
|
|
227
|
+
</span>
|
|
101
228
|
)}
|
|
229
|
+
<span className="ml-auto text-text-3/40">
|
|
230
|
+
{relativeDate(stats.lastActivity)}
|
|
231
|
+
</span>
|
|
102
232
|
</div>
|
|
233
|
+
|
|
234
|
+
{/* Progress bar */}
|
|
235
|
+
{stats.tasks > 0 && (
|
|
236
|
+
<div className="mt-3 flex items-center gap-2.5">
|
|
237
|
+
<div className="flex-1 h-1.5 rounded-full bg-white/[0.06] overflow-hidden">
|
|
238
|
+
<div
|
|
239
|
+
className="h-full rounded-full transition-all duration-500"
|
|
240
|
+
style={{
|
|
241
|
+
width: `${progressPct}%`,
|
|
242
|
+
backgroundColor: progressPct === 100 ? '#22C55E' : (project.color || '#6366F1'),
|
|
243
|
+
}}
|
|
244
|
+
/>
|
|
245
|
+
</div>
|
|
246
|
+
<span className={`text-[10px] font-mono font-600 ${progressPct === 100 ? 'text-emerald-400' : 'text-text-3/50'}`}>
|
|
247
|
+
{progressPct}%
|
|
248
|
+
</span>
|
|
249
|
+
</div>
|
|
250
|
+
)}
|
|
103
251
|
</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
252
|
</div>
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
</div>
|
|
117
|
-
</div>
|
|
118
|
-
)
|
|
119
|
-
})}
|
|
253
|
+
)
|
|
254
|
+
})}
|
|
255
|
+
</div>
|
|
120
256
|
</div>
|
|
121
257
|
</div>
|
|
122
258
|
)
|
|
@@ -86,7 +86,7 @@ export function ProviderList({ inSidebar }: { inSidebar?: boolean }) {
|
|
|
86
86
|
<span className="font-display text-[14px] font-600 text-text truncate">{item.name}</span>
|
|
87
87
|
<div className="flex items-center gap-2 shrink-0">
|
|
88
88
|
<span className={`text-[10px] font-600 px-2 py-0.5 rounded-[5px] uppercase tracking-wider
|
|
89
|
-
${item.type === 'builtin' ? 'bg-white/[0.04] text-text-3' : 'bg-
|
|
89
|
+
${item.type === 'builtin' ? 'bg-white/[0.04] text-text-3' : 'bg-accent-bright/10 text-[#6366F1]'}`}>
|
|
90
90
|
{item.type === 'builtin' ? 'Built-in' : 'Custom'}
|
|
91
91
|
</span>
|
|
92
92
|
{!inSidebar && item.type === 'custom' && (
|
|
@@ -94,7 +94,7 @@ export function ProviderList({ inSidebar }: { inSidebar?: boolean }) {
|
|
|
94
94
|
<div
|
|
95
95
|
onClick={(e) => handleToggle(e, item.id, item.isEnabled)}
|
|
96
96
|
className={`w-9 h-5 rounded-full transition-all relative cursor-pointer shrink-0
|
|
97
|
-
${item.isEnabled ? 'bg-
|
|
97
|
+
${item.isEnabled ? 'bg-accent-bright' : 'bg-white/[0.08]'}`}
|
|
98
98
|
>
|
|
99
99
|
<div className={`absolute top-0.5 w-4 h-4 rounded-full bg-white transition-all
|
|
100
100
|
${item.isEnabled ? 'left-[18px]' : 'left-0.5'}`} />
|
|
@@ -342,7 +342,7 @@ export function ProviderSheet() {
|
|
|
342
342
|
<div
|
|
343
343
|
onClick={() => setRequiresApiKey(!requiresApiKey)}
|
|
344
344
|
className={`w-11 h-6 rounded-full transition-all duration-200 relative cursor-pointer
|
|
345
|
-
${requiresApiKey ? 'bg-
|
|
345
|
+
${requiresApiKey ? 'bg-accent-bright' : 'bg-white/[0.08]'}`}
|
|
346
346
|
>
|
|
347
347
|
<div className={`absolute top-0.5 w-5 h-5 rounded-full bg-white transition-all duration-200
|
|
348
348
|
${requiresApiKey ? 'left-[22px]' : 'left-0.5'}`} />
|
|
@@ -441,7 +441,7 @@ export function ProviderSheet() {
|
|
|
441
441
|
<div
|
|
442
442
|
onClick={() => setIsEnabled(!isEnabled)}
|
|
443
443
|
className={`w-11 h-6 rounded-full transition-all duration-200 relative cursor-pointer
|
|
444
|
-
${isEnabled ? 'bg-
|
|
444
|
+
${isEnabled ? 'bg-accent-bright' : 'bg-white/[0.08]'}`}
|
|
445
445
|
>
|
|
446
446
|
<div className={`absolute top-0.5 w-5 h-5 rounded-full bg-white transition-all duration-200
|
|
447
447
|
${isEnabled ? 'left-[22px]' : 'left-0.5'}`} />
|
|
@@ -485,7 +485,7 @@ export function ProviderSheet() {
|
|
|
485
485
|
<button
|
|
486
486
|
onClick={handleSave}
|
|
487
487
|
disabled={isBuiltin ? false : (!name.trim() || !baseUrl.trim())}
|
|
488
|
-
className="flex-1 py-3.5 rounded-[14px] border-none bg-
|
|
488
|
+
className="flex-1 py-3.5 rounded-[14px] border-none bg-accent-bright text-white text-[15px] font-600 cursor-pointer active:scale-[0.97] disabled:opacity-30 transition-all shadow-[0_4px_20px_rgba(99,102,241,0.25)] hover:brightness-110"
|
|
489
489
|
style={{ fontFamily: 'inherit' }}
|
|
490
490
|
>
|
|
491
491
|
{editing ? 'Save' : 'Create'}
|
|
@@ -71,7 +71,7 @@ export function ScheduleCard({ schedule, inSidebar }: Props) {
|
|
|
71
71
|
<div
|
|
72
72
|
onClick={handleToggle}
|
|
73
73
|
className={`w-9 h-5 rounded-full transition-all relative cursor-pointer shrink-0
|
|
74
|
-
${schedule.status === 'active' ? 'bg-
|
|
74
|
+
${schedule.status === 'active' ? 'bg-accent-bright' : 'bg-white/[0.08]'}`}
|
|
75
75
|
>
|
|
76
76
|
<div className={`absolute top-0.5 w-4 h-4 rounded-full bg-white transition-all
|
|
77
77
|
${schedule.status === 'active' ? 'left-[18px]' : 'left-0.5'}`} />
|
|
@@ -41,7 +41,7 @@ export function ScheduleList({ inSidebar }: Props) {
|
|
|
41
41
|
{!inSidebar && (
|
|
42
42
|
<button
|
|
43
43
|
onClick={() => setScheduleSheetOpen(true)}
|
|
44
|
-
className="mt-3 px-8 py-3 rounded-[14px] border-none bg-
|
|
44
|
+
className="mt-3 px-8 py-3 rounded-[14px] border-none bg-accent-bright text-white
|
|
45
45
|
text-[14px] font-600 cursor-pointer active:scale-95 transition-all duration-200
|
|
46
46
|
shadow-[0_4px_16px_rgba(99,102,241,0.2)]"
|
|
47
47
|
style={{ fontFamily: 'inherit' }}
|