@@ -12,12 +12,15 @@ import (
"github.com/atotto/clipboard"
"github.com/charmbracelet/catwalk/pkg/catwalk"
"github.com/charmbracelet/crush/internal/agent"
+ hyperp "github.com/charmbracelet/crush/internal/agent/hyper"
"github.com/charmbracelet/crush/internal/config"
"github.com/charmbracelet/crush/internal/home"
"github.com/charmbracelet/crush/internal/tui/components/chat"
"github.com/charmbracelet/crush/internal/tui/components/core"
"github.com/charmbracelet/crush/internal/tui/components/core/layout"
"github.com/charmbracelet/crush/internal/tui/components/dialogs/claude"
+ "github.com/charmbracelet/crush/internal/tui/components/dialogs/copilot"
+ "github.com/charmbracelet/crush/internal/tui/components/dialogs/hyper"
"github.com/charmbracelet/crush/internal/tui/components/dialogs/models"
"github.com/charmbracelet/crush/internal/tui/components/logo"
lspcomponent "github.com/charmbracelet/crush/internal/tui/components/lsp"
@@ -55,6 +58,12 @@ type Splash interface {
// IsClaudeOAuthComplete returns whether Claude OAuth flow is complete
IsClaudeOAuthComplete() bool
+
+ // IsShowingClaudeOAuth2 returns whether showing Hyper OAuth2 flow
+ IsShowingHyperOAuth2() bool
+
+ // IsShowingClaudeOAuth2 returns whether showing GitHub Copilot OAuth2 flow
+ IsShowingCopilotOAuth2() bool
}
const (
@@ -87,6 +96,14 @@ type splashCmp struct {
isAPIKeyValid bool
apiKeyValue string
+ // Hyper device flow state
+ hyperDeviceFlow *hyper.DeviceFlow
+ showHyperDeviceFlow bool
+
+ // Copilot device flow state
+ copilotDeviceFlow *copilot.DeviceFlow
+ showCopilotDeviceFlow bool
+
// Claude state
claudeAuthMethodChooser *claude.AuthMethodChooser
claudeOAuth2 *claude.OAuth2
@@ -186,6 +203,26 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
}
return s, tea.Batch(cmds...)
+ case hyper.DeviceFlowCompletedMsg:
+ s.showHyperDeviceFlow = false
+ return s, s.saveAPIKeyAndContinue(msg.Token, true)
+ case hyper.DeviceAuthInitiatedMsg, hyper.DeviceFlowErrorMsg:
+ if s.hyperDeviceFlow != nil {
+ u, cmd := s.hyperDeviceFlow.Update(msg)
+ s.hyperDeviceFlow = u.(*hyper.DeviceFlow)
+ return s, cmd
+ }
+ return s, nil
+ case copilot.DeviceAuthInitiatedMsg, copilot.DeviceFlowErrorMsg:
+ if s.copilotDeviceFlow != nil {
+ u, cmd := s.copilotDeviceFlow.Update(msg)
+ s.copilotDeviceFlow = u.(*copilot.DeviceFlow)
+ return s, cmd
+ }
+ return s, nil
+ case copilot.DeviceFlowCompletedMsg:
+ s.showCopilotDeviceFlow = false
+ return s, s.saveAPIKeyAndContinue(msg.Token, true)
case claude.AuthenticationCompleteMsg:
s.showClaudeAuthMethodChooser = false
s.showClaudeOAuth2 = false
@@ -205,6 +242,10 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
}
case tea.KeyPressMsg:
switch {
+ case key.Matches(msg, s.keyMap.Copy) && s.showHyperDeviceFlow:
+ return s, s.hyperDeviceFlow.CopyCode()
+ case key.Matches(msg, s.keyMap.Copy) && s.showCopilotDeviceFlow:
+ return s, s.copilotDeviceFlow.CopyCode()
case key.Matches(msg, s.keyMap.Copy) && s.showClaudeOAuth2 && s.claudeOAuth2.State == claude.OAuthStateURL:
return s, tea.Sequence(
tea.SetClipboard(s.claudeOAuth2.URL),
@@ -223,21 +264,27 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
s.claudeOAuth2 = u.(*claude.OAuth2)
return s, cmd
case key.Matches(msg, s.keyMap.Back):
- if s.showClaudeAuthMethodChooser {
+ switch {
+ case s.showClaudeAuthMethodChooser:
s.claudeAuthMethodChooser.SetDefaults()
s.showClaudeAuthMethodChooser = false
return s, nil
- }
- if s.showClaudeOAuth2 {
+ case s.showClaudeOAuth2:
s.claudeOAuth2.SetDefaults()
s.showClaudeOAuth2 = false
s.showClaudeAuthMethodChooser = true
return s, nil
- }
- if s.isAPIKeyValid {
+ case s.showHyperDeviceFlow:
+ s.hyperDeviceFlow = nil
+ s.showHyperDeviceFlow = false
return s, nil
- }
- if s.needsAPIKey {
+ case s.showCopilotDeviceFlow:
+ s.copilotDeviceFlow = nil
+ s.showCopilotDeviceFlow = false
+ return s, nil
+ case s.isAPIKeyValid:
+ return s, nil
+ case s.needsAPIKey:
if s.selectedModel.Provider.ID == catwalk.InferenceProviderAnthropic {
s.showClaudeAuthMethodChooser = true
}
@@ -249,7 +296,8 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
return s, nil
}
case key.Matches(msg, s.keyMap.Select):
- if s.showClaudeAuthMethodChooser {
+ switch {
+ case s.showClaudeAuthMethodChooser:
selectedItem := s.modelList.SelectedModel()
if selectedItem == nil {
return s, nil
@@ -267,16 +315,17 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
s.showClaudeOAuth2 = true
}
return s, nil
- }
- if s.showClaudeOAuth2 {
+ case s.showClaudeOAuth2:
m2, cmd2 := s.claudeOAuth2.ValidationConfirm()
s.claudeOAuth2 = m2.(*claude.OAuth2)
return s, cmd2
- }
- if s.isAPIKeyValid {
+ case s.showHyperDeviceFlow:
+ return s, s.hyperDeviceFlow.CopyCodeAndOpenURL()
+ case s.showCopilotDeviceFlow:
+ return s, s.copilotDeviceFlow.CopyCodeAndOpenURL()
+ case s.isAPIKeyValid:
return s, s.saveAPIKeyAndContinue(s.apiKeyValue, true)
- }
- if s.isOnboarding && !s.needsAPIKey {
+ case s.isOnboarding && !s.needsAPIKey:
selectedItem := s.modelList.SelectedModel()
if selectedItem == nil {
return s, nil
@@ -286,9 +335,22 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
s.isOnboarding = false
return s, tea.Batch(cmd, util.CmdHandler(OnboardingCompleteMsg{}))
} else {
- if selectedItem.Provider.ID == catwalk.InferenceProviderAnthropic {
+ switch selectedItem.Provider.ID {
+ case catwalk.InferenceProviderAnthropic:
s.showClaudeAuthMethodChooser = true
return s, nil
+ case hyperp.Name:
+ s.selectedModel = selectedItem
+ s.showHyperDeviceFlow = true
+ s.hyperDeviceFlow = hyper.NewDeviceFlow()
+ s.hyperDeviceFlow.SetWidth(min(s.width-2, 60))
+ return s, s.hyperDeviceFlow.Init()
+ case catwalk.InferenceProviderCopilot:
+ s.selectedModel = selectedItem
+ s.showCopilotDeviceFlow = true
+ s.copilotDeviceFlow = copilot.NewDeviceFlow()
+ s.copilotDeviceFlow.SetWidth(min(s.width-2, 60))
+ return s, s.copilotDeviceFlow.Init()
}
// Provider not configured, show API key input
s.needsAPIKey = true
@@ -296,7 +358,7 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
s.apiKeyInput.SetProviderName(selectedItem.Provider.Name)
return s, nil
}
- } else if s.needsAPIKey {
+ case s.needsAPIKey:
// Handle API key submission
s.apiKeyValue = strings.TrimSpace(s.apiKeyInput.Value())
if s.apiKeyValue == "" {
@@ -337,7 +399,7 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
}
},
)
- } else if s.needsProjectInit {
+ case s.needsProjectInit:
return s, s.initializeProject()
}
case key.Matches(msg, s.keyMap.Tab, s.keyMap.LeftRight):
@@ -385,44 +447,71 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
return s, s.initializeProject()
}
default:
- if s.showClaudeAuthMethodChooser {
+ switch {
+ case s.showClaudeAuthMethodChooser:
u, cmd := s.claudeAuthMethodChooser.Update(msg)
s.claudeAuthMethodChooser = u.(*claude.AuthMethodChooser)
return s, cmd
- } else if s.showClaudeOAuth2 {
+ case s.showClaudeOAuth2:
u, cmd := s.claudeOAuth2.Update(msg)
s.claudeOAuth2 = u.(*claude.OAuth2)
return s, cmd
- } else if s.needsAPIKey {
+ case s.showHyperDeviceFlow:
+ u, cmd := s.hyperDeviceFlow.Update(msg)
+ s.hyperDeviceFlow = u.(*hyper.DeviceFlow)
+ return s, cmd
+ case s.showCopilotDeviceFlow:
+ u, cmd := s.copilotDeviceFlow.Update(msg)
+ s.copilotDeviceFlow = u.(*copilot.DeviceFlow)
+ return s, cmd
+ case s.needsAPIKey:
u, cmd := s.apiKeyInput.Update(msg)
s.apiKeyInput = u.(*models.APIKeyInput)
return s, cmd
- } else if s.isOnboarding {
+ case s.isOnboarding:
u, cmd := s.modelList.Update(msg)
s.modelList = u
return s, cmd
}
}
case tea.PasteMsg:
- if s.showClaudeOAuth2 {
+ switch {
+ case s.showClaudeOAuth2:
u, cmd := s.claudeOAuth2.Update(msg)
s.claudeOAuth2 = u.(*claude.OAuth2)
return s, cmd
- } else if s.needsAPIKey {
+ case s.showHyperDeviceFlow:
+ u, cmd := s.hyperDeviceFlow.Update(msg)
+ s.hyperDeviceFlow = u.(*hyper.DeviceFlow)
+ return s, cmd
+ case s.showCopilotDeviceFlow:
+ u, cmd := s.copilotDeviceFlow.Update(msg)
+ s.copilotDeviceFlow = u.(*copilot.DeviceFlow)
+ return s, cmd
+ case s.needsAPIKey:
u, cmd := s.apiKeyInput.Update(msg)
s.apiKeyInput = u.(*models.APIKeyInput)
return s, cmd
- } else if s.isOnboarding {
+ case s.isOnboarding:
var cmd tea.Cmd
s.modelList, cmd = s.modelList.Update(msg)
return s, cmd
}
case spinner.TickMsg:
- if s.showClaudeOAuth2 {
+ switch {
+ case s.showClaudeOAuth2:
u, cmd := s.claudeOAuth2.Update(msg)
s.claudeOAuth2 = u.(*claude.OAuth2)
return s, cmd
- } else {
+ case s.showHyperDeviceFlow:
+ u, cmd := s.hyperDeviceFlow.Update(msg)
+ s.hyperDeviceFlow = u.(*hyper.DeviceFlow)
+ return s, cmd
+ case s.showCopilotDeviceFlow:
+ u, cmd := s.copilotDeviceFlow.Update(msg)
+ s.copilotDeviceFlow = u.(*copilot.DeviceFlow)
+ return s, cmd
+ default:
u, cmd := s.apiKeyInput.Update(msg)
s.apiKeyInput = u.(*models.APIKeyInput)
return s, cmd
@@ -560,7 +649,9 @@ func (s *splashCmp) isProviderConfigured(providerID string) bool {
func (s *splashCmp) View() string {
t := styles.CurrentTheme()
var content string
- if s.showClaudeAuthMethodChooser {
+
+ switch {
+ case s.showClaudeAuthMethodChooser:
remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
chooserView := s.claudeAuthMethodChooser.View()
authMethodSelector := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
@@ -576,7 +667,7 @@ func (s *splashCmp) View() string {
s.logoRendered,
authMethodSelector,
)
- } else if s.showClaudeOAuth2 {
+ case s.showClaudeOAuth2:
remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
oauth2View := s.claudeOAuth2.View()
oauthSelector := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
@@ -592,7 +683,39 @@ func (s *splashCmp) View() string {
s.logoRendered,
oauthSelector,
)
- } else if s.needsAPIKey {
+ case s.showHyperDeviceFlow:
+ remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
+ hyperView := s.hyperDeviceFlow.View()
+ hyperSelector := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
+ lipgloss.JoinVertical(
+ lipgloss.Left,
+ t.S().Base.PaddingLeft(1).Foreground(t.Primary).Render("Let's Auth Hyper"),
+ "",
+ hyperView,
+ ),
+ )
+ content = lipgloss.JoinVertical(
+ lipgloss.Left,
+ s.logoRendered,
+ hyperSelector,
+ )
+ case s.showCopilotDeviceFlow:
+ remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
+ copilotView := s.copilotDeviceFlow.View()
+ copilotSelector := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
+ lipgloss.JoinVertical(
+ lipgloss.Left,
+ t.S().Base.PaddingLeft(1).Foreground(t.Primary).Render("Let's Auth GitHub Copilot"),
+ "",
+ copilotView,
+ ),
+ )
+ content = lipgloss.JoinVertical(
+ lipgloss.Left,
+ s.logoRendered,
+ copilotSelector,
+ )
+ case s.needsAPIKey:
remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
apiKeyView := t.S().Base.PaddingLeft(1).Render(s.apiKeyInput.View())
apiKeySelector := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
@@ -606,7 +729,7 @@ func (s *splashCmp) View() string {
s.logoRendered,
apiKeySelector,
)
- } else if s.isOnboarding {
+ case s.isOnboarding:
modelListView := s.modelList.View()
remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
modelSelector := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
@@ -622,7 +745,7 @@ func (s *splashCmp) View() string {
s.logoRendered,
modelSelector,
)
- } else if s.needsProjectInit {
+ case s.needsProjectInit:
titleStyle := t.S().Base.Foreground(t.FgBase)
pathStyle := t.S().Base.Foreground(t.Success).PaddingLeft(2)
bodyStyle := t.S().Base.Foreground(t.FgMuted)
@@ -673,7 +796,7 @@ func (s *splashCmp) View() string {
"",
initContent,
)
- } else {
+ default:
parts := []string{
s.logoRendered,
s.infoSection(),
@@ -690,28 +813,25 @@ func (s *splashCmp) View() string {
}
func (s *splashCmp) Cursor() *tea.Cursor {
- if s.showClaudeAuthMethodChooser {
+ switch {
+ case s.showClaudeAuthMethodChooser:
return nil
- }
- if s.showClaudeOAuth2 {
+ case s.showClaudeOAuth2:
if cursor := s.claudeOAuth2.CodeInput.Cursor(); cursor != nil {
cursor.Y += 2 // FIXME(@andreynering): Why do we need this?
return s.moveCursor(cursor)
}
return nil
- }
- if s.needsAPIKey {
+ case s.needsAPIKey:
cursor := s.apiKeyInput.Cursor()
if cursor != nil {
return s.moveCursor(cursor)
}
- } else if s.isOnboarding {
+ case s.isOnboarding:
cursor := s.modelList.Cursor()
if cursor != nil {
return s.moveCursor(cursor)
}
- } else {
- return nil
}
return nil
}
@@ -803,13 +923,14 @@ func (s *splashCmp) logoGap() int {
// Bindings implements SplashPage.
func (s *splashCmp) Bindings() []key.Binding {
- if s.showClaudeAuthMethodChooser {
+ switch {
+ case s.showClaudeAuthMethodChooser:
return []key.Binding{
s.keyMap.Select,
s.keyMap.Tab,
s.keyMap.Back,
}
- } else if s.showClaudeOAuth2 {
+ case s.showClaudeOAuth2:
bindings := []key.Binding{
s.keyMap.Select,
}
@@ -817,18 +938,18 @@ func (s *splashCmp) Bindings() []key.Binding {
bindings = append(bindings, s.keyMap.Copy)
}
return bindings
- } else if s.needsAPIKey {
+ case s.needsAPIKey:
return []key.Binding{
s.keyMap.Select,
s.keyMap.Back,
}
- } else if s.isOnboarding {
+ case s.isOnboarding:
return []key.Binding{
s.keyMap.Select,
s.keyMap.Next,
s.keyMap.Previous,
}
- } else if s.needsProjectInit {
+ case s.needsProjectInit:
return []key.Binding{
s.keyMap.Select,
s.keyMap.Yes,
@@ -836,8 +957,9 @@ func (s *splashCmp) Bindings() []key.Binding {
s.keyMap.Tab,
s.keyMap.LeftRight,
}
+ default:
+ return []key.Binding{}
}
- return []key.Binding{}
}
func (s *splashCmp) getMaxInfoWidth() int {
@@ -938,3 +1060,11 @@ func (s *splashCmp) IsClaudeOAuthURLState() bool {
func (s *splashCmp) IsClaudeOAuthComplete() bool {
return s.showClaudeOAuth2 && s.claudeOAuth2.State == claude.OAuthStateCode && s.claudeOAuth2.ValidationState == claude.OAuthValidationStateValid
}
+
+func (s *splashCmp) IsShowingHyperOAuth2() bool {
+ return s.showHyperDeviceFlow
+}
+
+func (s *splashCmp) IsShowingCopilotOAuth2() bool {
+ return s.showCopilotDeviceFlow
+}
@@ -174,13 +174,10 @@ func (m *modelDialogCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
case tea.KeyPressMsg:
switch {
// Handle Hyper device flow keys
- case key.Matches(msg, key.NewBinding(key.WithKeys("c", "C"))) && (m.showHyperDeviceFlow || m.showCopilotDeviceFlow):
- if m.hyperDeviceFlow != nil {
- return m, m.hyperDeviceFlow.CopyCode()
- }
- if m.copilotDeviceFlow != nil {
- return m, m.copilotDeviceFlow.CopyCode()
- }
+ case key.Matches(msg, key.NewBinding(key.WithKeys("c", "C"))) && m.showHyperDeviceFlow:
+ return m, m.hyperDeviceFlow.CopyCode()
+ case key.Matches(msg, key.NewBinding(key.WithKeys("c", "C"))) && m.showCopilotDeviceFlow:
+ return m, m.copilotDeviceFlow.CopyCode()
case key.Matches(msg, key.NewBinding(key.WithKeys("c", "C"))) && m.showClaudeOAuth2 && m.claudeOAuth2.State == claude.OAuthStateURL:
return m, tea.Sequence(
tea.SetClipboard(m.claudeOAuth2.URL),
@@ -337,28 +334,26 @@ func (m *modelDialogCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
return m, m.modelList.SetModelType(LargeModelType)
}
case key.Matches(msg, m.keyMap.Close):
- if m.showHyperDeviceFlow {
+ switch {
+ case m.showHyperDeviceFlow:
if m.hyperDeviceFlow != nil {
m.hyperDeviceFlow.Cancel()
}
m.showHyperDeviceFlow = false
m.selectedModel = nil
- }
- if m.showCopilotDeviceFlow {
+ case m.showCopilotDeviceFlow:
if m.copilotDeviceFlow != nil {
m.copilotDeviceFlow.Cancel()
}
m.showCopilotDeviceFlow = false
m.selectedModel = nil
- }
- if m.showClaudeAuthMethodChooser {
+ case m.showClaudeAuthMethodChooser:
m.claudeAuthMethodChooser.SetDefaults()
m.showClaudeAuthMethodChooser = false
m.keyMap.isClaudeAuthChoiceHelp = false
m.keyMap.isClaudeOAuthHelp = false
return m, nil
- }
- if m.needsAPIKey {
+ case m.needsAPIKey:
if m.isAPIKeyValid {
return m, nil
}
@@ -369,37 +364,40 @@ func (m *modelDialogCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
m.apiKeyValue = ""
m.apiKeyInput.Reset()
return m, nil
+ default:
+ return m, util.CmdHandler(dialogs.CloseDialogMsg{})
}
- return m, util.CmdHandler(dialogs.CloseDialogMsg{})
default:
- if m.showClaudeAuthMethodChooser {
+ switch {
+ case m.showClaudeAuthMethodChooser:
u, cmd := m.claudeAuthMethodChooser.Update(msg)
m.claudeAuthMethodChooser = u.(*claude.AuthMethodChooser)
return m, cmd
- } else if m.showClaudeOAuth2 {
+ case m.showClaudeOAuth2:
u, cmd := m.claudeOAuth2.Update(msg)
m.claudeOAuth2 = u.(*claude.OAuth2)
return m, cmd
- } else if m.needsAPIKey {
+ case m.needsAPIKey:
u, cmd := m.apiKeyInput.Update(msg)
m.apiKeyInput = u.(*APIKeyInput)
return m, cmd
- } else {
+ default:
u, cmd := m.modelList.Update(msg)
m.modelList = u
return m, cmd
}
}
case tea.PasteMsg:
- if m.showClaudeOAuth2 {
+ switch {
+ case m.showClaudeOAuth2:
u, cmd := m.claudeOAuth2.Update(msg)
m.claudeOAuth2 = u.(*claude.OAuth2)
return m, cmd
- } else if m.needsAPIKey {
+ case m.needsAPIKey:
u, cmd := m.apiKeyInput.Update(msg)
m.apiKeyInput = u.(*APIKeyInput)
return m, cmd
- } else {
+ default:
var cmd tea.Cmd
m.modelList, cmd = m.modelList.Update(msg)
return m, cmd
@@ -31,7 +31,9 @@ import (
"github.com/charmbracelet/crush/internal/tui/components/dialogs"
"github.com/charmbracelet/crush/internal/tui/components/dialogs/claude"
"github.com/charmbracelet/crush/internal/tui/components/dialogs/commands"
+ "github.com/charmbracelet/crush/internal/tui/components/dialogs/copilot"
"github.com/charmbracelet/crush/internal/tui/components/dialogs/filepicker"
+ "github.com/charmbracelet/crush/internal/tui/components/dialogs/hyper"
"github.com/charmbracelet/crush/internal/tui/components/dialogs/models"
"github.com/charmbracelet/crush/internal/tui/components/dialogs/reasoning"
"github.com/charmbracelet/crush/internal/tui/page"
@@ -335,7 +337,14 @@ func (p *chatPage) Update(msg tea.Msg) (util.Model, tea.Cmd) {
cmds = append(cmds, cmd)
return p, tea.Batch(cmds...)
- case claude.ValidationCompletedMsg, claude.AuthenticationCompleteMsg:
+ case claude.ValidationCompletedMsg,
+ claude.AuthenticationCompleteMsg,
+ hyper.DeviceFlowCompletedMsg,
+ hyper.DeviceAuthInitiatedMsg,
+ hyper.DeviceFlowErrorMsg,
+ copilot.DeviceAuthInitiatedMsg,
+ copilot.DeviceFlowErrorMsg,
+ copilot.DeviceFlowCompletedMsg:
if p.focusedPane == PanelTypeSplash {
u, cmd := p.splash.Update(msg)
p.splash = u.(splash.Splash)
@@ -1050,7 +1059,8 @@ func (p *chatPage) Help() help.KeyMap {
fullList = append(fullList, []key.Binding{v})
}
case p.isOnboarding && p.splash.IsShowingClaudeOAuth2():
- if p.splash.IsClaudeOAuthURLState() {
+ switch {
+ case p.splash.IsClaudeOAuthURLState():
shortList = append(shortList,
key.NewBinding(
key.WithKeys("enter"),
@@ -1061,14 +1071,25 @@ func (p *chatPage) Help() help.KeyMap {
key.WithHelp("c", "copy url"),
),
)
- } else if p.splash.IsClaudeOAuthComplete() {
+ case p.splash.IsClaudeOAuthComplete():
shortList = append(shortList,
key.NewBinding(
key.WithKeys("enter"),
key.WithHelp("enter", "continue"),
),
)
- } else {
+ case p.splash.IsShowingHyperOAuth2() || p.splash.IsShowingCopilotOAuth2():
+ shortList = append(shortList,
+ key.NewBinding(
+ key.WithKeys("enter"),
+ key.WithHelp("enter", "copy url & open signup"),
+ ),
+ key.NewBinding(
+ key.WithKeys("c"),
+ key.WithHelp("c", "copy url"),
+ ),
+ )
+ default:
shortList = append(shortList,
key.NewBinding(
key.WithKeys("enter"),