| @@ -106,132 +106,16 @@ func (m authTabModel) Update(msg tea.Msg) (authTabModel, tea.Cmd) { · 105 unmodified lines |
| 106 | case tea.KeyMsg: | 106 | case tea.KeyMsg: |
| 107 | // ---- Editing mode ---- | 107 | // ---- Editing mode ---- |
| 108 | if m.editing { | 108 | if m.editing { |
| 109 | switch msg.String() { | 109 | return m.handleEditInput(msg) |
| 110 | case "enter": | | |
| 111 | value := m.editInput.Value() | | |
| 112 | fieldKey := authEditableFields[m.editField].key | | |
| 113 | fileName := m.editFileName | | |
| 114 | m.editing = false | | |
| 115 | m.editInput.Blur() | | |
| 116 | fields := map[string]any{} | | |
| 117 | if fieldKey == "priority" { | | |
| 118 | p, err := strconv.Atoi(value) | | |
| 119 | if err != nil { | | |
| 120 | return m, func() tea.Msg { | | |
| 121 | return authActionMsg{err: fmt.Errorf("invalid priority: must be a number")} | | |
| 122 | } | | |
| 123 | } | | |
| 124 | fields[fieldKey] = p | | |
| 125 | } else { | | |
| 126 | fields[fieldKey] = value | | |
| 127 | } | | |
| 128 | return m, func() tea.Msg { | | |
| 129 | err := m.client.PatchAuthFileFields(fileName, fields) | | |
| 130 | if err != nil { | | |
| 131 | return authActionMsg{err: err} | | |
| 132 | } | | |
| 133 | return authActionMsg{action: fmt.Sprintf(T("updated_field"), fieldKey, fileName)} | | |
| 134 | } | | |
| 135 | case "esc": | | |
| 136 | m.editing = false | | |
| 137 | m.editInput.Blur() | | |
| 138 | m.viewport.SetContent(m.renderContent()) | | |
| 139 | return m, nil | | |
| 140 | default: | | |
| 141 | var cmd tea.Cmd | | |
| 142 | m.editInput, cmd = m.editInput.Update(msg) | | |
| 143 | m.viewport.SetContent(m.renderContent()) | | |
| 144 | return m, cmd | | |
| 145 | } | | |
| 146 | } | 110 | } |
| 147 | | 111 | |
| 148 | // ---- Delete confirmation mode ---- | 112 | // ---- Delete confirmation mode ---- |
| 149 | if m.confirm >= 0 { | 113 | if m.confirm >= 0 { |
| 150 | switch msg.String() { | 114 | return m.handleConfirmInput(msg) |
| 151 | case "y", "Y": | | |
| 152 | idx := m.confirm | | |
| 153 | m.confirm = -1 | | |
| 154 | if idx < len(m.files) { | | |
| 155 | name := getString(m.files[idx], "name") | | |
| 156 | return m, func() tea.Msg { | | |
| 157 | err := m.client.DeleteAuthFile(name) | | |
| 158 | if err != nil { | | |
| 159 | return authActionMsg{err: err} | | |
| 160 | } | | |
| 161 | return authActionMsg{action: fmt.Sprintf(T("deleted"), name)} | | |
| 162 | } | | |
| 163 | } | | |
| 164 | m.viewport.SetContent(m.renderContent()) | | |
| 165 | return m, nil | | |
| 166 | case "n", "N", "esc": | | |
| 167 | m.confirm = -1 | | |
| 168 | m.viewport.SetContent(m.renderContent()) | | |
| 169 | return m, nil | | |
| 170 | } | | |
| 171 | return m, nil | | |
| 172 | } | 115 | } |
| 173 | | 116 | |
| 174 | // ---- Normal mode ---- | 117 | // ---- Normal mode ---- |
| 175 | switch msg.String() { | 118 | return m.handleNormalInput(msg) |
| 176 | case "j", "down": | | |
| 177 | if len(m.files) > 0 { | | |
| 178 | m.cursor = (m.cursor + 1) % len(m.files) | | |
| 179 | m.viewport.SetContent(m.renderContent()) | | |
| 180 | } | | |
| 181 | return m, nil | | |
| 182 | case "k", "up": | | |
| 183 | if len(m.files) > 0 { | | |
| 184 | m.cursor = (m.cursor - 1 + len(m.files)) % len(m.files) | | |
| 185 | m.viewport.SetContent(m.renderContent()) | | |
| 186 | } | | |
| 187 | return m, nil | | |
| 188 | case "enter", " ": | | |
| 189 | if m.expanded == m.cursor { | | |
| 190 | m.expanded = -1 | | |
| 191 | } else { | | |
| 192 | m.expanded = m.cursor | | |
| 193 | } | | |
| 194 | m.viewport.SetContent(m.renderContent()) | | |
| 195 | return m, nil | | |
| 196 | case "d", "D": | | |
| 197 | if m.cursor < len(m.files) { | | |
| 198 | m.confirm = m.cursor | | |
| 199 | m.viewport.SetContent(m.renderContent()) | | |
| 200 | } | | |
| 201 | return m, nil | | |
| 202 | case "e", "E": | | |
| 203 | if m.cursor < len(m.files) { | | |
| 204 | f := m.files[m.cursor] | | |
| 205 | name := getString(f, "name") | | |
| 206 | disabled := getBool(f, "disabled") | | |
| 207 | newDisabled := !disabled | | |
| 208 | return m, func() tea.Msg { | | |
| 209 | err := m.client.ToggleAuthFile(name, newDisabled) | | |
| 210 | if err != nil { | | |
| 211 | return authActionMsg{err: err} | | |
| 212 | } | | |
| 213 | action := T("enabled") | | |
| 214 | if newDisabled { | | |
| 215 | action = T("disabled") | | |
| 216 | } | | |
| 217 | return authActionMsg{action: fmt.Sprintf("%s %s", action, name)} | | |
| 218 | } | | |
| 219 | } | | |
| 220 | return m, nil | | |
| 221 | case "1": | | |
| 222 | return m, m.startEdit(0) // prefix | | |
| 223 | case "2": | | |
| 224 | return m, m.startEdit(1) // proxy_url | | |
| 225 | case "3": | | |
| 226 | return m, m.startEdit(2) // priority | | |
| 227 | case "r": | | |
| 228 | m.status = "" | | |
| 229 | return m, m.fetchFiles | | |
| 230 | default: | | |
| 231 | var cmd tea.Cmd | | |
| 232 | m.viewport, cmd = m.viewport.Update(msg) | | |
| 233 | return m, cmd | | |
| 234 | } | | |
| 235 | } | 119 | } |
| 236 | | 120 | |
| 237 | var cmd tea.Cmd | 121 | var cmd tea.Cmd |
| @@ -442,3 +326,131 @@ func max(a, b int) int { · 204 unmodified lines |
| 442 | } | 326 | } |
| 443 | return b | 327 | return b |
| 444 | } | 328 | } |
| | 329 | |
| | 330 | func (m authTabModel) handleEditInput(msg tea.KeyMsg) (authTabModel, tea.Cmd) { |
| | 331 | switch msg.String() { |
| | 332 | case "enter": |
| | 333 | value := m.editInput.Value() |
| | 334 | fieldKey := authEditableFields[m.editField].key |
| | 335 | fileName := m.editFileName |
| | 336 | m.editing = false |
| | 337 | m.editInput.Blur() |
| | 338 | fields := map[string]any{} |
| | 339 | if fieldKey == "priority" { |
| | 340 | p, err := strconv.Atoi(value) |
| | 341 | if err != nil { |
| | 342 | return m, func() tea.Msg { |
| | 343 | return authActionMsg{err: fmt.Errorf("%s: %s", T("invalid_int"), value)} |
| | 344 | } |
| | 345 | } |
| | 346 | fields[fieldKey] = p |
| | 347 | } else { |
| | 348 | fields[fieldKey] = value |
| | 349 | } |
| | 350 | return m, func() tea.Msg { |
| | 351 | err := m.client.PatchAuthFileFields(fileName, fields) |
| | 352 | if err != nil { |
| | 353 | return authActionMsg{err: err} |
| | 354 | } |
| | 355 | return authActionMsg{action: fmt.Sprintf(T("updated_field"), fieldKey, fileName)} |
| | 356 | } |
| | 357 | case "esc": |
| | 358 | m.editing = false |
| | 359 | m.editInput.Blur() |
| | 360 | m.viewport.SetContent(m.renderContent()) |
| | 361 | return m, nil |
| | 362 | default: |
| | 363 | var cmd tea.Cmd |
| | 364 | m.editInput, cmd = m.editInput.Update(msg) |
| | 365 | m.viewport.SetContent(m.renderContent()) |
| | 366 | return m, cmd |
| | 367 | } |
| | 368 | } |
| | 369 | |
| | 370 | func (m authTabModel) handleConfirmInput(msg tea.KeyMsg) (authTabModel, tea.Cmd) { |
| | 371 | switch msg.String() { |
| | 372 | case "y", "Y": |
| | 373 | idx := m.confirm |
| | 374 | m.confirm = -1 |
| | 375 | if idx < len(m.files) { |
| | 376 | name := getString(m.files[idx], "name") |
| | 377 | return m, func() tea.Msg { |
| | 378 | err := m.client.DeleteAuthFile(name) |
| | 379 | if err != nil { |
| | 380 | return authActionMsg{err: err} |
| | 381 | } |
| | 382 | return authActionMsg{action: fmt.Sprintf(T("deleted"), name)} |
| | 383 | } |
| | 384 | } |
| | 385 | m.viewport.SetContent(m.renderContent()) |
| | 386 | return m, nil |
| | 387 | case "n", "N", "esc": |
| | 388 | m.confirm = -1 |
| | 389 | m.viewport.SetContent(m.renderContent()) |
| | 390 | return m, nil |
| | 391 | } |
| | 392 | return m, nil |
| | 393 | } |
| | 394 | |
| | 395 | func (m authTabModel) handleNormalInput(msg tea.KeyMsg) (authTabModel, tea.Cmd) { |
| | 396 | switch msg.String() { |
| | 397 | case "j", "down": |
| | 398 | if len(m.files) > 0 { |
| | 399 | m.cursor = (m.cursor + 1) % len(m.files) |
| | 400 | m.viewport.SetContent(m.renderContent()) |
| | 401 | } |
| | 402 | return m, nil |
| | 403 | case "k", "up": |
| | 404 | if len(m.files) > 0 { |
| | 405 | m.cursor = (m.cursor - 1 + len(m.files)) % len(m.files) |
| | 406 | m.viewport.SetContent(m.renderContent()) |
| | 407 | } |
| | 408 | return m, nil |
| | 409 | case "enter", " ": |
| | 410 | if m.expanded == m.cursor { |
| | 411 | m.expanded = -1 |
| | 412 | } else { |
| | 413 | m.expanded = m.cursor |
| | 414 | } |
| | 415 | m.viewport.SetContent(m.renderContent()) |
| | 416 | return m, nil |
| | 417 | case "d", "D": |
| | 418 | if m.cursor < len(m.files) { |
| | 419 | m.confirm = m.cursor |
| | 420 | m.viewport.SetContent(m.renderContent()) |
| | 421 | } |
| | 422 | return m, nil |
| | 423 | case "e", "E": |
| | 424 | if m.cursor < len(m.files) { |
| | 425 | f := m.files[m.cursor] |
| | 426 | name := getString(f, "name") |
| | 427 | disabled := getBool(f, "disabled") |
| | 428 | newDisabled := !disabled |
| | 429 | return m, func() tea.Msg { |
| | 430 | err := m.client.ToggleAuthFile(name, newDisabled) |
| | 431 | if err != nil { |
| | 432 | return authActionMsg{err: err} |
| | 433 | } |
| | 434 | action := T("enabled") |
| | 435 | if newDisabled { |
| | 436 | action = T("disabled") |
| | 437 | } |
| | 438 | return authActionMsg{action: fmt.Sprintf("%s %s", action, name)} |
| | 439 | } |
| | 440 | } |
| | 441 | return m, nil |
| | 442 | case "1": |
| | 443 | return m, m.startEdit(0) // prefix |
| | 444 | case "2": |
| | 445 | return m, m.startEdit(1) // proxy_url |
| | 446 | case "3": |
| | 447 | return m, m.startEdit(2) // priority |
| | 448 | case "r": |
| | 449 | m.status = "" |
| | 450 | return m, m.fetchFiles |
| | 451 | default: |
| | 452 | var cmd tea.Cmd |
| | 453 | m.viewport, cmd = m.viewport.Update(msg) |
| | 454 | return m, cmd |
| | 455 | } |
| | 456 | } |