diff --git a/frontend/src/app/pages/Dashboard/cards/DashboardViewCard.tsx b/frontend/src/app/pages/Dashboard/cards/DashboardViewCard.tsx index 52d016a3..6406c226 100644 --- a/frontend/src/app/pages/Dashboard/cards/DashboardViewCard.tsx +++ b/frontend/src/app/pages/Dashboard/cards/DashboardViewCard.tsx @@ -30,6 +30,7 @@ import TerminalPanel, { TerminalLine } from '@/app/pages/Views/TerminalPanel'; import AppCodePanel from '@/app/pages/Views/AppCodePanel'; import HistoryPanel from '@/app/pages/Views/HistoryPanel'; import ShareButton from '@/app/components/share/ShareButton'; +import { CONTENT_OVERLAY_Z, RESIZE_HANDLE_Z } from './cardLayers'; import ShareModal from '@/app/components/share/ShareModal'; import { getDefault } from '@/shared/inputSchemaDefaults'; import { useOverlayScrollPassthrough } from '../hooks/interaction/useOverlayScrollPassthrough'; @@ -885,7 +886,7 @@ const DashboardViewCard: React.FC = ({ /> {/* Code/Terminal overlay the always-mounted preview instead of replacing it: unmounting the webview kills the app's live state and forces a reload on switch-back. */} {output.workspace_id && activeView !== 'preview' && ( - + {activeView === 'terminal' ? ( ) : activeView === 'history' ? ( @@ -916,7 +917,7 @@ const DashboardViewCard: React.FC = ({ position: 'absolute', cursor: CURSOR_MAP[dir], opacity: 0, - zIndex: 10, + zIndex: RESIZE_HANDLE_Z, ...sx, }} /> diff --git a/frontend/src/app/pages/Dashboard/cards/cardLayers.test.ts b/frontend/src/app/pages/Dashboard/cards/cardLayers.test.ts new file mode 100644 index 00000000..c450f67d --- /dev/null +++ b/frontend/src/app/pages/Dashboard/cards/cardLayers.test.ts @@ -0,0 +1,21 @@ +// Run: npm test (frontend/scripts/run-tests.mjs) +// +// ENG-290. App cards could only be resized on the preview tab: the code/terminal/history panel fills +// the card and sat ABOVE the resize handles, so the grab strips were buried the moment you switched +// tabs. The bug was purely an ordering mistake between two numbers written 700 lines apart, which is +// the kind of thing nobody re-derives while reading a component. +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { CONTENT_OVERLAY_Z, RESIZE_HANDLE_Z } from './cardLayers.ts'; + +test('resize handles sit above any content that fills the card', () => { + assert.ok( + RESIZE_HANDLE_Z > CONTENT_OVERLAY_Z, + `handles at ${RESIZE_HANDLE_Z} are under the content overlay at ${CONTENT_OVERLAY_Z}, so the ` + + 'card cannot be resized wherever that content shows', + ); +}); + +test('the two layers are distinct, so neither can silently absorb the other', () => { + assert.notEqual(RESIZE_HANDLE_Z, CONTENT_OVERLAY_Z); +}); diff --git a/frontend/src/app/pages/Dashboard/cards/cardLayers.ts b/frontend/src/app/pages/Dashboard/cards/cardLayers.ts new file mode 100644 index 00000000..c075388a --- /dev/null +++ b/frontend/src/app/pages/Dashboard/cards/cardLayers.ts @@ -0,0 +1,16 @@ +// Where things stack inside a canvas card (ENG-290). +// +// App cards could only be resized on the preview tab. The Code/Terminal/History panel renders only +// when `activeView !== 'preview'`, covers the card with `inset: 0`, and sat at z-index 13 while the +// resize handles sat at 10, so switching tabs buried the handles under the panel. AgentCard already +// used 20 for the same handles, so this was one component drifting rather than a design question. +// +// Named and ordered here so the next panel someone adds has an obvious ceiling to stay under, and +// so the ordering is a thing a test can check rather than a number to eyeball. + +/** Full-card content that replaces the preview (code, terminal, history). */ +export const CONTENT_OVERLAY_Z = 13; + +/** The invisible grab strips at the card's edges. Must sit ABOVE any content that fills the card, + * or the edges stop being grabbable wherever that content is showing. */ +export const RESIZE_HANDLE_Z = 20;