on main help sign in
feat: add yolo mode command (#654)

* feat: add yolo mode command * chore: small improvements * chore(theme): move yolo mode styling into theme --------- Co-authored-by: Christian Rocha <christian@rocha.is>

commita47d761651f29a1e3f0c35b66fa9f47b076b47fc authorKujtim Hoxha <kujtimii.h@gmail.com> dateFri 8 Aug 2025, 22:27 (1 year ago) parent2f0fd54
7 files changed+80−12
internal/permission/permission.go+10 internal/tui/components/chat/editor/editor.go+41−11 internal/tui/components/dialogs/commands/commands.go+9 internal/tui/page/chat/chat.go+5−1 internal/tui/styles/charmtone.go+6 internal/tui/styles/theme.go+7 internal/tui/tui.go+2
internal/permission/permission.go+10
@@ -49,6 +49,8 @@ type Service interface {
4949 Deny(permission PermissionRequest)
5050 Request(opts CreatePermissionRequest) bool
5151 AutoApproveSession(sessionID string)
52+ SetSkipRequests(skip bool)
53+ SkipRequests() bool
5254 SubscribeNotifications(ctx context.Context) <-chan pubsub.Event[PermissionNotification]
5355}
5456
@@ -210,6 +212,14 @@ func (s *permissionService) SubscribeNotifications(ctx context.Context) <-chan p
210212 return s.notificationBroker.Subscribe(ctx)
211213}
212214
215+func (s *permissionService) SetSkipRequests(skip bool) {
216+ s.skip = skip
217+}
218+
219+func (s *permissionService) SkipRequests() bool {
220+ return s.skip
221+}
222+
213223func NewPermissionService(workingDir string, skip bool, allowedTools []string) Service {
214224 return &permissionService{
215225 Broker: pubsub.NewBroker[PermissionRequest](),
internal/tui/components/chat/editor/editor.go+41−11
@@ -263,6 +263,9 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
263263 Attachment: attachment,
264264 })
265265
266+ case commands.ToggleYoloModeMsg:
267+ m.setEditorPrompt()
268+ return m, nil
266269 case tea.KeyPressMsg:
267270 cur := m.textarea.Cursor()
268271 curIdx := m.textarea.Width()*cur.Y + cur.X
@@ -368,6 +371,14 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
368371 return m, tea.Batch(cmds...)
369372}
370373
374+func (m *editorCmp) setEditorPrompt() {
375+ if m.app.Permissions.SkipRequests() {
376+ m.textarea.SetPromptFunc(4, yoloPromptFunc)
377+ return
378+ }
379+ m.textarea.SetPromptFunc(4, normalPromptFunc)
380+}
381+
371382func (m *editorCmp) completionsPosition() (int, int) {
372383 cur := m.textarea.Cursor()
373384 if cur == nil {
@@ -416,6 +427,9 @@ func (m *editorCmp) View() string {
416427 } else {
417428 m.textarea.Placeholder = m.readyPlaceholder
418429 }
430+ if m.app.Permissions.SkipRequests() {
431+ m.textarea.Placeholder = "Yolo mode!"
432+ }
419433 if len(m.attachments) == 0 {
420434 content := t.S().Base.Padding(1).Render(
421435 m.textarea.View(),
@@ -529,31 +543,47 @@ func (c *editorCmp) HasAttachments() bool {
529543 return len(c.attachments) > 0
530544}
531545
532−func New(app *app.App) Editor {
546+func normalPromptFunc(info textarea.PromptInfo) string {
533547 t := styles.CurrentTheme()
534− ta := textarea.New()
535− ta.SetStyles(t.S().TextArea)
536− ta.SetPromptFunc(4, func(info textarea.PromptInfo) string {
537− if info.LineNumber == 0 {
538− return " > "
539− }
548+ if info.LineNumber == 0 {
549+ return " > "
550+ }
551+ if info.Focused {
552+ return t.S().Base.Foreground(t.GreenDark).Render("::: ")
553+ }
554+ return t.S().Muted.Render("::: ")
555+}
556+
557+func yoloPromptFunc(info textarea.PromptInfo) string {
558+ t := styles.CurrentTheme()
559+ if info.LineNumber == 0 {
540560 if info.Focused {
541− return t.S().Base.Foreground(t.GreenDark).Render("::: ")
561+ return fmt.Sprintf("%s ", t.YoloIconFocused)
542562 } else {
543− return t.S().Muted.Render("::: ")
563+ return fmt.Sprintf("%s ", t.YoloIconBlurred)
544564 }
545− })
565+ }
566+ if info.Focused {
567+ return fmt.Sprintf("%s ", t.YoloDotsFocused)
568+ }
569+ return fmt.Sprintf("%s ", t.YoloDotsBlurred)
570+}
571+
572+func New(app *app.App) Editor {
573+ t := styles.CurrentTheme()
574+ ta := textarea.New()
575+ ta.SetStyles(t.S().TextArea)
546576 ta.ShowLineNumbers = false
547577 ta.CharLimit = -1
548578 ta.SetVirtualCursor(false)
549579 ta.Focus()
550−
551580 e := &editorCmp{
552581 // TODO: remove the app instance from here
553582 app: app,
554583 textarea: ta,
555584 keyMap: DefaultEditorKeyMap(),
556585 }
586+ e.setEditorPrompt()
557587
558588 e.randomizePlaceholders()
559589 e.textarea.Placeholder = e.readyPlaceholder
internal/tui/components/dialogs/commands/commands.go+9
@@ -69,6 +69,7 @@ type (
6969 ToggleCompactModeMsg struct{}
7070 ToggleThinkingMsg struct{}
7171 OpenExternalEditorMsg struct{}
72+ ToggleYoloModeMsg struct{}
7273 CompactMsg struct {
7374 SessionID string
7475 }
@@ -362,6 +363,14 @@ func (c *commandDialogCmp) defaultCommands() []Command {
362363 }
363364
364365 return append(commands, []Command{
366+ {
367+ ID: "toggle_yolo",
368+ Title: "Toggle Yolo Mode",
369+ Description: "Toggle yolo mode",
370+ Handler: func(cmd Command) tea.Cmd {
371+ return util.CmdHandler(ToggleYoloModeMsg{})
372+ },
373+ },
365374 {
366375 ID: "toggle_help",
367376 Title: "Toggle Help",
internal/tui/page/chat/chat.go+5−1
@@ -300,7 +300,11 @@ func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
300300 }
301301
302302 return p, tea.Batch(cmds...)
303−
303+ case commands.ToggleYoloModeMsg:
304+ // update the editor style
305+ u, cmd := p.editor.Update(msg)
306+ p.editor = u.(editor.Editor)
307+ return p, cmd
304308 case pubsub.Event[history.File], sidebar.SessionFilesMsg:
305309 u, cmd := p.sidebar.Update(msg)
306310 p.sidebar = u.(sidebar.Sidebar)
internal/tui/styles/charmtone.go+6
@@ -45,6 +45,7 @@ func NewCharmtoneTheme() *Theme {
4545 Blue: charmtone.Malibu,
4646
4747 Yellow: charmtone.Mustard,
48+ Citron: charmtone.Citron,
4849
4950 Green: charmtone.Julep,
5051 GreenDark: charmtone.Guac,
@@ -65,5 +66,10 @@ func NewCharmtoneTheme() *Theme {
6566 t.ItemErrorIcon = t.ItemOfflineIcon.Foreground(charmtone.Coral)
6667 t.ItemOnlineIcon = t.ItemOfflineIcon.Foreground(charmtone.Guac)
6768
69+ t.YoloIconFocused = lipgloss.NewStyle().Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ")
70+ t.YoloIconBlurred = t.YoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid)
71+ t.YoloDotsFocused = lipgloss.NewStyle().Foreground(charmtone.Zest).SetString(":::")
72+ t.YoloDotsBlurred = t.YoloDotsFocused.Foreground(charmtone.Squid)
73+
6874 return t
6975}
internal/tui/styles/theme.go+7
@@ -62,6 +62,7 @@ type Theme struct {
6262
6363 // Yellows
6464 Yellow color.Color
65+ Citron color.Color
6566
6667 // Greens
6768 Green color.Color
@@ -83,6 +84,12 @@ type Theme struct {
8384 ItemErrorIcon lipgloss.Style
8485 ItemOnlineIcon lipgloss.Style
8586
87+ // Editor: Yolo Mode
88+ YoloIconFocused lipgloss.Style
89+ YoloIconBlurred lipgloss.Style
90+ YoloDotsFocused lipgloss.Style
91+ YoloDotsBlurred lipgloss.Style
92+
8693 styles *Styles
8794}
8895
internal/tui/tui.go+2
@@ -174,6 +174,8 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
174174 return a, util.CmdHandler(dialogs.OpenDialogMsg{
175175 Model: quit.NewQuitDialog(),
176176 })
177+ case commands.ToggleYoloModeMsg:
178+ a.app.Permissions.SetSkipRequests(!a.app.Permissions.SkipRequests())
177179 case commands.ToggleHelpMsg:
178180 a.status.ToggleFullHelp()
179181 a.showingFullHelp = !a.showingFullHelp