-
Channels
+
+ {/* Page Header */}
+
+
Channels
- Connect messaging platforms to your Agent.
+ Channels let you talk to your agent from other platforms like Telegram or Slack. Connect one to chat with your agent anywhere.
- {loading ? (
-
Loading...
- ) : error ? (
-
{error}
- ) : (
-
- )}
+ {/* Configuration Area */}
+
+ {loading ? (
+
Loading...
+ ) : error ? (
+
{error}
+ ) : (
+
+ )}
+
)
}
diff --git a/apps/desktop/src/renderer/src/pages/chat.tsx b/apps/desktop/src/renderer/src/pages/chat.tsx
index 1581bdf6..8900a10a 100644
--- a/apps/desktop/src/renderer/src/pages/chat.tsx
+++ b/apps/desktop/src/renderer/src/pages/chat.tsx
@@ -1,128 +1,9 @@
-import { Button } from '@multica/ui/components/ui/button'
-import { RemoteChat } from '../components/remote-chat'
import { LocalChat } from '../components/local-chat'
-import { useChatModeStore } from '../stores/chat-mode'
-import { useGatewayConnection, type UseGatewayConnectionReturn } from '@multica/hooks/use-gateway-connection'
-
-function ModeNav({ gateway }: { gateway: UseGatewayConnectionReturn }) {
- const { mode, setMode } = useChatModeStore()
-
- if (mode === 'select') return null
-
- return (
-
-
setMode('local')}>
- Local
-
-
setMode('remote')}>
- Remote
-
-
- {mode === 'remote' && gateway.pageState === 'connected' && (
- <>
-
-
- Disconnect
-
- >
- )}
-
- )
-}
-
-function NavButton({
- active,
- onClick,
- children,
-}: {
- active: boolean
- onClick: () => void
- children: React.ReactNode
-}) {
- return (
-
- {children}
-
- )
-}
-
-function ModeSelect() {
- const setMode = useChatModeStore((s) => s.setMode)
-
- return (
-
-
-
Start a Conversation
-
- Choose how you want to connect
-
-
-
-
- setMode('local')}
- className="w-full"
- >
- Local Agent
- (Direct IPC)
-
-
- setMode('remote')}
- className="w-full"
- >
- Remote Agent
- (Via Gateway)
-
-
-
- )
-}
export default function ChatPage() {
- const mode = useChatModeStore((s) => s.mode)
- const gateway = useGatewayConnection()
-
return (
-
-
- {mode === 'select' && }
-
- {mode === 'local' && }
-
-
-
-
-
- )
-}
-
-function ChatPanel({
- visible,
- children,
-}: {
- visible: boolean
- children: React.ReactNode
-}) {
- return (
-
- {children}
+
)
}
diff --git a/apps/desktop/src/renderer/src/pages/crons.tsx b/apps/desktop/src/renderer/src/pages/crons.tsx
index 0947d5c3..d278a09d 100644
--- a/apps/desktop/src/renderer/src/pages/crons.tsx
+++ b/apps/desktop/src/renderer/src/pages/crons.tsx
@@ -1,43 +1,30 @@
-import {
- Card,
- CardContent,
- CardDescription,
- CardHeader,
- CardTitle,
-} from '@multica/ui/components/ui/card'
-import { useCronJobs } from '../hooks/use-cron-jobs'
+import { useCronJobsStore } from '../stores/cron-jobs'
import { CronJobList } from '../components/cron-job-list'
export default function CronsPage() {
- const {
- jobs,
- loading,
- error,
- toggleJob,
- removeJob,
- refresh,
- } = useCronJobs()
+ const { jobs, loading, error, toggleJob, removeJob, refresh } = useCronJobsStore()
return (
-
-
-
- Cron Jobs
-
- View and manage scheduled tasks. Create new jobs by asking the Agent in Chat.
-
-
-
-
-
-
+
+ {/* Page Header */}
+
+
Scheduled Tasks
+
+ Scheduled tasks run automatically at set times. Ask your agent to create one, like "remind me every morning" or "check my inbox daily."
+
+
+
+ {/* Configuration Area */}
+
+
+
)
}
diff --git a/apps/desktop/src/renderer/src/pages/layout.tsx b/apps/desktop/src/renderer/src/pages/layout.tsx
index 9d0f0c31..01921318 100644
--- a/apps/desktop/src/renderer/src/pages/layout.tsx
+++ b/apps/desktop/src/renderer/src/pages/layout.tsx
@@ -1,18 +1,22 @@
-import { Outlet, NavLink, useLocation } from 'react-router-dom'
+import { Outlet, NavLink, useLocation, useNavigate } from 'react-router-dom'
import { Toaster } from '@multica/ui/components/ui/sonner'
+import { Button } from '@multica/ui/components/ui/button'
import { HugeiconsIcon } from '@hugeicons/react'
import {
Home01Icon,
Comment01Icon,
- CodeIcon,
- PlugIcon,
- Share08Icon,
- Time04Icon,
+ PuzzleIcon,
+ Wrench01Icon,
+ Message01Icon,
+ RepeatIcon,
+ ArrowLeft02Icon,
+ ArrowRight02Icon,
} from '@hugeicons/core-free-icons'
import {
Sidebar,
SidebarContent,
SidebarGroup,
+ SidebarGroupLabel,
SidebarHeader,
SidebarInset,
SidebarMenu,
@@ -26,19 +30,67 @@ import { cn } from '@multica/ui/lib/utils'
import { ModeToggle } from '../components/mode-toggle'
import { DeviceConfirmDialog } from '../components/device-confirm-dialog'
-const navItems = [
+const mainNavItems = [
{ path: '/', label: 'Home', icon: Home01Icon },
{ path: '/chat', label: 'Chat', icon: Comment01Icon },
- { path: '/tools', label: 'Tools', icon: CodeIcon },
- { path: '/skills', label: 'Skills', icon: PlugIcon },
- { path: '/channels', label: 'Channels', icon: Share08Icon },
- { path: '/crons', label: 'Crons', icon: Time04Icon },
]
+const configNavItems = [
+ { path: '/skills', label: 'Skills', icon: PuzzleIcon },
+ { path: '/tools', label: 'Tools', icon: Wrench01Icon },
+ { path: '/channels', label: 'Channels', icon: Message01Icon },
+ { path: '/crons', label: 'Crons', icon: RepeatIcon },
+]
+
+// All nav items for header lookup
+const allNavItems = [...mainNavItems, ...configNavItems]
+
+function NavigationButtons() {
+ const navigate = useNavigate()
+ // useLocation() triggers re-render on route change so we can re-evaluate history state
+ useLocation()
+
+ const historyIdx = window.history.state?.idx ?? 0
+ const canGoBack = historyIdx > 0
+ const canGoForward = historyIdx < window.history.length - 1
+
+ return (
+
+ navigate(-1)}
+ disabled={!canGoBack}
+ >
+
+
+ navigate(1)}
+ disabled={!canGoForward}
+ >
+
+
+
+ )
+}
+
function MainHeader() {
const { state, isMobile } = useSidebar()
+ const location = useLocation()
const needsTrafficLightSpace = state === 'collapsed' || isMobile
+ // Find current page info
+ const currentPage = allNavItems.find((item) =>
+ item.path === '/'
+ ? location.pathname === '/'
+ : location.pathname.startsWith(item.path)
+ )
+
return (