99 MothershipResourcesProvider ,
1010 MothershipView ,
1111} from '@/app/workspace/[workspaceId]/home/components'
12+ import { getResourceConfig } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry'
1213import type {
1314 MothershipResource ,
1415 MothershipResourceType ,
@@ -91,10 +92,14 @@ export function WorkflowWithChat() {
9192
9293 // ── Stage stack ──────────────────────────────────────────────────────────
9394 // Non-workflow resources never replace the editor: they slide in as a card
94- // IN FRONT of it (toast-stack depth), with the workflow's title strip
95- // peeking above. Clicking the peek, the ×, or pressing Escape brings the
96- // workflow forward. Tabs are the same workspace-owned strip as everywhere.
95+ // IN FRONT of it (toast-stack depth) with the workflow's identity bar
96+ // peeking above. The stack is a two-way flip — bringing the workflow
97+ // forward tucks the resource card into a small tab at the bottom edge, so
98+ // both stay one click apart. Only the card's × (or closing the last tab)
99+ // tears the stack down. Tabs are the same workspace-owned strip as
100+ // everywhere.
97101 const [ stackOpen , setStackOpen ] = useState < boolean > ( ( ) => Boolean ( searchParams . get ( 'resource' ) ) )
102+ const [ stageFront , setStageFront ] = useState < 'card' | 'editor' > ( 'card' )
98103 const initialStageIdRef = useRef ( searchParams . get ( 'resource' ) )
99104
100105 const workspaceTabs = useMothershipTabsStore ( ( s ) =>
@@ -109,19 +114,22 @@ export function WorkflowWithChat() {
109114 [ workspaceTabs ]
110115 )
111116 const stageActiveId = workspaceTabs ?. activeTabId ?? null
117+ const activeStageTab = stageTabs . find ( ( tab ) => tab . id === stageActiveId ) ?? stageTabs [ 0 ]
112118
113119 const stageResource = useCallback (
114120 ( resource : MothershipResource ) => {
115121 if ( ! workspaceId ) return
116122 openTabs ( workspaceId , [ resource ] , { focusId : resource . id } )
117123 setStackOpen ( true )
124+ setStageFront ( 'card' )
118125 reflectParam ( 'resource' , resource . id )
119126 } ,
120127 [ openTabs , workspaceId , reflectParam ]
121128 )
122129
123130 const collapseStack = useCallback ( ( ) => {
124131 setStackOpen ( false )
132+ setStageFront ( 'card' )
125133 reflectParam ( 'resource' , null )
126134 } , [ reflectParam ] )
127135
@@ -155,14 +163,15 @@ export function WorkflowWithChat() {
155163 return useMothershipTabsStore . persist . onFinishHydration ( apply )
156164 } , [ workspaceId , setActiveTab , openTabs ] )
157165
166+ /** Escape flips the editor forward (the stack stays one click away). */
158167 useEffect ( ( ) => {
159- if ( ! stackOpen ) return
168+ if ( ! stackOpen || stageFront !== 'card' ) return
160169 const onKeyDown = ( event : KeyboardEvent ) => {
161- if ( event . key === 'Escape' ) collapseStack ( )
170+ if ( event . key === 'Escape' ) setStageFront ( 'editor' )
162171 }
163172 window . addEventListener ( 'keydown' , onKeyDown )
164173 return ( ) => window . removeEventListener ( 'keydown' , onKeyDown )
165- } , [ stackOpen , collapseStack ] )
174+ } , [ stackOpen , stageFront ] )
166175
167176 /**
168177 * Closing the last tab leaves nothing to show — the editor comes forward.
@@ -310,7 +319,7 @@ export function WorkflowWithChat() {
310319 workflowId = { workflowId }
311320 chatDock = { { isOpen : dock . open , onSelectChat : openChat } }
312321 />
313- { stackOpen && (
322+ { stackOpen && stageFront === 'card' && (
314323 < >
315324 { /* Opaque stage backdrop: the editor stays mounted and live
316325 underneath, but the back card shows only its identity — never
@@ -323,19 +332,19 @@ export function WorkflowWithChat() {
323332 < button
324333 type = 'button'
325334 aria-label = 'Back to workflow'
326- onClick = { collapseStack }
327- className = 'absolute inset-x-4 top-2 z-30 flex h-[52px ] items-start rounded-t-xl border border-[var(--border-1)] bg-[var(--surface-5 )] px-3 transition-colors hover-hover:bg-[var(--surface-active)] dark:bg-[var(--surface-4 )]'
335+ onClick = { ( ) => setStageFront ( 'editor' ) }
336+ className = 'absolute inset-x-4 top-2 z-30 flex h-[38px ] items-start rounded-t-lg border border-[var(--border-1)] bg-[var(--bg )] px-3 transition-colors hover-hover:bg-[var(--surface-active)]'
328337 >
329- < span className = 'flex h-[42px ] min-w-0 items-center gap-1.5' >
330- < WorkflowIcon className = 'size-[14px ] flex-shrink-0 text-[var(--text-icon)]' />
331- < span className = 'truncate font-medium text-[14px ] text-[var(--text-body)]' >
338+ < span className = 'flex h-[26px ] min-w-0 items-center gap-1.5' >
339+ < WorkflowIcon className = 'size-[12px ] flex-shrink-0 text-[var(--text-icon)]' />
340+ < span className = 'truncate font-medium text-[12px ] text-[var(--text-body)]' >
332341 { workflowName }
333342 </ span >
334343 </ span >
335344 </ button >
336345 { /* The front card: the workspace resource tabs + active content,
337346 a fully detached rounded pane over the back card. */ }
338- < div className = 'absolute inset-x-2 top-[46px ] bottom-2 z-30 flex animate-slide-in-bottom flex-col overflow-hidden rounded-xl border border-[var(--border-1)] bg-[var(--bg)] shadow-sm' >
347+ < div className = 'absolute inset-x-2 top-[32px ] bottom-2 z-30 flex animate-slide-in-bottom flex-col overflow-hidden rounded-xl border border-[var(--border-1)] bg-[var(--bg)] shadow-sm' >
339348 < MothershipResourcesProvider
340349 selectResource = { selectStageTab }
341350 addResource = { addStageTab }
@@ -364,12 +373,38 @@ export function WorkflowWithChat() {
364373 </ button >
365374 </ Tooltip . Trigger >
366375 < Tooltip . Content side = 'bottom' >
367- < p > Back to workflow </ p >
376+ < p > Close resources </ p >
368377 </ Tooltip . Content >
369378 </ Tooltip . Root >
370379 </ div >
371380 </ >
372381 ) }
382+ { stackOpen && stageFront === 'editor' && activeStageTab && (
383+ /* The resource card tucked at the bottom edge while the editor is
384+ forward — same identity-bar treatment as the workflow's, so the
385+ two sides of the stack stay one click apart. */
386+ < button
387+ type = 'button'
388+ aria-label = 'Show resources'
389+ onClick = { ( ) => setStageFront ( 'card' ) }
390+ className = 'absolute right-4 bottom-0 z-30 flex h-[32px] items-start rounded-t-lg border border-[var(--border-1)] border-b-0 bg-[var(--bg)] px-3 shadow-sm transition-colors hover-hover:bg-[var(--surface-active)]'
391+ >
392+ < span className = 'flex h-[30px] min-w-0 items-center gap-1.5' >
393+ { getResourceConfig ( activeStageTab . type ) . renderTabIcon (
394+ activeStageTab ,
395+ 'size-[12px] flex-shrink-0 text-[var(--text-icon)]'
396+ ) }
397+ < span className = 'max-w-[200px] truncate font-medium text-[12px] text-[var(--text-body)]' >
398+ { activeStageTab . title }
399+ </ span >
400+ { stageTabs . length > 1 && (
401+ < span className = 'text-[11px] text-[var(--text-muted)]' >
402+ +{ stageTabs . length - 1 }
403+ </ span >
404+ ) }
405+ </ span >
406+ </ button >
407+ ) }
373408 </ div >
374409 </ div >
375410 )
0 commit comments