-
-
{r.name}
-
by {r.owner}
+
+ {ranked.map((r, i) => {
+ const theme = themeForIndex(i)
+ const installedFolder = installedByName.get(r.name)
+ return (
+
installedFolder ? onInstalled(installedFolder) : void startInstall(r.name)}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault()
+ if (installedFolder) onInstalled(installedFolder)
+ else void startInstall(r.name)
+ }
+ }}
+ className={`ma-card ma-pat-${patternFor(r.name)}`}
+ style={{ '--accent': theme.accent, '--glow': theme.glow } as React.CSSProperties}
+ >
+
+ {installedFolder && INSTALLED}
+
+
+
{r.name}
+
by {r.owner}
+
{r.description || 'No description.'}
+
+ {r.repo}
+ {installedFolder ? (
+
+ ) : (
+
+ )}
-
{r.description || 'No description.'}
-
- {installedByName.has(r.name) ? (
-
- ) : (
-
- )}
-
- ))}
+ )
+ })}
)}
diff --git a/apps/x/apps/renderer/src/components/sidebar-content.tsx b/apps/x/apps/renderer/src/components/sidebar-content.tsx
index c8697205..8dfd79de 100644
--- a/apps/x/apps/renderer/src/components/sidebar-content.tsx
+++ b/apps/x/apps/renderer/src/components/sidebar-content.tsx
@@ -3,6 +3,7 @@
import * as React from "react"
import { useCallback, useEffect, useRef, useState } from "react"
import {
+ AppWindow,
ArrowUpRight,
Bot,
ChevronRight,
@@ -16,6 +17,7 @@ import {
LayoutGrid,
Mic,
MoreVertical,
+ PanelLeftClose,
Pencil,
Pin,
SquarePen,
@@ -71,7 +73,14 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
+import {
+ ContextMenu,
+ ContextMenuContent,
+ ContextMenuItem,
+ ContextMenuTrigger,
+} from "@/components/ui/context-menu"
import { cn } from "@/lib/utils"
+import { getPinnedApps, onPinnedAppsChanged, unpinApp } from "@/lib/pinned-apps"
import { isOutOfCredits, CREDIT_EXHAUSTED_EVENT, CREDIT_REPLENISHED_EVENT } from "@/lib/credit-status"
import { SettingsDialog } from "@/components/settings-dialog"
import { MascotFaceIcon } from "@/components/talking-head"
@@ -180,6 +189,8 @@ type SidebarContentPanelProps = {
onOpenCode?: () => void
onOpenBgTasks?: () => void
onOpenApps?: () => void
+ /** Open a specific app (pinned in the sidebar) inside the Apps view. */
+ onOpenApp?: (folder: string) => void
onOpenAgent?: (slug: string) => void
recentRuns?: { id: string; title?: string; createdAt: string; modifiedAt?: string }[]
onOpenRun?: (runId: string) => void
@@ -436,6 +447,7 @@ export function SidebarContentPanel({
onOpenCode,
onOpenBgTasks,
onOpenApps,
+ onOpenApp,
recentRuns = [],
onOpenRun,
onRenameRun,
@@ -604,6 +616,27 @@ export function SidebarContentPanel({
return [...pinned, ...rest.slice(0, Math.max(0, 10 - pinned.length))]
}, [recentRuns, pinnedChatIds])
+ // Apps pinned to the sidebar (right-click an app card in the Apps view).
+ // Names resolve via apps:list; until then rows show the folder slug, and
+ // pins whose app no longer exists are hidden (but kept in storage).
+ const [pinnedAppFolders, setPinnedAppFolders] = useState
(() => getPinnedApps())
+ const [pinnedAppNames, setPinnedAppNames] = useState