diff --git a/frontend/src/components/AppStoreView.tsx b/frontend/src/components/AppStoreView.tsx index 7b037774..a9279dd8 100644 --- a/frontend/src/components/AppStoreView.tsx +++ b/frontend/src/components/AppStoreView.tsx @@ -13,7 +13,6 @@ import { apiFetch } from '@/lib/api'; import { useDeployFeedback } from '@/context/DeployFeedbackContext'; import { useNodes } from '@/context/NodeContext'; import { useAuth } from '@/context/AuthContext'; -import { CursorProvider, CursorContainer, Cursor, CursorFollow } from '@/components/animate-ui/primitives/animate/cursor'; import { CategorySidebar } from '@/components/appstore/CategorySidebar'; import { FeaturedHero } from '@/components/appstore/FeaturedHero'; import { TemplateTile } from '@/components/appstore/TemplateTile'; @@ -252,6 +251,22 @@ export function AppStoreView({ onDeploySuccess }: AppStoreViewProps) { return [...base].sort((a, b) => (b.stars ?? 0) - (a.stars ?? 0)); }, [filtered, featuredTemplate]); + const essentialsConflicts = useMemo(() => { + if (!selectedTemplate?.ports) return []; + const seen = new Set(); + const conflicts: Array<{ host: string; info: PortInUseInfo }> = []; + for (const p of selectedTemplate.ports) { + const parts = p.split(':'); + if (parts.length < 2) continue; + const host = portVars[p] || parts[0]; + if (!host || seen.has(host)) continue; + seen.add(host); + const info = portsInUse[host]; + if (info) conflicts.push({ host, info }); + } + return conflicts; + }, [selectedTemplate, portVars, portsInUse]); + return (
@@ -427,10 +442,30 @@ export function AppStoreView({ onDeploySuccess }: AppStoreViewProps) {

-
- Deploy with defaults: the template's recommended ports, volumes, and environment variables. - Use the Advanced tab to customize. -
+ {essentialsConflicts.length > 0 ? ( +
+ +
+ Port conflict +
+
    + {essentialsConflicts.map(({ host, info }) => ( +
  • + Port {host} is in use by{' '} + {info.stack ?? 'an external app'}. +
  • + ))} +
+

+ Switch to the Advanced tab to remap. +

+
+ ) : ( +
+ Deploy with defaults: the template's recommended ports, volumes, and environment variables. + Use the Advanced tab to customize. +
+ )} )} @@ -454,28 +489,15 @@ export function AppStoreView({ onDeploySuccess }: AppStoreViewProps) { }} className={cn('w-24 text-center font-mono', hostPort && !isValidPort(hostPort) && 'border-destructive')} /> - {conflict ? ( - - - : {parts[1]} - - - -
- - -
- - {conflict.stack !== null - ? <>Port {hostPort} used by {conflict.stack} - : <>Port {hostPort} used by an external app - } - -
-
- - ) : ( - : {parts[1]} + : {parts[1]} + {conflict && ( + <> + + in use by{' '} + {conflict.stack ?? 'an external app'} + + + )}
);