@@ -12,12 +12,15 @@ import (
12 "github.com/atotto/clipboard"
12 "github.com/atotto/clipboard"
13 "github.com/charmbracelet/catwalk/pkg/catwalk"
13 "github.com/charmbracelet/catwalk/pkg/catwalk"
14 "github.com/charmbracelet/crush/internal/agent"
14 "github.com/charmbracelet/crush/internal/agent"
15+ hyperp "github.com/charmbracelet/crush/internal/agent/hyper"
15 "github.com/charmbracelet/crush/internal/config"
16 "github.com/charmbracelet/crush/internal/config"
16 "github.com/charmbracelet/crush/internal/home"
17 "github.com/charmbracelet/crush/internal/home"
17 "github.com/charmbracelet/crush/internal/tui/components/chat"
18 "github.com/charmbracelet/crush/internal/tui/components/chat"
18 "github.com/charmbracelet/crush/internal/tui/components/core"
19 "github.com/charmbracelet/crush/internal/tui/components/core"
19 "github.com/charmbracelet/crush/internal/tui/components/core/layout"
20 "github.com/charmbracelet/crush/internal/tui/components/core/layout"
20 "github.com/charmbracelet/crush/internal/tui/components/dialogs/claude"
21 "github.com/charmbracelet/crush/internal/tui/components/dialogs/claude"
22+ "github.com/charmbracelet/crush/internal/tui/components/dialogs/copilot"
23+ "github.com/charmbracelet/crush/internal/tui/components/dialogs/hyper"
21 "github.com/charmbracelet/crush/internal/tui/components/dialogs/models"
24 "github.com/charmbracelet/crush/internal/tui/components/dialogs/models"
22 "github.com/charmbracelet/crush/internal/tui/components/logo"
25 "github.com/charmbracelet/crush/internal/tui/components/logo"
23 lspcomponent "github.com/charmbracelet/crush/internal/tui/components/lsp"
26 lspcomponent "github.com/charmbracelet/crush/internal/tui/components/lsp"
@@ -55,6 +58,12 @@ type Splash interface {
56 // IsClaudeOAuthComplete returns whether Claude OAuth flow is complete
59 // IsClaudeOAuthComplete returns whether Claude OAuth flow is complete
57 IsClaudeOAuthComplete() bool
60 IsClaudeOAuthComplete() bool
62+ // IsShowingClaudeOAuth2 returns whether showing Hyper OAuth2 flow
63+ IsShowingHyperOAuth2() bool
65+ // IsShowingClaudeOAuth2 returns whether showing GitHub Copilot OAuth2 flow
66+ IsShowingCopilotOAuth2() bool
@@ -87,6 +96,14 @@ type splashCmp struct {
87 isAPIKeyValid bool
96 isAPIKeyValid bool
88 apiKeyValue string
97 apiKeyValue string
99+ // Hyper device flow state
100+ hyperDeviceFlow *hyper.DeviceFlow
101+ showHyperDeviceFlow bool
103+ // Copilot device flow state
104+ copilotDeviceFlow *copilot.DeviceFlow
105+ showCopilotDeviceFlow bool
90 // Claude state
107 // Claude state
91 claudeAuthMethodChooser *claude.AuthMethodChooser
108 claudeAuthMethodChooser *claude.AuthMethodChooser
92 claudeOAuth2 *claude.OAuth2
109 claudeOAuth2 *claude.OAuth2
@@ -186,6 +203,26 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
188 return s, tea.Batch(cmds...)
205 return s, tea.Batch(cmds...)
206+ case hyper.DeviceFlowCompletedMsg:
207+ s.showHyperDeviceFlow = false
208+ return s, s.saveAPIKeyAndContinue(msg.Token, true)
209+ case hyper.DeviceAuthInitiatedMsg, hyper.DeviceFlowErrorMsg:
210+ if s.hyperDeviceFlow != nil {
211+ u, cmd := s.hyperDeviceFlow.Update(msg)
212+ s.hyperDeviceFlow = u.(*hyper.DeviceFlow)
216+ case copilot.DeviceAuthInitiatedMsg, copilot.DeviceFlowErrorMsg:
217+ if s.copilotDeviceFlow != nil {
218+ u, cmd := s.copilotDeviceFlow.Update(msg)
219+ s.copilotDeviceFlow = u.(*copilot.DeviceFlow)
223+ case copilot.DeviceFlowCompletedMsg:
224+ s.showCopilotDeviceFlow = false
225+ return s, s.saveAPIKeyAndContinue(msg.Token, true)
189 case claude.AuthenticationCompleteMsg:
226 case claude.AuthenticationCompleteMsg:
190 s.showClaudeAuthMethodChooser = false
227 s.showClaudeAuthMethodChooser = false
191 s.showClaudeOAuth2 = false
228 s.showClaudeOAuth2 = false
@@ -205,6 +242,10 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
206 case tea.KeyPressMsg:
243 case tea.KeyPressMsg:
245+ case key.Matches(msg, s.keyMap.Copy) && s.showHyperDeviceFlow:
246+ return s, s.hyperDeviceFlow.CopyCode()
247+ case key.Matches(msg, s.keyMap.Copy) && s.showCopilotDeviceFlow:
248+ return s, s.copilotDeviceFlow.CopyCode()
208 case key.Matches(msg, s.keyMap.Copy) && s.showClaudeOAuth2 && s.claudeOAuth2.State == claude.OAuthStateURL:
249 case key.Matches(msg, s.keyMap.Copy) && s.showClaudeOAuth2 && s.claudeOAuth2.State == claude.OAuthStateURL:
209 return s, tea.Sequence(
250 return s, tea.Sequence(
210 tea.SetClipboard(s.claudeOAuth2.URL),
251 tea.SetClipboard(s.claudeOAuth2.URL),
@@ -223,21 +264,27 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
223 s.claudeOAuth2 = u.(*claude.OAuth2)
264 s.claudeOAuth2 = u.(*claude.OAuth2)
224 return s, cmd
265 return s, cmd
225 case key.Matches(msg, s.keyMap.Back):
266 case key.Matches(msg, s.keyMap.Back):
226− if s.showClaudeAuthMethodChooser {
268+ case s.showClaudeAuthMethodChooser:
227 s.claudeAuthMethodChooser.SetDefaults()
269 s.claudeAuthMethodChooser.SetDefaults()
228 s.showClaudeAuthMethodChooser = false
270 s.showClaudeAuthMethodChooser = false
229 return s, nil
271 return s, nil
231− if s.showClaudeOAuth2 {
272+ case s.showClaudeOAuth2:
232 s.claudeOAuth2.SetDefaults()
273 s.claudeOAuth2.SetDefaults()
233 s.showClaudeOAuth2 = false
274 s.showClaudeOAuth2 = false
234 s.showClaudeAuthMethodChooser = true
275 s.showClaudeAuthMethodChooser = true
235 return s, nil
276 return s, nil
236− }
277+ case s.showHyperDeviceFlow:
237− if s.isAPIKeyValid {
278+ s.hyperDeviceFlow = nil
279+ s.showHyperDeviceFlow = false
238 return s, nil
280 return s, nil
239− }
281+ case s.showCopilotDeviceFlow:
282+ s.copilotDeviceFlow = nil
283+ s.showCopilotDeviceFlow = false
285+ case s.isAPIKeyValid:
240− if s.needsAPIKey {
287+ case s.needsAPIKey:
241 if s.selectedModel.Provider.ID == catwalk.InferenceProviderAnthropic {
288 if s.selectedModel.Provider.ID == catwalk.InferenceProviderAnthropic {
242 s.showClaudeAuthMethodChooser = true
289 s.showClaudeAuthMethodChooser = true
@@ -249,7 +296,8 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
249 return s, nil
296 return s, nil
251 case key.Matches(msg, s.keyMap.Select):
298 case key.Matches(msg, s.keyMap.Select):
252− if s.showClaudeAuthMethodChooser {
300+ case s.showClaudeAuthMethodChooser:
253 selectedItem := s.modelList.SelectedModel()
301 selectedItem := s.modelList.SelectedModel()
254 if selectedItem == nil {
302 if selectedItem == nil {
255 return s, nil
303 return s, nil
@@ -267,16 +315,17 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
267 s.showClaudeOAuth2 = true
315 s.showClaudeOAuth2 = true
269 return s, nil
317 return s, nil
271− if s.showClaudeOAuth2 {
318+ case s.showClaudeOAuth2:
272 m2, cmd2 := s.claudeOAuth2.ValidationConfirm()
319 m2, cmd2 := s.claudeOAuth2.ValidationConfirm()
273 s.claudeOAuth2 = m2.(*claude.OAuth2)
320 s.claudeOAuth2 = m2.(*claude.OAuth2)
274 return s, cmd2
321 return s, cmd2
275− }
322+ case s.showHyperDeviceFlow:
323+ return s, s.hyperDeviceFlow.CopyCodeAndOpenURL()
324+ case s.showCopilotDeviceFlow:
325+ return s, s.copilotDeviceFlow.CopyCodeAndOpenURL()
276− if s.isAPIKeyValid {
326+ case s.isAPIKeyValid:
277 return s, s.saveAPIKeyAndContinue(s.apiKeyValue, true)
327 return s, s.saveAPIKeyAndContinue(s.apiKeyValue, true)
279− if s.isOnboarding && !s.needsAPIKey {
328+ case s.isOnboarding && !s.needsAPIKey:
280 selectedItem := s.modelList.SelectedModel()
329 selectedItem := s.modelList.SelectedModel()
281 if selectedItem == nil {
330 if selectedItem == nil {
282 return s, nil
331 return s, nil
@@ -286,9 +335,22 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
286 s.isOnboarding = false
335 s.isOnboarding = false
287 return s, tea.Batch(cmd, util.CmdHandler(OnboardingCompleteMsg{}))
336 return s, tea.Batch(cmd, util.CmdHandler(OnboardingCompleteMsg{}))
338+ switch selectedItem.Provider.ID {
289− if selectedItem.Provider.ID == catwalk.InferenceProviderAnthropic {
339+ case catwalk.InferenceProviderAnthropic:
290 s.showClaudeAuthMethodChooser = true
340 s.showClaudeAuthMethodChooser = true
291 return s, nil
341 return s, nil
343+ s.selectedModel = selectedItem
344+ s.showHyperDeviceFlow = true
345+ s.hyperDeviceFlow = hyper.NewDeviceFlow()
346+ s.hyperDeviceFlow.SetWidth(min(s.width-2, 60))
347+ return s, s.hyperDeviceFlow.Init()
348+ case catwalk.InferenceProviderCopilot:
349+ s.selectedModel = selectedItem
350+ s.showCopilotDeviceFlow = true
351+ s.copilotDeviceFlow = copilot.NewDeviceFlow()
352+ s.copilotDeviceFlow.SetWidth(min(s.width-2, 60))
353+ return s, s.copilotDeviceFlow.Init()
293 // Provider not configured, show API key input
355 // Provider not configured, show API key input
294 s.needsAPIKey = true
356 s.needsAPIKey = true
@@ -296,7 +358,7 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
296 s.apiKeyInput.SetProviderName(selectedItem.Provider.Name)
358 s.apiKeyInput.SetProviderName(selectedItem.Provider.Name)
297 return s, nil
359 return s, nil
299− } else if s.needsAPIKey {
361+ case s.needsAPIKey:
300 // Handle API key submission
362 // Handle API key submission
301 s.apiKeyValue = strings.TrimSpace(s.apiKeyInput.Value())
363 s.apiKeyValue = strings.TrimSpace(s.apiKeyInput.Value())
302 if s.apiKeyValue == "" {
364 if s.apiKeyValue == "" {
@@ -337,7 +399,7 @@ func (s *splashCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) {
340− } else if s.needsProjectInit {
402+ case s.needsProjectInit:
341 return s, s.initializeProject()
403 return s, s.initializeProject()
343 case key.Matches(msg, s.keyMap.Tab, s.keyMap.LeftRight):
405 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) {
385 return s, s.initializeProject()
447 return s, s.initializeProject()
388− if s.showClaudeAuthMethodChooser {
451+ case s.showClaudeAuthMethodChooser:
389 u, cmd := s.claudeAuthMethodChooser.Update(msg)
452 u, cmd := s.claudeAuthMethodChooser.Update(msg)
390 s.claudeAuthMethodChooser = u.(*claude.AuthMethodChooser)
453 s.claudeAuthMethodChooser = u.(*claude.AuthMethodChooser)
391 return s, cmd
454 return s, cmd
392− } else if s.showClaudeOAuth2 {
455+ case s.showClaudeOAuth2:
393 u, cmd := s.claudeOAuth2.Update(msg)
456 u, cmd := s.claudeOAuth2.Update(msg)
394 s.claudeOAuth2 = u.(*claude.OAuth2)
457 s.claudeOAuth2 = u.(*claude.OAuth2)
395 return s, cmd
458 return s, cmd
459+ case s.showHyperDeviceFlow:
460+ u, cmd := s.hyperDeviceFlow.Update(msg)
461+ s.hyperDeviceFlow = u.(*hyper.DeviceFlow)
463+ case s.showCopilotDeviceFlow:
464+ u, cmd := s.copilotDeviceFlow.Update(msg)
465+ s.copilotDeviceFlow = u.(*copilot.DeviceFlow)
396− } else if s.needsAPIKey {
467+ case s.needsAPIKey:
397 u, cmd := s.apiKeyInput.Update(msg)
468 u, cmd := s.apiKeyInput.Update(msg)
398 s.apiKeyInput = u.(*models.APIKeyInput)
469 s.apiKeyInput = u.(*models.APIKeyInput)
399 return s, cmd
470 return s, cmd
400− } else if s.isOnboarding {
471+ case s.isOnboarding:
401 u, cmd := s.modelList.Update(msg)
472 u, cmd := s.modelList.Update(msg)
402 s.modelList = u
473 s.modelList = u
403 return s, cmd
474 return s, cmd
406 case tea.PasteMsg:
477 case tea.PasteMsg:
407− if s.showClaudeOAuth2 {
479+ case s.showClaudeOAuth2:
408 u, cmd := s.claudeOAuth2.Update(msg)
480 u, cmd := s.claudeOAuth2.Update(msg)
409 s.claudeOAuth2 = u.(*claude.OAuth2)
481 s.claudeOAuth2 = u.(*claude.OAuth2)
410 return s, cmd
482 return s, cmd
483+ case s.showHyperDeviceFlow:
484+ u, cmd := s.hyperDeviceFlow.Update(msg)
485+ s.hyperDeviceFlow = u.(*hyper.DeviceFlow)
487+ case s.showCopilotDeviceFlow:
488+ u, cmd := s.copilotDeviceFlow.Update(msg)
489+ s.copilotDeviceFlow = u.(*copilot.DeviceFlow)
411− } else if s.needsAPIKey {
491+ case s.needsAPIKey:
412 u, cmd := s.apiKeyInput.Update(msg)
492 u, cmd := s.apiKeyInput.Update(msg)
413 s.apiKeyInput = u.(*models.APIKeyInput)
493 s.apiKeyInput = u.(*models.APIKeyInput)
414 return s, cmd
494 return s, cmd
415− } else if s.isOnboarding {
495+ case s.isOnboarding:
416 var cmd tea.Cmd
496 var cmd tea.Cmd
417 s.modelList, cmd = s.modelList.Update(msg)
497 s.modelList, cmd = s.modelList.Update(msg)
418 return s, cmd
498 return s, cmd
420 case spinner.TickMsg:
500 case spinner.TickMsg:
421− if s.showClaudeOAuth2 {
502+ case s.showClaudeOAuth2:
422 u, cmd := s.claudeOAuth2.Update(msg)
503 u, cmd := s.claudeOAuth2.Update(msg)
423 s.claudeOAuth2 = u.(*claude.OAuth2)
504 s.claudeOAuth2 = u.(*claude.OAuth2)
424 return s, cmd
505 return s, cmd
425− } else {
506+ case s.showHyperDeviceFlow:
507+ u, cmd := s.hyperDeviceFlow.Update(msg)
508+ s.hyperDeviceFlow = u.(*hyper.DeviceFlow)
510+ case s.showCopilotDeviceFlow:
511+ u, cmd := s.copilotDeviceFlow.Update(msg)
512+ s.copilotDeviceFlow = u.(*copilot.DeviceFlow)
426 u, cmd := s.apiKeyInput.Update(msg)
515 u, cmd := s.apiKeyInput.Update(msg)
427 s.apiKeyInput = u.(*models.APIKeyInput)
516 s.apiKeyInput = u.(*models.APIKeyInput)
428 return s, cmd
517 return s, cmd
@@ -560,7 +649,9 @@ func (s *splashCmp) isProviderConfigured(providerID string) bool {
560func (s *splashCmp) View() string {
649func (s *splashCmp) View() string {
561 t := styles.CurrentTheme()
650 t := styles.CurrentTheme()
562 var content string
651 var content string
563− if s.showClaudeAuthMethodChooser {
654+ case s.showClaudeAuthMethodChooser:
564 remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
655 remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
565 chooserView := s.claudeAuthMethodChooser.View()
656 chooserView := s.claudeAuthMethodChooser.View()
566 authMethodSelector := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
657 authMethodSelector := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
@@ -576,7 +667,7 @@ func (s *splashCmp) View() string {
576 s.logoRendered,
667 s.logoRendered,
577 authMethodSelector,
668 authMethodSelector,
579− } else if s.showClaudeOAuth2 {
670+ case s.showClaudeOAuth2:
580 remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
671 remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
581 oauth2View := s.claudeOAuth2.View()
672 oauth2View := s.claudeOAuth2.View()
582 oauthSelector := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
673 oauthSelector := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
@@ -592,7 +683,39 @@ func (s *splashCmp) View() string {
592 s.logoRendered,
683 s.logoRendered,
593 oauthSelector,
684 oauthSelector,
686+ case s.showHyperDeviceFlow:
687+ remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
688+ hyperView := s.hyperDeviceFlow.View()
689+ hyperSelector := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
690+ lipgloss.JoinVertical(
692+ t.S().Base.PaddingLeft(1).Foreground(t.Primary).Render("Let's Auth Hyper"),
697+ content = lipgloss.JoinVertical(
702+ case s.showCopilotDeviceFlow:
703+ remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
704+ copilotView := s.copilotDeviceFlow.View()
705+ copilotSelector := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
706+ lipgloss.JoinVertical(
708+ t.S().Base.PaddingLeft(1).Foreground(t.Primary).Render("Let's Auth GitHub Copilot"),
713+ content = lipgloss.JoinVertical(
595− } else if s.needsAPIKey {
718+ case s.needsAPIKey:
596 remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
719 remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
597 apiKeyView := t.S().Base.PaddingLeft(1).Render(s.apiKeyInput.View())
720 apiKeyView := t.S().Base.PaddingLeft(1).Render(s.apiKeyInput.View())
598 apiKeySelector := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
721 apiKeySelector := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
@@ -606,7 +729,7 @@ func (s *splashCmp) View() string {
606 s.logoRendered,
729 s.logoRendered,
607 apiKeySelector,
730 apiKeySelector,
609− } else if s.isOnboarding {
732+ case s.isOnboarding:
610 modelListView := s.modelList.View()
733 modelListView := s.modelList.View()
611 remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
734 remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
612 modelSelector := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
735 modelSelector := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
@@ -622,7 +745,7 @@ func (s *splashCmp) View() string {
622 s.logoRendered,
745 s.logoRendered,
623 modelSelector,
746 modelSelector,
625− } else if s.needsProjectInit {
748+ case s.needsProjectInit:
626 titleStyle := t.S().Base.Foreground(t.FgBase)
749 titleStyle := t.S().Base.Foreground(t.FgBase)
627 pathStyle := t.S().Base.Foreground(t.Success).PaddingLeft(2)
750 pathStyle := t.S().Base.Foreground(t.Success).PaddingLeft(2)
628 bodyStyle := t.S().Base.Foreground(t.FgMuted)
751 bodyStyle := t.S().Base.Foreground(t.FgMuted)
@@ -673,7 +796,7 @@ func (s *splashCmp) View() string {
674 initContent,
797 initContent,
676− } else {
799+ default:
677 parts := []string{
800 parts := []string{
678 s.logoRendered,
801 s.logoRendered,
679 s.infoSection(),
802 s.infoSection(),
@@ -690,28 +813,25 @@ func (s *splashCmp) View() string {
692func (s *splashCmp) Cursor() *tea.Cursor {
815func (s *splashCmp) Cursor() *tea.Cursor {
693− if s.showClaudeAuthMethodChooser {
817+ case s.showClaudeAuthMethodChooser:
694 return nil
818 return nil
696− if s.showClaudeOAuth2 {
819+ case s.showClaudeOAuth2:
697 if cursor := s.claudeOAuth2.CodeInput.Cursor(); cursor != nil {
820 if cursor := s.claudeOAuth2.CodeInput.Cursor(); cursor != nil {
698 cursor.Y += 2 // FIXME(@andreynering): Why do we need this?
821 cursor.Y += 2 // FIXME(@andreynering): Why do we need this?
699 return s.moveCursor(cursor)
822 return s.moveCursor(cursor)
701 return nil
824 return nil
703− if s.needsAPIKey {
825+ case s.needsAPIKey:
704 cursor := s.apiKeyInput.Cursor()
826 cursor := s.apiKeyInput.Cursor()
705 if cursor != nil {
827 if cursor != nil {
706 return s.moveCursor(cursor)
828 return s.moveCursor(cursor)
708− } else if s.isOnboarding {
830+ case s.isOnboarding:
709 cursor := s.modelList.Cursor()
831 cursor := s.modelList.Cursor()
710 if cursor != nil {
832 if cursor != nil {
711 return s.moveCursor(cursor)
833 return s.moveCursor(cursor)
716 return nil
836 return nil
@@ -803,13 +923,14 @@ func (s *splashCmp) logoGap() int {
804// Bindings implements SplashPage.
924// Bindings implements SplashPage.
805func (s *splashCmp) Bindings() []key.Binding {
925func (s *splashCmp) Bindings() []key.Binding {
806− if s.showClaudeAuthMethodChooser {
927+ case s.showClaudeAuthMethodChooser:
807 return []key.Binding{
928 return []key.Binding{
808 s.keyMap.Select,
929 s.keyMap.Select,
809 s.keyMap.Tab,
930 s.keyMap.Tab,
810 s.keyMap.Back,
931 s.keyMap.Back,
812− } else if s.showClaudeOAuth2 {
933+ case s.showClaudeOAuth2:
813 bindings := []key.Binding{
934 bindings := []key.Binding{
814 s.keyMap.Select,
935 s.keyMap.Select,
@@ -817,18 +938,18 @@ func (s *splashCmp) Bindings() []key.Binding {
817 bindings = append(bindings, s.keyMap.Copy)
938 bindings = append(bindings, s.keyMap.Copy)
819 return bindings
940 return bindings
820− } else if s.needsAPIKey {
941+ case s.needsAPIKey:
821 return []key.Binding{
942 return []key.Binding{
822 s.keyMap.Select,
943 s.keyMap.Select,
823 s.keyMap.Back,
944 s.keyMap.Back,
825− } else if s.isOnboarding {
946+ case s.isOnboarding:
826 return []key.Binding{
947 return []key.Binding{
827 s.keyMap.Select,
948 s.keyMap.Select,
828 s.keyMap.Next,
949 s.keyMap.Next,
829 s.keyMap.Previous,
950 s.keyMap.Previous,
831− } else if s.needsProjectInit {
952+ case s.needsProjectInit:
832 return []key.Binding{
953 return []key.Binding{
833 s.keyMap.Select,
954 s.keyMap.Select,
834 s.keyMap.Yes,
955 s.keyMap.Yes,
@@ -836,8 +957,9 @@ func (s *splashCmp) Bindings() []key.Binding {
836 s.keyMap.Tab,
957 s.keyMap.Tab,
837 s.keyMap.LeftRight,
958 s.keyMap.LeftRight,
961+ return []key.Binding{}
840− return []key.Binding{}
843func (s *splashCmp) getMaxInfoWidth() int {
965func (s *splashCmp) getMaxInfoWidth() int {
@@ -938,3 +1060,11 @@ func (s *splashCmp) IsClaudeOAuthURLState() bool {
938func (s *splashCmp) IsClaudeOAuthComplete() bool {
1060func (s *splashCmp) IsClaudeOAuthComplete() bool {
939 return s.showClaudeOAuth2 && s.claudeOAuth2.State == claude.OAuthStateCode && s.claudeOAuth2.ValidationState == claude.OAuthValidationStateValid
1061 return s.showClaudeOAuth2 && s.claudeOAuth2.State == claude.OAuthStateCode && s.claudeOAuth2.ValidationState == claude.OAuthValidationStateValid
1064+func (s *splashCmp) IsShowingHyperOAuth2() bool {
1065+ return s.showHyperDeviceFlow
1068+func (s *splashCmp) IsShowingCopilotOAuth2() bool {
1069+ return s.showCopilotDeviceFlow