Skip to content

Commit 442f04d

Browse files
authored
Merge pull request #2253 from dgageot/board/lean-tui-mode-without-dual-code-paths-d3210296
Add --lean flag for simplified TUI mode
2 parents e669dd0 + 463361d commit 442f04d

4 files changed

Lines changed: 173 additions & 43 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) {

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)