[eric] marketplace: its own dock app via a shell-global Directory host, dialogs grouped under Directory/dialogs

This commit is contained in:
ciregenz
2026-08-05 10:06:15 -07:00
parent 3e8aa56614
commit d81690efb5
5 changed files with 31 additions and 4 deletions
@@ -28,6 +28,7 @@ import { fetchOutputs } from '@/shared/state/outputsSlice';
import UpdateReadyPill from '@/app/components/Layout/UpdateReadyPill';
import ShareRequestHost from '@/app/components/share/ShareRequestHost';
import CardContextMenu from '@/app/pages/Dashboard/desktop/CardContextMenu';
import MarketplaceHost from '@/app/pages/Directory/MarketplaceHost';
import { findBrowserByWebContentsId } from '@/shared/browserRegistry';
import { byPreviewRecency } from '@/shared/previewOrder';
import { useClaudeTokens, useThemeAccent, useThemeWash } from '@/shared/styles/ThemeContext';
@@ -581,6 +582,8 @@ const AppShell: React.FC = () => {
{/* Shell-global right-click host (portals to body): chat surfaces render on non-dashboard routes too, so the menu can't live inside DashboardCanvas. */}
<CardContextMenu />
<MarketplaceHost />
</Box>
);
};
@@ -1,12 +1,13 @@
import React from 'react';
import Box from '@mui/material/Box';
import Tooltip from '@mui/material/Tooltip';
import { Globe, CalendarClock, Settings, LayoutGrid } from 'lucide-react';
import { Globe, CalendarClock, Settings, LayoutGrid, Store } from 'lucide-react';
import { useAppDispatch } from '@/shared/hooks';
import { openSettingsCard, openWorkflowsApp } from '@/shared/state/dashboardLayoutSlice';
import { openMarketplace } from '@/app/pages/Directory/MarketplaceHost';
// The dock reserves room for these before it knows what they are, so the count lives with the list.
export const DOCK_ACTION_COUNT = 4;
export const DOCK_ACTION_COUNT = 5;
interface DockActionTilesProps {
tile: number;
@@ -21,6 +22,7 @@ function DockActionTiles({ tile, onAddBrowser, onApplications, onHoverAway }: Do
const actions: { label: string; Icon: typeof Globe; act: () => void; divider?: boolean }[] = [
{ label: 'New browser', Icon: Globe, act: onAddBrowser },
{ label: 'Workflows', Icon: CalendarClock, act: () => dispatch(openWorkflowsApp()) },
{ label: 'Marketplace', Icon: Store, act: () => openMarketplace() },
{ label: 'Settings', Icon: Settings, act: () => dispatch(openSettingsCard()), divider: true },
{ label: 'Applications', Icon: LayoutGrid, act: onApplications },
];
@@ -19,7 +19,7 @@ import {
RegistrySkill,
} from '@/shared/state/skillRegistrySlice';
import DirectoryFilterBar from './DirectoryFilterBar';
import CommunityInstallConfirm from './CommunityInstallConfirm';
import CommunityInstallConfirm from './dialogs/CommunityInstallConfirm';
interface Props {
onOpenInstalled?: (skillId: string) => void;
@@ -0,0 +1,22 @@
import React, { useEffect, useState } from 'react';
import DirectoryDialog, { DirectoryTab } from './DirectoryDialog';
export const MARKETPLACE_OPEN_EVENT = 'openswarm:open-marketplace';
export function openMarketplace(tab: DirectoryTab = 'skills'): void {
window.dispatchEvent(new CustomEvent(MARKETPLACE_OPEN_EVENT, { detail: { tab } }));
}
// Shell-global Directory mount: the marketplace is its own surface (dock tile, launcher), not just a settings drill-down.
const MarketplaceHost: React.FC = () => {
const [openTab, setOpenTab] = useState<DirectoryTab | null>(null);
useEffect(() => {
const onOpen = (e: Event): void => setOpenTab(((e as CustomEvent).detail?.tab as DirectoryTab) ?? 'skills');
window.addEventListener(MARKETPLACE_OPEN_EVENT, onOpen);
return () => window.removeEventListener(MARKETPLACE_OPEN_EVENT, onOpen);
}, []);
if (!openTab) return null;
return <DirectoryDialog open initialTab={openTab} onClose={() => setOpenTab(null)} />;
};
export default MarketplaceHost;
@@ -9,7 +9,7 @@ import StorefrontIcon from '@mui/icons-material/Storefront';
import AddLinkIcon from '@mui/icons-material/AddLink';
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
import DirectoryDialog from '../../Directory/DirectoryDialog';
import AddCustomConnectorDialog from '../../Directory/AddCustomConnectorDialog';
import AddCustomConnectorDialog from '../../Directory/dialogs/AddCustomConnectorDialog';
interface Props {
devMode: boolean;