Skip to content

Commit 513fcc3

Browse files
committed
fix(app): open task manager inside ssh session
1 parent c29d8b6 commit 513fcc3

6 files changed

Lines changed: 257 additions & 19 deletions

File tree

18 KB
Loading
44 KB
Loading

packages/app/src/web/app-ready-terminal-actions.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,28 @@ import { deleteProjectTerminalSession } from "./api.js"
1111
import type { createActionContext } from "./app-ready-actions.js"
1212
import type { ReadyState } from "./app-ready-hooks.js"
1313
import { browserMenuIndex } from "./menu.js"
14-
import { projectPickerScreen } from "./screen.js"
14+
15+
type TerminalTaskManagerState = Pick<
16+
ReadyState,
17+
| "setProjectTaskLogs"
18+
| "setProjectTasks"
19+
| "setProjectTasksIncludeDefault"
20+
| "setSelectedMenuIndex"
21+
| "setSelectedProjectId"
22+
>
23+
24+
export const openTerminalTaskManager = (
25+
actionContext: ReturnType<typeof createActionContext>,
26+
state: TerminalTaskManagerState,
27+
projectId: string
28+
): void => {
29+
state.setSelectedProjectId(projectId)
30+
state.setSelectedMenuIndex(browserMenuIndex("Tasks"))
31+
state.setProjectTasks(null)
32+
state.setProjectTaskLogs("")
33+
state.setProjectTasksIncludeDefault(false)
34+
loadProjectTasksById(actionContext, projectId, { includeDefault: false })
35+
}
1536

1637
export const bindTerminalActions = (
1738
actionContext: ReturnType<typeof createActionContext>,
@@ -30,15 +51,7 @@ export const bindTerminalActions = (
3051
connectProjectById(projectId, actionContext, projectKey)
3152
},
3253
onOpenProjectTaskManagerById: (projectId: string) => {
33-
state.setSelectedProjectId(projectId)
34-
state.setSelectedMenuIndex(browserMenuIndex("Tasks"))
35-
state.setProjectNavigationArmed(true)
36-
state.setActiveScreen(projectPickerScreen())
37-
state.deactivateTerminalWorkspace()
38-
state.setProjectTasks(null)
39-
state.setProjectTaskLogs("")
40-
state.setProjectTasksIncludeDefault(false)
41-
loadProjectTasksById(actionContext, projectId, { includeDefault: false })
54+
openTerminalTaskManager(actionContext, state, projectId)
4255
},
4356
onAttachProjectTerminalSession: (
4457
projectId: string,

packages/app/src/web/app-ready-terminal-screen.tsx

Lines changed: 151 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,40 @@
11
import { Effect } from "effect"
2-
import type { CSSProperties, JSX } from "react"
2+
import { type CSSProperties, type JSX, useEffect, useState } from "react"
33

44
import { deleteTerminalSessionByPath } from "./api.js"
55
import { canOpenProjectBrowser } from "./app-ready-browser-openable.js"
66
import type { ReadyLayoutProps } from "./app-ready-layout.js"
77
import { Box, Text } from "./elements.js"
8+
import { TaskPanel } from "./panel-tasks.js"
89
import { TerminalPanel } from "./panel-terminal.js"
910
import { type BrowserScreen, projectPickerScreen } from "./screen.js"
1011
import { shouldShowTerminalTabs } from "./terminal-mobile-layout.js"
1112
import { terminalSessionId } from "./terminal-state.js"
1213
import type { ActiveTerminalSession } from "./terminal.js"
1314

15+
type TerminalWorkspaceView = "terminal" | "tasks"
16+
1417
type TerminalScreenProps = Pick<
1518
ReadyLayoutProps,
1619
| "activeTerminalSessionId"
1720
| "onApplyProjectById"
1821
| "onOpenProjectBrowserById"
1922
| "onOpenProjectTaskManagerById"
2023
| "onOpenProjectTerminalById"
24+
| "onLoadProjectTaskLogs"
25+
| "onProjectTasksIncludeDefaultChange"
26+
| "onRefreshProjectTasks"
2127
| "onSelectTerminal"
2228
| "onSetActiveScreen"
29+
| "onStopProjectTask"
2330
| "onTerminalClose"
2431
| "onTerminalMessage"
32+
| "project"
2533
| "projectBrowser"
34+
| "projectTaskLogs"
35+
| "projectTasks"
36+
| "projectTasksIncludeDefault"
37+
| "selectedProjectSummary"
2638
| "terminalSessions"
2739
| "viewportLayout"
2840
>
@@ -34,13 +46,24 @@ type TerminalPaneProps =
3446
| "onOpenProjectBrowserById"
3547
| "onOpenProjectTaskManagerById"
3648
| "onOpenProjectTerminalById"
49+
| "onLoadProjectTaskLogs"
50+
| "onProjectTasksIncludeDefaultChange"
51+
| "onRefreshProjectTasks"
3752
| "onSetActiveScreen"
53+
| "onStopProjectTask"
3854
| "onTerminalClose"
3955
| "onTerminalMessage"
56+
| "project"
4057
| "projectBrowser"
58+
| "projectTaskLogs"
59+
| "projectTasks"
60+
| "projectTasksIncludeDefault"
61+
| "selectedProjectSummary"
4162
| "viewportLayout"
4263
>
4364
& {
65+
readonly taskManagerOpen: boolean
66+
readonly onCloseTaskManager: () => void
4467
readonly singleSession: boolean
4568
readonly terminalSession: ActiveTerminalSession
4669
}
@@ -72,8 +95,83 @@ const activeTerminalPaneStyle: CSSProperties = {
7295
overflow: "hidden"
7396
}
7497

98+
const taskManagerBodyStyle: CSSProperties = {
99+
background: "#080a0d",
100+
boxSizing: "border-box",
101+
color: "#d6e5f7",
102+
height: "100%",
103+
overflow: "auto",
104+
padding: "10px"
105+
}
106+
107+
const taskManagerToolbarStyle: CSSProperties = {
108+
alignItems: "center",
109+
display: "flex",
110+
justifyContent: "flex-end",
111+
marginBottom: "10px"
112+
}
113+
114+
const taskManagerReturnButtonStyle: CSSProperties = {
115+
background: "#171d24",
116+
border: "1px solid #3a4652",
117+
borderRadius: "8px",
118+
color: "#d6e5f7",
119+
cursor: "pointer",
120+
font: "inherit",
121+
padding: "6px 10px"
122+
}
123+
75124
const terminalTabLabel = (session: ActiveTerminalSession): string => session.browserProjectName ?? session.header
76125

126+
const TerminalTaskManagerBody = (
127+
{
128+
onClose,
129+
onLoadProjectTaskLogs,
130+
onProjectTasksIncludeDefaultChange,
131+
onRefreshProjectTasks,
132+
onStopProjectTask,
133+
project,
134+
projectTaskLogs,
135+
projectTasks,
136+
projectTasksIncludeDefault,
137+
selectedProjectSummary
138+
}:
139+
& Pick<
140+
TerminalScreenProps,
141+
| "onLoadProjectTaskLogs"
142+
| "onProjectTasksIncludeDefaultChange"
143+
| "onRefreshProjectTasks"
144+
| "onStopProjectTask"
145+
| "project"
146+
| "projectTaskLogs"
147+
| "projectTasks"
148+
| "projectTasksIncludeDefault"
149+
| "selectedProjectSummary"
150+
>
151+
& {
152+
readonly onClose: () => void
153+
}
154+
): JSX.Element => (
155+
<div style={taskManagerBodyStyle}>
156+
<div style={taskManagerToolbarStyle}>
157+
<button onClick={onClose} style={taskManagerReturnButtonStyle} type="button">
158+
Terminal
159+
</button>
160+
</div>
161+
<TaskPanel
162+
includeDefault={projectTasksIncludeDefault}
163+
logs={projectTaskLogs}
164+
onIncludeDefaultChange={onProjectTasksIncludeDefaultChange}
165+
onLoadLogs={onLoadProjectTaskLogs}
166+
onRefreshTasks={onRefreshProjectTasks}
167+
onStopTask={onStopProjectTask}
168+
project={project}
169+
selectedProjectSummary={selectedProjectSummary}
170+
snapshot={projectTasks}
171+
/>
172+
</div>
173+
)
174+
77175
const TerminalTab = (
78176
{
79177
active,
@@ -211,14 +309,25 @@ const TerminalTabs = (
211309
const TerminalPane = (
212310
{
213311
onApplyProjectById,
312+
onCloseTaskManager,
313+
onLoadProjectTaskLogs,
214314
onOpenProjectBrowserById,
215315
onOpenProjectTaskManagerById,
216316
onOpenProjectTerminalById,
317+
onProjectTasksIncludeDefaultChange,
318+
onRefreshProjectTasks,
217319
onSetActiveScreen,
320+
onStopProjectTask,
218321
onTerminalClose,
219322
onTerminalMessage,
323+
project,
220324
projectBrowser,
325+
projectTaskLogs,
326+
projectTasks,
327+
projectTasksIncludeDefault,
328+
selectedProjectSummary,
221329
singleSession,
330+
taskManagerOpen,
222331
terminalSession,
223332
viewportLayout
224333
}: TerminalPaneProps
@@ -227,6 +336,22 @@ const TerminalPane = (
227336
const browserProjectId = terminalSession.browserProjectId
228337
const browserProjectKey = terminalSession.browserProjectKey
229338
const canOpenBrowser = canOpenProjectBrowser(projectBrowser, browserProjectId)
339+
const bodyContent = taskManagerOpen && browserProjectId !== undefined
340+
? (
341+
<TerminalTaskManagerBody
342+
onClose={onCloseTaskManager}
343+
onLoadProjectTaskLogs={onLoadProjectTaskLogs}
344+
onProjectTasksIncludeDefaultChange={onProjectTasksIncludeDefaultChange}
345+
onRefreshProjectTasks={onRefreshProjectTasks}
346+
onStopProjectTask={onStopProjectTask}
347+
project={project}
348+
projectTaskLogs={projectTaskLogs}
349+
projectTasks={projectTasks}
350+
projectTasksIncludeDefault={projectTasksIncludeDefault}
351+
selectedProjectSummary={selectedProjectSummary}
352+
/>
353+
)
354+
: undefined
230355
const detachTerminalSession = (): void => {
231356
onTerminalClose(sessionId)
232357
if (singleSession) {
@@ -236,6 +361,7 @@ const TerminalPane = (
236361
return (
237362
<div style={activeTerminalPaneStyle}>
238363
<TerminalPanel
364+
bodyContent={bodyContent}
239365
keyboardOpen={viewportLayout.keyboardOpen}
240366
mobileMode={viewportLayout.mode === "mobile"}
241367
onAttachFailure={() => {
@@ -279,12 +405,16 @@ const TerminalPane = (
279405
}
280406

281407
export const TerminalScreen = (props: TerminalScreenProps): JSX.Element | null => {
282-
if (props.terminalSessions.length === 0) {
283-
return null
284-
}
408+
const [terminalView, setTerminalView] = useState<TerminalWorkspaceView>("terminal")
285409
const mobileMode = props.viewportLayout.mode === "mobile"
286410
const activeSessionId = resolveActiveTerminalSessionId(props.terminalSessions, props.activeTerminalSessionId)
287411
const activeSession = props.terminalSessions.find((session) => terminalSessionId(session) === activeSessionId)
412+
useEffect(() => {
413+
setTerminalView("terminal")
414+
}, [activeSession?.browserProjectId, activeSessionId])
415+
if (props.terminalSessions.length === 0) {
416+
return null
417+
}
288418
return (
289419
<Box flexDirection="column" flexGrow={1} gap={mobileMode ? "4px" : 1} minHeight={0} overflow="hidden">
290420
{shouldShowTerminalTabs(mobileMode, props.terminalSessions.length)
@@ -305,14 +435,30 @@ export const TerminalScreen = (props: TerminalScreenProps): JSX.Element | null =
305435
<TerminalPane
306436
key={terminalSessionId(activeSession)}
307437
onApplyProjectById={props.onApplyProjectById}
438+
onCloseTaskManager={() => {
439+
setTerminalView("terminal")
440+
}}
441+
onLoadProjectTaskLogs={props.onLoadProjectTaskLogs}
308442
onOpenProjectBrowserById={props.onOpenProjectBrowserById}
309-
onOpenProjectTaskManagerById={props.onOpenProjectTaskManagerById}
443+
onOpenProjectTaskManagerById={(projectId) => {
444+
setTerminalView("tasks")
445+
props.onOpenProjectTaskManagerById(projectId)
446+
}}
310447
onOpenProjectTerminalById={props.onOpenProjectTerminalById}
448+
onProjectTasksIncludeDefaultChange={props.onProjectTasksIncludeDefaultChange}
449+
onRefreshProjectTasks={props.onRefreshProjectTasks}
311450
onSetActiveScreen={props.onSetActiveScreen}
451+
onStopProjectTask={props.onStopProjectTask}
312452
onTerminalClose={props.onTerminalClose}
313453
onTerminalMessage={props.onTerminalMessage}
454+
project={props.project}
314455
projectBrowser={props.projectBrowser}
456+
projectTaskLogs={props.projectTaskLogs}
457+
projectTasks={props.projectTasks}
458+
projectTasksIncludeDefault={props.projectTasksIncludeDefault}
459+
selectedProjectSummary={props.selectedProjectSummary}
315460
singleSession={props.terminalSessions.length === 1}
461+
taskManagerOpen={terminalView === "tasks"}
316462
terminalSession={activeSession}
317463
viewportLayout={props.viewportLayout}
318464
/>

packages/app/src/web/panel-terminal.tsx

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type TerminalPanelProps = {
2929
readonly onOpenTaskManager?: (() => void) | undefined
3030
readonly onOpenTerminal?: (() => void) | undefined
3131
readonly session: ActiveTerminalSession
32+
readonly bodyContent?: JSX.Element | undefined
3233
}
3334

3435
const panelStyle: CSSProperties = {
@@ -88,6 +89,31 @@ const terminalBodyStyle = (compactTypingMode: boolean, mobileMode: boolean): CSS
8889
return mobileMode ? bodyStyleMobile : bodyStyle
8990
}
9091

92+
const terminalBodyFrameStyle = (compactTypingMode: boolean, mobileMode: boolean): CSSProperties => ({
93+
...terminalBodyStyle(compactTypingMode, mobileMode),
94+
boxSizing: "border-box",
95+
overflow: "hidden",
96+
position: "relative"
97+
})
98+
99+
const terminalHostStyle: CSSProperties = {
100+
height: "100%",
101+
minHeight: 0,
102+
overflow: "hidden"
103+
}
104+
105+
const terminalBodyContentStyle: CSSProperties = {
106+
bottom: 0,
107+
height: "100%",
108+
left: 0,
109+
minHeight: 0,
110+
overflow: "auto",
111+
position: "absolute",
112+
right: 0,
113+
top: 0,
114+
zIndex: 1
115+
}
116+
91117
const closeButtonStyle: CSSProperties = {
92118
background: "#171d24",
93119
border: "1px solid #3a4652",
@@ -504,6 +530,7 @@ const MobileTerminalControls = (
504530

505531
export const TerminalPanel = (
506532
{
533+
bodyContent,
507534
keyboardOpen,
508535
mobileMode,
509536
onApplyProject,
@@ -539,6 +566,7 @@ export const TerminalPanel = (
539566
}, [])
540567
const compactHeaderMode = resolveTerminalCompactHeaderMode(mobileMode)
541568
const compactTypingMode = resolveTerminalTypingMode(mobileMode, keyboardOpen)
569+
const hasBodyContent = bodyContent !== undefined
542570

543571
useEffect(() => {
544572
if (!mobileMode) {
@@ -611,10 +639,12 @@ export const TerminalPanel = (
611639
status={status}
612640
/>
613641
<div
614-
ref={hostRef}
615-
style={terminalBodyStyle(compactTypingMode, mobileMode)}
616-
/>
617-
{mobileMode
642+
style={terminalBodyFrameStyle(compactTypingMode, mobileMode)}
643+
>
644+
<div ref={hostRef} style={terminalHostStyle} />
645+
{hasBodyContent ? <div style={terminalBodyContentStyle}>{bodyContent}</div> : null}
646+
</div>
647+
{mobileMode && !hasBodyContent
618648
? (
619649
<MobileTerminalControls
620650
collapsed={mobileControlsCollapsed}

0 commit comments

Comments
 (0)