diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-28 09:53:37 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-28 12:50:12 +0200 |
| commit | 2dc3e8caf088c01169080ca381454c653d4af4ad (patch) | |
| tree | 448a6068649ac9af173c11e7f5171245099fef32 /internal/ui/table.go | |
| parent | 18f0573596392c98b9567bd441e61fc35026bfc6 (diff) | |
refactor(ui): group Model fields into embedded sub-structs by concern
SRP/HIGH task (UUID d2de999b): the Model struct had 50+ fields spanning
blink animation, search, detail-view, and inline editing concerns. Group
related state into four anonymous embedded sub-structs:
blinkState – row blink animation (5 fields)
searchState – task-table and help-screen search (10 fields)
detailViewState – task detail overlay (11 fields)
editState – inline field editing (20 fields)
Uses Go anonymous embedding so all existing field-access syntax
(m.blinkID, m.searching, …) is preserved — no other files changed.
Each sub-struct has a comment explaining its purpose and design rationale,
including why detailDescEditing lives in detailViewState rather than editState.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/ui/table.go')
| -rw-r--r-- | internal/ui/table.go | 115 |
1 files changed, 69 insertions, 46 deletions
diff --git a/internal/ui/table.go b/internal/ui/table.go index aa4ed38..7348dab 100644 --- a/internal/ui/table.go +++ b/internal/ui/table.go @@ -35,13 +35,56 @@ type cellMatch struct { col int } -// Model wraps a Bubble Tea table.Model to display tasks. +// blinkState holds row-level blink animation state for the task table. +// A blink cycles the selected row's highlight on/off after a modification. +type blinkState struct { + blinkID int // task ID currently being blinked (0 = none) + blinkRow int // row index in the table (-1 if not found) + blinkOn bool // whether the highlight is currently inverted + blinkCount int // number of blink cycles completed so far + blinkMarkDone bool // whether to mark the task done after blinking + blinkEnabled bool // when false, skip animation and complete immediately +} -type Model struct { - tbl atable.Model - tblStyles atable.Styles - showHelp bool +// searchState holds task-table and help-screen search state. +// Both search modes share this struct because only one is active at a time. +type searchState struct { + searching bool + searchInput textinput.Model + searchRegex *regexp.Regexp + searchMatches []cellMatch + searchIndex int + helpSearching bool + helpSearchInput textinput.Model + helpSearchRegex *regexp.Regexp + helpSearchMatches []int // line indices that match + helpSearchIndex int +} + +// detailViewState holds all state for the task detail overlay. +// Blink fields here are separate from blinkState because they drive a +// per-field highlight inside the detail view rather than a table row. +type detailViewState struct { + showTaskDetail bool + currentTaskDetail *task.Task + detailSearching bool + detailSearchInput textinput.Model + detailSearchRegex *regexp.Regexp + detailFieldIndex int // currently selected field (-1 = none) + detailBlinkField int // field currently blinking (-1 = none) + detailBlinkOn bool // whether the blink is currently on + detailBlinkCount int // number of blink cycles completed so far + // detailDescEditing lives here (not in editState) because it drives an + // external-editor launch from the detail overlay, not inline text input. + detailDescEditing bool // whether the description editor is open + detailDescTempFile string // temp file path for description editing +} + +// editState holds inline field-editing state for the task table. +// Each editing mode (annotate, desc, tags, …) is mutually exclusive; +// clearEditingModes resets them all before activating a new one. +type editState struct { annotating bool annotateID int annotateInput textinput.Model @@ -73,37 +116,26 @@ type Model struct { addingTask bool addInput textinput.Model - searching bool - searchInput textinput.Model - searchRegex *regexp.Regexp - searchMatches []cellMatch - searchIndex int - - helpSearching bool - helpSearchInput textinput.Model - helpSearchRegex *regexp.Regexp - helpSearchMatches []int // line indices that match - helpSearchIndex int - prioritySelecting bool priorityID int priorityIndex int - filters []string - tasks []task.Task - - undoStack []string - - browserCmd string + editID int // task ID being edited in an external editor +} - editID int +// Model wraps a Bubble Tea table.Model to display tasks. +// Related fields are grouped into anonymous embedded sub-structs so that +// each concern can be reasoned about independently without changing the +// existing field-access syntax throughout the package. +type Model struct { + tbl atable.Model + tblStyles atable.Styles + showHelp bool - blinkID int - blinkRow int - blinkOn bool - blinkCount int - blinkMarkDone bool - blinkEnabled bool + blinkState // row blink animation (see blinkState) + searchState // task-table and help-screen search (see searchState) + detailViewState // task detail overlay (see detailViewState) + editState // inline field editing (see editState) cellExpanded bool @@ -124,26 +156,17 @@ type Model struct { inProgress int due int + filters []string + tasks []task.Task + undoStack []string + browserCmd string + theme Theme defaultTheme Theme - disco bool // disco mode changes theme on every task modification + disco bool // disco mode changes theme on every task modification statusMsg string // temporary status message shown in status bar - // Task detail view fields - showTaskDetail bool - currentTaskDetail *task.Task - detailSearching bool - detailSearchInput textinput.Model - detailSearchRegex *regexp.Regexp - detailFieldIndex int // Current selected field in detail view - detailBlinkField int // Field that is currently blinking (-1 for none) - detailBlinkOn bool // Whether the blink is currently on - detailBlinkCount int // Number of blinks remaining - detailDescEditing bool // Whether we're editing description in detail view - detailDescTempFile string // Temp file path for description editing - - // Help view fields helpViewport viewport.Model } @@ -289,7 +312,7 @@ func (m *Model) startBlink(id int, markDone bool) tea.Cmd { // New creates a new UI model with the provided rows. func New(filters []string, browserCmd string) (Model, error) { - m := Model{filters: filters, browserCmd: browserCmd, blinkEnabled: true} + m := Model{filters: filters, browserCmd: browserCmd, blinkState: blinkState{blinkEnabled: true}} m.annotateInput = textinput.New() m.annotateInput.Prompt = "annotation: " m.descInput = textinput.New() |
