cowork-os 0.3.21 → 0.3.25
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 +372 -10
- package/connectors/README.md +20 -0
- package/connectors/asana-mcp/README.md +24 -0
- package/connectors/asana-mcp/dist/index.js +427 -0
- package/connectors/asana-mcp/package.json +15 -0
- package/connectors/asana-mcp/src/index.ts +553 -0
- package/connectors/asana-mcp/tsconfig.json +13 -0
- package/connectors/hubspot-mcp/README.md +35 -0
- package/connectors/hubspot-mcp/dist/index.js +454 -0
- package/connectors/hubspot-mcp/package.json +15 -0
- package/connectors/hubspot-mcp/src/index.ts +562 -0
- package/connectors/hubspot-mcp/tsconfig.json +13 -0
- package/connectors/jira-mcp/README.md +49 -0
- package/connectors/jira-mcp/dist/index.js +588 -0
- package/connectors/jira-mcp/package.json +15 -0
- package/connectors/jira-mcp/src/index.ts +711 -0
- package/connectors/jira-mcp/tsconfig.json +13 -0
- package/connectors/linear-mcp/README.md +22 -0
- package/connectors/linear-mcp/dist/index.js +402 -0
- package/connectors/linear-mcp/package.json +15 -0
- package/connectors/linear-mcp/src/index.ts +522 -0
- package/connectors/linear-mcp/tsconfig.json +13 -0
- package/connectors/okta-mcp/README.md +24 -0
- package/connectors/okta-mcp/dist/index.js +411 -0
- package/connectors/okta-mcp/package.json +15 -0
- package/connectors/okta-mcp/src/index.ts +520 -0
- package/connectors/okta-mcp/tsconfig.json +13 -0
- package/connectors/salesforce-mcp/README.md +47 -0
- package/connectors/salesforce-mcp/dist/index.js +584 -0
- package/connectors/salesforce-mcp/package.json +15 -0
- package/connectors/salesforce-mcp/src/index.ts +722 -0
- package/connectors/salesforce-mcp/tsconfig.json +13 -0
- package/connectors/servicenow-mcp/README.md +26 -0
- package/connectors/servicenow-mcp/dist/index.js +400 -0
- package/connectors/servicenow-mcp/package.json +15 -0
- package/connectors/servicenow-mcp/src/index.ts +500 -0
- package/connectors/servicenow-mcp/tsconfig.json +13 -0
- package/connectors/templates/mcp-connector/README.md +31 -0
- package/connectors/templates/mcp-connector/package.json +15 -0
- package/connectors/templates/mcp-connector/src/index.ts +330 -0
- package/connectors/templates/mcp-connector/tsconfig.json +13 -0
- package/connectors/zendesk-mcp/README.md +40 -0
- package/connectors/zendesk-mcp/dist/index.js +431 -0
- package/connectors/zendesk-mcp/package.json +15 -0
- package/connectors/zendesk-mcp/src/index.ts +543 -0
- package/connectors/zendesk-mcp/tsconfig.json +13 -0
- package/dist/electron/electron/agent/custom-skill-loader.js +31 -1
- package/dist/electron/electron/agent/daemon.js +189 -13
- package/dist/electron/electron/agent/executor.js +895 -78
- package/dist/electron/electron/agent/llm/anthropic-compatible-provider.js +177 -0
- package/dist/electron/electron/agent/llm/azure-openai-provider.js +328 -0
- package/dist/electron/electron/agent/llm/bedrock-provider.js +49 -9
- package/dist/electron/electron/agent/llm/github-copilot-provider.js +97 -0
- package/dist/electron/electron/agent/llm/groq-provider.js +33 -0
- package/dist/electron/electron/agent/llm/index.js +13 -1
- package/dist/electron/electron/agent/llm/kimi-provider.js +33 -0
- package/dist/electron/electron/agent/llm/openai-compatible-provider.js +116 -0
- package/dist/electron/electron/agent/llm/openai-compatible.js +111 -0
- package/dist/electron/electron/agent/llm/openai-oauth.js +2 -1
- package/dist/electron/electron/agent/llm/openrouter-provider.js +1 -1
- package/dist/electron/electron/agent/llm/provider-factory.js +350 -4
- package/dist/electron/electron/agent/llm/types.js +66 -1
- package/dist/electron/electron/agent/llm/xai-provider.js +33 -0
- package/dist/electron/electron/agent/search/provider-factory.js +38 -2
- package/dist/electron/electron/agent/tools/box-tools.js +231 -0
- package/dist/electron/electron/agent/tools/builtin-settings.js +28 -0
- package/dist/electron/electron/agent/tools/dropbox-tools.js +237 -0
- package/dist/electron/electron/agent/tools/file-tools.js +66 -3
- package/dist/electron/electron/agent/tools/google-drive-tools.js +227 -0
- package/dist/electron/electron/agent/tools/grep-tools.js +90 -10
- package/dist/electron/electron/agent/tools/image-tools.js +11 -1
- package/dist/electron/electron/agent/tools/notion-tools.js +312 -0
- package/dist/electron/electron/agent/tools/onedrive-tools.js +217 -0
- package/dist/electron/electron/agent/tools/registry.js +548 -10
- package/dist/electron/electron/agent/tools/search-tools.js +28 -10
- package/dist/electron/electron/agent/tools/sharepoint-tools.js +243 -0
- package/dist/electron/electron/agent/tools/shell-tools.js +12 -3
- package/dist/electron/electron/agent/tools/x-tools.js +1 -1
- package/dist/electron/electron/agents/agent-dispatch.js +63 -0
- package/dist/electron/electron/database/repositories.js +19 -5
- package/dist/electron/electron/database/schema.js +8 -0
- package/dist/electron/electron/gateway/channels/whatsapp.js +55 -0
- package/dist/electron/electron/gateway/index.js +75 -1
- package/dist/electron/electron/gateway/router.js +209 -154
- package/dist/electron/electron/ipc/canvas-handlers.js +5 -0
- package/dist/electron/electron/ipc/handlers.js +763 -267
- package/dist/electron/electron/main.js +63 -0
- package/dist/electron/electron/mcp/oauth/connector-oauth.js +333 -0
- package/dist/electron/electron/mcp/registry/MCPRegistryManager.js +503 -154
- package/dist/electron/electron/memory/MemoryService.js +2 -1
- package/dist/electron/electron/preload.js +78 -1
- package/dist/electron/electron/settings/appearance-manager.js +18 -1
- package/dist/electron/electron/settings/box-manager.js +54 -0
- package/dist/electron/electron/settings/dropbox-manager.js +54 -0
- package/dist/electron/electron/settings/google-drive-manager.js +54 -0
- package/dist/electron/electron/settings/notion-manager.js +56 -0
- package/dist/electron/electron/settings/onedrive-manager.js +54 -0
- package/dist/electron/electron/settings/sharepoint-manager.js +54 -0
- package/dist/electron/electron/utils/box-api.js +153 -0
- package/dist/electron/electron/utils/dropbox-api.js +144 -0
- package/dist/electron/electron/utils/env-migration.js +19 -0
- package/dist/electron/electron/utils/google-drive-api.js +152 -0
- package/dist/electron/electron/utils/notion-api.js +103 -0
- package/dist/electron/electron/utils/onedrive-api.js +113 -0
- package/dist/electron/electron/utils/sharepoint-api.js +109 -0
- package/dist/electron/electron/utils/validation.js +98 -3
- package/dist/electron/electron/utils/x-cli.js +1 -1
- package/dist/electron/shared/channelMessages.js +284 -3
- package/dist/electron/shared/llm-provider-catalog.js +198 -0
- package/dist/electron/shared/types.js +90 -1
- package/package.json +14 -3
- package/resources/skills/nano-banana-pro.json +4 -4
- package/resources/skills/openai-image-gen.json +3 -3
- package/resources/skills/scripts/gen.py +163 -0
- package/resources/skills/scripts/generate_image.py +91 -0
- package/src/electron/agent/custom-skill-loader.ts +34 -1
- package/src/electron/agent/daemon.ts +210 -14
- package/src/electron/agent/executor.ts +1124 -85
- package/src/electron/agent/llm/anthropic-compatible-provider.ts +214 -0
- package/src/electron/agent/llm/azure-openai-provider.ts +388 -0
- package/src/electron/agent/llm/bedrock-provider.ts +62 -9
- package/src/electron/agent/llm/github-copilot-provider.ts +117 -0
- package/src/electron/agent/llm/groq-provider.ts +39 -0
- package/src/electron/agent/llm/index.ts +6 -0
- package/src/electron/agent/llm/kimi-provider.ts +39 -0
- package/src/electron/agent/llm/openai-compatible-provider.ts +153 -0
- package/src/electron/agent/llm/openai-compatible.ts +133 -0
- package/src/electron/agent/llm/openai-oauth.ts +2 -1
- package/src/electron/agent/llm/openrouter-provider.ts +2 -1
- package/src/electron/agent/llm/provider-factory.ts +459 -6
- package/src/electron/agent/llm/types.ts +95 -1
- package/src/electron/agent/llm/xai-provider.ts +39 -0
- package/src/electron/agent/search/provider-factory.ts +43 -2
- package/src/electron/agent/tools/box-tools.ts +239 -0
- package/src/electron/agent/tools/builtin-settings.ts +36 -0
- package/src/electron/agent/tools/dropbox-tools.ts +237 -0
- package/src/electron/agent/tools/file-tools.ts +66 -3
- package/src/electron/agent/tools/gmail-tools.ts +240 -0
- package/src/electron/agent/tools/google-calendar-tools.ts +258 -0
- package/src/electron/agent/tools/google-drive-tools.ts +228 -0
- package/src/electron/agent/tools/grep-tools.ts +97 -12
- package/src/electron/agent/tools/image-tools.ts +11 -1
- package/src/electron/agent/tools/notion-tools.ts +330 -0
- package/src/electron/agent/tools/onedrive-tools.ts +217 -0
- package/src/electron/agent/tools/registry.ts +794 -10
- package/src/electron/agent/tools/search-tools.ts +29 -11
- package/src/electron/agent/tools/sharepoint-tools.ts +247 -0
- package/src/electron/agent/tools/shell-tools.ts +11 -3
- package/src/electron/agent/tools/x-tools.ts +1 -1
- package/src/electron/agents/agent-dispatch.ts +79 -0
- package/src/electron/database/SecureSettingsRepository.ts +7 -1
- package/src/electron/database/repositories.ts +58 -6
- package/src/electron/database/schema.ts +8 -0
- package/src/electron/gateway/channels/discord.ts +4 -0
- package/src/electron/gateway/channels/google-chat.ts +3 -0
- package/src/electron/gateway/channels/line.ts +3 -0
- package/src/electron/gateway/channels/matrix-client.ts +15 -0
- package/src/electron/gateway/channels/matrix.ts +31 -0
- package/src/electron/gateway/channels/mattermost.ts +3 -0
- package/src/electron/gateway/channels/signal.ts +3 -0
- package/src/electron/gateway/channels/slack.ts +9 -4
- package/src/electron/gateway/channels/teams.ts +4 -0
- package/src/electron/gateway/channels/telegram.ts +2 -0
- package/src/electron/gateway/channels/twitch.ts +2 -0
- package/src/electron/gateway/channels/types.ts +8 -0
- package/src/electron/gateway/channels/whatsapp.ts +66 -0
- package/src/electron/gateway/index.ts +95 -2
- package/src/electron/gateway/router.ts +231 -161
- package/src/electron/gateway/security.ts +21 -9
- package/src/electron/ipc/canvas-handlers.ts +10 -0
- package/src/electron/ipc/handlers.ts +848 -292
- package/src/electron/main.ts +35 -0
- package/src/electron/mcp/oauth/connector-oauth.ts +448 -0
- package/src/electron/mcp/registry/MCPRegistryManager.ts +343 -12
- package/src/electron/memory/MemoryService.ts +7 -1
- package/src/electron/preload.ts +200 -5
- package/src/electron/settings/appearance-manager.ts +20 -2
- package/src/electron/settings/box-manager.ts +58 -0
- package/src/electron/settings/dropbox-manager.ts +58 -0
- package/src/electron/settings/google-workspace-manager.ts +59 -0
- package/src/electron/settings/notion-manager.ts +60 -0
- package/src/electron/settings/onedrive-manager.ts +58 -0
- package/src/electron/settings/sharepoint-manager.ts +58 -0
- package/src/electron/utils/box-api.ts +184 -0
- package/src/electron/utils/dropbox-api.ts +171 -0
- package/src/electron/utils/env-migration.ts +22 -0
- package/src/electron/utils/gmail-api.ts +121 -0
- package/src/electron/utils/google-calendar-api.ts +115 -0
- package/src/electron/utils/google-workspace-api.ts +228 -0
- package/src/electron/utils/google-workspace-auth.ts +109 -0
- package/src/electron/utils/google-workspace-oauth.ts +232 -0
- package/src/electron/utils/notion-api.ts +126 -0
- package/src/electron/utils/onedrive-api.ts +137 -0
- package/src/electron/utils/sharepoint-api.ts +132 -0
- package/src/electron/utils/validation.ts +128 -1
- package/src/electron/utils/x-cli.ts +1 -1
- package/src/renderer/App.tsx +119 -8
- package/src/renderer/components/ActivityFeedItem.tsx +34 -17
- package/src/renderer/components/AgentWorkingStatePanel.tsx +7 -5
- package/src/renderer/components/AppearanceSettings.tsx +37 -2
- package/src/renderer/components/BlueBubblesSettings.tsx +18 -7
- package/src/renderer/components/BoxSettings.tsx +203 -0
- package/src/renderer/components/BrowserView.tsx +101 -0
- package/src/renderer/components/BuiltinToolsSettings.tsx +105 -0
- package/src/renderer/components/CanvasPreview.tsx +68 -1
- package/src/renderer/components/ConnectorEnvModal.tsx +116 -0
- package/src/renderer/components/ConnectorSetupModal.tsx +566 -0
- package/src/renderer/components/ConnectorsSettings.tsx +397 -0
- package/src/renderer/components/ControlPlaneSettings.tsx +2 -0
- package/src/renderer/components/DiscordSettings.tsx +18 -7
- package/src/renderer/components/DropboxSettings.tsx +202 -0
- package/src/renderer/components/EmailSettings.tsx +18 -7
- package/src/renderer/components/FileViewer.tsx +21 -13
- package/src/renderer/components/GoogleChatSettings.tsx +17 -7
- package/src/renderer/components/GoogleWorkspaceSettings.tsx +332 -0
- package/src/renderer/components/ImessageSettings.tsx +22 -11
- package/src/renderer/components/LineIcons.tsx +376 -0
- package/src/renderer/components/LineSettings.tsx +18 -7
- package/src/renderer/components/MCPSettings.tsx +56 -0
- package/src/renderer/components/MainContent.tsx +740 -76
- package/src/renderer/components/MatrixSettings.tsx +18 -7
- package/src/renderer/components/MattermostSettings.tsx +18 -7
- package/src/renderer/components/NodesSettings.tsx +58 -99
- package/src/renderer/components/NotificationPanel.tsx +25 -11
- package/src/renderer/components/NotionSettings.tsx +231 -0
- package/src/renderer/components/Onboarding/Onboarding.tsx +13 -1
- package/src/renderer/components/OnboardingModal.tsx +70 -1
- package/src/renderer/components/OneDriveSettings.tsx +212 -0
- package/src/renderer/components/RightPanel.tsx +141 -28
- package/src/renderer/components/ScheduledTasksSettings.tsx +10 -62
- package/src/renderer/components/SearchSettings.tsx +118 -114
- package/src/renderer/components/Settings.tsx +1425 -651
- package/src/renderer/components/SharePointSettings.tsx +224 -0
- package/src/renderer/components/Sidebar.tsx +94 -19
- package/src/renderer/components/SignalSettings.tsx +18 -7
- package/src/renderer/components/SkillHubBrowser.tsx +144 -185
- package/src/renderer/components/SlackSettings.tsx +18 -7
- package/src/renderer/components/TaskQuickActions.tsx +11 -6
- package/src/renderer/components/TaskTimeline.tsx +58 -26
- package/src/renderer/components/TeamsSettings.tsx +18 -7
- package/src/renderer/components/TelegramSettings.tsx +18 -7
- package/src/renderer/components/ThemeIcon.tsx +16 -0
- package/src/renderer/components/TwitchSettings.tsx +18 -7
- package/src/renderer/components/VoiceSettings.tsx +30 -74
- package/src/renderer/components/WhatsAppSettings.tsx +48 -37
- package/src/renderer/components/WorkingStateHistory.tsx +7 -5
- package/src/renderer/components/WorkspaceSelector.tsx +42 -13
- package/src/renderer/hooks/useOnboardingFlow.ts +21 -0
- package/src/renderer/styles/index.css +2333 -209
- package/src/shared/channelMessages.ts +367 -4
- package/src/shared/llm-provider-catalog.ts +217 -0
- package/src/shared/types.ts +251 -2
package/src/renderer/App.tsx
CHANGED
|
@@ -5,11 +5,12 @@ import { RightPanel } from './components/RightPanel';
|
|
|
5
5
|
import { Settings } from './components/Settings';
|
|
6
6
|
import { DisclaimerModal } from './components/DisclaimerModal';
|
|
7
7
|
import { Onboarding } from './components/Onboarding';
|
|
8
|
+
import { BrowserView } from './components/BrowserView';
|
|
8
9
|
// TaskQueuePanel moved to RightPanel
|
|
9
10
|
import { ToastContainer } from './components/Toast';
|
|
10
11
|
import { QuickTaskFAB } from './components/QuickTaskFAB';
|
|
11
12
|
import { NotificationPanel } from './components/NotificationPanel';
|
|
12
|
-
import { Task, Workspace, TaskEvent, LLMModelInfo, LLMProviderInfo, SuccessCriteria, UpdateInfo, ThemeMode, AccentColor, QueueStatus, ToastNotification } from '../shared/types';
|
|
13
|
+
import { Task, Workspace, TaskEvent, LLMModelInfo, LLMProviderInfo, SuccessCriteria, UpdateInfo, ThemeMode, VisualTheme, AccentColor, QueueStatus, ToastNotification, TEMP_WORKSPACE_ID } from '../shared/types';
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
// Helper to get effective theme based on system preference
|
|
@@ -20,14 +21,15 @@ function getEffectiveTheme(themeMode: ThemeMode): 'light' | 'dark' {
|
|
|
20
21
|
return themeMode;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
type AppView = 'main' | 'settings';
|
|
24
|
+
type AppView = 'main' | 'settings' | 'browser';
|
|
24
25
|
|
|
25
26
|
export function App() {
|
|
26
27
|
const [currentWorkspace, setCurrentWorkspace] = useState<Workspace | null>(null);
|
|
27
28
|
const [tasks, setTasks] = useState<Task[]>([]);
|
|
28
29
|
const [selectedTaskId, setSelectedTaskId] = useState<string | null>(null);
|
|
29
30
|
const [currentView, setCurrentView] = useState<AppView>('main');
|
|
30
|
-
const [
|
|
31
|
+
const [browserUrl, setBrowserUrl] = useState<string>('');
|
|
32
|
+
const [settingsTab, setSettingsTab] = useState<'appearance' | 'llm' | 'search' | 'telegram' | 'slack' | 'whatsapp' | 'teams' | 'x' | 'morechannels' | 'integrations' | 'updates' | 'guardrails' | 'queue' | 'skills' | 'scheduled' | 'voice' | 'missioncontrol'>('appearance');
|
|
31
33
|
const [events, setEvents] = useState<TaskEvent[]>([]);
|
|
32
34
|
|
|
33
35
|
// Model selection state
|
|
@@ -41,6 +43,7 @@ export function App() {
|
|
|
41
43
|
|
|
42
44
|
// Theme state (loaded from main process on mount)
|
|
43
45
|
const [themeMode, setThemeMode] = useState<ThemeMode>('dark');
|
|
46
|
+
const [visualTheme, setVisualTheme] = useState<VisualTheme>('terminal');
|
|
44
47
|
const [accentColor, setAccentColor] = useState<AccentColor>('cyan');
|
|
45
48
|
|
|
46
49
|
// Queue state
|
|
@@ -82,6 +85,11 @@ export function App() {
|
|
|
82
85
|
loadLLMConfig();
|
|
83
86
|
};
|
|
84
87
|
|
|
88
|
+
const handleOpenBrowserView = (url?: string) => {
|
|
89
|
+
setBrowserUrl(url || '');
|
|
90
|
+
setCurrentView('browser');
|
|
91
|
+
};
|
|
92
|
+
|
|
85
93
|
const handleShowOnboarding = () => {
|
|
86
94
|
// Reset onboarding state to show the wizard again
|
|
87
95
|
setOnboardingCompleted(false);
|
|
@@ -106,12 +114,22 @@ export function App() {
|
|
|
106
114
|
loadLLMConfig();
|
|
107
115
|
}, []);
|
|
108
116
|
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
const handler = () => {
|
|
119
|
+
setSettingsTab('llm');
|
|
120
|
+
setCurrentView('settings');
|
|
121
|
+
};
|
|
122
|
+
window.addEventListener('open-settings', handler as EventListener);
|
|
123
|
+
return () => window.removeEventListener('open-settings', handler as EventListener);
|
|
124
|
+
}, []);
|
|
125
|
+
|
|
109
126
|
// Load appearance settings on mount
|
|
110
127
|
useEffect(() => {
|
|
111
128
|
const loadAppearanceSettings = async () => {
|
|
112
129
|
try {
|
|
113
130
|
const settings = await window.electronAPI.getAppearanceSettings();
|
|
114
131
|
setThemeMode(settings.themeMode);
|
|
132
|
+
setVisualTheme(settings.visualTheme || 'terminal');
|
|
115
133
|
setAccentColor(settings.accentColor);
|
|
116
134
|
setDisclaimerAccepted(settings.disclaimerAccepted ?? false);
|
|
117
135
|
setOnboardingCompleted(settings.onboardingCompleted ?? false);
|
|
@@ -218,17 +236,23 @@ export function App() {
|
|
|
218
236
|
// Remove existing theme classes
|
|
219
237
|
root.classList.remove('theme-light', 'theme-dark');
|
|
220
238
|
|
|
221
|
-
// Apply theme
|
|
239
|
+
// Apply theme mode class
|
|
222
240
|
if (effectiveTheme === 'light') {
|
|
223
241
|
root.classList.add('theme-light');
|
|
224
242
|
}
|
|
243
|
+
// dark is default, no class needed unless specified otherwise by visual styles
|
|
244
|
+
|
|
245
|
+
// Remove existing visual theme classes
|
|
246
|
+
root.classList.remove('visual-terminal', 'visual-warm', 'visual-oblivion');
|
|
247
|
+
const resolvedVisualTheme = visualTheme === 'warm' ? 'oblivion' : visualTheme;
|
|
248
|
+
root.classList.add(`visual-${resolvedVisualTheme}`);
|
|
225
249
|
|
|
226
250
|
// Remove existing accent classes
|
|
227
|
-
root.classList.remove('accent-cyan', 'accent-blue', 'accent-purple', 'accent-pink', 'accent-rose', 'accent-orange', 'accent-green', 'accent-teal');
|
|
251
|
+
root.classList.remove('accent-cyan', 'accent-blue', 'accent-purple', 'accent-pink', 'accent-rose', 'accent-orange', 'accent-green', 'accent-teal', 'accent-coral');
|
|
228
252
|
|
|
229
253
|
// Apply accent class
|
|
230
254
|
root.classList.add(`accent-${accentColor}`);
|
|
231
|
-
}, [themeMode, accentColor]);
|
|
255
|
+
}, [themeMode, visualTheme, accentColor]);
|
|
232
256
|
|
|
233
257
|
// Listen for system theme changes when in 'system' mode
|
|
234
258
|
useEffect(() => {
|
|
@@ -277,6 +301,42 @@ export function App() {
|
|
|
277
301
|
}
|
|
278
302
|
}, [currentWorkspace]);
|
|
279
303
|
|
|
304
|
+
// Sync current workspace to the selected task's workspace
|
|
305
|
+
useEffect(() => {
|
|
306
|
+
if (!selectedTaskId) return;
|
|
307
|
+
const task = tasks.find(t => t.id === selectedTaskId);
|
|
308
|
+
if (!task) return;
|
|
309
|
+
if (currentWorkspace?.id === task.workspaceId) return;
|
|
310
|
+
|
|
311
|
+
let cancelled = false;
|
|
312
|
+
|
|
313
|
+
const loadTaskWorkspace = async () => {
|
|
314
|
+
try {
|
|
315
|
+
const resolved = task.workspaceId === TEMP_WORKSPACE_ID
|
|
316
|
+
? await window.electronAPI.getTempWorkspace()
|
|
317
|
+
: await window.electronAPI.selectWorkspace(task.workspaceId);
|
|
318
|
+
if (!cancelled && resolved) {
|
|
319
|
+
setCurrentWorkspace(resolved);
|
|
320
|
+
}
|
|
321
|
+
} catch (error) {
|
|
322
|
+
console.error('Failed to load task workspace:', error);
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
void loadTaskWorkspace();
|
|
327
|
+
return () => {
|
|
328
|
+
cancelled = true;
|
|
329
|
+
};
|
|
330
|
+
}, [selectedTaskId, tasks, currentWorkspace?.id]);
|
|
331
|
+
|
|
332
|
+
// Track recency when the active workspace changes
|
|
333
|
+
useEffect(() => {
|
|
334
|
+
if (!currentWorkspace || currentWorkspace.id === TEMP_WORKSPACE_ID) return;
|
|
335
|
+
window.electronAPI.touchWorkspace(currentWorkspace.id).catch((error: unknown) => {
|
|
336
|
+
console.error('Failed to update workspace recency:', error);
|
|
337
|
+
});
|
|
338
|
+
}, [currentWorkspace?.id]);
|
|
339
|
+
|
|
280
340
|
// Toast helper functions
|
|
281
341
|
const addToast = (toast: Omit<ToastNotification, 'id'>) => {
|
|
282
342
|
const id = `toast-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
@@ -334,6 +394,38 @@ export function App() {
|
|
|
334
394
|
void window.electronAPI.resumeTask(event.taskId);
|
|
335
395
|
}
|
|
336
396
|
|
|
397
|
+
if (event.type === 'task_paused' || event.type === 'approval_requested') {
|
|
398
|
+
const isApproval = event.type === 'approval_requested';
|
|
399
|
+
const task = tasksRef.current.find(t => t.id === event.taskId);
|
|
400
|
+
const baseTitle = isApproval ? 'Approval needed' : 'Input needed';
|
|
401
|
+
const title = task?.title ? `${baseTitle} · ${task.title}` : baseTitle;
|
|
402
|
+
const message =
|
|
403
|
+
(isApproval
|
|
404
|
+
? event.payload?.approval?.description
|
|
405
|
+
: event.payload?.message) || 'Awaiting your input.';
|
|
406
|
+
|
|
407
|
+
void (async () => {
|
|
408
|
+
try {
|
|
409
|
+
const existing = await window.electronAPI.listNotifications();
|
|
410
|
+
const hasExisting = existing.some((n) =>
|
|
411
|
+
n.type === 'input_required' &&
|
|
412
|
+
n.taskId === event.taskId &&
|
|
413
|
+
!n.read
|
|
414
|
+
);
|
|
415
|
+
if (hasExisting) return;
|
|
416
|
+
await window.electronAPI.addNotification({
|
|
417
|
+
type: 'input_required',
|
|
418
|
+
title,
|
|
419
|
+
message,
|
|
420
|
+
taskId: event.taskId,
|
|
421
|
+
workspaceId: task?.workspaceId,
|
|
422
|
+
});
|
|
423
|
+
} catch (error) {
|
|
424
|
+
console.error('Failed to add input-required notification:', error);
|
|
425
|
+
}
|
|
426
|
+
})();
|
|
427
|
+
}
|
|
428
|
+
|
|
337
429
|
// Show toast notifications for task completion/failure
|
|
338
430
|
if (event.type === 'task_completed') {
|
|
339
431
|
const task = tasksRef.current.find(t => t.id === event.taskId);
|
|
@@ -515,13 +607,19 @@ export function App() {
|
|
|
515
607
|
const handleThemeChange = (theme: ThemeMode) => {
|
|
516
608
|
setThemeMode(theme);
|
|
517
609
|
// Persist to main process
|
|
518
|
-
window.electronAPI.saveAppearanceSettings({ themeMode: theme, accentColor });
|
|
610
|
+
window.electronAPI.saveAppearanceSettings({ themeMode: theme, visualTheme, accentColor });
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
const handleVisualThemeChange = (visual: VisualTheme) => {
|
|
614
|
+
setVisualTheme(visual);
|
|
615
|
+
// Persist to main process
|
|
616
|
+
window.electronAPI.saveAppearanceSettings({ themeMode, visualTheme: visual, accentColor });
|
|
519
617
|
};
|
|
520
618
|
|
|
521
619
|
const handleAccentChange = (accent: AccentColor) => {
|
|
522
620
|
setAccentColor(accent);
|
|
523
621
|
// Persist to main process
|
|
524
|
-
window.electronAPI.saveAppearanceSettings({ themeMode, accentColor: accent });
|
|
622
|
+
window.electronAPI.saveAppearanceSettings({ themeMode, visualTheme, accentColor: accent });
|
|
525
623
|
};
|
|
526
624
|
|
|
527
625
|
// Show loading state while checking disclaimer/onboarding status
|
|
@@ -710,6 +808,10 @@ export function App() {
|
|
|
710
808
|
selectedTaskId={selectedTaskId}
|
|
711
809
|
onSelectTask={setSelectedTaskId}
|
|
712
810
|
onOpenSettings={() => setCurrentView('settings')}
|
|
811
|
+
onOpenMissionControl={() => {
|
|
812
|
+
setSettingsTab('missioncontrol');
|
|
813
|
+
setCurrentView('settings');
|
|
814
|
+
}}
|
|
713
815
|
onTasksChanged={loadTasks}
|
|
714
816
|
/>
|
|
715
817
|
)}
|
|
@@ -727,6 +829,7 @@ export function App() {
|
|
|
727
829
|
setCurrentView('settings');
|
|
728
830
|
}}
|
|
729
831
|
onStopTask={handleCancelTask}
|
|
832
|
+
onOpenBrowserView={handleOpenBrowserView}
|
|
730
833
|
selectedModel={selectedModel}
|
|
731
834
|
availableModels={availableModels}
|
|
732
835
|
onModelChange={handleModelChange}
|
|
@@ -762,14 +865,22 @@ export function App() {
|
|
|
762
865
|
onBack={() => setCurrentView('main')}
|
|
763
866
|
onSettingsChanged={loadLLMConfig}
|
|
764
867
|
themeMode={themeMode}
|
|
868
|
+
visualTheme={visualTheme}
|
|
765
869
|
accentColor={accentColor}
|
|
766
870
|
onThemeChange={handleThemeChange}
|
|
871
|
+
onVisualThemeChange={handleVisualThemeChange}
|
|
767
872
|
onAccentChange={handleAccentChange}
|
|
768
873
|
initialTab={settingsTab}
|
|
769
874
|
onShowOnboarding={handleShowOnboarding}
|
|
770
875
|
onboardingCompletedAt={onboardingCompletedAt}
|
|
771
876
|
/>
|
|
772
877
|
)}
|
|
878
|
+
{currentView === 'browser' && (
|
|
879
|
+
<BrowserView
|
|
880
|
+
initialUrl={browserUrl}
|
|
881
|
+
onBack={() => setCurrentView('main')}
|
|
882
|
+
/>
|
|
883
|
+
)}
|
|
773
884
|
</div>
|
|
774
885
|
);
|
|
775
886
|
}
|
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
import { ActivityData, ActivityType, ActivityActorType } from '../../electron/preload';
|
|
2
|
+
import { ThemeIcon } from './ThemeIcon';
|
|
3
|
+
import {
|
|
4
|
+
AlertTriangleIcon,
|
|
5
|
+
AtIcon,
|
|
6
|
+
BotIcon,
|
|
7
|
+
CheckIcon,
|
|
8
|
+
ClipboardIcon,
|
|
9
|
+
CodeIcon,
|
|
10
|
+
FileIcon,
|
|
11
|
+
InfoIcon,
|
|
12
|
+
MessageIcon,
|
|
13
|
+
PauseIcon,
|
|
14
|
+
PlayIcon,
|
|
15
|
+
SlidersIcon,
|
|
16
|
+
TrashIcon,
|
|
17
|
+
XIcon,
|
|
18
|
+
} from './LineIcons';
|
|
2
19
|
|
|
3
20
|
interface ActivityFeedItemProps {
|
|
4
21
|
activity: ActivityData;
|
|
@@ -8,23 +25,23 @@ interface ActivityFeedItemProps {
|
|
|
8
25
|
compact?: boolean;
|
|
9
26
|
}
|
|
10
27
|
|
|
11
|
-
const ACTIVITY_ICONS: Record<ActivityType,
|
|
12
|
-
task_created:
|
|
13
|
-
task_started:
|
|
14
|
-
task_completed:
|
|
15
|
-
task_failed:
|
|
16
|
-
task_paused:
|
|
17
|
-
task_resumed:
|
|
18
|
-
comment:
|
|
19
|
-
file_created:
|
|
20
|
-
file_modified:
|
|
21
|
-
file_deleted:
|
|
22
|
-
command_executed:
|
|
23
|
-
tool_used:
|
|
24
|
-
mention:
|
|
25
|
-
agent_assigned:
|
|
26
|
-
error:
|
|
27
|
-
info:
|
|
28
|
+
const ACTIVITY_ICONS: Record<ActivityType, React.ReactNode> = {
|
|
29
|
+
task_created: <ThemeIcon emoji="📋" icon={<ClipboardIcon size={16} />} />,
|
|
30
|
+
task_started: <ThemeIcon emoji="▶️" icon={<PlayIcon size={16} />} />,
|
|
31
|
+
task_completed: <ThemeIcon emoji="✅" icon={<CheckIcon size={16} />} />,
|
|
32
|
+
task_failed: <ThemeIcon emoji="❌" icon={<XIcon size={16} />} />,
|
|
33
|
+
task_paused: <ThemeIcon emoji="⏸️" icon={<PauseIcon size={16} />} />,
|
|
34
|
+
task_resumed: <ThemeIcon emoji="▶️" icon={<PlayIcon size={16} />} />,
|
|
35
|
+
comment: <ThemeIcon emoji="💬" icon={<MessageIcon size={16} />} />,
|
|
36
|
+
file_created: <ThemeIcon emoji="📄" icon={<FileIcon size={16} />} />,
|
|
37
|
+
file_modified: <ThemeIcon emoji="✏️" icon={<FileIcon size={16} />} />,
|
|
38
|
+
file_deleted: <ThemeIcon emoji="🗑️" icon={<TrashIcon size={16} />} />,
|
|
39
|
+
command_executed: <ThemeIcon emoji="💻" icon={<CodeIcon size={16} />} />,
|
|
40
|
+
tool_used: <ThemeIcon emoji="🔧" icon={<SlidersIcon size={16} />} />,
|
|
41
|
+
mention: <ThemeIcon emoji="@" icon={<AtIcon size={16} />} />,
|
|
42
|
+
agent_assigned: <ThemeIcon emoji="🤖" icon={<BotIcon size={16} />} />,
|
|
43
|
+
error: <ThemeIcon emoji="⚠️" icon={<AlertTriangleIcon size={16} />} />,
|
|
44
|
+
info: <ThemeIcon emoji="ℹ️" icon={<InfoIcon size={16} />} />,
|
|
28
45
|
};
|
|
29
46
|
|
|
30
47
|
const ACTIVITY_COLORS: Record<ActivityType, string> = {
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
AgentRoleData,
|
|
6
6
|
} from '../../electron/preload';
|
|
7
7
|
import { useAgentContext } from '../hooks/useAgentContext';
|
|
8
|
+
import { ThemeIcon } from './ThemeIcon';
|
|
9
|
+
import { ChartIcon, ClipboardIcon, EditIcon, TargetIcon } from './LineIcons';
|
|
8
10
|
|
|
9
11
|
interface AgentWorkingStatePanelProps {
|
|
10
12
|
agentRoleId: string;
|
|
@@ -13,25 +15,25 @@ interface AgentWorkingStatePanelProps {
|
|
|
13
15
|
onEdit?: (state: AgentWorkingStateData) => void;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
const STATE_TYPE_LABELS: Record<WorkingStateType, { label: string; icon:
|
|
18
|
+
const STATE_TYPE_LABELS: Record<WorkingStateType, { label: string; icon: React.ReactNode; description: string }> = {
|
|
17
19
|
context: {
|
|
18
20
|
label: 'Context',
|
|
19
|
-
icon:
|
|
21
|
+
icon: <ThemeIcon emoji="📋" icon={<ClipboardIcon size={16} />} />,
|
|
20
22
|
description: 'Background information and current understanding',
|
|
21
23
|
},
|
|
22
24
|
progress: {
|
|
23
25
|
label: 'Progress',
|
|
24
|
-
icon:
|
|
26
|
+
icon: <ThemeIcon emoji="📊" icon={<ChartIcon size={16} />} />,
|
|
25
27
|
description: 'Current work progress and status',
|
|
26
28
|
},
|
|
27
29
|
notes: {
|
|
28
30
|
label: 'Notes',
|
|
29
|
-
icon:
|
|
31
|
+
icon: <ThemeIcon emoji="📝" icon={<EditIcon size={16} />} />,
|
|
30
32
|
description: 'Important observations and reminders',
|
|
31
33
|
},
|
|
32
34
|
plan: {
|
|
33
35
|
label: 'Plan',
|
|
34
|
-
icon:
|
|
36
|
+
icon: <ThemeIcon emoji="🎯" icon={<TargetIcon size={16} />} />,
|
|
35
37
|
description: 'Action plan and next steps',
|
|
36
38
|
},
|
|
37
39
|
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { ThemeMode, AccentColor, ACCENT_COLORS } from '../../shared/types';
|
|
1
|
+
import { ThemeMode, AccentColor, VisualTheme, ACCENT_COLORS } from '../../shared/types';
|
|
2
2
|
|
|
3
3
|
interface AppearanceSettingsProps {
|
|
4
4
|
themeMode: ThemeMode;
|
|
5
|
+
visualTheme: VisualTheme;
|
|
5
6
|
accentColor: AccentColor;
|
|
6
7
|
onThemeChange: (theme: ThemeMode) => void;
|
|
8
|
+
onVisualThemeChange: (theme: VisualTheme) => void;
|
|
7
9
|
onAccentChange: (accent: AccentColor) => void;
|
|
8
10
|
onShowOnboarding?: () => void;
|
|
9
11
|
onboardingCompletedAt?: string;
|
|
@@ -11,12 +13,15 @@ interface AppearanceSettingsProps {
|
|
|
11
13
|
|
|
12
14
|
export function AppearanceSettings({
|
|
13
15
|
themeMode,
|
|
16
|
+
visualTheme,
|
|
14
17
|
accentColor,
|
|
15
18
|
onThemeChange,
|
|
19
|
+
onVisualThemeChange,
|
|
16
20
|
onAccentChange,
|
|
17
21
|
onShowOnboarding,
|
|
18
22
|
onboardingCompletedAt,
|
|
19
23
|
}: AppearanceSettingsProps) {
|
|
24
|
+
const isWarmVisualTheme = visualTheme === 'warm' || visualTheme === 'oblivion';
|
|
20
25
|
const formatCompletedDate = (isoString?: string) => {
|
|
21
26
|
if (!isoString) return null;
|
|
22
27
|
try {
|
|
@@ -62,9 +67,39 @@ export function AppearanceSettings({
|
|
|
62
67
|
</p>
|
|
63
68
|
</div>
|
|
64
69
|
|
|
70
|
+
{/* Visual Style */}
|
|
71
|
+
<div className="appearance-section">
|
|
72
|
+
<h4>Visual Style</h4>
|
|
73
|
+
<div className="theme-switcher">
|
|
74
|
+
<button
|
|
75
|
+
className={`theme-option ${visualTheme === 'terminal' ? 'selected' : ''}`}
|
|
76
|
+
onClick={() => onVisualThemeChange('terminal')}
|
|
77
|
+
>
|
|
78
|
+
<div className="theme-option-preview terminal">
|
|
79
|
+
<div className="theme-option-preview-line code-line" />
|
|
80
|
+
<div className="theme-option-preview-line code-line" />
|
|
81
|
+
<div className="theme-option-preview-line code-line" />
|
|
82
|
+
</div>
|
|
83
|
+
<span className="theme-option-label">Terminal</span>
|
|
84
|
+
</button>
|
|
85
|
+
|
|
86
|
+
<button
|
|
87
|
+
className={`theme-option ${isWarmVisualTheme ? 'selected' : ''}`}
|
|
88
|
+
onClick={() => onVisualThemeChange('warm')}
|
|
89
|
+
>
|
|
90
|
+
<div className="theme-option-preview warm">
|
|
91
|
+
<div className="theme-option-preview-line ui-line" />
|
|
92
|
+
<div className="theme-option-preview-line ui-line" />
|
|
93
|
+
<div className="theme-option-preview-line ui-line" />
|
|
94
|
+
</div>
|
|
95
|
+
<span className="theme-option-label">Studio</span>
|
|
96
|
+
</button>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
65
100
|
{/* Theme Mode */}
|
|
66
101
|
<div className="appearance-section">
|
|
67
|
-
<h4>
|
|
102
|
+
<h4>Color Mode</h4>
|
|
68
103
|
<div className="theme-switcher">
|
|
69
104
|
<button
|
|
70
105
|
className={`theme-option ${themeMode === 'light' ? 'selected' : ''}`}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
2
|
import { ChannelData, ChannelUserData, SecurityMode, ContextType, ContextPolicy } from '../../shared/types';
|
|
3
3
|
import { PairingCodeDisplay } from './PairingCodeDisplay';
|
|
4
4
|
import { ContextPolicySettings } from './ContextPolicySettings';
|
|
@@ -32,11 +32,7 @@ export function BlueBubblesSettings({ onStatusChange }: BlueBubblesSettingsProps
|
|
|
32
32
|
const [contextPolicies, setContextPolicies] = useState<Record<ContextType, ContextPolicy>>({} as Record<ContextType, ContextPolicy>);
|
|
33
33
|
const [savingPolicy, setSavingPolicy] = useState(false);
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
loadChannel();
|
|
37
|
-
}, []);
|
|
38
|
-
|
|
39
|
-
const loadChannel = async () => {
|
|
35
|
+
const loadChannel = useCallback(async () => {
|
|
40
36
|
try {
|
|
41
37
|
setLoading(true);
|
|
42
38
|
const channels = await window.electronAPI.getGatewayChannels();
|
|
@@ -74,7 +70,22 @@ export function BlueBubblesSettings({ onStatusChange }: BlueBubblesSettingsProps
|
|
|
74
70
|
} finally {
|
|
75
71
|
setLoading(false);
|
|
76
72
|
}
|
|
77
|
-
};
|
|
73
|
+
}, [onStatusChange]);
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
loadChannel();
|
|
77
|
+
}, [loadChannel]);
|
|
78
|
+
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
const unsubscribe = window.electronAPI?.onGatewayUsersUpdated?.((data) => {
|
|
81
|
+
if (data?.channelType !== 'bluebubbles') return;
|
|
82
|
+
if (channel && data?.channelId && data.channelId !== channel.id) return;
|
|
83
|
+
loadChannel();
|
|
84
|
+
});
|
|
85
|
+
return () => {
|
|
86
|
+
if (unsubscribe) unsubscribe();
|
|
87
|
+
};
|
|
88
|
+
}, [channel?.id, loadChannel]);
|
|
78
89
|
|
|
79
90
|
const handleAddChannel = async () => {
|
|
80
91
|
if (!serverUrl.trim() || !password.trim()) {
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { BoxSettingsData } from '../../shared/types';
|
|
3
|
+
|
|
4
|
+
export function BoxSettings() {
|
|
5
|
+
const [settings, setSettings] = useState<BoxSettingsData | null>(null);
|
|
6
|
+
const [saving, setSaving] = useState(false);
|
|
7
|
+
const [testing, setTesting] = useState(false);
|
|
8
|
+
const [testResult, setTestResult] = useState<{ success: boolean; error?: string; name?: string; userId?: string } | null>(null);
|
|
9
|
+
const [status, setStatus] = useState<{ configured: boolean; connected: boolean; name?: string; error?: string } | null>(null);
|
|
10
|
+
const [statusLoading, setStatusLoading] = useState(false);
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
loadSettings();
|
|
14
|
+
refreshStatus();
|
|
15
|
+
}, []);
|
|
16
|
+
|
|
17
|
+
const loadSettings = async () => {
|
|
18
|
+
try {
|
|
19
|
+
const loaded = await window.electronAPI.getBoxSettings();
|
|
20
|
+
setSettings(loaded);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.error('Failed to load Box settings:', error);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const updateSettings = (updates: Partial<BoxSettingsData>) => {
|
|
27
|
+
if (!settings) return;
|
|
28
|
+
setSettings({ ...settings, ...updates });
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const handleSave = async () => {
|
|
32
|
+
if (!settings) return;
|
|
33
|
+
setSaving(true);
|
|
34
|
+
setTestResult(null);
|
|
35
|
+
try {
|
|
36
|
+
const payload: BoxSettingsData = { ...settings };
|
|
37
|
+
await window.electronAPI.saveBoxSettings(payload);
|
|
38
|
+
setSettings(payload);
|
|
39
|
+
await refreshStatus();
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('Failed to save Box settings:', error);
|
|
42
|
+
} finally {
|
|
43
|
+
setSaving(false);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const refreshStatus = async () => {
|
|
48
|
+
try {
|
|
49
|
+
setStatusLoading(true);
|
|
50
|
+
const result = await window.electronAPI.getBoxStatus();
|
|
51
|
+
setStatus(result);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('Failed to load Box status:', error);
|
|
54
|
+
} finally {
|
|
55
|
+
setStatusLoading(false);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const handleTestConnection = async () => {
|
|
60
|
+
setTesting(true);
|
|
61
|
+
setTestResult(null);
|
|
62
|
+
try {
|
|
63
|
+
const result = await window.electronAPI.testBoxConnection();
|
|
64
|
+
setTestResult(result);
|
|
65
|
+
await refreshStatus();
|
|
66
|
+
} catch (error: any) {
|
|
67
|
+
setTestResult({ success: false, error: error.message || 'Failed to test connection' });
|
|
68
|
+
} finally {
|
|
69
|
+
setTesting(false);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
if (!settings) {
|
|
74
|
+
return <div className="settings-loading">Loading Box settings...</div>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const statusLabel = !status?.configured
|
|
78
|
+
? 'Missing Token'
|
|
79
|
+
: status.connected
|
|
80
|
+
? 'Connected'
|
|
81
|
+
: 'Configured';
|
|
82
|
+
|
|
83
|
+
const statusClass = !status?.configured
|
|
84
|
+
? 'missing'
|
|
85
|
+
: status.connected
|
|
86
|
+
? 'connected'
|
|
87
|
+
: 'configured';
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div className="box-settings">
|
|
91
|
+
<div className="settings-section">
|
|
92
|
+
<div className="settings-section-header">
|
|
93
|
+
<div className="settings-title-with-badge">
|
|
94
|
+
<h3>Connect Box</h3>
|
|
95
|
+
{status && (
|
|
96
|
+
<span
|
|
97
|
+
className={`box-status-badge ${statusClass}`}
|
|
98
|
+
title={!status.configured ? 'Access token not configured' : status.connected ? 'Connected to Box' : 'Configured'}
|
|
99
|
+
>
|
|
100
|
+
{statusLabel}
|
|
101
|
+
</span>
|
|
102
|
+
)}
|
|
103
|
+
{statusLoading && !status && (
|
|
104
|
+
<span className="box-status-badge configured">Checking…</span>
|
|
105
|
+
)}
|
|
106
|
+
</div>
|
|
107
|
+
<button className="btn-secondary btn-sm" onClick={refreshStatus} disabled={statusLoading}>
|
|
108
|
+
{statusLoading ? 'Checking...' : 'Refresh Status'}
|
|
109
|
+
</button>
|
|
110
|
+
</div>
|
|
111
|
+
<p className="settings-description">
|
|
112
|
+
Connect the agent to Box using a developer token or OAuth access token, then use the built-in `box_action`
|
|
113
|
+
tool to search and manage files.
|
|
114
|
+
</p>
|
|
115
|
+
{status?.error && (
|
|
116
|
+
<p className="settings-hint">Status check: {status.error}</p>
|
|
117
|
+
)}
|
|
118
|
+
<div className="settings-actions">
|
|
119
|
+
<button
|
|
120
|
+
className="btn-secondary btn-sm"
|
|
121
|
+
onClick={() => window.electronAPI.openExternal('https://app.box.com/developers/console')}
|
|
122
|
+
>
|
|
123
|
+
Open Box Console
|
|
124
|
+
</button>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div className="settings-section">
|
|
129
|
+
<div className="settings-field">
|
|
130
|
+
<label>Enable Integration</label>
|
|
131
|
+
<label className="settings-toggle">
|
|
132
|
+
<input
|
|
133
|
+
type="checkbox"
|
|
134
|
+
checked={settings.enabled}
|
|
135
|
+
onChange={(e) => updateSettings({ enabled: e.target.checked })}
|
|
136
|
+
/>
|
|
137
|
+
<span className="toggle-slider" />
|
|
138
|
+
</label>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<div className="settings-field">
|
|
142
|
+
<label>Access Token</label>
|
|
143
|
+
<input
|
|
144
|
+
type="password"
|
|
145
|
+
className="settings-input"
|
|
146
|
+
placeholder="Box access token"
|
|
147
|
+
value={settings.accessToken || ''}
|
|
148
|
+
onChange={(e) => updateSettings({ accessToken: e.target.value || undefined })}
|
|
149
|
+
/>
|
|
150
|
+
<p className="settings-hint">Use a developer token or OAuth access token with required scopes.</p>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<div className="settings-field">
|
|
154
|
+
<label>Timeout (ms)</label>
|
|
155
|
+
<input
|
|
156
|
+
type="number"
|
|
157
|
+
className="settings-input"
|
|
158
|
+
min={1000}
|
|
159
|
+
max={120000}
|
|
160
|
+
value={settings.timeoutMs ?? 20000}
|
|
161
|
+
onChange={(e) => updateSettings({ timeoutMs: Number(e.target.value) })}
|
|
162
|
+
/>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<div className="settings-actions">
|
|
166
|
+
<button className="btn-secondary btn-sm" onClick={handleTestConnection} disabled={testing}>
|
|
167
|
+
{testing ? 'Testing...' : 'Test Connection'}
|
|
168
|
+
</button>
|
|
169
|
+
<button className="btn-primary btn-sm" onClick={handleSave} disabled={saving}>
|
|
170
|
+
{saving ? 'Saving...' : 'Save Settings'}
|
|
171
|
+
</button>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
{testResult && (
|
|
175
|
+
<div className={`test-result ${testResult.success ? 'success' : 'error'}`}>
|
|
176
|
+
{testResult.success ? (
|
|
177
|
+
<span>Connected{testResult.name ? ` as ${testResult.name}` : ''}</span>
|
|
178
|
+
) : (
|
|
179
|
+
<span>Connection failed: {testResult.error}</span>
|
|
180
|
+
)}
|
|
181
|
+
</div>
|
|
182
|
+
)}
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<div className="settings-section">
|
|
186
|
+
<h4>Quick Usage</h4>
|
|
187
|
+
<pre className="settings-info-box">{`// List root folder items
|
|
188
|
+
box_action({
|
|
189
|
+
action: "list_folder_items",
|
|
190
|
+
folder_id: "0",
|
|
191
|
+
limit: 25
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Upload a file to root
|
|
195
|
+
box_action({
|
|
196
|
+
action: "upload_file",
|
|
197
|
+
file_path: "reports/summary.pdf",
|
|
198
|
+
parent_id: "0"
|
|
199
|
+
});`}</pre>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
);
|
|
203
|
+
}
|