Skip to content

Commit 463361d

Browse files
committed
Add --lean flag for simplified TUI mode
Introduce a --lean flag to 'docker agent run' that renders a minimal TUI with just the message stream, a working indicator, and the editor input. No sidebar, tab bar, resize handle, status bar, or overlay dialogs are shown. The implementation reuses the existing TUI code path with conditional checks rather than maintaining a separate TUI. Assisted-By: docker-agent
1 parent 2021b77 commit 463361d

5 files changed

Lines changed: 174 additions & 44 deletions

File tree

cmd/root/new.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,10 @@ func (f *newFlags) runNewCommand(cmd *cobra.Command, args []string) error {
7979

8080
sess := session.New(sessOpts...)
8181

82-
return runTUI(ctx, rt, sess, nil, nil, appOpts...)
82+
return runTUI(ctx, rt, sess, nil, nil, nil, appOpts...)
8383
}
8484

85-
func runTUI(ctx context.Context, rt runtime.Runtime, sess *session.Session, spawner tui.SessionSpawner, cleanup func(), opts ...app.Opt) error {
85+
func runTUI(ctx context.Context, rt runtime.Runtime, sess *session.Session, spawner tui.SessionSpawner, cleanup func(), tuiOpts []tui.Option, opts ...app.Opt) error {
8686
if gen := rt.TitleGenerator(); gen != nil {
8787
opts = append(opts, app.WithTitleGenerator(gen))
8888
}
@@ -105,7 +105,7 @@ func runTUI(ctx context.Context, rt runtime.Runtime, sess *session.Session, spaw
105105
cleanup = func() {}
106106
}
107107
wd, _ := os.Getwd()
108-
model := tui.New(ctx, spawner, a, wd, cleanup)
108+
model := tui.New(ctx, spawner, a, wd, cleanup, tuiOpts...)
109109

110110
p := tea.NewProgram(model, tea.WithContext(ctx), tea.WithFilter(filter))
111111
coalescer.SetSender(p.Send)

cmd/root/run.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ type runExecFlags struct {
6060

6161
// Run only
6262
hideToolResults bool
63+
lean bool
6364

6465
// globalPermissions holds the user-level global permission checker built
6566
// from user config settings. Nil when no global permissions are configured.
@@ -117,6 +118,7 @@ func addRunOrExecFlags(cmd *cobra.Command, flags *runExecFlags) {
117118
_ = cmd.PersistentFlags().MarkHidden("memprofile")
118119
cmd.PersistentFlags().BoolVar(&flags.forceTUI, "force-tui", false, "Force TUI mode even when not in a terminal")
119120
_ = cmd.PersistentFlags().MarkHidden("force-tui")
121+
cmd.PersistentFlags().BoolVar(&flags.lean, "lean", false, "Use a simplified TUI with minimal chrome")
120122
cmd.PersistentFlags().BoolVar(&flags.sandbox, "sandbox", false, "Run the agent inside a Docker sandbox (requires Docker Desktop with sandbox support)")
121123
cmd.PersistentFlags().StringVar(&flags.sandboxTemplate, "template", "", "Template image for the sandbox (passed to docker sandbox create -t)")
122124
cmd.MarkFlagsMutuallyExclusive("fake", "record")
@@ -276,7 +278,7 @@ func (f *runExecFlags) runOrExec(ctx context.Context, out *cli.Printer, args []s
276278
}
277279

278280
sessStore := rt.SessionStore()
279-
return runTUI(ctx, rt, sess, f.createSessionSpawner(agentSource, sessStore), initialTeamCleanup, opts...)
281+
return runTUI(ctx, rt, sess, f.createSessionSpawner(agentSource, sessStore), initialTeamCleanup, f.tuiOpts(), opts...)
280282
}
281283

282284
func (f *runExecFlags) loadAgentFrom(ctx context.Context, agentSource config.Source) (*teamloader.LoadResult, error) {
@@ -455,7 +457,16 @@ func (f *runExecFlags) launchTUI(ctx context.Context, out *cli.Printer, rt runti
455457
return err
456458
}
457459

458-
return runTUI(ctx, rt, sess, nil, nil, opts...)
460+
return runTUI(ctx, rt, sess, nil, nil, f.tuiOpts(), opts...)
461+
}
462+
463+
// tuiOpts returns the TUI options derived from the current flags.
464+
func (f *runExecFlags) tuiOpts() []tui.Option {
465+
var opts []tui.Option
466+
if f.lean {
467+
opts = append(opts, tui.WithLeanMode())
468+
}
469+
return opts
459470
}
460471

461472
func (f *runExecFlags) buildAppOpts(args []string) ([]app.Opt, error) {

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ require (
185185
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
186186
github.com/opencontainers/go-digest v1.0.0 // indirect
187187
github.com/opencontainers/image-spec v1.1.1 // indirect
188-
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
188+
github.com/patrickmn/go-cache v2.1.0+incompatible
189189
github.com/perimeterx/marshmallow v1.1.5 // indirect
190190
github.com/pjbgf/sha1cd v0.3.2 // indirect
191191
github.com/pmezard/go-difflib v1.0.0 // indirect

pkg/tui/page/chat/chat.go

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ type chatPage struct {
139139
sessionState *service.SessionState
140140

141141
// State
142-
working bool
142+
working bool
143+
leanMode bool
143144

144145
msgCancel context.CancelFunc
145146
streamCancelled bool
@@ -174,6 +175,16 @@ type chatPage struct {
174175
func (p *chatPage) computeSidebarLayout() sidebarLayout {
175176
innerWidth := p.width - appPaddingHorizontal
176177

178+
// Lean mode: no sidebar at all
179+
if p.leanMode {
180+
return sidebarLayout{
181+
mode: sidebarCollapsedNarrow,
182+
innerWidth: innerWidth,
183+
chatWidth: innerWidth,
184+
chatHeight: max(1, p.height),
185+
}
186+
}
187+
177188
var mode sidebarLayoutMode
178189
switch {
179190
case p.width >= minWindowWidth && !p.sidebar.IsCollapsed():
@@ -300,7 +311,7 @@ func getEditorDisplayNameFromEnv(visual, editorEnv string) string {
300311
}
301312

302313
// New creates a new chat page
303-
func New(a *app.App, sessionState *service.SessionState) Page {
314+
func New(a *app.App, sessionState *service.SessionState, opts ...PageOption) Page {
304315
p := &chatPage{
305316
sidebar: sidebar.New(sessionState),
306317
messages: messages.New(sessionState),
@@ -309,9 +320,23 @@ func New(a *app.App, sessionState *service.SessionState) Page {
309320
sessionState: sessionState,
310321
}
311322

323+
for _, opt := range opts {
324+
opt(p)
325+
}
326+
312327
return p
313328
}
314329

330+
// PageOption configures a chat page.
331+
type PageOption func(*chatPage)
332+
333+
// WithLeanMode creates a lean chat page with no sidebar.
334+
func WithLeanMode() PageOption {
335+
return func(p *chatPage) {
336+
p.leanMode = true
337+
}
338+
}
339+
315340
// Init initializes the chat page
316341
func (p *chatPage) Init() tea.Cmd {
317342
var cmds []tea.Cmd
@@ -518,19 +543,26 @@ func (p *chatPage) View() string {
518543
bodyContent = lipgloss.JoinHorizontal(lipgloss.Left, chatView, toggleCol, sidebarView)
519544

520545
case sidebarCollapsed, sidebarCollapsedNarrow:
521-
sidebarRendered := p.renderCollapsedSidebar(sl)
522-
523-
chatView := styles.ChatStyle.
524-
Height(sl.chatHeight).
525-
Width(sl.innerWidth).
526-
Render(messagesView)
527-
528-
bodyContent = lipgloss.JoinVertical(lipgloss.Top, sidebarRendered, chatView)
546+
if p.leanMode {
547+
// Lean mode: no sidebar header, no fixed height
548+
bodyContent = styles.ChatStyle.
549+
Width(sl.innerWidth).
550+
Render(messagesView)
551+
} else {
552+
sidebarRendered := p.renderCollapsedSidebar(sl)
553+
chatView := styles.ChatStyle.
554+
Height(sl.chatHeight).
555+
Width(sl.innerWidth).
556+
Render(messagesView)
557+
bodyContent = lipgloss.JoinVertical(lipgloss.Top, sidebarRendered, chatView)
558+
}
529559
}
530560

531-
return styles.AppStyle.
532-
Height(p.height).
533-
Render(bodyContent)
561+
appStyle := styles.AppStyle
562+
if !p.leanMode {
563+
appStyle = appStyle.Height(p.height)
564+
}
565+
return appStyle.Render(bodyContent)
534566
}
535567

536568
// renderSidebarHandle renders the sidebar toggle/resize handle.

0 commit comments

Comments
 (0)