diff --git a/frontend/src/app/pages/Dashboard/desktop/ApplicationsWindow.tsx b/frontend/src/app/pages/Dashboard/desktop/ApplicationsWindow.tsx
index 4458522e..1d63b931 100644
--- a/frontend/src/app/pages/Dashboard/desktop/ApplicationsWindow.tsx
+++ b/frontend/src/app/pages/Dashboard/desktop/ApplicationsWindow.tsx
@@ -19,33 +19,22 @@ function hueFor(name: string): number {
return h % 360;
}
+// Every tile is a generated ICON (gradient + monogram): a screenshot crushed to 68px reads as mud,
+// so the screenshot lives in the hover preview instead and the row stays dock-uniform.
function AppTile({ output }: { output: Output }): React.ReactElement {
- const tile = {
- width: 68,
- height: 68,
- borderRadius: '16px',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- flexShrink: 0,
- // Hairline inset so screenshots and light tiles read as crafted app icons on the glass, not raw pasted images.
- boxShadow: 'inset 0 0 0 1px rgba(255,255,255,0.14), 0 6px 18px rgba(0,0,0,0.32)',
- } as const;
- if (output.thumbnail) {
- return (
-
-
- {/* A soft top gloss makes a raw screenshot read as an app ICON instead of a pasted image. */}
-
-
- );
- }
const glyph = (output.icon || '').trim();
const h = hueFor(output.name || '?');
return (
+
+
+ );
+}
+
/** Launchpad-style window over the canvas: the user's OpenSwarm apps, newest first. Deliberately NOT the machine's /Applications; this launcher is for things built in OpenSwarm. */
function ApplicationsWindow({ outputs, onOpenApp, onClose }: ApplicationsWindowProps): React.ReactElement {
const [query, setQuery] = useState('');
+ const [preview, setPreview] = useState<{ src: string; x: number; y: number; below: boolean } | null>(null);
const apps = useMemo(() => {
const all = Object.values(outputs);
@@ -153,6 +170,13 @@ function ApplicationsWindow({ outputs, onOpenApp, onClose }: ApplicationsWindowP
{ onOpenApp(output.id); onClose(); }}
+ onMouseEnter={(e: React.MouseEvent) => {
+ if (!output.thumbnail) { setPreview(null); return; }
+ const r = e.currentTarget.getBoundingClientRect();
+ const below = r.top < 170;
+ setPreview({ src: output.thumbnail, x: r.left + r.width / 2, y: below ? r.bottom + 10 : r.top - 148, below });
+ }}
+ onMouseLeave={() => setPreview(null)}
title={output.description || output.name}
sx={{
display: 'flex',
@@ -180,6 +204,7 @@ function ApplicationsWindow({ outputs, onOpenApp, onClose }: ApplicationsWindowP
)}
+ {preview && }
>
);
}