1package app23import (4 "github.com/FelineStateMachine/puzzletea/game"56 "charm.land/bubbles/v2/key"7 tea "charm.land/bubbletea/v2"8)910func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {11 switch msg := msg.(type) {12 case spawnCompleteMsg:13 cmd := newSessionController(&m).handleSpawnComplete(msg.jobID, msg.result)14 return m, cmd15 case game.SpawnCompleteMsg:16 cmd := newSessionController(&m).handleSpawnComplete(m.session.spawnJobID, msg)17 return m, cmd18 case exportCompleteMsg:19 next, cmd := m.handleExportComplete(msg)20 return next, cmd21 case exportSubmitAction:22 next, cmd := m.handleExportSubmit()23 return next, cmd24 case backAction:25 return msg.applyToModel(m)26 case tea.WindowSizeMsg:27 m = m.handleWindowSize(msg)28 if m.state != gameView {29 return m, nil30 }31 }3233 next, cmd, handled := m.handleGlobalKey(msg)34 if handled {35 return next, cmd36 }37 m = next3839 if m.state == gameView {40 gameCmd := newSessionController(&m).updateActiveGame(msg)41 return m, gameCmd42 }4344 screen := m.activeScreen()45 if screen == nil {46 return m, nil47 }4849 // Clear stale notices when the user interacts with the export form.50 if m.state == exportView {51 switch msg.(type) {52 case tea.KeyPressMsg, tea.MouseClickMsg:53 m = m.clearNotice()54 }55 }5657 nextScreen, screenCmd, action := screen.Update(msg)58 if m.screens == nil {59 m.screens = make(map[viewState]screenModel)60 }61 m.screens[m.state] = nextScreen62 if action == nil {63 return m, screenCmd64 }6566 next, actionCmd := m.handleScreenAction(action)67 return next, tea.Batch(screenCmd, actionCmd)68}6970// handleGlobalKey dispatches key messages to the domain that owns the current71// state. Pending generation and active game keys are handled by the session72// controller; pending export keys by the export workflow; normal screen keys73// by shell-level handling. Non-key messages are never consumed here.74func (m model) handleGlobalKey(msg tea.Msg) (model, tea.Cmd, bool) {75 keyMsg, ok := msg.(tea.KeyPressMsg)76 if !ok {77 return m, nil, false78 }7980 if m.state == generatingView {81 return newSessionController(&m).handleGeneratingKey(keyMsg)82 }8384 if m.state == exportRunningView {85 return m.handleExportRunningKey(keyMsg)86 }8788 if m.state == gameView {89 return newSessionController(&m).handleGameKey(keyMsg)90 }9192 // Normal screen shell keys.93 switch {94 case key.Matches(keyMsg, rootKeys.Quit):95 return m, tea.Quit, true96 case key.Matches(keyMsg, rootKeys.Debug):97 m.debug.enabled = !m.debug.enabled98 return m, nil, true99 case key.Matches(keyMsg, rootKeys.FullHelp):100 m.help.showFull = !m.help.showFull101 return m, nil, true102 default:103 return m, nil, false104 }105}106107func (m model) handleScreenAction(action screenAction) (model, tea.Cmd) {108 return action.applyToModel(m)109}