@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
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState } from 'react'
|
|
4
|
+
import ReactMarkdown from 'react-markdown'
|
|
5
|
+
import remarkGfm from 'remark-gfm'
|
|
4
6
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
7
|
import { createTask, updateTask, archiveTask, unarchiveTask } from '@/lib/tasks'
|
|
6
8
|
import { BottomSheet } from '@/components/shared/bottom-sheet'
|
|
9
|
+
import { AgentPickerList } from '@/components/shared/agent-picker-list'
|
|
7
10
|
import { DirBrowser } from '@/components/shared/dir-browser'
|
|
11
|
+
import { SheetFooter } from '@/components/shared/sheet-footer'
|
|
12
|
+
import { inputClass } from '@/components/shared/form-styles'
|
|
8
13
|
import type { BoardTask, TaskComment } from '@/types'
|
|
14
|
+
import { SectionLabel } from '@/components/shared/section-label'
|
|
15
|
+
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
9
16
|
|
|
10
17
|
function fmtTime(ts: number) {
|
|
11
18
|
const d = new Date(ts)
|
|
@@ -25,6 +32,13 @@ export function TaskSheet() {
|
|
|
25
32
|
const agents = useAppStore((s) => s.agents)
|
|
26
33
|
const loadAgents = useAppStore((s) => s.loadAgents)
|
|
27
34
|
|
|
35
|
+
const projects = useAppStore((s) => s.projects)
|
|
36
|
+
const loadProjects = useAppStore((s) => s.loadProjects)
|
|
37
|
+
const activeProjectFilter = useAppStore((s) => s.activeProjectFilter)
|
|
38
|
+
|
|
39
|
+
const viewOnly = useAppStore((s) => s.taskSheetViewOnly)
|
|
40
|
+
const setViewOnly = useAppStore((s) => s.setTaskSheetViewOnly)
|
|
41
|
+
|
|
28
42
|
const appSettings = useAppStore((s) => s.appSettings)
|
|
29
43
|
const loadSettings = useAppStore((s) => s.loadSettings)
|
|
30
44
|
|
|
@@ -36,23 +50,27 @@ export function TaskSheet() {
|
|
|
36
50
|
const [uploading, setUploading] = useState(false)
|
|
37
51
|
const [cwd, setCwd] = useState('')
|
|
38
52
|
const [file, setFile] = useState<string | null>(null)
|
|
53
|
+
const [projectId, setProjectId] = useState('')
|
|
39
54
|
const [tags, setTags] = useState<string[]>([])
|
|
40
55
|
const [tagInput, setTagInput] = useState('')
|
|
41
56
|
const [blockedBy, setBlockedBy] = useState<string[]>([])
|
|
42
57
|
const [dueAt, setDueAt] = useState<string>('')
|
|
43
58
|
const [customFields, setCustomFields] = useState<Record<string, string | number | boolean>>({})
|
|
59
|
+
const [priority, setPriority] = useState<'low' | 'medium' | 'high' | 'critical' | ''>('')
|
|
44
60
|
|
|
45
61
|
const editing = editingId ? tasks[editingId] : null
|
|
46
|
-
const agentList = Object.values(agents)
|
|
62
|
+
const agentList = Object.values(agents).sort((a, b) => a.name.localeCompare(b.name))
|
|
47
63
|
|
|
48
64
|
useEffect(() => {
|
|
49
65
|
if (open) {
|
|
50
66
|
loadAgents()
|
|
67
|
+
loadProjects()
|
|
51
68
|
loadSettings()
|
|
52
69
|
if (editing) {
|
|
53
70
|
setTitle(editing.title)
|
|
54
71
|
setDescription(editing.description)
|
|
55
72
|
setAgentId(editing.agentId)
|
|
73
|
+
setProjectId(editing.projectId || '')
|
|
56
74
|
setImages(editing.images || [])
|
|
57
75
|
setCwd(editing.cwd || '')
|
|
58
76
|
setFile(editing.file || null)
|
|
@@ -60,10 +78,12 @@ export function TaskSheet() {
|
|
|
60
78
|
setBlockedBy(editing.blockedBy || [])
|
|
61
79
|
setDueAt(editing.dueAt ? new Date(editing.dueAt).toISOString().slice(0, 10) : '')
|
|
62
80
|
setCustomFields(editing.customFields || {})
|
|
81
|
+
setPriority(editing.priority || '')
|
|
63
82
|
} else {
|
|
64
83
|
setTitle('')
|
|
65
84
|
setDescription('')
|
|
66
85
|
setAgentId(agentList[0]?.id || '')
|
|
86
|
+
setProjectId(activeProjectFilter || '')
|
|
67
87
|
setImages([])
|
|
68
88
|
setCwd('')
|
|
69
89
|
setFile(null)
|
|
@@ -71,6 +91,7 @@ export function TaskSheet() {
|
|
|
71
91
|
setBlockedBy([])
|
|
72
92
|
setDueAt('')
|
|
73
93
|
setCustomFields({})
|
|
94
|
+
setPriority('')
|
|
74
95
|
}
|
|
75
96
|
}
|
|
76
97
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -89,12 +110,15 @@ export function TaskSheet() {
|
|
|
89
110
|
}
|
|
90
111
|
|
|
91
112
|
const handleSave = async () => {
|
|
92
|
-
|
|
93
|
-
|
|
113
|
+
// projectId uses null (not undefined) so the API can distinguish "clear" from "not sent"
|
|
114
|
+
// projectId uses null (not undefined) so the API can distinguish "clear" from "not sent"
|
|
115
|
+
const payload = {
|
|
116
|
+
title: title.trim() || 'Untitled Task', description, agentId, projectId: projectId || null, images,
|
|
94
117
|
cwd: cwd || undefined, file: file || undefined,
|
|
95
118
|
tags, blockedBy, dueAt: dueAt ? new Date(dueAt).getTime() : null,
|
|
96
119
|
customFields: Object.keys(customFields).length > 0 ? customFields : undefined,
|
|
97
|
-
|
|
120
|
+
priority: priority || undefined,
|
|
121
|
+
} as Partial<BoardTask> & { title: string; description: string; agentId: string }
|
|
98
122
|
if (editing) {
|
|
99
123
|
await updateTask(editing.id, payload)
|
|
100
124
|
} else {
|
|
@@ -116,7 +140,9 @@ export function TaskSheet() {
|
|
|
116
140
|
})
|
|
117
141
|
const data = await res.json()
|
|
118
142
|
if (data.url) setImages((prev) => [...prev, data.url])
|
|
119
|
-
} catch
|
|
143
|
+
} catch (err: unknown) {
|
|
144
|
+
console.error('Image upload failed:', err instanceof Error ? err.message : String(err))
|
|
145
|
+
}
|
|
120
146
|
setUploading(false)
|
|
121
147
|
e.target.value = ''
|
|
122
148
|
}
|
|
@@ -159,11 +185,296 @@ export function TaskSheet() {
|
|
|
159
185
|
setCommentText('')
|
|
160
186
|
}
|
|
161
187
|
|
|
162
|
-
const
|
|
188
|
+
const PRIORITY_STYLES: Record<string, string> = {
|
|
189
|
+
low: 'bg-sky-500/10 border-sky-500/20 text-sky-400',
|
|
190
|
+
medium: 'bg-amber-500/10 border-amber-500/20 text-amber-400',
|
|
191
|
+
high: 'bg-orange-500/10 border-orange-500/20 text-orange-400',
|
|
192
|
+
critical: 'bg-red-500/10 border-red-500/20 text-red-400',
|
|
193
|
+
}
|
|
194
|
+
const STATUS_STYLES: Record<string, string> = {
|
|
195
|
+
backlog: 'bg-white/[0.06] text-text-3',
|
|
196
|
+
queued: 'bg-amber-500/10 text-amber-400',
|
|
197
|
+
'in-progress': 'bg-sky-500/10 text-sky-400',
|
|
198
|
+
completed: 'bg-emerald-500/10 text-emerald-400',
|
|
199
|
+
failed: 'bg-red-500/10 text-red-400',
|
|
200
|
+
archived: 'bg-white/[0.04] text-text-3/60',
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const taskAgent = editing ? agents[editing.agentId] : null
|
|
204
|
+
const taskProject = editing?.projectId ? projects[editing.projectId] : null
|
|
205
|
+
|
|
206
|
+
/* ───── View-only mode ───── */
|
|
207
|
+
if (viewOnly && editing) {
|
|
208
|
+
return (
|
|
209
|
+
<BottomSheet open={open} onClose={onClose}>
|
|
210
|
+
{/* Header: title + badges + timestamps */}
|
|
211
|
+
<div className="mb-8">
|
|
212
|
+
<h2 className="font-display text-[28px] font-700 tracking-[-0.03em] mb-3">
|
|
213
|
+
{editing.title}
|
|
214
|
+
</h2>
|
|
215
|
+
<div className="flex flex-wrap items-center gap-2 mb-3">
|
|
216
|
+
<span className={`px-2.5 py-1 rounded-[8px] text-[12px] font-600 border border-transparent ${STATUS_STYLES[editing.status] || 'bg-white/[0.06] text-text-3'}`}>
|
|
217
|
+
{editing.status}
|
|
218
|
+
</span>
|
|
219
|
+
{editing.priority && (
|
|
220
|
+
<span className={`px-2.5 py-1 rounded-[8px] text-[12px] font-600 border ${PRIORITY_STYLES[editing.priority] || ''}`}>
|
|
221
|
+
{editing.priority}
|
|
222
|
+
</span>
|
|
223
|
+
)}
|
|
224
|
+
</div>
|
|
225
|
+
<div className="flex flex-wrap gap-x-4 gap-y-1 text-[12px] text-text-3">
|
|
226
|
+
<span>Created {fmtTime(editing.createdAt)}</span>
|
|
227
|
+
{editing.startedAt && <span>Started {fmtTime(editing.startedAt)}</span>}
|
|
228
|
+
{editing.completedAt && <span>Completed {fmtTime(editing.completedAt)}</span>}
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
{/* Description */}
|
|
233
|
+
{editing.description && (
|
|
234
|
+
<div className="mb-8">
|
|
235
|
+
<SectionLabel>Description</SectionLabel>
|
|
236
|
+
<div className="msg-content text-[14px] leading-[1.7] text-text-2 break-words p-4 rounded-[14px] border border-white/[0.06] bg-surface">
|
|
237
|
+
<ReactMarkdown remarkPlugins={[remarkGfm]}>{editing.description}</ReactMarkdown>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
)}
|
|
241
|
+
|
|
242
|
+
{/* Agent */}
|
|
243
|
+
{taskAgent && (
|
|
244
|
+
<div className="mb-8">
|
|
245
|
+
<SectionLabel>Agent</SectionLabel>
|
|
246
|
+
<div className="flex items-center gap-2.5 px-4 py-3 rounded-[14px] border border-white/[0.06] bg-surface">
|
|
247
|
+
<AgentAvatar seed={taskAgent.avatarSeed || null} name={taskAgent.name} size={24} />
|
|
248
|
+
<span className="text-[14px] font-600 text-text">{taskAgent.name}</span>
|
|
249
|
+
</div>
|
|
250
|
+
</div>
|
|
251
|
+
)}
|
|
252
|
+
|
|
253
|
+
{/* Project */}
|
|
254
|
+
{taskProject && (
|
|
255
|
+
<div className="mb-8">
|
|
256
|
+
<SectionLabel>Project</SectionLabel>
|
|
257
|
+
<span className="inline-flex items-center gap-2 px-3 py-2 rounded-[10px] border border-white/[0.06] bg-surface text-[13px] font-600 text-text-2">
|
|
258
|
+
<span className="w-2.5 h-2.5 rounded-full shrink-0" style={{ backgroundColor: taskProject.color || '#6366F1' }} />
|
|
259
|
+
{taskProject.name}
|
|
260
|
+
</span>
|
|
261
|
+
</div>
|
|
262
|
+
)}
|
|
263
|
+
|
|
264
|
+
{/* Directory / File */}
|
|
265
|
+
{(editing.cwd || editing.file) && (
|
|
266
|
+
<div className="mb-8">
|
|
267
|
+
<SectionLabel>{editing.file ? 'File' : 'Directory'}</SectionLabel>
|
|
268
|
+
<code className="block px-4 py-3 rounded-[14px] border border-white/[0.06] bg-surface text-[13px] text-text-2 font-mono break-all">
|
|
269
|
+
{editing.file || editing.cwd}
|
|
270
|
+
</code>
|
|
271
|
+
</div>
|
|
272
|
+
)}
|
|
273
|
+
|
|
274
|
+
{/* Tags */}
|
|
275
|
+
{editing.tags && editing.tags.length > 0 && (
|
|
276
|
+
<div className="mb-8">
|
|
277
|
+
<SectionLabel>Tags</SectionLabel>
|
|
278
|
+
<div className="flex flex-wrap gap-1.5">
|
|
279
|
+
{editing.tags.map((tag) => (
|
|
280
|
+
<span key={tag} className="px-2.5 py-1 rounded-[8px] bg-indigo-500/10 text-indigo-400 text-[12px] font-600">
|
|
281
|
+
{tag}
|
|
282
|
+
</span>
|
|
283
|
+
))}
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
)}
|
|
287
|
+
|
|
288
|
+
{/* Blocked By */}
|
|
289
|
+
{editing.blockedBy && editing.blockedBy.length > 0 && (
|
|
290
|
+
<div className="mb-8">
|
|
291
|
+
<SectionLabel>Blocked By</SectionLabel>
|
|
292
|
+
<div className="flex flex-wrap gap-1.5">
|
|
293
|
+
{editing.blockedBy.map((bid) => {
|
|
294
|
+
const bt = tasks[bid]
|
|
295
|
+
return (
|
|
296
|
+
<span key={bid} className="px-2.5 py-1 rounded-[8px] bg-white/[0.04] text-text-3 text-[12px] font-600">
|
|
297
|
+
{bt ? bt.title : bid}
|
|
298
|
+
</span>
|
|
299
|
+
)
|
|
300
|
+
})}
|
|
301
|
+
</div>
|
|
302
|
+
</div>
|
|
303
|
+
)}
|
|
304
|
+
|
|
305
|
+
{/* Blocks */}
|
|
306
|
+
{editing.blocks && editing.blocks.length > 0 && (
|
|
307
|
+
<div className="mb-8">
|
|
308
|
+
<SectionLabel>Blocks</SectionLabel>
|
|
309
|
+
<div className="flex flex-wrap gap-1.5">
|
|
310
|
+
{editing.blocks.map((bid) => {
|
|
311
|
+
const bt = tasks[bid]
|
|
312
|
+
return bt ? (
|
|
313
|
+
<span key={bid} className="px-2.5 py-1 rounded-[8px] bg-white/[0.04] text-text-3 text-[12px] font-600">{bt.title}</span>
|
|
314
|
+
) : null
|
|
315
|
+
})}
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
)}
|
|
319
|
+
|
|
320
|
+
{/* Due Date */}
|
|
321
|
+
{editing.dueAt && (
|
|
322
|
+
<div className="mb-8">
|
|
323
|
+
<SectionLabel>Due Date</SectionLabel>
|
|
324
|
+
<span className="text-[14px] text-text-2">{new Date(editing.dueAt).toLocaleDateString([], { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' })}</span>
|
|
325
|
+
</div>
|
|
326
|
+
)}
|
|
327
|
+
|
|
328
|
+
{/* Custom Fields */}
|
|
329
|
+
{editing.customFields && Object.keys(editing.customFields).length > 0 && (
|
|
330
|
+
<div className="mb-8">
|
|
331
|
+
<SectionLabel>Custom Fields</SectionLabel>
|
|
332
|
+
<div className="space-y-2">
|
|
333
|
+
{Object.entries(editing.customFields).map(([key, val]) => {
|
|
334
|
+
const def = appSettings.taskCustomFieldDefs?.find((d) => d.key === key)
|
|
335
|
+
return (
|
|
336
|
+
<div key={key} className="flex items-baseline gap-2">
|
|
337
|
+
<span className="text-[12px] font-600 text-text-3">{def?.label || key}:</span>
|
|
338
|
+
<span className="text-[13px] text-text-2">{String(val)}</span>
|
|
339
|
+
</div>
|
|
340
|
+
)
|
|
341
|
+
})}
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
)}
|
|
345
|
+
|
|
346
|
+
{/* Images (thumbnails only, no remove/upload) */}
|
|
347
|
+
{editing.images && editing.images.length > 0 && (
|
|
348
|
+
<div className="mb-8">
|
|
349
|
+
<SectionLabel>Images</SectionLabel>
|
|
350
|
+
<div className="flex gap-2 flex-wrap">
|
|
351
|
+
{editing.images.map((url, i) => (
|
|
352
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
353
|
+
<img key={i} src={url} alt="" className="w-20 h-20 rounded-[10px] object-cover border border-white/[0.08]" />
|
|
354
|
+
))}
|
|
355
|
+
</div>
|
|
356
|
+
</div>
|
|
357
|
+
)}
|
|
358
|
+
|
|
359
|
+
{/* Result */}
|
|
360
|
+
{editing.result && (
|
|
361
|
+
<div className="mb-8">
|
|
362
|
+
<SectionLabel>Result</SectionLabel>
|
|
363
|
+
<div className="p-4 rounded-[14px] border border-white/[0.06] bg-surface text-[13px] text-text-2 whitespace-pre-wrap max-h-[200px] overflow-y-auto">
|
|
364
|
+
{editing.result}
|
|
365
|
+
</div>
|
|
366
|
+
</div>
|
|
367
|
+
)}
|
|
368
|
+
|
|
369
|
+
{/* CLI Sessions */}
|
|
370
|
+
{(editing.claudeResumeId || editing.codexResumeId || editing.opencodeResumeId || editing.cliResumeId) && (
|
|
371
|
+
<div className="mb-8">
|
|
372
|
+
<SectionLabel>CLI Sessions</SectionLabel>
|
|
373
|
+
<div className="flex flex-wrap gap-2">
|
|
374
|
+
{editing.claudeResumeId && (
|
|
375
|
+
<div className="flex items-center gap-2 px-3 py-2 rounded-[10px] border border-white/[0.06] bg-surface">
|
|
376
|
+
<span className="text-[11px] font-600 text-amber-400">Claude</span>
|
|
377
|
+
<code className="text-[11px] text-text-3 font-mono">{editing.claudeResumeId}</code>
|
|
378
|
+
</div>
|
|
379
|
+
)}
|
|
380
|
+
{editing.codexResumeId && (
|
|
381
|
+
<div className="flex items-center gap-2 px-3 py-2 rounded-[10px] border border-white/[0.06] bg-surface">
|
|
382
|
+
<span className="text-[11px] font-600 text-emerald-400">Codex</span>
|
|
383
|
+
<code className="text-[11px] text-text-3 font-mono">{editing.codexResumeId}</code>
|
|
384
|
+
</div>
|
|
385
|
+
)}
|
|
386
|
+
{editing.opencodeResumeId && (
|
|
387
|
+
<div className="flex items-center gap-2 px-3 py-2 rounded-[10px] border border-white/[0.06] bg-surface">
|
|
388
|
+
<span className="text-[11px] font-600 text-sky-400">OpenCode</span>
|
|
389
|
+
<code className="text-[11px] text-text-3 font-mono">{editing.opencodeResumeId}</code>
|
|
390
|
+
</div>
|
|
391
|
+
)}
|
|
392
|
+
{!(editing.claudeResumeId || editing.codexResumeId || editing.opencodeResumeId) && editing.cliResumeId && (
|
|
393
|
+
<div className="flex items-center gap-2 px-3 py-2 rounded-[10px] border border-white/[0.06] bg-surface">
|
|
394
|
+
<span className="text-[11px] font-600 text-text-2">{editing.cliProvider || 'CLI'}</span>
|
|
395
|
+
<code className="text-[11px] text-text-3 font-mono">{editing.cliResumeId}</code>
|
|
396
|
+
</div>
|
|
397
|
+
)}
|
|
398
|
+
</div>
|
|
399
|
+
</div>
|
|
400
|
+
)}
|
|
401
|
+
|
|
402
|
+
{/* Error */}
|
|
403
|
+
{editing.error && (
|
|
404
|
+
<div className="mb-8">
|
|
405
|
+
<label className="block font-display text-[12px] font-600 text-red-400 uppercase tracking-[0.08em] mb-3">Error</label>
|
|
406
|
+
<div className="p-4 rounded-[14px] border border-red-500/10 bg-red-500/[0.03] text-[13px] text-red-400/80 whitespace-pre-wrap">
|
|
407
|
+
{editing.error}
|
|
408
|
+
</div>
|
|
409
|
+
</div>
|
|
410
|
+
)}
|
|
411
|
+
|
|
412
|
+
{/* Comments (with input — adding comments from view mode is useful) */}
|
|
413
|
+
<div className="mb-8">
|
|
414
|
+
<SectionLabel>Comments {editing.comments?.length ? `(${editing.comments.length})` : ''}</SectionLabel>
|
|
415
|
+
|
|
416
|
+
{editing.comments && editing.comments.length > 0 && (
|
|
417
|
+
<div className="space-y-3 mb-4 max-h-[300px] overflow-y-auto">
|
|
418
|
+
{editing.comments.map((c) => (
|
|
419
|
+
<div key={c.id} className="p-3.5 rounded-[12px] border border-white/[0.06] bg-surface">
|
|
420
|
+
<div className="flex items-center gap-2 mb-1.5">
|
|
421
|
+
<span className={`text-[12px] font-600 ${c.agentId ? 'text-accent-bright' : 'text-text-2'}`}>
|
|
422
|
+
{c.author}
|
|
423
|
+
</span>
|
|
424
|
+
<span className="text-[10px] text-text-3/50 font-mono">{fmtTime(c.createdAt)}</span>
|
|
425
|
+
</div>
|
|
426
|
+
<p className="text-[13px] text-text-2 leading-[1.5] whitespace-pre-wrap">{c.text}</p>
|
|
427
|
+
</div>
|
|
428
|
+
))}
|
|
429
|
+
</div>
|
|
430
|
+
)}
|
|
431
|
+
|
|
432
|
+
<div className="flex gap-2">
|
|
433
|
+
<input
|
|
434
|
+
type="text"
|
|
435
|
+
value={commentText}
|
|
436
|
+
onChange={(e) => setCommentText(e.target.value)}
|
|
437
|
+
placeholder="Add a comment..."
|
|
438
|
+
className={`${inputClass} flex-1`}
|
|
439
|
+
style={{ fontFamily: 'inherit' }}
|
|
440
|
+
onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleAddComment() } }}
|
|
441
|
+
/>
|
|
442
|
+
<button
|
|
443
|
+
onClick={handleAddComment}
|
|
444
|
+
disabled={!commentText.trim()}
|
|
445
|
+
className="px-4 py-3 rounded-[14px] border-none bg-accent-soft text-accent-bright text-[13px] font-600 cursor-pointer disabled:opacity-30 hover:brightness-110 transition-all shrink-0"
|
|
446
|
+
style={{ fontFamily: 'inherit' }}
|
|
447
|
+
>
|
|
448
|
+
Post
|
|
449
|
+
</button>
|
|
450
|
+
</div>
|
|
451
|
+
</div>
|
|
452
|
+
|
|
453
|
+
{/* Footer: Edit + Close */}
|
|
454
|
+
<div className="flex gap-3 pt-2 border-t border-white/[0.04]">
|
|
455
|
+
<button
|
|
456
|
+
onClick={onClose}
|
|
457
|
+
className="flex-1 py-3.5 rounded-[14px] border border-white/[0.08] bg-transparent text-text-2 text-[15px] font-600 cursor-pointer hover:bg-surface-2 transition-all"
|
|
458
|
+
style={{ fontFamily: 'inherit' }}
|
|
459
|
+
>
|
|
460
|
+
Close
|
|
461
|
+
</button>
|
|
462
|
+
<button
|
|
463
|
+
onClick={() => setViewOnly(false)}
|
|
464
|
+
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] transition-all shadow-[0_4px_20px_rgba(99,102,241,0.25)] hover:brightness-110"
|
|
465
|
+
style={{ fontFamily: 'inherit' }}
|
|
466
|
+
>
|
|
467
|
+
Edit
|
|
468
|
+
</button>
|
|
469
|
+
</div>
|
|
470
|
+
</BottomSheet>
|
|
471
|
+
)
|
|
472
|
+
}
|
|
163
473
|
|
|
474
|
+
/* ───── Edit / Create mode ───── */
|
|
164
475
|
return (
|
|
165
476
|
<BottomSheet open={open} onClose={onClose}>
|
|
166
|
-
<div className="mb-
|
|
477
|
+
<div className="mb-8">
|
|
167
478
|
<h2 className="font-display text-[28px] font-700 tracking-[-0.03em] mb-2">
|
|
168
479
|
{editing ? 'Edit Task' : 'New Task'}
|
|
169
480
|
</h2>
|
|
@@ -173,7 +484,7 @@ export function TaskSheet() {
|
|
|
173
484
|
</div>
|
|
174
485
|
|
|
175
486
|
<div className="mb-8">
|
|
176
|
-
<
|
|
487
|
+
<SectionLabel>Title</SectionLabel>
|
|
177
488
|
<input
|
|
178
489
|
type="text"
|
|
179
490
|
value={title}
|
|
@@ -185,22 +496,45 @@ export function TaskSheet() {
|
|
|
185
496
|
</div>
|
|
186
497
|
|
|
187
498
|
<div className="mb-8">
|
|
188
|
-
<
|
|
499
|
+
<SectionLabel>Description</SectionLabel>
|
|
189
500
|
<textarea
|
|
190
501
|
value={description}
|
|
191
502
|
onChange={(e) => setDescription(e.target.value)}
|
|
192
|
-
placeholder="Detailed task instructions
|
|
503
|
+
placeholder="Detailed task instructions... Use @AgentName to auto-assign"
|
|
193
504
|
rows={4}
|
|
194
505
|
className={`${inputClass} resize-y min-h-[100px]`}
|
|
195
506
|
style={{ fontFamily: 'inherit' }}
|
|
196
507
|
/>
|
|
197
508
|
</div>
|
|
198
509
|
|
|
510
|
+
{/* Priority */}
|
|
511
|
+
<div className="mb-8">
|
|
512
|
+
<SectionLabel>Priority <span className="normal-case tracking-normal font-normal text-text-3">(optional)</span></SectionLabel>
|
|
513
|
+
<div className="flex flex-wrap gap-2">
|
|
514
|
+
{([['', 'None', 'bg-surface border-white/[0.06] text-text-2'],
|
|
515
|
+
['low', 'Low', 'bg-sky-500/10 border-sky-500/20 text-sky-400'],
|
|
516
|
+
['medium', 'Medium', 'bg-amber-500/10 border-amber-500/20 text-amber-400'],
|
|
517
|
+
['high', 'High', 'bg-orange-500/10 border-orange-500/20 text-orange-400'],
|
|
518
|
+
['critical', 'Critical', 'bg-red-500/10 border-red-500/20 text-red-400'],
|
|
519
|
+
] as const).map(([val, label, cls]) => (
|
|
520
|
+
<button
|
|
521
|
+
key={val}
|
|
522
|
+
onClick={() => setPriority(val as typeof priority)}
|
|
523
|
+
className={`px-4 py-3 rounded-[12px] text-[14px] font-600 cursor-pointer transition-all border
|
|
524
|
+
${priority === val
|
|
525
|
+
? `${cls} ring-1 ring-current`
|
|
526
|
+
: 'bg-surface border-white/[0.06] text-text-2 hover:bg-surface-2'}`}
|
|
527
|
+
style={{ fontFamily: 'inherit' }}
|
|
528
|
+
>
|
|
529
|
+
{label}
|
|
530
|
+
</button>
|
|
531
|
+
))}
|
|
532
|
+
</div>
|
|
533
|
+
</div>
|
|
534
|
+
|
|
199
535
|
{/* Images */}
|
|
200
536
|
<div className="mb-8">
|
|
201
|
-
<
|
|
202
|
-
Images <span className="normal-case tracking-normal font-normal text-text-3">(optional — reference designs, mockups, etc.)</span>
|
|
203
|
-
</label>
|
|
537
|
+
<SectionLabel>Images <span className="normal-case tracking-normal font-normal text-text-3">(optional — reference designs, mockups, etc.)</span></SectionLabel>
|
|
204
538
|
{images.length > 0 && (
|
|
205
539
|
<div className="flex gap-2 flex-wrap mb-3">
|
|
206
540
|
{images.map((url, i) => (
|
|
@@ -229,33 +563,48 @@ export function TaskSheet() {
|
|
|
229
563
|
</div>
|
|
230
564
|
|
|
231
565
|
<div className="mb-8">
|
|
232
|
-
<
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
566
|
+
<SectionLabel>Agent</SectionLabel>
|
|
567
|
+
<AgentPickerList
|
|
568
|
+
agents={agentList}
|
|
569
|
+
selected={agentId}
|
|
570
|
+
onSelect={(id) => setAgentId(id)}
|
|
571
|
+
/>
|
|
572
|
+
</div>
|
|
573
|
+
|
|
574
|
+
{/* Project (optional) */}
|
|
575
|
+
<div className="mb-8">
|
|
576
|
+
<SectionLabel>Project <span className="normal-case tracking-normal font-normal text-text-3">(optional)</span></SectionLabel>
|
|
577
|
+
<div className="flex flex-wrap gap-2">
|
|
578
|
+
<button
|
|
579
|
+
onClick={() => setProjectId('')}
|
|
580
|
+
className={`px-4 py-3 rounded-[12px] text-[14px] font-600 cursor-pointer transition-all border
|
|
581
|
+
${!projectId
|
|
582
|
+
? 'bg-accent-soft border-accent-bright/25 text-accent-bright'
|
|
583
|
+
: 'bg-surface border-white/[0.06] text-text-2 hover:bg-surface-2'}`}
|
|
584
|
+
style={{ fontFamily: 'inherit' }}
|
|
585
|
+
>
|
|
586
|
+
None
|
|
587
|
+
</button>
|
|
588
|
+
{Object.values(projects).map((p) => (
|
|
589
|
+
<button
|
|
590
|
+
key={p.id}
|
|
591
|
+
onClick={() => setProjectId(p.id)}
|
|
592
|
+
className={`px-4 py-3 rounded-[12px] text-[14px] font-600 cursor-pointer transition-all border flex items-center gap-2
|
|
593
|
+
${projectId === p.id
|
|
594
|
+
? 'bg-accent-soft border-accent-bright/25 text-accent-bright'
|
|
595
|
+
: 'bg-surface border-white/[0.06] text-text-2 hover:bg-surface-2'}`}
|
|
596
|
+
style={{ fontFamily: 'inherit' }}
|
|
597
|
+
>
|
|
598
|
+
<span className="w-2.5 h-2.5 rounded-full shrink-0" style={{ backgroundColor: p.color || '#6366F1' }} />
|
|
599
|
+
{p.name}
|
|
600
|
+
</button>
|
|
601
|
+
))}
|
|
602
|
+
</div>
|
|
252
603
|
</div>
|
|
253
604
|
|
|
254
605
|
{/* Directory (optional) */}
|
|
255
606
|
<div className="mb-8">
|
|
256
|
-
<
|
|
257
|
-
Directory <span className="normal-case tracking-normal font-normal text-text-3">(optional — project to work in)</span>
|
|
258
|
-
</label>
|
|
607
|
+
<SectionLabel>Directory <span className="normal-case tracking-normal font-normal text-text-3">(optional — project to work in)</span></SectionLabel>
|
|
259
608
|
<DirBrowser
|
|
260
609
|
value={cwd || null}
|
|
261
610
|
file={file}
|
|
@@ -273,9 +622,7 @@ export function TaskSheet() {
|
|
|
273
622
|
|
|
274
623
|
{/* Tags */}
|
|
275
624
|
<div className="mb-8">
|
|
276
|
-
<
|
|
277
|
-
Tags <span className="normal-case tracking-normal font-normal text-text-3">(optional)</span>
|
|
278
|
-
</label>
|
|
625
|
+
<SectionLabel>Tags <span className="normal-case tracking-normal font-normal text-text-3">(optional)</span></SectionLabel>
|
|
279
626
|
{tags.length > 0 && (
|
|
280
627
|
<div className="flex flex-wrap gap-1.5 mb-3">
|
|
281
628
|
{tags.map((tag) => (
|
|
@@ -315,11 +662,10 @@ export function TaskSheet() {
|
|
|
315
662
|
|
|
316
663
|
{/* Dependencies */}
|
|
317
664
|
<div className="mb-8">
|
|
318
|
-
<
|
|
319
|
-
Blocked By <span className="normal-case tracking-normal font-normal text-text-3">(tasks that must complete first)</span>
|
|
320
|
-
</label>
|
|
665
|
+
<SectionLabel>Blocked By <span className="normal-case tracking-normal font-normal text-text-3">(tasks that must complete first)</span></SectionLabel>
|
|
321
666
|
<select
|
|
322
667
|
multiple
|
|
668
|
+
aria-label="Assign agents"
|
|
323
669
|
value={blockedBy}
|
|
324
670
|
onChange={(e) => setBlockedBy(Array.from(e.target.selectedOptions, (o) => o.value))}
|
|
325
671
|
className="w-full px-4 py-3 rounded-[14px] border border-white/[0.08] bg-surface text-text text-[13px] outline-none min-h-[80px] focus-glow"
|
|
@@ -346,9 +692,7 @@ export function TaskSheet() {
|
|
|
346
692
|
|
|
347
693
|
{/* Due Date */}
|
|
348
694
|
<div className="mb-8">
|
|
349
|
-
<
|
|
350
|
-
Due Date <span className="normal-case tracking-normal font-normal text-text-3">(optional)</span>
|
|
351
|
-
</label>
|
|
695
|
+
<SectionLabel>Due Date <span className="normal-case tracking-normal font-normal text-text-3">(optional)</span></SectionLabel>
|
|
352
696
|
<input
|
|
353
697
|
type="date"
|
|
354
698
|
value={dueAt}
|
|
@@ -361,7 +705,7 @@ export function TaskSheet() {
|
|
|
361
705
|
{/* Custom Fields */}
|
|
362
706
|
{appSettings.taskCustomFieldDefs && appSettings.taskCustomFieldDefs.length > 0 && (
|
|
363
707
|
<div className="mb-8">
|
|
364
|
-
<
|
|
708
|
+
<SectionLabel>Custom Fields</SectionLabel>
|
|
365
709
|
<div className="space-y-4">
|
|
366
710
|
{appSettings.taskCustomFieldDefs.map((def) => (
|
|
367
711
|
<div key={def.key}>
|
|
@@ -396,7 +740,7 @@ export function TaskSheet() {
|
|
|
396
740
|
|
|
397
741
|
{editing?.result && (
|
|
398
742
|
<div className="mb-8">
|
|
399
|
-
<
|
|
743
|
+
<SectionLabel>Result</SectionLabel>
|
|
400
744
|
<div className="p-4 rounded-[14px] border border-white/[0.06] bg-surface text-[13px] text-text-2 whitespace-pre-wrap max-h-[200px] overflow-y-auto">
|
|
401
745
|
{editing.result}
|
|
402
746
|
</div>
|
|
@@ -405,7 +749,7 @@ export function TaskSheet() {
|
|
|
405
749
|
|
|
406
750
|
{editing && (editing.claudeResumeId || editing.codexResumeId || editing.opencodeResumeId || editing.cliResumeId) && (
|
|
407
751
|
<div className="mb-8">
|
|
408
|
-
<
|
|
752
|
+
<SectionLabel>CLI Sessions</SectionLabel>
|
|
409
753
|
<div className="flex flex-wrap gap-2">
|
|
410
754
|
{editing.claudeResumeId && (
|
|
411
755
|
<div className="flex items-center gap-2 px-3 py-2 rounded-[10px] border border-white/[0.06] bg-surface">
|
|
@@ -447,9 +791,7 @@ export function TaskSheet() {
|
|
|
447
791
|
{/* Comments */}
|
|
448
792
|
{editing && (
|
|
449
793
|
<div className="mb-8">
|
|
450
|
-
<
|
|
451
|
-
Comments {editing.comments?.length ? `(${editing.comments.length})` : ''}
|
|
452
|
-
</label>
|
|
794
|
+
<SectionLabel>Comments {editing.comments?.length ? `(${editing.comments.length})` : ''}</SectionLabel>
|
|
453
795
|
|
|
454
796
|
{editing.comments && editing.comments.length > 0 && (
|
|
455
797
|
<div className="space-y-3 mb-4 max-h-[300px] overflow-y-auto">
|
|
@@ -489,29 +831,29 @@ export function TaskSheet() {
|
|
|
489
831
|
</div>
|
|
490
832
|
)}
|
|
491
833
|
|
|
492
|
-
<
|
|
493
|
-
{
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
834
|
+
<SheetFooter
|
|
835
|
+
onCancel={onClose}
|
|
836
|
+
onSave={handleSave}
|
|
837
|
+
saveLabel={editing ? 'Save' : 'Create'}
|
|
838
|
+
saveDisabled={!title.trim() || !agentId}
|
|
839
|
+
left={<>
|
|
840
|
+
{editing && editing.status !== 'archived' && (
|
|
841
|
+
<button onClick={handleArchive} className="py-3.5 px-6 rounded-[14px] border border-white/[0.08] bg-transparent text-text-3 text-[15px] font-600 cursor-pointer hover:bg-white/[0.04] transition-all" style={{ fontFamily: 'inherit' }}>
|
|
842
|
+
Archive
|
|
843
|
+
</button>
|
|
844
|
+
)}
|
|
845
|
+
{editing && editing.status === 'archived' && (
|
|
846
|
+
<button onClick={handleUnarchive} className="py-3.5 px-6 rounded-[14px] border border-accent-bright/20 bg-transparent text-accent-bright text-[15px] font-600 cursor-pointer hover:bg-accent-bright/10 transition-all" style={{ fontFamily: 'inherit' }}>
|
|
847
|
+
Unarchive
|
|
848
|
+
</button>
|
|
849
|
+
)}
|
|
850
|
+
{editing && editing.status === 'backlog' && (
|
|
851
|
+
<button onClick={handleQueue} className="py-3.5 px-6 rounded-[14px] border border-amber-500/20 bg-transparent text-amber-400 text-[15px] font-600 cursor-pointer hover:bg-amber-500/10 transition-all" style={{ fontFamily: 'inherit' }}>
|
|
852
|
+
Queue
|
|
853
|
+
</button>
|
|
854
|
+
)}
|
|
855
|
+
</>}
|
|
856
|
+
/>
|
|
515
857
|
</BottomSheet>
|
|
516
858
|
)
|
|
517
859
|
}
|