@@ -211,97 +340,14 @@ export const CapabilityPage = ({ kind, endpoint, queryKey }: CapabilityPageProps
-
-
{kind}s
- {!creating && (
-
- )}
-
- {creating && (
-
- setNewName(ev.target.value)}
- onKeyDown={(ev) => {
- if (ev.key === 'Enter') {
- ev.preventDefault();
- handleCreate();
- }
- if (ev.key === 'Escape') {
- setCreating(false);
- setNewName('');
- }
- }}
- placeholder={`${kind} name...`}
- className="flex-1 min-w-0 rounded border border-duck-dark/20 bg-white px-2 py-1 text-base md:text-sm text-duck-dark placeholder:text-duck-dark/30 focus:outline-none focus:ring-1 focus:ring-duck-teal/30"
- />
-
-
-
- )}
-
-
-
- setSearch(ev.target.value)}
- placeholder={`Search ${kind.toLowerCase()}s...`}
- className="w-full rounded border border-duck-dark/15 bg-white/80 pl-7 pr-2 py-1 text-base md:text-sm text-duck-dark placeholder:text-duck-dark/30 focus:outline-none focus:ring-1 focus:ring-duck-teal/30"
- />
-
-
-
- {filtered.map((item) => (
-
- ))}
- {items.length === 0 && (
-
No {kind.toLowerCase()}s found
- )}
- {items.length > 0 && filtered.length === 0 && (
-
No matches
- )}
-
+
{/* Right panel — detail + chat */}
diff --git a/src/apps/officer-web/Screens/Dashboard/Chat/ChatPanel.tsx b/src/apps/officer-web/Screens/Dashboard/Chat/ChatPanel.tsx
index cebaeac7..419a97b0 100644
--- a/src/apps/officer-web/Screens/Dashboard/Chat/ChatPanel.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Chat/ChatPanel.tsx
@@ -2,7 +2,7 @@ import { useRef, useEffect, useState } from 'react';
import { useNavigate, useLocation } from 'react-router';
import { useChatSessions } from '@/state/useChatSessions';
import { useSlashCommands } from '@/state/useSlashCommands';
-import { SessionBar } from 'widgets/ChatHistory';
+import { SessionBar } from 'apps/ChatHistory';
import type { ModelOption } from '@/state/useModels';
import type { useClaude } from './useClaude';
import { EmbeddableChat, type Attachment } from './EmbeddableChat';
diff --git a/src/apps/officer-web/Screens/Dashboard/Chat/EmbeddableChat.tsx b/src/apps/officer-web/Screens/Dashboard/Chat/EmbeddableChat.tsx
index 43e007a5..bb5c9e03 100644
--- a/src/apps/officer-web/Screens/Dashboard/Chat/EmbeddableChat.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Chat/EmbeddableChat.tsx
@@ -4,7 +4,7 @@ import { toast } from 'sonner';
import { useClient } from 'hooks/useClient';
import type { ModelOption } from '@/state/useModels';
import type { useClaude } from './useClaude';
-import { MessageList } from 'widgets/Chat';
+import { MessageList } from 'apps/Chat';
import { InputArea } from './InputArea';
export type Attachment =
diff --git a/src/apps/officer-web/Screens/Dashboard/Chat/InputArea.tsx b/src/apps/officer-web/Screens/Dashboard/Chat/InputArea.tsx
index c4495c81..2cda1c70 100644
--- a/src/apps/officer-web/Screens/Dashboard/Chat/InputArea.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Chat/InputArea.tsx
@@ -11,7 +11,7 @@ import {
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import type { ModelOption } from '@/state/useModels';
-import type { ChatMessage } from 'widgets/Chat';
+import type { ChatMessage } from 'apps/Chat';
import type { Attachment } from './EmbeddableChat';
import { Settings } from './Settings';
diff --git a/src/apps/officer-web/Screens/Dashboard/Chat/Settings.tsx b/src/apps/officer-web/Screens/Dashboard/Chat/Settings.tsx
index 16f932dd..8ccabaa0 100644
--- a/src/apps/officer-web/Screens/Dashboard/Chat/Settings.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Chat/Settings.tsx
@@ -1,7 +1,7 @@
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { useAuth } from 'hooks/useAuth';
import type { ModelOption } from '@/state/useModels';
-import type { ChatMessage } from 'widgets/Chat';
+import type { ChatMessage } from 'apps/Chat';
import { OpenCodeModelPicker } from './OpenCodeModelPicker';
type SettingsProps = {
diff --git a/src/apps/officer-web/Screens/Dashboard/Chat/index.tsx b/src/apps/officer-web/Screens/Dashboard/Chat/index.tsx
index 582f7afe..4bd3325e 100644
--- a/src/apps/officer-web/Screens/Dashboard/Chat/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Chat/index.tsx
@@ -1,7 +1,7 @@
import { useState } from 'react';
import { useParams } from 'react-router';
import { useQueryClient } from '@tanstack/react-query';
-import type { SessionEntry } from 'widgets/Chat';
+import type { SessionEntry } from 'apps/Chat';
import { useClaude } from './useClaude';
import { useOpenCode } from './useOpenCode';
import { ChatPanel } from './ChatPanel';
diff --git a/src/apps/officer-web/Screens/Dashboard/Chat/useClaude.ts b/src/apps/officer-web/Screens/Dashboard/Chat/useClaude.ts
index cdcc234e..21252da6 100644
--- a/src/apps/officer-web/Screens/Dashboard/Chat/useClaude.ts
+++ b/src/apps/officer-web/Screens/Dashboard/Chat/useClaude.ts
@@ -1,7 +1,7 @@
import { useState, useEffect, useRef } from 'react';
import { useChatWebSocket } from 'hooks/useChatWebSocket';
import { useChatSessions } from '@/state/useChatSessions';
-import type { ChatMessage, ServerMessage, TaskInfo } from 'widgets/Chat';
+import type { ChatMessage, ServerMessage, TaskInfo } from 'apps/Chat';
const SAVE_DEBOUNCE_MS = 1000;
diff --git a/src/apps/officer-web/Screens/Dashboard/Chat/useOpenCode.ts b/src/apps/officer-web/Screens/Dashboard/Chat/useOpenCode.ts
index acb773bc..637dd707 100644
--- a/src/apps/officer-web/Screens/Dashboard/Chat/useOpenCode.ts
+++ b/src/apps/officer-web/Screens/Dashboard/Chat/useOpenCode.ts
@@ -3,7 +3,7 @@ import { useChatWebSocket } from 'hooks/useChatWebSocket';
import { useSettings } from '@/state/useSettings';
import { useVisibleOpenCodeModels } from '@/state/useModels';
import { useChatSessions } from '@/state/useChatSessions';
-import type { ChatMessage, ServerMessage, TaskInfo } from 'widgets/Chat';
+import type { ChatMessage, ServerMessage, TaskInfo } from 'apps/Chat';
const SYSTEM_RE = /^
([\s\S]*?)<\/system>\s*/;
diff --git a/src/apps/officer-web/Screens/Dashboard/ChatHistory/Widget.tsx b/src/apps/officer-web/Screens/Dashboard/ChatHistory/Widget.tsx
index 41d1943a..88e21e47 100644
--- a/src/apps/officer-web/Screens/Dashboard/ChatHistory/Widget.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/ChatHistory/Widget.tsx
@@ -1,6 +1,6 @@
import { Link } from 'react-router';
import { MessageSquare, Trash2 } from 'lucide-react';
-import { Widget } from '@/components/Widget';
+import { Widget } from 'widgets/Widget';
import { useChatSessions } from '@/state/useChatSessions';
export const ChatHistory = () => {
diff --git a/src/apps/officer-web/Screens/Dashboard/ChatHistory/index.tsx b/src/apps/officer-web/Screens/Dashboard/ChatHistory/index.tsx
index 031f0f94..9eff1472 100644
--- a/src/apps/officer-web/Screens/Dashboard/ChatHistory/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/ChatHistory/index.tsx
@@ -1,2 +1,2 @@
-export { ChatHistory as ChatHistoryWidget } from './Widget';
+export { ChatHistory as ChatHistoryApp } from './Widget';
export { SessionList } from './Screen';
diff --git a/src/apps/officer-web/Screens/Dashboard/CodeEditor/index.tsx b/src/apps/officer-web/Screens/Dashboard/CodeEditor/index.tsx
index eb3066ed..e713d7a2 100644
--- a/src/apps/officer-web/Screens/Dashboard/CodeEditor/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/CodeEditor/index.tsx
@@ -1,5 +1,5 @@
-import { CodeEditorView } from 'widgets/CodeEditor';
-import { Widget } from '@/components/Widget';
+import { CodeEditorView } from 'apps/CodeEditor';
+import { Widget } from 'widgets/Widget';
export const CodeEditor = () => (
diff --git a/src/apps/officer-web/Screens/Dashboard/Files/Screen/FileGrid.tsx b/src/apps/officer-web/Screens/Dashboard/Files/Screen/FileGrid.tsx
index 4f1f9ac1..d212715f 100644
--- a/src/apps/officer-web/Screens/Dashboard/Files/Screen/FileGrid.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Files/Screen/FileGrid.tsx
@@ -1,6 +1,6 @@
import { useRef, useCallback, useMemo, useState, useEffect } from 'react';
import { useVirtualizer } from '@tanstack/react-virtual';
-import type { DirEntry, TaskSummary } from 'widgets/FileBrowser';
+import type { DirEntry, TaskSummary } from 'apps/FileBrowser';
import { FileItem } from './FileItem';
type ClipboardState = { paths: string[]; mode: 'copy' | 'cut' } | null;
diff --git a/src/apps/officer-web/Screens/Dashboard/Files/Screen/FileItem.tsx b/src/apps/officer-web/Screens/Dashboard/Files/Screen/FileItem.tsx
index d103dcdc..55b1d2cf 100644
--- a/src/apps/officer-web/Screens/Dashboard/Files/Screen/FileItem.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Files/Screen/FileItem.tsx
@@ -22,7 +22,7 @@ import {
ContextMenuTrigger,
} from '@/components/ui/context-menu';
import { cardStyle } from '@/components/Card';
-import type { DirEntry, TaskSummary } from 'widgets/FileBrowser';
+import type { DirEntry, TaskSummary } from 'apps/FileBrowser';
export type FileItemProps = {
entry: DirEntry;
diff --git a/src/apps/officer-web/Screens/Dashboard/Files/Screen/FileViewer.tsx b/src/apps/officer-web/Screens/Dashboard/Files/Screen/FileViewer.tsx
index e361d8ef..59710bce 100644
--- a/src/apps/officer-web/Screens/Dashboard/Files/Screen/FileViewer.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Files/Screen/FileViewer.tsx
@@ -26,7 +26,7 @@ import rehypeSlug from 'rehype-slug';
import { Dialog, DialogOverlay, DialogPortal } from '@/components/ui/dialog';
import * as DialogPrimitive from '@radix-ui/react-dialog';
import { cardStyle } from '@/components/Card';
-import { useFiles } from 'widgets/FileBrowser';
+import { useFiles } from 'apps/FileBrowser';
import { getHeaders } from 'hooks/useClient';
import { config } from 'config';
import { toast } from 'sonner';
diff --git a/src/apps/officer-web/Screens/Dashboard/Files/Screen/TaskRunnerModal.tsx b/src/apps/officer-web/Screens/Dashboard/Files/Screen/TaskRunnerModal.tsx
index 86a4cdc2..b959964b 100644
--- a/src/apps/officer-web/Screens/Dashboard/Files/Screen/TaskRunnerModal.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Files/Screen/TaskRunnerModal.tsx
@@ -3,13 +3,13 @@ import { X } from 'lucide-react';
import { Dialog, DialogOverlay, DialogPortal } from '@/components/ui/dialog';
import * as DialogPrimitive from '@radix-ui/react-dialog';
import { cardStyle } from '@/components/Card';
-import type { TaskInfo } from 'widgets/Chat';
+import type { TaskInfo } from 'apps/Chat';
import { useClaude } from '@/Screens/Dashboard/Chat/useClaude';
import { useOpenCode } from '@/Screens/Dashboard/Chat/useOpenCode';
import { EmbeddableChat } from '@/Screens/Dashboard/Chat/EmbeddableChat';
import { useVisibleClaudeModels, useVisibleOpenCodeModels } from '@/state/useModels';
import { useSettings } from '@/state/useSettings';
-import type { TaskSummary } from 'widgets/FileBrowser';
+import type { TaskSummary } from 'apps/FileBrowser';
const playDing = () => {
const ctx = new AudioContext();
diff --git a/src/apps/officer-web/Screens/Dashboard/Files/Screen/index.tsx b/src/apps/officer-web/Screens/Dashboard/Files/Screen/index.tsx
index 0320944b..7c65899a 100644
--- a/src/apps/officer-web/Screens/Dashboard/Files/Screen/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Files/Screen/index.tsx
@@ -22,7 +22,7 @@ import {
} from 'lucide-react';
import { getIcon } from 'material-file-icons';
import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger } from '@/components/ui/context-menu';
-import { useFiles, type DirEntry, useTasks, type TaskSummary, Breadcrumb, Toolbar } from 'widgets/FileBrowser';
+import { useFiles, type DirEntry, useTasks, type TaskSummary, Breadcrumb, Toolbar } from 'apps/FileBrowser';
import { useUserState } from '@/state/useUserState';
import { useAuth } from 'hooks/useAuth';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
diff --git a/src/apps/officer-web/Screens/Dashboard/Files/Widget.tsx b/src/apps/officer-web/Screens/Dashboard/Files/Widget.tsx
index f7c552aa..2449a000 100644
--- a/src/apps/officer-web/Screens/Dashboard/Files/Widget.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Files/Widget.tsx
@@ -2,10 +2,10 @@ import { useState, useEffect, useRef, useMemo } from 'react';
import { useNavigate } from 'react-router';
import { Folder, Pin, PinOff, Search, Clock, FolderOpen, Loader2, X } from 'lucide-react';
import { getIcon } from 'material-file-icons';
-import { useFiles, type DirEntry, Breadcrumb } from 'widgets/FileBrowser';
+import { useFiles, type DirEntry, Breadcrumb } from 'apps/FileBrowser';
import { useRecentFiles } from './state/useRecentFiles';
import { usePinnedFiles } from './state/usePinnedFiles';
-import { Widget } from '@/components/Widget';
+import { Widget } from 'widgets/Widget';
type Tab = 'browse' | 'recent' | 'pinned';
diff --git a/src/apps/officer-web/Screens/Dashboard/Files/index.tsx b/src/apps/officer-web/Screens/Dashboard/Files/index.tsx
index 0b19261e..f208760c 100644
--- a/src/apps/officer-web/Screens/Dashboard/Files/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Files/index.tsx
@@ -11,4 +11,4 @@ export const FilesPage = () => (
);
-export { FileBrowser as FileBrowserWidget } from './Widget';
+export { FileBrowser as FileBrowserApp } from './Widget';
diff --git a/src/apps/officer-web/Screens/Dashboard/Home/ChatLauncher.tsx b/src/apps/officer-web/Screens/Dashboard/Home/ChatLauncher.tsx
index 9f149d4c..24087f3c 100644
--- a/src/apps/officer-web/Screens/Dashboard/Home/ChatLauncher.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Home/ChatLauncher.tsx
@@ -14,7 +14,7 @@ import {
import { toast } from 'sonner';
import { useClient } from 'hooks/useClient';
import { Button } from '@/components/ui/button';
-import { Widget } from '@/components/Widget';
+import { Widget } from 'widgets/Widget';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog';
import {
DropdownMenu,
diff --git a/src/apps/officer-web/Screens/Dashboard/Home/index.tsx b/src/apps/officer-web/Screens/Dashboard/Home/index.tsx
index 03c519e8..374ff1f2 100644
--- a/src/apps/officer-web/Screens/Dashboard/Home/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Home/index.tsx
@@ -1,52 +1,24 @@
-import { useUserState } from '@/state/useUserState';
-import { WorkspaceView } from '@/components/Workspace';
+import { useState } from 'react';
import type { LayoutNode } from '@/components/Workspace';
-import { widgetRegistry } from '@/Screens/Dashboard/Workspaces/widget-registry';
+import { WorkspaceLayout } from '@/components/Workspace';
+import { appRegistry } from '../Workspaces/app-registry';
-const DEFAULT_HOME_LAYOUT: LayoutNode = {
+const initialLayout: LayoutNode = {
type: 'group',
id: 'home-root',
- direction: 'horizontal',
+ direction: 'vertical',
children: [
- {
- size: 50,
- node: {
- type: 'group',
- id: 'home-left',
- direction: 'vertical',
- children: [
- { size: 50, node: { type: 'panel', id: 'home-tl', widgetType: null } },
- { size: 50, node: { type: 'panel', id: 'home-bl', widgetType: null } },
- ],
- },
- },
- {
- size: 50,
- node: {
- type: 'group',
- id: 'home-right',
- direction: 'vertical',
- children: [
- { size: 50, node: { type: 'panel', id: 'home-tr', widgetType: null } },
- { size: 50, node: { type: 'panel', id: 'home-br', widgetType: null } },
- ],
- },
- },
+ { node: { type: 'panel', id: 'homepage-widget-panel', appType: 'widget-panel' }, size: 60 },
+ { node: { type: 'panel', id: 'home-bottom', appType: 'chat-launcher' }, size: 40 },
],
};
export const HomeScreen = () => {
- const [layout, setLayout] = useUserState('home-workspace-layout', DEFAULT_HOME_LAYOUT);
+ const [layout, setLayout] = useState(initialLayout);
return (
-
+
);
};
diff --git a/src/apps/officer-web/Screens/Dashboard/Layout/Dock.tsx b/src/apps/officer-web/Screens/Dashboard/Layout/Dock.tsx
index 3d458618..db2235b0 100644
--- a/src/apps/officer-web/Screens/Dashboard/Layout/Dock.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Layout/Dock.tsx
@@ -47,7 +47,7 @@ export const Dock = ({ items, className }: DockProps) => {
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
className={`fixed bottom-4 left-1/2 -translate-x-1/2 z-5 items-end gap-2 md:gap-6 px-2 py-1.5 md:px-3 md:py-2 rounded-2xl border backdrop-blur-xl shadow-lg ${className ?? 'flex'}`}
- style={{ backgroundColor: 'var(--dock-bg)', borderColor: 'var(--dock-border)' }}
+ style={{ backgroundColor: 'transparent', borderColor: 'transparent' }}
>
{items.map((item, index) => {
const iconCenter = DOCK_PADDING + index * (ICON_SIZE + ICON_GAP) + ICON_SIZE / 2;
@@ -91,18 +91,15 @@ export const Dock = ({ items, className }: DockProps) => {
};
-import { Terminal, TerminalSquare, FileText, FolderOpen, Code, LayoutGrid } from 'lucide-react';
-import { Sparkles, ClipboardList, ScrollText, Workflow } from 'lucide-react';
+import { MessageCircle, TerminalSquare, FileText, FolderOpen, Code, LayoutGrid, Bot, ScrollText } from 'lucide-react';
export const dockItems: DockItem[] = [
- { label: 'Chat', to: '/chat', icon: Terminal, color: '#60a5fa' },
{ label: 'Files', to: '/files', icon: FolderOpen, color: '#fbbf24' },
+ { label: 'Chat', to: '/chat', icon: MessageCircle, color: '#60a5fa' },
{ label: 'Terminal', to: '/terminal', icon: TerminalSquare, color: '#34d399' },
{ label: 'Editor', to: '/code-editor', icon: Code, color: '#a78bfa' },
{ label: 'Plans', to: '/plans', icon: FileText, color: '#f472b6' },
- { label: 'Skills', to: '/skills', icon: Sparkles, color: '#c084fc' },
- { label: 'Tasks', to: '/tasks', icon: ClipboardList, color: '#fb923c' },
- { label: 'Processes', to: '/processes', icon: Workflow, color: '#2dd4bf' },
+ { label: 'Automation', to: '/automation', icon: Bot, color: '#2dd4bf' },
{ label: 'Logs', to: '/task-logs', icon: ScrollText, color: '#94a3b8' },
{ label: 'Workspaces', to: '/workspaces', icon: LayoutGrid, color: '#8b5cf6' },
];
diff --git a/src/apps/officer-web/Screens/Dashboard/Settings/ResourceSettings/Applications.tsx b/src/apps/officer-web/Screens/Dashboard/Settings/ResourceSettings/Applications.tsx
new file mode 100644
index 00000000..42ae54ab
--- /dev/null
+++ b/src/apps/officer-web/Screens/Dashboard/Settings/ResourceSettings/Applications.tsx
@@ -0,0 +1,162 @@
+import { useState } from 'react';
+import { useQuery, useQueryClient } from '@tanstack/react-query';
+import { RefreshCw, Download, Circle, Copy, Check } from 'lucide-react';
+import { toast } from 'sonner';
+import { Button } from '@/components/ui/button';
+import { useClient } from 'hooks/useClient';
+
+type AppStatus = {
+ id: string;
+ name: string;
+ description: string;
+ installed: boolean;
+ version: string | null;
+ running: boolean | null;
+ hasInstall: boolean;
+ hasUpdate: boolean;
+ manualInstallCommand: string | null;
+ manualUpdateCommand: string | null;
+};
+
+const CopyCommand = ({ command }: { command: string }) => {
+ const [copied, setCopied] = useState(false);
+
+ const copy = () => {
+ navigator.clipboard.writeText(command);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 1500);
+ };
+
+ return (
+
+ {command}
+
+
+ );
+};
+
+export const Applications = () => {
+ const client = useClient();
+ const queryClient = useQueryClient();
+ const [actionInProgress, setActionInProgress] = useState(null);
+
+ const { data: apps, isLoading } = useQuery({
+ queryKey: ['APPLICATIONS'],
+ queryFn: () => client.get('/server-settings/applications'),
+ });
+
+ const runAction = async (id: string, action: 'install' | 'update') => {
+ setActionInProgress(id);
+ try {
+ await client.post(`/server-settings/applications/${id}/${action}`);
+ await queryClient.invalidateQueries({ queryKey: ['APPLICATIONS'] });
+ toast.success(`${action === 'install' ? 'Installed' : 'Updated'} successfully`);
+ } catch (err) {
+ const message = err instanceof Error ? err.message : `${action} failed`;
+ toast.error(message);
+ } finally {
+ setActionInProgress(null);
+ }
+ };
+
+ const getManualCommand = (app: AppStatus): string | null => {
+ if (!app.installed) return app.manualInstallCommand;
+ return app.manualUpdateCommand ?? app.manualInstallCommand;
+ };
+
+ const hasAutoAction = (app: AppStatus): boolean => {
+ if (!app.installed) return app.hasInstall && !app.manualInstallCommand;
+ return app.hasUpdate && !(app.manualUpdateCommand ?? app.manualInstallCommand);
+ };
+
+ return (
+
+
Applications
+
+ {isLoading &&
Checking applications...
}
+
+ {apps && (
+
+ {apps.map((app: AppStatus) => {
+ const manualCmd = getManualCommand(app);
+ const canAutoRun = hasAutoAction(app);
+
+ return (
+
+
+
+
+ {app.name}
+ {app.installed && (
+
+ {app.version}
+
+ )}
+ {!app.installed && (
+
+ Not installed
+
+ )}
+ {app.running !== null && (
+
+ )}
+
+
{app.description}
+
+
+
+ {canAutoRun && !app.installed && (
+
+ )}
+ {canAutoRun && app.installed && (
+
+ )}
+
+
+
+ {manualCmd && (
+
+ {app.installed ? 'Update' : 'Install'} manually:
+
+
+ )}
+
+ );
+ })}
+
+ )}
+
+ );
+};
diff --git a/src/apps/officer-web/Screens/Dashboard/Settings/ResourceSettings/ResourceSidebar.tsx b/src/apps/officer-web/Screens/Dashboard/Settings/ResourceSettings/ResourceSidebar.tsx
new file mode 100644
index 00000000..b1b6b3bc
--- /dev/null
+++ b/src/apps/officer-web/Screens/Dashboard/Settings/ResourceSettings/ResourceSidebar.tsx
@@ -0,0 +1,32 @@
+import { Box, AppWindow } from 'lucide-react';
+
+const sidebarItems = [
+ { id: 'applications', label: 'Applications', icon: AppWindow },
+] as const;
+
+export const ResourceSidebar = () => {
+ return (
+
+
+
+ {sidebarItems.map((item) => {
+ const Icon = item.icon;
+ return (
+
+ );
+ })}
+
+
+ );
+};
diff --git a/src/apps/officer-web/Screens/Dashboard/Settings/ResourceSettings/index.tsx b/src/apps/officer-web/Screens/Dashboard/Settings/ResourceSettings/index.tsx
index efa1012a..77c5664b 100644
--- a/src/apps/officer-web/Screens/Dashboard/Settings/ResourceSettings/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Settings/ResourceSettings/index.tsx
@@ -1,212 +1,32 @@
-import { useState } from 'react';
-import { useQuery, useQueryClient } from '@tanstack/react-query';
-import { RefreshCw, Download, Circle, Copy, Check } from 'lucide-react';
-import { toast } from 'sonner';
-import { Button } from '@/components/ui/button';
-import { Card } from '@/components/Card';
-import { useClient } from 'hooks/useClient';
-import { useClaude } from '@/Screens/Dashboard/Chat/useClaude';
-import { useOpenCode } from '@/Screens/Dashboard/Chat/useOpenCode';
-import { EmbeddableChat } from '@/Screens/Dashboard/Chat/EmbeddableChat';
-import { useVisibleClaudeModels, useVisibleOpenCodeModels } from '@/state/useModels';
+import { useMemo } from 'react';
+import type { LayoutNode, PanelComponents } from '@/components/Workspace';
+import { WorkspaceLayout } from '@/components/Workspace';
+import { appRegistry } from '../../Workspaces/app-registry';
+import { Applications } from './Applications';
+import { ResourceSidebar } from './ResourceSidebar';
-type AppStatus = {
- id: string;
- name: string;
- description: string;
- installed: boolean;
- version: string | null;
- running: boolean | null;
- hasInstall: boolean;
- hasUpdate: boolean;
- manualInstallCommand: string | null;
- manualUpdateCommand: string | null;
-};
-
-const CopyCommand = ({ command }: { command: string }) => {
- const [copied, setCopied] = useState(false);
-
- const copy = () => {
- navigator.clipboard.writeText(command);
- setCopied(true);
- setTimeout(() => setCopied(false), 1500);
- };
-
- return (
-
- {command}
-
-
- );
+const layout: LayoutNode = {
+ type: 'group',
+ id: 'resources-root',
+ direction: 'horizontal',
+ children: [
+ { node: { type: 'panel', id: 'resources-left', appType: null }, size: 20 },
+ { node: { type: 'panel', id: 'resources-right', appType: null }, size: 80 },
+ ],
};
export const ResourceSettings = () => {
- const client = useClient();
- const queryClient = useQueryClient();
- const [actionInProgress, setActionInProgress] = useState(null);
-
- const { data: apps, isLoading } = useQuery({
- queryKey: ['APPLICATIONS'],
- queryFn: () => client.get('/server-settings/applications'),
- });
-
- const runAction = async (id: string, action: 'install' | 'update') => {
- setActionInProgress(id);
- try {
- await client.post(`/server-settings/applications/${id}/${action}`);
- await queryClient.invalidateQueries({ queryKey: ['APPLICATIONS'] });
- toast.success(`${action === 'install' ? 'Installed' : 'Updated'} successfully`);
- } catch (err) {
- const message = err instanceof Error ? err.message : `${action} failed`;
- toast.error(message);
- } finally {
- setActionInProgress(null);
- }
- };
-
- const getManualCommand = (app: AppStatus): string | null => {
- if (!app.installed) return app.manualInstallCommand;
- return app.manualUpdateCommand ?? app.manualInstallCommand;
- };
-
- const hasAutoAction = (app: AppStatus): boolean => {
- if (!app.installed) return app.hasInstall && !app.manualInstallCommand;
- return app.hasUpdate && !(app.manualUpdateCommand ?? app.manualInstallCommand);
- };
-
- const [provider, setProvider] = useState<'claude' | 'opencode'>('claude');
+ const panelComponents: PanelComponents = useMemo(
+ () => ({
+ 'resources-left': ResourceSidebar,
+ 'resources-right': Applications,
+ }),
+ [],
+ );
return (
-
-
- Applications
- System tools and dependencies used by Officer.dev
-
- {isLoading && Checking applications...
}
-
- {apps && (
-
- {apps.map((app: AppStatus) => {
- const manualCmd = getManualCommand(app);
- const canAutoRun = hasAutoAction(app);
-
- return (
-
-
-
-
- {app.name}
- {app.installed && (
-
- {app.version}
-
- )}
- {!app.installed && (
-
- Not installed
-
- )}
- {app.running !== null && (
-
- )}
-
-
{app.description}
-
-
-
- {canAutoRun && !app.installed && (
-
- )}
- {canAutoRun && app.installed && (
-
- )}
-
-
-
- {manualCmd && (
-
- {app.installed ? 'Update' : 'Install'} manually:
-
-
- )}
-
- );
- })}
-
- )}
-
-
-
- {provider === 'claude' ? (
-
- ) : (
-
- )}
-
+
+ {}} registry={appRegistry} components={panelComponents} />
);
};
-
-type ChatInnerProps = {
- onProviderChange: (p: 'claude' | 'opencode') => void;
-};
-
-const ClaudeChat = ({ onProviderChange }: ChatInnerProps) => {
- const chat = useClaude(undefined, undefined, { replaceUrl: false });
- const models = useVisibleClaudeModels();
- return (
-
- );
-};
-
-const OpenCodeChat = ({ onProviderChange }: ChatInnerProps) => {
- const chat = useOpenCode(undefined, undefined, { replaceUrl: false });
- const models = useVisibleOpenCodeModels();
- return (
-
- );
-};
diff --git a/src/apps/officer-web/Screens/Dashboard/TaskLogs/index.tsx b/src/apps/officer-web/Screens/Dashboard/TaskLogs/index.tsx
index a5e53d08..2ed84df2 100644
--- a/src/apps/officer-web/Screens/Dashboard/TaskLogs/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/TaskLogs/index.tsx
@@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
import { Search, AlertCircle, CheckCircle2, Clock, ArrowLeft } from 'lucide-react';
import { useClient } from 'hooks/useClient';
import { Card } from '@/components/Card';
-import { MessageBubble, type ChatMessage } from 'widgets/Chat';
+import { MessageBubble, type ChatMessage } from 'apps/Chat';
type LogMetadata = {
filename: string;
diff --git a/src/apps/officer-web/Screens/Dashboard/Terminal/index.tsx b/src/apps/officer-web/Screens/Dashboard/Terminal/index.tsx
index de32955d..72ebdc9b 100644
--- a/src/apps/officer-web/Screens/Dashboard/Terminal/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Terminal/index.tsx
@@ -1,4 +1,4 @@
-import { TerminalView } from 'widgets/Terminal';
+import { TerminalView } from 'apps/Terminal';
export const Terminal = () => {
return
;
diff --git a/src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceListWidget.tsx b/src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceListApp.tsx
similarity index 97%
rename from src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceListWidget.tsx
rename to src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceListApp.tsx
index 09fefc58..3b822d31 100644
--- a/src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceListWidget.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceListApp.tsx
@@ -3,7 +3,7 @@ import { LayoutGrid, ArrowRight } from 'lucide-react';
import { useUserState } from '@/state/useUserState';
import type { WorkspaceDefinition } from '@/components/Workspace';
-export const WorkspaceListWidget = () => {
+export const WorkspaceListApp = () => {
const [workspaces] = useUserState
('workspaces', []);
return (
diff --git a/src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceListScreen.tsx b/src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceListScreen.tsx
index 350a6596..43bc9019 100644
--- a/src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceListScreen.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceListScreen.tsx
@@ -3,7 +3,7 @@ import { Link } from 'react-router';
import { Plus, Trash2, LayoutGrid, ArrowRight } from 'lucide-react';
import { useUserState } from '@/state/useUserState';
import type { WorkspaceDefinition } from '@/components/Workspace';
-import { Widget } from '@/components/Widget';
+import { Widget } from 'widgets/Widget';
import { Button } from '@/components/ui/button';
export const WorkspaceListScreen = () => {
diff --git a/src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceScreen.tsx b/src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceScreen.tsx
index 606ee643..ca6466dd 100644
--- a/src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceScreen.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Workspaces/WorkspaceScreen.tsx
@@ -2,7 +2,7 @@ import { useParams, Navigate } from 'react-router';
import { useUserState } from '@/state/useUserState';
import { WorkspaceView, createDefaultLayout } from '@/components/Workspace';
import type { LayoutNode, WorkspaceDefinition } from '@/components/Workspace';
-import { widgetRegistry } from './widget-registry';
+import { appRegistry } from './app-registry';
export const WorkspaceScreen = () => {
const { id } = useParams<{ id: string }>();
@@ -23,7 +23,7 @@ const WorkspaceScreenInner = ({ workspace }: { workspace: WorkspaceDefinition })
workspace={workspace}
layout={layout}
onLayoutChange={setLayout}
- registry={widgetRegistry}
+ registry={appRegistry}
/>
);
diff --git a/src/apps/officer-web/Screens/Dashboard/Workspaces/widget-registry.tsx b/src/apps/officer-web/Screens/Dashboard/Workspaces/app-registry.tsx
similarity index 71%
rename from src/apps/officer-web/Screens/Dashboard/Workspaces/widget-registry.tsx
rename to src/apps/officer-web/Screens/Dashboard/Workspaces/app-registry.tsx
index 39a661ae..60ac6671 100644
--- a/src/apps/officer-web/Screens/Dashboard/Workspaces/widget-registry.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Workspaces/app-registry.tsx
@@ -1,16 +1,19 @@
import { useState } from 'react';
-import { MessageSquare, FolderOpen, History, Music, Code, TerminalSquare, LayoutGrid } from 'lucide-react';
-import type { WidgetRegistry } from '@/components/Workspace';
-import { CodeEditorView } from 'widgets/CodeEditor';
-import { TerminalView } from 'widgets/Terminal';
+import { MessageSquare, FolderOpen, History, Music, Code, TerminalSquare, LayoutGrid, LayoutDashboard, Sparkles } from 'lucide-react';
+import type { AppRegistry } from '@/components/Workspace';
+import { CodeEditorView } from 'apps/CodeEditor';
+import { TerminalView } from 'apps/Terminal';
import { useClaude } from '../Chat/useClaude';
import { useOpenCode } from '../Chat/useOpenCode';
import { ChatPanel } from '../Chat/ChatPanel';
import { useVisibleClaudeModels, useVisibleOpenCodeModels } from '@/state/useModels';
-import { ChatHistoryWidget as ChatHistory } from '../ChatHistory';
+import { ChatHistoryApp as ChatHistory } from '../ChatHistory';
import { Files } from '../Files';
import { Catalog } from 'sounds';
-import { WorkspaceListWidget } from './WorkspaceListWidget';
+import { WorkspaceListApp } from './WorkspaceListApp';
+import { ChatLauncher } from '../Home/ChatLauncher';
+import { widgetRegistry } from 'widgets/widget-registry';
+import { WidgetPanel } from 'widgets/WidgetPanel';
const ChatWidget = () => {
const [provider, setProvider] = useState<'claude' | 'opencode'>('claude');
@@ -37,12 +40,15 @@ const CodeEditorWrapper = () => ;
const TerminalWrapper = () => ;
-export const widgetRegistry: WidgetRegistry = {
+export const appRegistry: AppRegistry = {
'chat': { name: 'Chat', icon: MessageSquare, component: ChatWidget },
'file-browser': { name: 'File Browser', icon: FolderOpen, component: () => },
'chat-history': { name: 'Chat History', icon: History, component: () => },
'sound-library': { name: 'Sound Library', icon: Music, component: () => },
'code-editor': { name: 'Code Editor', icon: Code, component: CodeEditorWrapper },
'terminal': { name: 'Terminal', icon: TerminalSquare, component: TerminalWrapper },
- 'workspace-list': { name: 'Workspaces', icon: LayoutGrid, component: () => },
+ 'workspace-list': { name: 'Workspaces', icon: LayoutGrid, component: () => },
+ 'chat-launcher': { name: 'Chat Launcher', icon: Sparkles, component: () => , fixedHeight: 180 },
+ 'widget-panel': { name: 'Widget Panel', icon: LayoutDashboard, component: WidgetPanel, transparent: true },
+ ...widgetRegistry,
};
diff --git a/src/apps/officer-web/Screens/Dashboard/index.tsx b/src/apps/officer-web/Screens/Dashboard/index.tsx
index 98edc0b2..4d32f14b 100644
--- a/src/apps/officer-web/Screens/Dashboard/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/index.tsx
@@ -7,6 +7,7 @@ export * from './Plans';
export * from './Processes';
export * from './CapabilityPage';
export * from './Settings';
+export * from './Automation';
export * from './Skills';
export * from './TaskLogs';
export * from './Tasks';
diff --git a/src/apps/officer-web/state/useChatSessions.ts b/src/apps/officer-web/state/useChatSessions.ts
index dd901b2d..fadeeea4 100644
--- a/src/apps/officer-web/state/useChatSessions.ts
+++ b/src/apps/officer-web/state/useChatSessions.ts
@@ -1,4 +1,4 @@
-import type { SessionEntry, ChatMessage } from 'widgets/Chat';
+import type { SessionEntry, ChatMessage } from 'apps/Chat';
import type { SlashCommandResult } from './useSlashCommands';
import { useAuth } from 'hooks/useAuth';
import { useClient } from 'hooks/useClient';
diff --git a/src/workspaces/widgets/Chat/MessageBubble.tsx b/src/workspaces/apps/Chat/MessageBubble.tsx
similarity index 100%
rename from src/workspaces/widgets/Chat/MessageBubble.tsx
rename to src/workspaces/apps/Chat/MessageBubble.tsx
diff --git a/src/workspaces/widgets/Chat/MessageList.tsx b/src/workspaces/apps/Chat/MessageList.tsx
similarity index 100%
rename from src/workspaces/widgets/Chat/MessageList.tsx
rename to src/workspaces/apps/Chat/MessageList.tsx
diff --git a/src/workspaces/widgets/Chat/QuestionActivity.tsx b/src/workspaces/apps/Chat/QuestionActivity.tsx
similarity index 100%
rename from src/workspaces/widgets/Chat/QuestionActivity.tsx
rename to src/workspaces/apps/Chat/QuestionActivity.tsx
diff --git a/src/workspaces/widgets/Chat/ToolActivity.tsx b/src/workspaces/apps/Chat/ToolActivity.tsx
similarity index 100%
rename from src/workspaces/widgets/Chat/ToolActivity.tsx
rename to src/workspaces/apps/Chat/ToolActivity.tsx
diff --git a/src/workspaces/widgets/Chat/index.ts b/src/workspaces/apps/Chat/index.ts
similarity index 100%
rename from src/workspaces/widgets/Chat/index.ts
rename to src/workspaces/apps/Chat/index.ts
diff --git a/src/workspaces/widgets/Chat/types.ts b/src/workspaces/apps/Chat/types.ts
similarity index 100%
rename from src/workspaces/widgets/Chat/types.ts
rename to src/workspaces/apps/Chat/types.ts
diff --git a/src/workspaces/widgets/ChatHistory/SessionBar.tsx b/src/workspaces/apps/ChatHistory/SessionBar.tsx
similarity index 100%
rename from src/workspaces/widgets/ChatHistory/SessionBar.tsx
rename to src/workspaces/apps/ChatHistory/SessionBar.tsx
diff --git a/src/workspaces/widgets/ChatHistory/index.ts b/src/workspaces/apps/ChatHistory/index.ts
similarity index 100%
rename from src/workspaces/widgets/ChatHistory/index.ts
rename to src/workspaces/apps/ChatHistory/index.ts
diff --git a/src/workspaces/widgets/CodeEditor/CodeEditor.tsx b/src/workspaces/apps/CodeEditor/CodeEditor.tsx
similarity index 98%
rename from src/workspaces/widgets/CodeEditor/CodeEditor.tsx
rename to src/workspaces/apps/CodeEditor/CodeEditor.tsx
index 4c1bd0ba..d4aa0183 100644
--- a/src/workspaces/widgets/CodeEditor/CodeEditor.tsx
+++ b/src/workspaces/apps/CodeEditor/CodeEditor.tsx
@@ -3,7 +3,7 @@ import Editor, { type OnMount } from '@monaco-editor/react';
import type { editor as MonacoEditor } from 'monaco-editor';
import { toast } from 'sonner';
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@/components/ui/resizable';
-import { useFiles } from 'widgets/FileBrowser';
+import { useFiles } from 'apps/FileBrowser';
import { FileTree } from './FileTree';
import { EditorTabs } from './EditorTabs';
import { useEditorState } from './useEditorState';
diff --git a/src/workspaces/widgets/CodeEditor/EditorTabs.tsx b/src/workspaces/apps/CodeEditor/EditorTabs.tsx
similarity index 100%
rename from src/workspaces/widgets/CodeEditor/EditorTabs.tsx
rename to src/workspaces/apps/CodeEditor/EditorTabs.tsx
diff --git a/src/workspaces/widgets/CodeEditor/FileTree.tsx b/src/workspaces/apps/CodeEditor/FileTree.tsx
similarity index 98%
rename from src/workspaces/widgets/CodeEditor/FileTree.tsx
rename to src/workspaces/apps/CodeEditor/FileTree.tsx
index 3b789fa3..36eda206 100644
--- a/src/workspaces/widgets/CodeEditor/FileTree.tsx
+++ b/src/workspaces/apps/CodeEditor/FileTree.tsx
@@ -1,7 +1,7 @@
import { useState, useMemo, useCallback } from 'react';
import { ChevronRight, ChevronDown, Folder } from 'lucide-react';
import { getIcon } from 'material-file-icons';
-import { useFiles, type DirEntry } from 'widgets/FileBrowser';
+import { useFiles, type DirEntry } from 'apps/FileBrowser';
type FileTreeProps = {
root: string;
diff --git a/src/workspaces/widgets/CodeEditor/index.ts b/src/workspaces/apps/CodeEditor/index.ts
similarity index 100%
rename from src/workspaces/widgets/CodeEditor/index.ts
rename to src/workspaces/apps/CodeEditor/index.ts
diff --git a/src/workspaces/widgets/CodeEditor/language-map.ts b/src/workspaces/apps/CodeEditor/language-map.ts
similarity index 100%
rename from src/workspaces/widgets/CodeEditor/language-map.ts
rename to src/workspaces/apps/CodeEditor/language-map.ts
diff --git a/src/workspaces/widgets/CodeEditor/useEditorState.ts b/src/workspaces/apps/CodeEditor/useEditorState.ts
similarity index 100%
rename from src/workspaces/widgets/CodeEditor/useEditorState.ts
rename to src/workspaces/apps/CodeEditor/useEditorState.ts
diff --git a/src/workspaces/widgets/FileBrowser/Breadcrumb.tsx b/src/workspaces/apps/FileBrowser/Breadcrumb.tsx
similarity index 100%
rename from src/workspaces/widgets/FileBrowser/Breadcrumb.tsx
rename to src/workspaces/apps/FileBrowser/Breadcrumb.tsx
diff --git a/src/workspaces/widgets/FileBrowser/Toolbar.tsx b/src/workspaces/apps/FileBrowser/Toolbar.tsx
similarity index 100%
rename from src/workspaces/widgets/FileBrowser/Toolbar.tsx
rename to src/workspaces/apps/FileBrowser/Toolbar.tsx
diff --git a/src/workspaces/widgets/FileBrowser/index.ts b/src/workspaces/apps/FileBrowser/index.ts
similarity index 100%
rename from src/workspaces/widgets/FileBrowser/index.ts
rename to src/workspaces/apps/FileBrowser/index.ts
diff --git a/src/workspaces/widgets/FileBrowser/useFiles.ts b/src/workspaces/apps/FileBrowser/useFiles.ts
similarity index 100%
rename from src/workspaces/widgets/FileBrowser/useFiles.ts
rename to src/workspaces/apps/FileBrowser/useFiles.ts
diff --git a/src/workspaces/widgets/FileBrowser/useTasks.ts b/src/workspaces/apps/FileBrowser/useTasks.ts
similarity index 100%
rename from src/workspaces/widgets/FileBrowser/useTasks.ts
rename to src/workspaces/apps/FileBrowser/useTasks.ts
diff --git a/src/workspaces/widgets/Terminal/Terminal.tsx b/src/workspaces/apps/Terminal/Terminal.tsx
similarity index 100%
rename from src/workspaces/widgets/Terminal/Terminal.tsx
rename to src/workspaces/apps/Terminal/Terminal.tsx
diff --git a/src/workspaces/widgets/Terminal/index.ts b/src/workspaces/apps/Terminal/index.ts
similarity index 100%
rename from src/workspaces/widgets/Terminal/index.ts
rename to src/workspaces/apps/Terminal/index.ts
diff --git a/src/workspaces/apps/package.json b/src/workspaces/apps/package.json
new file mode 100644
index 00000000..04bb5e56
--- /dev/null
+++ b/src/workspaces/apps/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "apps",
+ "private": true,
+ "exports": {
+ "./Terminal": "./Terminal/index.ts",
+ "./FileBrowser": "./FileBrowser/index.ts",
+ "./ChatHistory": "./ChatHistory/index.ts",
+ "./Chat": "./Chat/index.ts",
+ "./CodeEditor": "./CodeEditor/index.ts"
+ }
+}
diff --git a/src/workspaces/components/Workspace/WidgetPicker.tsx b/src/workspaces/components/Workspace/AppPicker.tsx
similarity index 75%
rename from src/workspaces/components/Workspace/WidgetPicker.tsx
rename to src/workspaces/components/Workspace/AppPicker.tsx
index d5c50f11..d04bd181 100644
--- a/src/workspaces/components/Workspace/WidgetPicker.tsx
+++ b/src/workspaces/components/Workspace/AppPicker.tsx
@@ -1,11 +1,11 @@
-import type { WidgetRegistry } from './types';
+import type { AppRegistry } from './types';
-type WidgetPickerProps = {
- registry: WidgetRegistry;
- onSelect: (widgetType: string) => void;
+type AppPickerProps = {
+ registry: AppRegistry;
+ onSelect: (appType: string) => void;
};
-export const WidgetPicker = ({ registry, onSelect }: WidgetPickerProps) => {
+export const AppPicker = ({ registry, onSelect }: AppPickerProps) => {
const entries = Object.entries(registry);
return (
diff --git a/src/workspaces/components/Workspace/LayoutEditor.tsx b/src/workspaces/components/Workspace/LayoutEditor.tsx
index 740e965c..617478a1 100644
--- a/src/workspaces/components/Workspace/LayoutEditor.tsx
+++ b/src/workspaces/components/Workspace/LayoutEditor.tsx
@@ -6,10 +6,10 @@ type LayoutEditorProps = {
isLastPanel: boolean;
onSplit: (panelId: string, direction: 'horizontal' | 'vertical') => void;
onRemove: (panelId: string) => void;
- onClearWidget: () => void;
+ onClearApp: () => void;
};
-export const LayoutEditor = ({ panelId, hasWidget, isLastPanel, onSplit, onRemove, onClearWidget }: LayoutEditorProps) => (
+export const LayoutEditor = ({ panelId, hasWidget, isLastPanel, onSplit, onRemove, onClearApp }: LayoutEditorProps) => (