Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/renderer/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { useRoxyStore } from './lib/store'
import roxy from './assets/roxy.png'
import Onboarding from './routes/Onboarding'
import Chat from './routes/Chat'
import Integrations from './routes/Integrations'
import Skills from './routes/Skills'
import Mcp from './routes/Mcp'
import Marketplace from './routes/Marketplace'
import Settings from './routes/Settings'

function Splash(): JSX.Element {
Expand Down Expand Up @@ -39,7 +39,9 @@ export default function App(): JSX.Element {
<Routes>
<Route path="/onboarding" element={<Onboarding />} />
<Route path="/" element={onboarded ? <Chat /> : <Navigate to="/onboarding" replace />} />
<Route path="/integrations" element={<Integrations />} />
<Route path="/marketplace" element={<Marketplace />} />
{/* Kept as the deep-dive surfaces the Marketplace links into: the skill
editor and the raw MCP JSON editor. The Marketplace is the front door. */}
<Route path="/skills" element={<Skills />} />
<Route path="/mcp" element={<Mcp />} />
<Route path="/settings" element={<Settings />} />
Expand Down
6 changes: 5 additions & 1 deletion src/renderer/src/components/PageShell.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { type ReactNode } from 'react'
import { ArrowLeft } from 'lucide-react'
import { cn } from '../lib/cn'

export function PageShell({
title,
subtitle,
onBack,
actions,
wide = false,
children
}: {
title: string
subtitle?: string
onBack: () => void
actions?: ReactNode
/** Widen the column for grid/browse pages (Marketplace) instead of prose. */
wide?: boolean
children: ReactNode
}): JSX.Element {
return (
Expand All @@ -28,7 +32,7 @@ export function PageShell({
{actions && <div className="ml-auto">{actions}</div>}
</header>
<div className="min-h-0 flex-1 overflow-y-auto border-t border-border">
<div className="mx-auto max-w-3xl px-6 py-8">
<div className={cn('mx-auto px-6 py-8', wide ? 'max-w-5xl' : 'max-w-3xl')}>
{subtitle && <p className="mb-6 text-sm text-text-muted">{subtitle}</p>}
{children}
</div>
Expand Down
54 changes: 29 additions & 25 deletions src/renderer/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@ import {
} from 'react'
import { useNavigate } from 'react-router-dom'
import {
Blocks,
ChevronRight,
FolderOpen,
GitBranch,
GitFork,
Hammer,
Lightbulb,
MonitorSmartphone,
PanelLeftClose,
PanelLeftOpen,
Plug,
Plus,
Repeat,
Settings as SettingsIcon,
Expand Down Expand Up @@ -439,18 +438,11 @@ export function Sidebar(): JSX.Element {
)}
</button>
<button
onClick={() => navigate('/skills')}
title="Skills"
onClick={() => navigate('/marketplace')}
title="Marketplace — skills, tool servers, channels"
className="press-scale flex h-8 w-8 items-center justify-center sq sq-lg rounded-lg text-text-muted hover:bg-white/5 hover:text-text"
>
<Lightbulb className="h-4 w-4" />
</button>
<button
onClick={() => navigate('/mcp')}
title="MCP Servers"
className="press-scale flex h-8 w-8 items-center justify-center sq sq-lg rounded-lg text-text-muted hover:bg-white/5 hover:text-text"
>
<Plug className="h-4 w-4" />
<Blocks className="h-4 w-4" />
</button>
<button
onClick={() => navigate('/settings')}
Expand Down Expand Up @@ -1008,15 +1000,19 @@ function SessionContextMenu({
)
}

/** Counts of discovered skills + configured MCP servers, refreshed when focused. */
function useCustomizeCounts(): { skills: number; mcp: number } {
const [counts, setCounts] = useState<{ skills: number; mcp: number }>({ skills: 0, mcp: 0 })
/**
* How many add-ons are installed — skills + MCP servers, counted together because
* the Marketplace presents them as one kind of thing. Refreshed on focus, since
* the agent can add either mid-session.
*/
function useAddonCount(): number {
const [count, setCount] = useState(0)
useEffect(() => {
let alive = true
const load = (): void => {
Promise.all([api.skills.list(), api.mcp.list()])
.then(([skills, mcp]) => {
if (alive) setCounts({ skills: skills.length, mcp: mcp.length })
if (alive) setCount(skills.length + mcp.length)
})
.catch(() => {})
}
Expand All @@ -1028,13 +1024,17 @@ function useCustomizeCounts(): { skills: number; mcp: number } {
window.removeEventListener('focus', load)
}
}, [])
return counts
return count
}

/**
* The utility section pinned to the bottom of the sidebar — quick access to
* Remote Workspace (share to phone), plus the Skills and MCP Servers pages
* (à la VS Code's Customizations panel), with a live count/indicator on each.
* The utility section pinned to the bottom of the sidebar.
*
* This used to be three entries — Skills, MCP Servers, and the Remote Workspace
* dialog — which asked the user to know which mechanism their wish belonged to
* before they could go looking for it. Now there is one **Marketplace** door for
* "what else can Roxy do?", and Remote Workspace keeps its button only because it
* is an *action* (start sharing now) rather than a thing to browse.
*/
function CustomizeNav({
onOpenRemote,
Expand All @@ -1044,17 +1044,21 @@ function CustomizeNav({
remoteDot: 'green' | 'amber' | null
}): JSX.Element {
const navigate = useNavigate()
const counts = useCustomizeCounts()
const addons = useAddonCount()
const items: {
label: string
icon: typeof Lightbulb
icon: typeof Blocks
onClick: () => void
count?: number
dot?: 'green' | 'amber' | null
}[] = [
{ label: 'Remote Workspace', icon: MonitorSmartphone, onClick: onOpenRemote, dot: remoteDot },
{ label: 'Skills', icon: Lightbulb, onClick: () => navigate('/skills'), count: counts.skills },
{ label: 'MCP Servers', icon: Plug, onClick: () => navigate('/mcp'), count: counts.mcp }
{
label: 'Marketplace',
icon: Blocks,
onClick: () => navigate('/marketplace'),
count: addons
},
{ label: 'Remote Workspace', icon: MonitorSmartphone, onClick: onOpenRemote, dot: remoteDot }
]
return (
<div className="border-t border-border px-3 py-2">
Expand Down
33 changes: 32 additions & 1 deletion src/renderer/src/lib/icons.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
import {
Brain,
Code,
Cpu,
Database,
FilePen,
FileText,
FlaskConical,
Folder,
GitBranch,
Globe,
Hash,
History,
Inbox,
LayoutGrid,
Library,
Mail,
MessageCircle,
MessagesSquare,
Mic,
Monitor,
MonitorSmartphone,
MousePointer,
Plug,
Search,
Send,
Shield,
Smartphone,
Terminal,
Timer,
type LucideIcon
} from 'lucide-react'

Expand All @@ -31,7 +46,23 @@ const ICONS: Record<string, LucideIcon> = {
folder: Folder,
terminal: Terminal,
search: Search,
code: Code
code: Code,
// Marketplace: add-on kinds, capabilities and catalog entries.
brain: Brain,
cpu: Cpu,
database: Database,
'file-pen': FilePen,
'file-text': FileText,
'flask-conical': FlaskConical,
history: History,
'layout-grid': LayoutGrid,
library: Library,
mic: Mic,
monitor: Monitor,
'monitor-smartphone': MonitorSmartphone,
'mouse-pointer': MousePointer,
plug: Plug,
timer: Timer
}

export function CatalogIcon({
Expand Down
56 changes: 0 additions & 56 deletions src/renderer/src/routes/Integrations.tsx

This file was deleted.

Loading