@@ -35,9 +35,8 @@ type openBlockingPickerMsg struct {
3535
3636// blockingItem wraps a bean to implement list.Item for the blocking picker
3737type blockingItem struct {
38- bean * bean.Bean
39- cfg * config.Config
40- isBlocking bool // true if current bean is blocking this one (pending state)
38+ bean * bean.Bean
39+ cfg * config.Config
4140}
4241
4342func (i blockingItem ) Title () string { return i .bean .Title }
@@ -46,7 +45,8 @@ func (i blockingItem) FilterValue() string { return i.bean.Title + " " + i.bean.
4645
4746// blockingItemDelegate handles rendering of blocking picker items
4847type blockingItemDelegate struct {
49- cfg * config.Config
48+ cfg * config.Config
49+ pendingBlocking * map [string ]bool // pointer to pending state for live updates
5050}
5151
5252func (d blockingItemDelegate ) Height () int { return 1 }
@@ -66,9 +66,10 @@ func (d blockingItemDelegate) Render(w io.Writer, m list.Model, index int, listI
6666 cursor = " "
6767 }
6868
69- // Show blocking indicator
69+ // Show blocking indicator - read from pending state for live updates
70+ isBlocking := (* d .pendingBlocking )[item .bean .ID ]
7071 var blockingIndicator string
71- if item . isBlocking {
72+ if isBlocking {
7273 blockingIndicator = lipgloss .NewStyle ().Foreground (ui .ColorDanger ).Bold (true ).Render ("● " ) // Red dot for blocking
7374 } else {
7475 blockingIndicator = lipgloss .NewStyle ().Foreground (ui .ColorMuted ).Render ("○ " ) // Empty circle for not blocking
@@ -90,14 +91,14 @@ func (d blockingItemDelegate) Render(w io.Writer, m list.Model, index int, listI
9091
9192// blockingPickerModel is the model for the blocking picker view
9293type blockingPickerModel struct {
93- list list.Model
94- beanID string // the bean we're setting blocking for
95- beanTitle string // the bean's title
96- originalBlocking map [string ]bool // original state (for computing diff)
97- pendingBlocking map [string ]bool // pending state (toggled by space)
98- cfg * config.Config
99- width int
100- height int
94+ list list.Model
95+ beanID string // the bean we're setting blocking for
96+ beanTitle string // the bean's title
97+ originalBlocking map [string ]bool // original state (for computing diff)
98+ pendingBlocking map [string ]bool // pending state (toggled by space)
99+ cfg * config.Config
100+ width int
101+ height int
101102}
102103
103104func newBlockingPickerModel (beanID , beanTitle string , currentBlocking []string , resolver * graph.Resolver , cfg * config.Config , width , height int ) blockingPickerModel {
@@ -134,23 +135,24 @@ func newBlockingPickerModel(beanID, beanTitle string, currentBlocking []string,
134135 return strings .ToLower (eligibleBeans [i ].Title ) < strings .ToLower (eligibleBeans [j ].Title )
135136 })
136137
137- delegate := blockingItemDelegate {cfg : cfg }
138-
139138 // Build items list
140139 items := make ([]list.Item , 0 , len (eligibleBeans ))
141140 for _ , b := range eligibleBeans {
142141 items = append (items , blockingItem {
143- bean : b ,
144- cfg : cfg ,
145- isBlocking : pendingBlocking [b .ID ],
142+ bean : b ,
143+ cfg : cfg ,
146144 })
147145 }
148146
149147 // Calculate modal dimensions (60% width, 60% height, with min/max constraints)
150148 modalWidth := max (40 , min (80 , width * 60 / 100 ))
151149 modalHeight := max (10 , min (20 , height * 60 / 100 ))
152150 listWidth := modalWidth - 6
153- listHeight := modalHeight - 7
151+ // Account for: header(1) + subtitle(1) + blank(1) + blank(1) + description(1) + blank(1) + help(1) + border(2) = 9
152+ listHeight := modalHeight - 9
153+
154+ // Create delegate with pointer to pending state (so it can read live updates)
155+ delegate := blockingItemDelegate {cfg : cfg , pendingBlocking : & pendingBlocking }
154156
155157 l := list .New (items , delegate , listWidth , listHeight )
156158 l .Title = "Manage Blocking"
@@ -189,41 +191,22 @@ func (m blockingPickerModel) Update(msg tea.Msg) (blockingPickerModel, tea.Cmd)
189191 modalWidth := max (40 , min (80 , msg .Width * 60 / 100 ))
190192 modalHeight := max (10 , min (20 , msg .Height * 60 / 100 ))
191193 listWidth := modalWidth - 6
192- listHeight := modalHeight - 7
194+ listHeight := modalHeight - 9 // Account for description line
193195 m .list .SetSize (listWidth , listHeight )
194196
195197 case tea.KeyMsg :
196198 if m .list .FilterState () != list .Filtering {
197199 switch msg .String () {
198200 case " " :
199- // Toggle the selected item locally
201+ // Toggle the selected item's pending state
202+ // The delegate reads from pendingBlocking directly, so no need to update items
200203 if item , ok := m .list .SelectedItem ().(blockingItem ); ok {
201204 targetID := item .bean .ID
202- currentIndex := m .list .Index ()
203-
204- // Toggle pending state
205205 if m .pendingBlocking [targetID ] {
206206 delete (m .pendingBlocking , targetID )
207207 } else {
208208 m .pendingBlocking [targetID ] = true
209209 }
210-
211- // Update the list items to reflect new state
212- oldItems := m .list .Items ()
213- newItems := make ([]list.Item , len (oldItems ))
214- for i , listItem := range oldItems {
215- if bi , ok := listItem .(blockingItem ); ok {
216- newItems [i ] = blockingItem {
217- bean : bi .bean ,
218- cfg : bi .cfg ,
219- isBlocking : m .pendingBlocking [bi .bean .ID ],
220- }
221- }
222- }
223- m .list .SetItems (newItems )
224-
225- // Restore selection position
226- m .list .Select (currentIndex )
227210 }
228211 return m , nil
229212
0 commit comments