Update is the Bubble Tea reducer.
(msg tea.Msg)
| 108 | |
| 109 | // Update is the Bubble Tea reducer. |
| 110 | func (a App) Update(msg tea.Msg) (tea.Model, tea.Cmd) { |
| 111 | switch msg := msg.(type) { |
| 112 | case tea.WindowSizeMsg: |
| 113 | a.width, a.height = msg.Width, msg.Height |
| 114 | case tea.KeyMsg: |
| 115 | // Global shortcuts first. |
| 116 | k := msg.String() |
| 117 | switch { |
| 118 | case k == "q" && a.active != tabRun: |
| 119 | // `q` quits — except on Run which uses `x` for stop. |
| 120 | // If the engine subprocess is still running, kill it first so |
| 121 | // it doesn't orphan and keep kernel-level WinDivert / NFQUEUE |
| 122 | // state alive with no owner. |
| 123 | return a, a.shutdown() |
| 124 | case k == "ctrl+c": |
| 125 | return a, a.shutdown() |
| 126 | case k == "?": |
| 127 | a.active = tabHelp |
| 128 | return a, nil |
| 129 | case k == "1": |
| 130 | a.active = tabHome |
| 131 | return a, nil |
| 132 | case k == "2": |
| 133 | a.active = tabProfiles |
| 134 | return a, nil |
| 135 | case k == "3": |
| 136 | a.active = tabScan |
| 137 | return a, nil |
| 138 | case k == "4": |
| 139 | a.active = tabRun |
| 140 | return a, nil |
| 141 | case k == "5": |
| 142 | a.active = tabSettings |
| 143 | return a, nil |
| 144 | case k == "6": |
| 145 | a.active = tabHelp |
| 146 | return a, nil |
| 147 | case k == "7": |
| 148 | a.active = tabAbout |
| 149 | return a, nil |
| 150 | case k == "]" || k == "ctrl+right": |
| 151 | a.active = nextTab(a.active) |
| 152 | return a, nil |
| 153 | case k == "[" || k == "ctrl+left": |
| 154 | a.active = prevTab(a.active) |
| 155 | return a, nil |
| 156 | } |
| 157 | case configReloadMsg: |
| 158 | cfg, err := config.Load(a.opts.ConfigPath) |
| 159 | a.cfg = cfg |
| 160 | a.cfgErr = err |
| 161 | a.statusLine = fmt.Sprintf("config reloaded: %s", a.opts.ConfigPath) |
| 162 | // Let screens rebuild any derived state. |
| 163 | a.profiles = newProfilesModel(&a) |
| 164 | case statusMsg: |
| 165 | a.statusLine = string(msg) |
| 166 | } |
| 167 |