diff --git a/internal/permission/permission.go b/internal/permission/permission.go index 7e71eb1caa861d4d702d9f028560e0dd4825abb7..77b2526a592d0d194f75fb71af05477ae75df80b 100644 --- a/internal/permission/permission.go +++ b/internal/permission/permission.go @@ -49,6 +49,8 @@ type Service interface { Deny(permission PermissionRequest) Request(opts CreatePermissionRequest) bool AutoApproveSession(sessionID string) + SetSkipRequests(skip bool) + SkipRequests() bool SubscribeNotifications(ctx context.Context) <-chan pubsub.Event[PermissionNotification] } @@ -210,6 +212,14 @@ func (s *permissionService) SubscribeNotifications(ctx context.Context) <-chan p return s.notificationBroker.Subscribe(ctx) } +func (s *permissionService) SetSkipRequests(skip bool) { + s.skip = skip +} + +func (s *permissionService) SkipRequests() bool { + return s.skip +} + func NewPermissionService(workingDir string, skip bool, allowedTools []string) Service { return &permissionService{ Broker: pubsub.NewBroker[PermissionRequest](), diff --git a/internal/tui/components/chat/editor/editor.go b/internal/tui/components/chat/editor/editor.go index e50f0b4f6de13a94c3017eabc708826b613399f3..297dda479ad6387a40afa9fcd69a643df3f458d2 100644 --- a/internal/tui/components/chat/editor/editor.go +++ b/internal/tui/components/chat/editor/editor.go @@ -263,6 +263,9 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { Attachment: attachment, }) + case commands.ToggleYoloModeMsg: + m.setEditorPrompt() + return m, nil case tea.KeyPressMsg: cur := m.textarea.Cursor() curIdx := m.textarea.Width()*cur.Y + cur.X @@ -368,6 +371,14 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, tea.Batch(cmds...) } +func (m *editorCmp) setEditorPrompt() { + if m.app.Permissions.SkipRequests() { + m.textarea.SetPromptFunc(4, yoloPromptFunc) + return + } + m.textarea.SetPromptFunc(4, normalPromptFunc) +} + func (m *editorCmp) completionsPosition() (int, int) { cur := m.textarea.Cursor() if cur == nil { @@ -416,6 +427,9 @@ func (m *editorCmp) View() string { } else { m.textarea.Placeholder = m.readyPlaceholder } + if m.app.Permissions.SkipRequests() { + m.textarea.Placeholder = "Yolo mode!" + } if len(m.attachments) == 0 { content := t.S().Base.Padding(1).Render( m.textarea.View(), @@ -529,31 +543,47 @@ func (c *editorCmp) HasAttachments() bool { return len(c.attachments) > 0 } -func New(app *app.App) Editor { +func normalPromptFunc(info textarea.PromptInfo) string { t := styles.CurrentTheme() - ta := textarea.New() - ta.SetStyles(t.S().TextArea) - ta.SetPromptFunc(4, func(info textarea.PromptInfo) string { - if info.LineNumber == 0 { - return " > " - } + if info.LineNumber == 0 { + return " > " + } + if info.Focused { + return t.S().Base.Foreground(t.GreenDark).Render("::: ") + } + return t.S().Muted.Render("::: ") +} + +func yoloPromptFunc(info textarea.PromptInfo) string { + t := styles.CurrentTheme() + if info.LineNumber == 0 { if info.Focused { - return t.S().Base.Foreground(t.GreenDark).Render("::: ") + return fmt.Sprintf("%s ", t.YoloIconFocused) } else { - return t.S().Muted.Render("::: ") + return fmt.Sprintf("%s ", t.YoloIconBlurred) } - }) + } + if info.Focused { + return fmt.Sprintf("%s ", t.YoloDotsFocused) + } + return fmt.Sprintf("%s ", t.YoloDotsBlurred) +} + +func New(app *app.App) Editor { + t := styles.CurrentTheme() + ta := textarea.New() + ta.SetStyles(t.S().TextArea) ta.ShowLineNumbers = false ta.CharLimit = -1 ta.SetVirtualCursor(false) ta.Focus() - e := &editorCmp{ // TODO: remove the app instance from here app: app, textarea: ta, keyMap: DefaultEditorKeyMap(), } + e.setEditorPrompt() e.randomizePlaceholders() e.textarea.Placeholder = e.readyPlaceholder diff --git a/internal/tui/components/dialogs/commands/commands.go b/internal/tui/components/dialogs/commands/commands.go index 139ec1ea5ac0461b0c4fa8de65c61c7293b8ac50..756e687c693da971e9ddd8bb72f08b9fc23eedae 100644 --- a/internal/tui/components/dialogs/commands/commands.go +++ b/internal/tui/components/dialogs/commands/commands.go @@ -69,6 +69,7 @@ type ( ToggleCompactModeMsg struct{} ToggleThinkingMsg struct{} OpenExternalEditorMsg struct{} + ToggleYoloModeMsg struct{} CompactMsg struct { SessionID string } @@ -362,6 +363,14 @@ func (c *commandDialogCmp) defaultCommands() []Command { } return append(commands, []Command{ + { + ID: "toggle_yolo", + Title: "Toggle Yolo Mode", + Description: "Toggle yolo mode", + Handler: func(cmd Command) tea.Cmd { + return util.CmdHandler(ToggleYoloModeMsg{}) + }, + }, { ID: "toggle_help", Title: "Toggle Help", diff --git a/internal/tui/page/chat/chat.go b/internal/tui/page/chat/chat.go index b418abc4989f33433c2c4d12d0aa5aab7069254d..adca016af9fa5b34f1cb05ab4bcf2e495447af0c 100644 --- a/internal/tui/page/chat/chat.go +++ b/internal/tui/page/chat/chat.go @@ -300,7 +300,11 @@ func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } return p, tea.Batch(cmds...) - + case commands.ToggleYoloModeMsg: + // update the editor style + u, cmd := p.editor.Update(msg) + p.editor = u.(editor.Editor) + return p, cmd case pubsub.Event[history.File], sidebar.SessionFilesMsg: u, cmd := p.sidebar.Update(msg) p.sidebar = u.(sidebar.Sidebar) diff --git a/internal/tui/styles/charmtone.go b/internal/tui/styles/charmtone.go index 2e3783f522eac79cd1feb432fe0e399be0802882..1d9a194273457f143216e1bfa472207d7b593fe1 100644 --- a/internal/tui/styles/charmtone.go +++ b/internal/tui/styles/charmtone.go @@ -45,6 +45,7 @@ func NewCharmtoneTheme() *Theme { Blue: charmtone.Malibu, Yellow: charmtone.Mustard, + Citron: charmtone.Citron, Green: charmtone.Julep, GreenDark: charmtone.Guac, @@ -65,5 +66,10 @@ func NewCharmtoneTheme() *Theme { t.ItemErrorIcon = t.ItemOfflineIcon.Foreground(charmtone.Coral) t.ItemOnlineIcon = t.ItemOfflineIcon.Foreground(charmtone.Guac) + t.YoloIconFocused = lipgloss.NewStyle().Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ") + t.YoloIconBlurred = t.YoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid) + t.YoloDotsFocused = lipgloss.NewStyle().Foreground(charmtone.Zest).SetString(":::") + t.YoloDotsBlurred = t.YoloDotsFocused.Foreground(charmtone.Squid) + return t } diff --git a/internal/tui/styles/theme.go b/internal/tui/styles/theme.go index 0503539ba720188a0894d26abd299b54d602494e..e4fcb57ff0763bad2d6f44ef41897654d97a9f42 100644 --- a/internal/tui/styles/theme.go +++ b/internal/tui/styles/theme.go @@ -62,6 +62,7 @@ type Theme struct { // Yellows Yellow color.Color + Citron color.Color // Greens Green color.Color @@ -83,6 +84,12 @@ type Theme struct { ItemErrorIcon lipgloss.Style ItemOnlineIcon lipgloss.Style + // Editor: Yolo Mode + YoloIconFocused lipgloss.Style + YoloIconBlurred lipgloss.Style + YoloDotsFocused lipgloss.Style + YoloDotsBlurred lipgloss.Style + styles *Styles } diff --git a/internal/tui/tui.go b/internal/tui/tui.go index 7ece536f27fecdcb0c2d8a4394ea67ee76060d28..bb865cc20c789a013b38c8f00d98cffed4f69db1 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -174,6 +174,8 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return a, util.CmdHandler(dialogs.OpenDialogMsg{ Model: quit.NewQuitDialog(), }) + case commands.ToggleYoloModeMsg: + a.app.Permissions.SetSkipRequests(!a.app.Permissions.SkipRequests()) case commands.ToggleHelpMsg: a.status.ToggleFullHelp() a.showingFullHelp = !a.showingFullHelp