@swarmclawai/swarmclaw 0.5.3 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/README.md +53 -9
  2. package/bin/server-cmd.js +1 -0
  3. package/bin/swarmclaw.js +76 -16
  4. package/next.config.ts +11 -1
  5. package/package.json +5 -2
  6. package/scripts/postinstall.mjs +18 -0
  7. package/src/app/api/canvas/[sessionId]/route.ts +31 -0
  8. package/src/app/api/chatrooms/[id]/chat/route.ts +284 -0
  9. package/src/app/api/chatrooms/[id]/members/route.ts +82 -0
  10. package/src/app/api/chatrooms/[id]/pins/route.ts +39 -0
  11. package/src/app/api/chatrooms/[id]/reactions/route.ts +42 -0
  12. package/src/app/api/chatrooms/[id]/route.ts +84 -0
  13. package/src/app/api/chatrooms/route.ts +50 -0
  14. package/src/app/api/connectors/[id]/route.ts +1 -0
  15. package/src/app/api/connectors/route.ts +2 -1
  16. package/src/app/api/credentials/route.ts +2 -3
  17. package/src/app/api/files/open/route.ts +43 -0
  18. package/src/app/api/knowledge/[id]/route.ts +13 -2
  19. package/src/app/api/knowledge/route.ts +8 -1
  20. package/src/app/api/memory/route.ts +8 -0
  21. package/src/app/api/notifications/route.ts +4 -0
  22. package/src/app/api/orchestrator/run/route.ts +1 -1
  23. package/src/app/api/plugins/install/route.ts +2 -2
  24. package/src/app/api/search/route.ts +53 -1
  25. package/src/app/api/sessions/[id]/chat/route.ts +2 -0
  26. package/src/app/api/sessions/[id]/edit-resend/route.ts +1 -1
  27. package/src/app/api/sessions/[id]/fork/route.ts +1 -1
  28. package/src/app/api/sessions/[id]/messages/route.ts +70 -2
  29. package/src/app/api/sessions/[id]/route.ts +4 -0
  30. package/src/app/api/sessions/route.ts +3 -3
  31. package/src/app/api/settings/route.ts +9 -0
  32. package/src/app/api/setup/check-provider/route.ts +3 -16
  33. package/src/app/api/skills/[id]/route.ts +6 -0
  34. package/src/app/api/skills/route.ts +6 -0
  35. package/src/app/api/tasks/[id]/route.ts +12 -0
  36. package/src/app/api/tasks/bulk/route.ts +100 -0
  37. package/src/app/api/tasks/metrics/route.ts +101 -0
  38. package/src/app/api/tasks/route.ts +18 -2
  39. package/src/app/api/tts/route.ts +3 -2
  40. package/src/app/api/tts/stream/route.ts +3 -2
  41. package/src/app/api/uploads/[filename]/route.ts +19 -34
  42. package/src/app/api/uploads/route.ts +94 -0
  43. package/src/app/api/webhooks/[id]/route.ts +15 -1
  44. package/src/app/globals.css +63 -15
  45. package/src/app/page.tsx +142 -13
  46. package/src/cli/index.js +40 -1
  47. package/src/cli/index.test.js +30 -0
  48. package/src/cli/spec.js +42 -0
  49. package/src/components/agents/agent-avatar.tsx +57 -10
  50. package/src/components/agents/agent-card.tsx +50 -17
  51. package/src/components/agents/agent-chat-list.tsx +148 -12
  52. package/src/components/agents/agent-list.tsx +50 -19
  53. package/src/components/agents/agent-sheet.tsx +120 -65
  54. package/src/components/agents/inspector-panel.tsx +81 -6
  55. package/src/components/agents/openclaw-skills-panel.tsx +32 -3
  56. package/src/components/agents/personality-builder.tsx +42 -14
  57. package/src/components/agents/soul-library-picker.tsx +89 -0
  58. package/src/components/auth/access-key-gate.tsx +10 -3
  59. package/src/components/auth/setup-wizard.tsx +2 -2
  60. package/src/components/auth/user-picker.tsx +31 -3
  61. package/src/components/canvas/canvas-panel.tsx +96 -0
  62. package/src/components/chat/activity-moment.tsx +173 -0
  63. package/src/components/chat/chat-area.tsx +46 -22
  64. package/src/components/chat/chat-header.tsx +457 -286
  65. package/src/components/chat/chat-preview-panel.tsx +1 -2
  66. package/src/components/chat/chat-tool-toggles.tsx +1 -1
  67. package/src/components/chat/delegation-banner.tsx +371 -0
  68. package/src/components/chat/file-path-chip.tsx +146 -0
  69. package/src/components/chat/heartbeat-history-panel.tsx +269 -0
  70. package/src/components/chat/markdown-utils.ts +9 -0
  71. package/src/components/chat/message-bubble.tsx +356 -315
  72. package/src/components/chat/message-list.tsx +230 -8
  73. package/src/components/chat/streaming-bubble.tsx +104 -47
  74. package/src/components/chat/suggestions-bar.tsx +1 -1
  75. package/src/components/chat/thinking-indicator.tsx +72 -10
  76. package/src/components/chat/tool-call-bubble.tsx +111 -73
  77. package/src/components/chat/tool-request-banner.tsx +31 -7
  78. package/src/components/chat/transfer-agent-picker.tsx +63 -0
  79. package/src/components/chatrooms/agent-hover-card.tsx +124 -0
  80. package/src/components/chatrooms/chatroom-input.tsx +320 -0
  81. package/src/components/chatrooms/chatroom-list.tsx +130 -0
  82. package/src/components/chatrooms/chatroom-message.tsx +432 -0
  83. package/src/components/chatrooms/chatroom-sheet.tsx +215 -0
  84. package/src/components/chatrooms/chatroom-tool-request-banner.tsx +134 -0
  85. package/src/components/chatrooms/chatroom-typing-bar.tsx +88 -0
  86. package/src/components/chatrooms/chatroom-view.tsx +344 -0
  87. package/src/components/chatrooms/reaction-picker.tsx +273 -0
  88. package/src/components/connectors/connector-list.tsx +168 -90
  89. package/src/components/connectors/connector-sheet.tsx +95 -56
  90. package/src/components/home/home-view.tsx +501 -0
  91. package/src/components/input/chat-input.tsx +107 -43
  92. package/src/components/knowledge/knowledge-list.tsx +31 -1
  93. package/src/components/knowledge/knowledge-sheet.tsx +83 -2
  94. package/src/components/layout/app-layout.tsx +194 -97
  95. package/src/components/layout/update-banner.tsx +2 -2
  96. package/src/components/logs/log-list.tsx +2 -2
  97. package/src/components/mcp-servers/mcp-server-sheet.tsx +1 -1
  98. package/src/components/memory/memory-agent-list.tsx +143 -0
  99. package/src/components/memory/memory-browser.tsx +205 -0
  100. package/src/components/memory/memory-card.tsx +34 -7
  101. package/src/components/memory/memory-detail.tsx +359 -120
  102. package/src/components/memory/memory-sheet.tsx +157 -23
  103. package/src/components/plugins/plugin-list.tsx +1 -1
  104. package/src/components/plugins/plugin-sheet.tsx +1 -1
  105. package/src/components/projects/project-detail.tsx +509 -0
  106. package/src/components/projects/project-list.tsx +195 -59
  107. package/src/components/providers/provider-list.tsx +2 -2
  108. package/src/components/providers/provider-sheet.tsx +3 -3
  109. package/src/components/schedules/schedule-card.tsx +1 -1
  110. package/src/components/schedules/schedule-list.tsx +1 -1
  111. package/src/components/schedules/schedule-sheet.tsx +259 -126
  112. package/src/components/secrets/secret-sheet.tsx +47 -24
  113. package/src/components/secrets/secrets-list.tsx +18 -8
  114. package/src/components/sessions/new-session-sheet.tsx +33 -65
  115. package/src/components/sessions/session-card.tsx +45 -14
  116. package/src/components/sessions/session-list.tsx +35 -18
  117. package/src/components/settings/gateway-disconnect-overlay.tsx +80 -0
  118. package/src/components/shared/agent-picker-list.tsx +90 -0
  119. package/src/components/shared/agent-switch-dialog.tsx +156 -0
  120. package/src/components/shared/attachment-chip.tsx +165 -0
  121. package/src/components/shared/avatar.tsx +10 -1
  122. package/src/components/shared/chatroom-picker-list.tsx +61 -0
  123. package/src/components/shared/check-icon.tsx +12 -0
  124. package/src/components/shared/confirm-dialog.tsx +1 -1
  125. package/src/components/shared/connector-platform-icon.tsx +51 -4
  126. package/src/components/shared/empty-state.tsx +32 -0
  127. package/src/components/shared/file-preview.tsx +34 -0
  128. package/src/components/shared/form-styles.ts +2 -0
  129. package/src/components/shared/icon-button.tsx +16 -2
  130. package/src/components/shared/keyboard-shortcuts-dialog.tsx +116 -0
  131. package/src/components/shared/notification-center.tsx +44 -6
  132. package/src/components/shared/profile-sheet.tsx +115 -0
  133. package/src/components/shared/reply-quote.tsx +26 -0
  134. package/src/components/shared/search-dialog.tsx +31 -15
  135. package/src/components/shared/section-label.tsx +12 -0
  136. package/src/components/shared/settings/plugin-manager.tsx +1 -1
  137. package/src/components/shared/settings/section-embedding.tsx +48 -13
  138. package/src/components/shared/settings/section-orchestrator.tsx +46 -15
  139. package/src/components/shared/settings/section-providers.tsx +1 -1
  140. package/src/components/shared/settings/section-secrets.tsx +1 -1
  141. package/src/components/shared/settings/section-storage.tsx +206 -0
  142. package/src/components/shared/settings/section-theme.tsx +95 -0
  143. package/src/components/shared/settings/section-user-preferences.tsx +57 -0
  144. package/src/components/shared/settings/section-voice.tsx +42 -21
  145. package/src/components/shared/settings/section-web-search.tsx +30 -6
  146. package/src/components/shared/settings/settings-page.tsx +182 -27
  147. package/src/components/shared/settings/settings-sheet.tsx +9 -73
  148. package/src/components/shared/settings/storage-browser.tsx +259 -0
  149. package/src/components/shared/sheet-footer.tsx +33 -0
  150. package/src/components/skills/skill-list.tsx +61 -30
  151. package/src/components/skills/skill-sheet.tsx +81 -2
  152. package/src/components/tasks/task-board.tsx +448 -26
  153. package/src/components/tasks/task-card.tsx +59 -9
  154. package/src/components/tasks/task-column.tsx +62 -3
  155. package/src/components/tasks/task-list.tsx +12 -4
  156. package/src/components/tasks/task-sheet.tsx +416 -74
  157. package/src/components/ui/hover-card.tsx +52 -0
  158. package/src/components/usage/metrics-dashboard.tsx +90 -6
  159. package/src/components/usage/usage-list.tsx +1 -1
  160. package/src/components/webhooks/webhook-sheet.tsx +1 -1
  161. package/src/hooks/use-continuous-speech.ts +10 -4
  162. package/src/hooks/use-view-router.ts +69 -19
  163. package/src/hooks/use-voice-conversation.ts +53 -10
  164. package/src/hooks/use-ws.ts +4 -2
  165. package/src/instrumentation.ts +15 -1
  166. package/src/lib/chat.ts +2 -0
  167. package/src/lib/memory.ts +3 -0
  168. package/src/lib/providers/anthropic.ts +13 -7
  169. package/src/lib/providers/index.ts +1 -0
  170. package/src/lib/providers/openai.ts +13 -7
  171. package/src/lib/server/chat-execution.ts +75 -15
  172. package/src/lib/server/chatroom-helpers.ts +146 -0
  173. package/src/lib/server/connectors/manager.ts +229 -7
  174. package/src/lib/server/context-manager.ts +225 -13
  175. package/src/lib/server/create-notification.ts +14 -2
  176. package/src/lib/server/daemon-state.ts +157 -10
  177. package/src/lib/server/execution-log.ts +1 -0
  178. package/src/lib/server/heartbeat-service.ts +48 -6
  179. package/src/lib/server/heartbeat-wake.ts +110 -0
  180. package/src/lib/server/langgraph-checkpoint.ts +1 -0
  181. package/src/lib/server/main-agent-loop.ts +1 -1
  182. package/src/lib/server/memory-consolidation.ts +105 -0
  183. package/src/lib/server/memory-db.ts +183 -10
  184. package/src/lib/server/mime.ts +51 -0
  185. package/src/lib/server/openclaw-gateway.ts +9 -1
  186. package/src/lib/server/orchestrator-lg.ts +2 -0
  187. package/src/lib/server/orchestrator.ts +5 -2
  188. package/src/lib/server/playwright-proxy.mjs +2 -3
  189. package/src/lib/server/prompt-runtime-context.ts +53 -0
  190. package/src/lib/server/provider-health.ts +125 -0
  191. package/src/lib/server/queue.ts +56 -10
  192. package/src/lib/server/scheduler.ts +8 -0
  193. package/src/lib/server/session-run-manager.ts +4 -0
  194. package/src/lib/server/session-tools/canvas.ts +67 -0
  195. package/src/lib/server/session-tools/chatroom.ts +136 -0
  196. package/src/lib/server/session-tools/connector.ts +83 -9
  197. package/src/lib/server/session-tools/context-mgmt.ts +36 -18
  198. package/src/lib/server/session-tools/crud.ts +21 -0
  199. package/src/lib/server/session-tools/delegate.ts +68 -4
  200. package/src/lib/server/session-tools/git.ts +71 -0
  201. package/src/lib/server/session-tools/http.ts +57 -0
  202. package/src/lib/server/session-tools/index.ts +10 -0
  203. package/src/lib/server/session-tools/memory.ts +7 -1
  204. package/src/lib/server/session-tools/search-providers.ts +16 -8
  205. package/src/lib/server/session-tools/subagent.ts +106 -0
  206. package/src/lib/server/session-tools/web.ts +115 -4
  207. package/src/lib/server/storage.ts +53 -29
  208. package/src/lib/server/stream-agent-chat.ts +185 -57
  209. package/src/lib/server/system-events.ts +49 -0
  210. package/src/lib/server/task-mention.ts +41 -0
  211. package/src/lib/server/ws-hub.ts +11 -0
  212. package/src/lib/sessions.ts +10 -0
  213. package/src/lib/soul-library.ts +103 -0
  214. package/src/lib/soul-suggestions.ts +109 -0
  215. package/src/lib/task-dedupe.ts +26 -0
  216. package/src/lib/tasks.ts +4 -1
  217. package/src/lib/tool-definitions.ts +2 -0
  218. package/src/lib/tts.ts +2 -2
  219. package/src/lib/view-routes.ts +36 -1
  220. package/src/lib/ws-client.ts +14 -4
  221. package/src/stores/use-app-store.ts +41 -3
  222. package/src/stores/use-chat-store.ts +113 -5
  223. package/src/stores/use-chatroom-store.ts +276 -0
  224. package/src/types/index.ts +88 -4
@@ -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
- const payload: Partial<BoardTask> & { title: string; description: string; agentId: string } = {
93
- title: title.trim() || 'Untitled Task', description, agentId, images,
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 { /* ignore */ }
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 inputClass = "w-full px-4 py-3.5 rounded-[14px] border border-white/[0.08] bg-surface text-text text-[15px] outline-none transition-all duration-200 placeholder:text-text-3/50 focus-glow"
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-10">
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
- <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">Title</label>
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
- <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">Description</label>
499
+ <SectionLabel>Description</SectionLabel>
189
500
  <textarea
190
501
  value={description}
191
502
  onChange={(e) => setDescription(e.target.value)}
192
- placeholder="Detailed task instructions for the agent..."
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
- <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">
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
- <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">Agent</label>
233
- {agentList.length > 0 ? (
234
- <div className="flex flex-wrap gap-2">
235
- {agentList.map((p) => (
236
- <button
237
- key={p.id}
238
- onClick={() => setAgentId(p.id)}
239
- className={`px-4 py-3 rounded-[12px] text-[14px] font-600 cursor-pointer transition-all border
240
- ${agentId === p.id
241
- ? 'bg-accent-soft border-accent-bright/25 text-accent-bright'
242
- : 'bg-surface border-white/[0.06] text-text-2 hover:bg-surface-2'}`}
243
- style={{ fontFamily: 'inherit' }}
244
- >
245
- {p.name}
246
- </button>
247
- ))}
248
- </div>
249
- ) : (
250
- <p className="text-[13px] text-text-3">No agents configured. Create one in Agents first.</p>
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
- <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">
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
- <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">
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
- <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">
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
- <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">
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
- <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">Custom Fields</label>
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
- <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">Result</label>
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
- <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">CLI Sessions</label>
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
- <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">
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
- <div className="flex gap-3 pt-2 border-t border-white/[0.04]">
493
- {editing && editing.status !== 'archived' && (
494
- <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' }}>
495
- Archive
496
- </button>
497
- )}
498
- {editing && editing.status === 'archived' && (
499
- <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' }}>
500
- Unarchive
501
- </button>
502
- )}
503
- {editing && editing.status === 'backlog' && (
504
- <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' }}>
505
- Queue
506
- </button>
507
- )}
508
- <button onClick={onClose} 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" style={{ fontFamily: 'inherit' }}>
509
- Cancel
510
- </button>
511
- <button onClick={handleSave} disabled={!title.trim() || !agentId} className="flex-1 py-3.5 rounded-[14px] border-none bg-[#6366F1] 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" style={{ fontFamily: 'inherit' }}>
512
- {editing ? 'Save' : 'Create'}
513
- </button>
514
- </div>
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
  }