diff --git a/frontend/src/app/components/Layout/AppShell.tsx b/frontend/src/app/components/Layout/AppShell.tsx
index 3159f6e6..43533e1d 100644
--- a/frontend/src/app/components/Layout/AppShell.tsx
+++ b/frontend/src/app/components/Layout/AppShell.tsx
@@ -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. */}
+
+
);
};
diff --git a/frontend/src/app/pages/Dashboard/desktop/DockActionTiles.tsx b/frontend/src/app/pages/Dashboard/desktop/DockActionTiles.tsx
index cea69f8b..626f2b06 100644
--- a/frontend/src/app/pages/Dashboard/desktop/DockActionTiles.tsx
+++ b/frontend/src/app/pages/Dashboard/desktop/DockActionTiles.tsx
@@ -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 },
];
diff --git a/frontend/src/app/pages/Directory/DirectorySkillsTab.tsx b/frontend/src/app/pages/Directory/DirectorySkillsTab.tsx
index b1d479f3..371c4d67 100644
--- a/frontend/src/app/pages/Directory/DirectorySkillsTab.tsx
+++ b/frontend/src/app/pages/Directory/DirectorySkillsTab.tsx
@@ -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;
diff --git a/frontend/src/app/pages/Directory/MarketplaceHost.tsx b/frontend/src/app/pages/Directory/MarketplaceHost.tsx
new file mode 100644
index 00000000..a617e9c9
--- /dev/null
+++ b/frontend/src/app/pages/Directory/MarketplaceHost.tsx
@@ -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(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 setOpenTab(null)} />;
+};
+
+export default MarketplaceHost;
diff --git a/frontend/src/app/pages/Tools/dialogs/ToolsAddMenu.tsx b/frontend/src/app/pages/Tools/dialogs/ToolsAddMenu.tsx
index 6f974566..c39194b3 100644
--- a/frontend/src/app/pages/Tools/dialogs/ToolsAddMenu.tsx
+++ b/frontend/src/app/pages/Tools/dialogs/ToolsAddMenu.tsx
@@ -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;