@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
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useRef, useMemo } from 'react'
|
|
4
|
+
|
|
5
|
+
const CATEGORIES: Array<{ id: string; label: string; icon: string; emojis: string[] }> = [
|
|
6
|
+
{
|
|
7
|
+
id: 'frequent',
|
|
8
|
+
label: 'Frequently Used',
|
|
9
|
+
icon: '🕐',
|
|
10
|
+
emojis: ['👍', '❤️', '😂', '🔥', '🎉', '👀', '🚀', '✅', '💯', '🤔'],
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: 'smileys',
|
|
14
|
+
label: 'Smileys & People',
|
|
15
|
+
icon: '😀',
|
|
16
|
+
emojis: [
|
|
17
|
+
'😀', '😃', '😄', '😁', '😆', '😅', '🤣', '😂', '🙂', '😊',
|
|
18
|
+
'😇', '🥰', '😍', '🤩', '😘', '😗', '😚', '😙', '🥲', '😋',
|
|
19
|
+
'😛', '😜', '🤪', '😝', '🤑', '🤗', '🤭', '🫢', '🤫', '🤔',
|
|
20
|
+
'🫡', '🤐', '🤨', '😐', '😑', '😶', '🫥', '😏', '😒', '🙄',
|
|
21
|
+
'😬', '🤥', '🫨', '😌', '😔', '😪', '🤤', '😴', '😷', '🤒',
|
|
22
|
+
'🤕', '🤢', '🤮', '🥴', '😵', '🤯', '🥳', '🥸', '😎', '🤓',
|
|
23
|
+
'🧐', '😕', '🫤', '😟', '🙁', '😮', '😯', '😲', '😳', '🥺',
|
|
24
|
+
'🥹', '😦', '😧', '😨', '😰', '😥', '😢', '😭', '😱', '😖',
|
|
25
|
+
'😣', '😞', '😓', '😩', '😫', '🥱', '😤', '😡', '😠', '🤬',
|
|
26
|
+
'😈', '👿', '💀', '☠️', '💩', '🤡', '👹', '👺', '👻', '👽',
|
|
27
|
+
'🤖', '😺', '😸', '😹', '😻', '😼', '😽', '🙀', '😿', '😾',
|
|
28
|
+
'🙈', '🙉', '🙊', '👋', '🤚', '🖐️', '✋', '🖖', '🫱', '🫲',
|
|
29
|
+
'🫳', '🫴', '👌', '🤌', '🤏', '✌️', '🤞', '🫰', '🤟', '🤘',
|
|
30
|
+
'🤙', '👈', '👉', '👆', '🖕', '👇', '☝️', '🫵', '👍', '👎',
|
|
31
|
+
'✊', '👊', '🤛', '🤜', '👏', '🙌', '🫶', '👐', '🤲', '🤝',
|
|
32
|
+
'🙏', '✍️', '💪', '🦾', '🧠', '👀', '👁️', '👅', '👄', '🫦',
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'nature',
|
|
37
|
+
label: 'Animals & Nature',
|
|
38
|
+
icon: '🐶',
|
|
39
|
+
emojis: [
|
|
40
|
+
'🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼', '🐻❄️', '🐨',
|
|
41
|
+
'🐯', '🦁', '🐮', '🐷', '🐸', '🐵', '🐔', '🐧', '🐦', '🐤',
|
|
42
|
+
'🦆', '🦅', '🦉', '🦇', '🐺', '🐗', '🐴', '🦄', '🐝', '🪱',
|
|
43
|
+
'🐛', '🦋', '🐌', '🐞', '🐜', '🪲', '🪳', '🕷️', '🦂', '🐢',
|
|
44
|
+
'🐍', '🦎', '🐙', '🦑', '🦐', '🦞', '🦀', '🐡', '🐠', '🐟',
|
|
45
|
+
'🐬', '🐳', '🐋', '🦈', '🪸', '🐊', '🐅', '🐆', '🦓', '🦍',
|
|
46
|
+
'🐘', '🦛', '🦏', '🐪', '🐫', '🦒', '🦘', '🦬', '🐃', '🐂',
|
|
47
|
+
'🐄', '🐎', '🐖', '🐏', '🐑', '🦙', '🐐', '🦌', '🐕', '🐩',
|
|
48
|
+
'🌵', '🎄', '🌲', '🌳', '🌴', '🪵', '🌱', '🌿', '☘️', '🍀',
|
|
49
|
+
'🍁', '🍂', '🍃', '🪹', '🪺', '🌺', '🌻', '🌹', '🥀', '🌷',
|
|
50
|
+
'🌼', '🌸', '💐', '🍄', '🌰', '🎃', '🌍', '🌙', '⭐', '🌟',
|
|
51
|
+
'💫', '✨', '⚡', '☀️', '🌤️', '🌈', '☁️', '🌧️', '❄️', '🔥',
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: 'food',
|
|
56
|
+
label: 'Food & Drink',
|
|
57
|
+
icon: '🍕',
|
|
58
|
+
emojis: [
|
|
59
|
+
'🍎', '🍐', '🍊', '🍋', '🍌', '🍉', '🍇', '🍓', '🫐', '🍈',
|
|
60
|
+
'🍒', '🍑', '🥭', '🍍', '🥥', '🥝', '🍅', '🥑', '🍆', '🥦',
|
|
61
|
+
'🥬', '🥒', '🌶️', '🫑', '🌽', '🥕', '🧄', '🧅', '🥔', '🍠',
|
|
62
|
+
'🥐', '🍞', '🥖', '🥨', '🧀', '🥚', '🍳', '🥞', '🧇', '🥓',
|
|
63
|
+
'🥩', '🍗', '🍖', '🌭', '🍔', '🍟', '🍕', '🫓', '🥪', '🥙',
|
|
64
|
+
'🧆', '🌮', '🌯', '🫔', '🥗', '🍝', '🍜', '🍲', '🍛', '🍣',
|
|
65
|
+
'🍱', '🥟', '🍤', '🍙', '🍚', '🍘', '🍥', '🥠', '🥮', '🍡',
|
|
66
|
+
'🍧', '🍨', '🍦', '🥧', '🧁', '🍰', '🎂', '🍮', '🍭', '🍬',
|
|
67
|
+
'🍫', '🍿', '🧈', '🥤', '☕', '🍵', '🧃', '🧉', '🍶', '🍺',
|
|
68
|
+
'🍻', '🥂', '🍷', '🍸', '🍹', '🍾', '🧊', '🥄', '🍴', '🥢',
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'activity',
|
|
73
|
+
label: 'Activities',
|
|
74
|
+
icon: '⚽',
|
|
75
|
+
emojis: [
|
|
76
|
+
'⚽', '🏀', '🏈', '⚾', '🥎', '🎾', '🏐', '🏉', '🥏', '🎱',
|
|
77
|
+
'🏓', '🏸', '🏒', '🥊', '🥋', '🥅', '⛳', '⛸️', '🎣', '🤿',
|
|
78
|
+
'🎿', '🛷', '🥌', '🎯', '🪀', '🪁', '🎮', '🕹️', '🎰', '🧩',
|
|
79
|
+
'♟️', '🎲', '🎭', '🎨', '🎬', '🎤', '🎧', '🎼', '🎹', '🥁',
|
|
80
|
+
'🎷', '🎺', '🪗', '🎸', '🎻', '🎪', '🎫', '🎟️', '🏆', '🥇',
|
|
81
|
+
'🥈', '🥉', '🏅', '🎖️', '🏵️', '🎗️', '🎁', '🎀', '🎈', '🎊',
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: 'travel',
|
|
86
|
+
label: 'Travel & Places',
|
|
87
|
+
icon: '✈️',
|
|
88
|
+
emojis: [
|
|
89
|
+
'🚗', '🚕', '🚙', '🚌', '🚎', '🏎️', '🚓', '🚑', '🚒', '🚐',
|
|
90
|
+
'🛻', '🚚', '🚛', '🚜', '🏍️', '🛵', '🚲', '🛴', '🛺', '🚔',
|
|
91
|
+
'🚍', '🚘', '🚖', '✈️', '🚀', '🛸', '🚁', '🛶', '⛵', '🚢',
|
|
92
|
+
'🏠', '🏡', '🏘️', '🏢', '🏣', '🏥', '🏦', '🏪', '🏫', '🏩',
|
|
93
|
+
'💒', '🏛️', '⛪', '🕌', '🛕', '🕍', '⛩️', '🏰', '🏯', '🗼',
|
|
94
|
+
'🗽', '🗿', '🏟️', '🎡', '🎢', '🎠', '⛲', '⛱️', '🏖️', '🏝️',
|
|
95
|
+
'🏔️', '🗻', '🌋', '🏕️', '🛤️', '🛣️', '🌅', '🌄', '🌃', '🌉',
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: 'objects',
|
|
100
|
+
label: 'Objects',
|
|
101
|
+
icon: '💡',
|
|
102
|
+
emojis: [
|
|
103
|
+
'⌚', '📱', '💻', '⌨️', '🖥️', '🖨️', '🖱️', '🖲️', '💽', '💾',
|
|
104
|
+
'💿', '📀', '🎥', '📷', '📸', '📹', '📼', '🔍', '🔎', '🕯️',
|
|
105
|
+
'💡', '🔦', '🏮', '🪔', '📔', '📕', '📖', '📗', '📘', '📙',
|
|
106
|
+
'📚', '📓', '📒', '📃', '📜', '📄', '📰', '📑', '🔖', '💰',
|
|
107
|
+
'🪙', '💴', '💵', '💶', '💷', '💸', '💳', '✉️', '📧', '📨',
|
|
108
|
+
'📩', '📤', '📥', '📦', '📫', '📪', '📬', '📭', '📮', '🗳️',
|
|
109
|
+
'✏️', '✒️', '🖋️', '🖊️', '🖌️', '🖍️', '📝', '📁', '📂', '🗂️',
|
|
110
|
+
'📅', '📆', '📇', '📈', '📉', '📊', '📋', '📌', '📍', '📎',
|
|
111
|
+
'🔐', '🔑', '🗝️', '🔨', '🪓', '⛏️', '⚒️', '🛠️', '🗡️', '⚔️',
|
|
112
|
+
'🔧', '🪛', '🔩', '⚙️', '🗜️', '⚖️', '🦯', '🔗', '⛓️', '🪝',
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: 'symbols',
|
|
117
|
+
label: 'Symbols',
|
|
118
|
+
icon: '❤️',
|
|
119
|
+
emojis: [
|
|
120
|
+
'❤️', '🧡', '💛', '💚', '💙', '💜', '🖤', '🤍', '🤎', '💔',
|
|
121
|
+
'❤️🔥', '❤️🩹', '❣️', '💕', '💞', '💓', '💗', '💖', '💘', '💝',
|
|
122
|
+
'💟', '☮️', '✝️', '☪️', '🕉️', '☸️', '✡️', '🔯', '🕎', '☯️',
|
|
123
|
+
'♈', '♉', '♊', '♋', '♌', '♍', '♎', '♏', '♐', '♑',
|
|
124
|
+
'♒', '♓', '⛎', '🔀', '🔁', '🔂', '▶️', '⏩', '⏭️', '⏯️',
|
|
125
|
+
'◀️', '⏪', '⏮️', '🔼', '⏫', '🔽', '⏬', '⏸️', '⏹️', '⏺️',
|
|
126
|
+
'⏏️', '🎦', '🔅', '🔆', '📶', '🛜', '📳', '📴', '♀️', '♂️',
|
|
127
|
+
'⚧️', '✖️', '➕', '➖', '➗', '🟰', '♾️', '‼️', '⁉️', '❓',
|
|
128
|
+
'❔', '❕', '❗', '〰️', '💱', '💲', '⚕️', '♻️', '⚜️', '🔱',
|
|
129
|
+
'✔️', '☑️', '✅', '❌', '❎', '➰', '➿', '〽️', '✳️', '✴️',
|
|
130
|
+
'❇️', '©️', '®️', '™️', '#️⃣', '*️⃣', '0️⃣', '1️⃣', '2️⃣', '3️⃣',
|
|
131
|
+
'🔴', '🟠', '🟡', '🟢', '🔵', '🟣', '⚫', '⚪', '🟤', '🔶',
|
|
132
|
+
'🔷', '🔸', '🔹', '🔺', '🔻', '💠', '🔘', '🔳', '🔲', '🏁',
|
|
133
|
+
'🚩', '🎌', '🏴', '🏳️', '🏳️🌈', '🏳️⚧️', '🏴☠️', '🇺🇸', '🇬🇧', '🇯🇵',
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
interface Props {
|
|
139
|
+
onSelect: (emoji: string) => void
|
|
140
|
+
onClose: () => void
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function ReactionPicker({ onSelect, onClose }: Props) {
|
|
144
|
+
const ref = useRef<HTMLDivElement>(null)
|
|
145
|
+
const searchRef = useRef<HTMLInputElement>(null)
|
|
146
|
+
const [search, setSearch] = useState('')
|
|
147
|
+
const [activeCategory, setActiveCategory] = useState('frequent')
|
|
148
|
+
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
const handler = (e: MouseEvent) => {
|
|
151
|
+
if (ref.current && !ref.current.contains(e.target as Node)) {
|
|
152
|
+
onClose()
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
document.addEventListener('mousedown', handler)
|
|
156
|
+
return () => document.removeEventListener('mousedown', handler)
|
|
157
|
+
}, [onClose])
|
|
158
|
+
|
|
159
|
+
// Auto-focus search on open
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
setTimeout(() => searchRef.current?.focus(), 50)
|
|
162
|
+
}, [])
|
|
163
|
+
|
|
164
|
+
const filteredEmojis = useMemo(() => {
|
|
165
|
+
if (!search.trim()) return null
|
|
166
|
+
const q = search.toLowerCase()
|
|
167
|
+
// Simple search: match against category names or just return all emojis that are visible
|
|
168
|
+
const results: string[] = []
|
|
169
|
+
const seen = new Set<string>()
|
|
170
|
+
for (const cat of CATEGORIES) {
|
|
171
|
+
if (cat.id === 'frequent') continue
|
|
172
|
+
for (const emoji of cat.emojis) {
|
|
173
|
+
if (!seen.has(emoji)) {
|
|
174
|
+
seen.add(emoji)
|
|
175
|
+
results.push(emoji)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// For basic emoji search, filter by category label matching
|
|
180
|
+
// Since emoji don't have text names in this simple implementation,
|
|
181
|
+
// we filter categories that match and show all their emojis
|
|
182
|
+
const matchingCats = CATEGORIES.filter(
|
|
183
|
+
(c) => c.id !== 'frequent' && c.label.toLowerCase().includes(q)
|
|
184
|
+
)
|
|
185
|
+
if (matchingCats.length > 0) {
|
|
186
|
+
const catResults: string[] = []
|
|
187
|
+
const catSeen = new Set<string>()
|
|
188
|
+
for (const cat of matchingCats) {
|
|
189
|
+
for (const emoji of cat.emojis) {
|
|
190
|
+
if (!catSeen.has(emoji)) {
|
|
191
|
+
catSeen.add(emoji)
|
|
192
|
+
catResults.push(emoji)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return catResults
|
|
197
|
+
}
|
|
198
|
+
// If no category match, just return all emojis (user can visually scan)
|
|
199
|
+
return results
|
|
200
|
+
}, [search])
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<div
|
|
204
|
+
ref={ref}
|
|
205
|
+
className="absolute right-0 bottom-8 z-50 bg-[#13131e] border border-white/[0.1] rounded-[12px] shadow-[0_8px_40px_rgba(0,0,0,0.6)] w-[320px] flex flex-col overflow-hidden"
|
|
206
|
+
style={{ animation: 'msg-in 0.15s ease-out both' }}
|
|
207
|
+
>
|
|
208
|
+
{/* Search */}
|
|
209
|
+
<div className="px-3 pt-3 pb-2">
|
|
210
|
+
<input
|
|
211
|
+
ref={searchRef}
|
|
212
|
+
type="text"
|
|
213
|
+
value={search}
|
|
214
|
+
onChange={(e) => setSearch(e.target.value)}
|
|
215
|
+
placeholder="Search emoji..."
|
|
216
|
+
className="w-full px-2.5 py-1.5 rounded-[8px] bg-white/[0.06] border border-white/[0.08] text-[12px] text-text placeholder:text-text-3 focus:outline-none focus:border-accent-bright/40"
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
{/* Category tabs */}
|
|
221
|
+
{!search.trim() && (
|
|
222
|
+
<div className="flex px-2 gap-0.5 pb-1">
|
|
223
|
+
{CATEGORIES.map((cat) => (
|
|
224
|
+
<button
|
|
225
|
+
key={cat.id}
|
|
226
|
+
onClick={() => setActiveCategory(cat.id)}
|
|
227
|
+
title={cat.label}
|
|
228
|
+
className={`flex-1 py-1 flex items-center justify-center rounded-[6px] text-[14px] cursor-pointer transition-all ${
|
|
229
|
+
activeCategory === cat.id ? 'bg-white/[0.08]' : 'hover:bg-white/[0.04]'
|
|
230
|
+
}`}
|
|
231
|
+
>
|
|
232
|
+
{cat.icon}
|
|
233
|
+
</button>
|
|
234
|
+
))}
|
|
235
|
+
</div>
|
|
236
|
+
)}
|
|
237
|
+
|
|
238
|
+
{/* Emoji grid */}
|
|
239
|
+
<div className="px-2 pb-2 max-h-[220px] overflow-y-auto">
|
|
240
|
+
{search.trim() ? (
|
|
241
|
+
<div className="grid grid-cols-8 gap-0.5">
|
|
242
|
+
{filteredEmojis?.map((emoji, i) => (
|
|
243
|
+
<button
|
|
244
|
+
key={`${emoji}-${i}`}
|
|
245
|
+
onClick={() => onSelect(emoji)}
|
|
246
|
+
className="w-[34px] h-[34px] flex items-center justify-center rounded-[6px] hover:bg-white/[0.08] transition-all cursor-pointer text-[18px]"
|
|
247
|
+
>
|
|
248
|
+
{emoji}
|
|
249
|
+
</button>
|
|
250
|
+
))}
|
|
251
|
+
</div>
|
|
252
|
+
) : (
|
|
253
|
+
CATEGORIES.filter((c) => c.id === activeCategory).map((cat) => (
|
|
254
|
+
<div key={cat.id}>
|
|
255
|
+
<div className="text-[10px] font-600 text-text-3 uppercase tracking-wider px-1 py-1.5">{cat.label}</div>
|
|
256
|
+
<div className="grid grid-cols-8 gap-0.5">
|
|
257
|
+
{cat.emojis.map((emoji, i) => (
|
|
258
|
+
<button
|
|
259
|
+
key={`${emoji}-${i}`}
|
|
260
|
+
onClick={() => onSelect(emoji)}
|
|
261
|
+
className="w-[34px] h-[34px] flex items-center justify-center rounded-[6px] hover:bg-white/[0.08] transition-all cursor-pointer text-[18px]"
|
|
262
|
+
>
|
|
263
|
+
{emoji}
|
|
264
|
+
</button>
|
|
265
|
+
))}
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
))
|
|
269
|
+
)}
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
)
|
|
273
|
+
}
|
|
@@ -2,27 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
import { useCallback, useEffect, useState } from 'react'
|
|
4
4
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
|
+
import { useChatroomStore } from '@/stores/use-chatroom-store'
|
|
5
6
|
import { useWs } from '@/hooks/use-ws'
|
|
6
7
|
import { api } from '@/lib/api-client'
|
|
7
8
|
import type { Connector } from '@/types'
|
|
8
|
-
import { ConnectorPlatformBadge, getConnectorPlatformLabel } from '@/components/shared/connector-platform-icon'
|
|
9
|
+
import { ConnectorPlatformIcon, ConnectorPlatformBadge, CONNECTOR_PLATFORM_META, getConnectorPlatformLabel } from '@/components/shared/connector-platform-icon'
|
|
10
|
+
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
function relativeTime(ts: number): string {
|
|
13
|
+
const diff = Date.now() - ts
|
|
14
|
+
if (diff < 60_000) return 'just now'
|
|
15
|
+
if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m ago`
|
|
16
|
+
if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`
|
|
17
|
+
const d = new Date(ts)
|
|
18
|
+
return d.toLocaleDateString([], { month: 'short', day: 'numeric' })
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function ConnectorList({ inSidebar }: { inSidebar?: boolean }) {
|
|
12
22
|
const connectors = useAppStore((s) => s.connectors)
|
|
13
23
|
const loadConnectors = useAppStore((s) => s.loadConnectors)
|
|
14
24
|
const setConnectorSheetOpen = useAppStore((s) => s.setConnectorSheetOpen)
|
|
15
25
|
const setEditingConnectorId = useAppStore((s) => s.setEditingConnectorId)
|
|
16
26
|
const agents = useAppStore((s) => s.agents)
|
|
17
27
|
const loadAgents = useAppStore((s) => s.loadAgents)
|
|
28
|
+
const chatrooms = useChatroomStore((s) => s.chatrooms)
|
|
29
|
+
const loadChatrooms = useChatroomStore((s) => s.loadChatrooms)
|
|
18
30
|
const [toggling, setToggling] = useState<string | null>(null)
|
|
19
31
|
const [reconnecting, setReconnecting] = useState<string | null>(null)
|
|
20
32
|
const [loaded, setLoaded] = useState(false)
|
|
21
33
|
const [error, setError] = useState<string | null>(null)
|
|
22
34
|
|
|
23
35
|
const refresh = useCallback(async () => {
|
|
24
|
-
await Promise.all([loadConnectors(), loadAgents()])
|
|
36
|
+
await Promise.all([loadConnectors(), loadAgents(), loadChatrooms()])
|
|
25
37
|
setLoaded(true)
|
|
38
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
26
39
|
}, [loadConnectors, loadAgents])
|
|
27
40
|
|
|
28
41
|
useEffect(() => { void refresh() }, [refresh])
|
|
@@ -55,7 +68,6 @@ export function ConnectorList({ inSidebar: _inSidebar }: { inSidebar?: boolean }
|
|
|
55
68
|
setReconnecting(c.id)
|
|
56
69
|
setError(null)
|
|
57
70
|
try {
|
|
58
|
-
// Stop then start to reconnect
|
|
59
71
|
try { await api('PUT', `/connectors/${c.id}`, { action: 'stop' }) } catch { /* may already be stopped */ }
|
|
60
72
|
await api('PUT', `/connectors/${c.id}`, { action: 'start' })
|
|
61
73
|
await refresh()
|
|
@@ -92,104 +104,170 @@ export function ConnectorList({ inSidebar: _inSidebar }: { inSidebar?: boolean }
|
|
|
92
104
|
)
|
|
93
105
|
}
|
|
94
106
|
|
|
107
|
+
// Sidebar: compact list layout
|
|
108
|
+
if (inSidebar) {
|
|
109
|
+
return (
|
|
110
|
+
<div className="flex-1 overflow-y-auto pb-20">
|
|
111
|
+
{error && (
|
|
112
|
+
<div className="mx-4 mt-2 mb-1 px-3 py-2 rounded-[8px] bg-red-500/10 border border-red-500/20 text-red-400 text-[11px] leading-snug">
|
|
113
|
+
{error}
|
|
114
|
+
</div>
|
|
115
|
+
)}
|
|
116
|
+
{list.map((c) => {
|
|
117
|
+
const agent = c.agentId ? agents[c.agentId] : null
|
|
118
|
+
const chatroom = c.chatroomId ? chatrooms[c.chatroomId] : null
|
|
119
|
+
const isRunning = c.status === 'running'
|
|
120
|
+
const meta = CONNECTOR_PLATFORM_META[c.platform]
|
|
121
|
+
return (
|
|
122
|
+
<button
|
|
123
|
+
key={c.id}
|
|
124
|
+
onClick={() => { setEditingConnectorId(c.id); setConnectorSheetOpen(true) }}
|
|
125
|
+
className="w-full flex items-center gap-3 px-5 py-2.5 hover:bg-white/[0.02] transition-colors cursor-pointer bg-transparent border-none text-left"
|
|
126
|
+
>
|
|
127
|
+
<ConnectorPlatformIcon platform={c.platform} size={16} />
|
|
128
|
+
<div className="flex-1 min-w-0">
|
|
129
|
+
<span className="text-[13px] font-600 text-text truncate block">{c.name}</span>
|
|
130
|
+
<span className="text-[11px] text-text-3 truncate block">
|
|
131
|
+
{chatroom ? chatroom.name : agent?.name || meta?.label}
|
|
132
|
+
</span>
|
|
133
|
+
</div>
|
|
134
|
+
<span className={`shrink-0 w-2 h-2 rounded-full ${
|
|
135
|
+
isRunning ? 'bg-green-400' : c.status === 'error' ? 'bg-red-400' : 'bg-white/20'
|
|
136
|
+
}`} />
|
|
137
|
+
</button>
|
|
138
|
+
)
|
|
139
|
+
})}
|
|
140
|
+
</div>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Main view: card grid
|
|
95
145
|
return (
|
|
96
|
-
<div className="flex-1 overflow-y-auto pb-20">
|
|
146
|
+
<div className="flex-1 overflow-y-auto pb-20 px-5 pt-2">
|
|
97
147
|
{error && (
|
|
98
|
-
<div className="
|
|
148
|
+
<div className="mb-3 px-3 py-2 rounded-[8px] bg-red-500/10 border border-red-500/20 text-red-400 text-[11px] leading-snug">
|
|
99
149
|
{error}
|
|
100
150
|
</div>
|
|
101
151
|
)}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
>
|
|
118
|
-
{/* Clickable area — opens editor */}
|
|
152
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
|
153
|
+
{list.map((c) => {
|
|
154
|
+
const platformLabel = getConnectorPlatformLabel(c.platform)
|
|
155
|
+
const agent = c.agentId ? agents[c.agentId] : null
|
|
156
|
+
const chatroom = c.chatroomId ? chatrooms[c.chatroomId] : null
|
|
157
|
+
const isRunning = c.status === 'running'
|
|
158
|
+
const isToggling = toggling === c.id
|
|
159
|
+
const hasCredentials = c.platform === 'whatsapp'
|
|
160
|
+
|| c.platform === 'openclaw'
|
|
161
|
+
|| c.platform === 'signal'
|
|
162
|
+
|| (c.platform === 'bluebubbles' && (!!c.credentialId || !!c.config?.password))
|
|
163
|
+
|| !!c.credentialId
|
|
164
|
+
const lastMsg = c.presence?.lastMessageAt
|
|
165
|
+
|
|
166
|
+
return (
|
|
119
167
|
<button
|
|
168
|
+
key={c.id}
|
|
120
169
|
onClick={() => { setEditingConnectorId(c.id); setConnectorSheetOpen(true) }}
|
|
121
|
-
className="flex
|
|
170
|
+
className="group relative flex flex-col rounded-[14px] border border-white/[0.06] bg-surface p-4 cursor-pointer transition-all hover:border-white/[0.12] hover:bg-white/[0.02] text-left w-full"
|
|
171
|
+
style={{ fontFamily: 'inherit' }}
|
|
122
172
|
>
|
|
123
|
-
|
|
173
|
+
{/* Header: platform badge + status */}
|
|
174
|
+
<div className="flex items-center gap-3 mb-3">
|
|
175
|
+
<ConnectorPlatformBadge platform={c.platform} size={40} iconSize={20} roundedClassName="rounded-[10px]" />
|
|
176
|
+
<div className="flex-1 min-w-0">
|
|
177
|
+
<div className="flex items-center gap-2">
|
|
178
|
+
<span className="text-[14px] font-600 text-text truncate">{c.name}</span>
|
|
179
|
+
<span className={`shrink-0 w-2 h-2 rounded-full ${
|
|
180
|
+
isRunning ? 'bg-green-400' : c.status === 'error' ? 'bg-red-400' : 'bg-white/20'
|
|
181
|
+
}`} />
|
|
182
|
+
</div>
|
|
183
|
+
<span className="text-[11px] text-text-3 block">
|
|
184
|
+
{isRunning ? 'Connected' : c.status === 'error' ? 'Error' : 'Stopped'}
|
|
185
|
+
{c.qrDataUrl && ' · QR ready'}
|
|
186
|
+
</span>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
124
189
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
className=
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
190
|
+
{/* Route target: agent or chatroom */}
|
|
191
|
+
<div className="flex items-center gap-2.5 mb-2.5 px-0.5">
|
|
192
|
+
{chatroom ? (
|
|
193
|
+
<>
|
|
194
|
+
<div className="w-6 h-6 rounded-full bg-white/[0.06] flex items-center justify-center shrink-0">
|
|
195
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="text-text-3">
|
|
196
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
|
197
|
+
</svg>
|
|
198
|
+
</div>
|
|
199
|
+
<div className="flex-1 min-w-0">
|
|
200
|
+
<span className="text-[12px] font-600 text-text-2 block truncate">{chatroom.name}</span>
|
|
201
|
+
<span className="text-[10px] text-text-3/60 block">
|
|
202
|
+
{chatroom.agentIds.length} agent{chatroom.agentIds.length !== 1 ? 's' : ''}
|
|
203
|
+
{chatroom.chatMode === 'parallel' ? ' · parallel' : ' · sequential'}
|
|
204
|
+
</span>
|
|
205
|
+
</div>
|
|
206
|
+
</>
|
|
207
|
+
) : agent ? (
|
|
208
|
+
<>
|
|
209
|
+
<AgentAvatar seed={agent.avatarSeed || null} name={agent.name} size={24} />
|
|
210
|
+
<div className="flex-1 min-w-0">
|
|
211
|
+
<span className="text-[12px] font-600 text-text-2 block truncate">{agent.name}</span>
|
|
212
|
+
<span className="text-[10px] text-text-3/60 block">{agent.provider}/{agent.model}</span>
|
|
213
|
+
</div>
|
|
214
|
+
</>
|
|
215
|
+
) : (
|
|
216
|
+
<span className="text-[11px] text-text-3/50">{platformLabel}</span>
|
|
217
|
+
)}
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
{/* Footer: last message time + error */}
|
|
221
|
+
<div className="flex items-center gap-2 mt-auto pt-2 border-t border-white/[0.04]">
|
|
222
|
+
{c.lastError ? (
|
|
223
|
+
<span className="text-[10px] text-red-400 truncate flex-1">
|
|
224
|
+
{c.lastError.slice(0, 50)}{c.lastError.length > 50 ? '...' : ''}
|
|
225
|
+
</span>
|
|
226
|
+
) : lastMsg ? (
|
|
227
|
+
<span className="text-[10px] text-text-3/60 flex-1">Last message {relativeTime(lastMsg)}</span>
|
|
228
|
+
) : (
|
|
229
|
+
<span className="text-[10px] text-text-3/40 flex-1">No messages yet</span>
|
|
230
|
+
)}
|
|
231
|
+
|
|
232
|
+
{/* Action buttons */}
|
|
233
|
+
<div className="flex gap-1 shrink-0" onClick={(e) => e.stopPropagation()}>
|
|
234
|
+
{c.status === 'error' && hasCredentials && (
|
|
235
|
+
<button
|
|
236
|
+
onClick={(e) => handleReconnect(e, c)}
|
|
237
|
+
disabled={reconnecting === c.id}
|
|
238
|
+
title="Reconnect"
|
|
239
|
+
className="px-2 py-1 rounded-[6px] text-[10px] font-600 transition-all cursor-pointer border-none opacity-0 group-hover:opacity-100 bg-amber-500/10 text-amber-400 hover:bg-amber-500/20 disabled:opacity-50"
|
|
240
|
+
>
|
|
241
|
+
{reconnecting === c.id ? '...' : 'Reconnect'}
|
|
242
|
+
</button>
|
|
243
|
+
)}
|
|
244
|
+
{hasCredentials && (
|
|
245
|
+
<button
|
|
246
|
+
onClick={(e) => handleToggle(e, c)}
|
|
247
|
+
disabled={isToggling}
|
|
248
|
+
title={isRunning ? 'Stop' : 'Start'}
|
|
249
|
+
className={`w-7 h-7 rounded-[6px] flex items-center justify-center transition-all cursor-pointer border-none ${
|
|
250
|
+
isToggling ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'
|
|
251
|
+
} ${isRunning
|
|
252
|
+
? 'bg-red-500/10 text-red-400 hover:bg-red-500/20'
|
|
253
|
+
: 'bg-green-500/10 text-green-400 hover:bg-green-500/20'
|
|
254
|
+
} disabled:opacity-50`}
|
|
255
|
+
>
|
|
256
|
+
{isToggling ? (
|
|
257
|
+
<span className="w-3 h-3 rounded-full border-2 border-current border-t-transparent animate-spin" />
|
|
258
|
+
) : isRunning ? (
|
|
259
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor"><rect x="4" y="4" width="16" height="16" rx="2" /></svg>
|
|
260
|
+
) : (
|
|
261
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor"><polygon points="6,3 21,12 6,21" /></svg>
|
|
262
|
+
)}
|
|
263
|
+
</button>
|
|
138
264
|
)}
|
|
139
|
-
</div>
|
|
140
|
-
<div className="text-[11px] text-text-3 truncate">
|
|
141
|
-
{c.lastError
|
|
142
|
-
? <span className="text-red-400">{c.lastError.slice(0, 60)}{c.lastError.length > 60 ? '...' : ''}</span>
|
|
143
|
-
: <>{platformLabel} {agent ? `\u2192 ${agent.name}` : ''}</>
|
|
144
|
-
}
|
|
145
265
|
</div>
|
|
146
266
|
</div>
|
|
147
267
|
</button>
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
<button
|
|
152
|
-
onClick={(e) => handleReconnect(e, c)}
|
|
153
|
-
disabled={reconnecting === c.id}
|
|
154
|
-
title="Reconnect"
|
|
155
|
-
aria-label="Reconnect connector"
|
|
156
|
-
className={`shrink-0 px-2.5 py-1.5 rounded-[8px] text-[11px] font-600 transition-all cursor-pointer border-none
|
|
157
|
-
${reconnecting === c.id ? 'opacity-50' : 'opacity-0 group-hover:opacity-100 focus:opacity-100'}
|
|
158
|
-
bg-amber-500/10 text-amber-400 hover:bg-amber-500/20`}
|
|
159
|
-
>
|
|
160
|
-
{reconnecting === c.id ? '...' : 'Reconnect'}
|
|
161
|
-
</button>
|
|
162
|
-
)}
|
|
163
|
-
|
|
164
|
-
{/* Toggle button — visible on hover, only if connector has credentials */}
|
|
165
|
-
{hasCredentials && <button
|
|
166
|
-
onClick={(e) => handleToggle(e, c)}
|
|
167
|
-
disabled={isToggling}
|
|
168
|
-
title={isRunning ? 'Stop connector' : 'Start connector'}
|
|
169
|
-
aria-label={isRunning ? 'Stop connector' : 'Start connector'}
|
|
170
|
-
className={`shrink-0 w-8 h-8 rounded-[8px] flex items-center justify-center transition-all cursor-pointer border-none ${
|
|
171
|
-
isToggling ? 'opacity-100' : 'opacity-0 group-hover:opacity-100 focus:opacity-100'
|
|
172
|
-
} ${
|
|
173
|
-
isRunning
|
|
174
|
-
? 'bg-red-500/10 text-red-400 hover:bg-red-500/20'
|
|
175
|
-
: 'bg-green-500/10 text-green-400 hover:bg-green-500/20'
|
|
176
|
-
} disabled:opacity-50`}
|
|
177
|
-
>
|
|
178
|
-
{isToggling ? (
|
|
179
|
-
<span className="w-3 h-3 rounded-full border-2 border-current border-t-transparent animate-spin" />
|
|
180
|
-
) : isRunning ? (
|
|
181
|
-
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">
|
|
182
|
-
<rect x="4" y="4" width="16" height="16" rx="2" />
|
|
183
|
-
</svg>
|
|
184
|
-
) : (
|
|
185
|
-
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">
|
|
186
|
-
<polygon points="6,3 21,12 6,21" />
|
|
187
|
-
</svg>
|
|
188
|
-
)}
|
|
189
|
-
</button>}
|
|
190
|
-
</div>
|
|
191
|
-
)
|
|
192
|
-
})}
|
|
268
|
+
)
|
|
269
|
+
})}
|
|
270
|
+
</div>
|
|
193
271
|
</div>
|
|
194
272
|
)
|
|
195
273
|
}
|