2036620528a4
mark the replayed-away originals in the history view and refresh it after actions (3.0.8)
- time
- 2026-09-23 21:43 UTC · 2 weeks ago
- machine
- koh2
- agent
- claude-opus-5-5
- session
- koh 3 review fixes
- parent
- 1af9dc58a448
↓ koh-20366205.tar.gzatlas at this save
files changed4 files
VERSION+1 −1src/view/view.v+23 −12src/view/view_test.v+25 −7v.mod+1 −1src/view/view.v+23 −12
@@ -79,6 +79,8 @@7979 mode Mode8080 target history.Save8181 typed string82+ // set when an action changed history, so the lists are read again83+ stale bool8284 }8385 8486 // run shows the view until the user leaves it. `act` performs a chosen action@@ -89,17 +91,22 @@8991 defer {9092 leave_screen()9193 }92- every := history.all_saves(store)!93- on_line := on_line_set(store)94+ mut every := []history.Save{}9495 mut session := Session{9596 screen: Screen{96- tab: start97- on_line: on_line98- flags: events.latest_flags(store)!99- counts: count_tabs(every, on_line)97+ tab: start10098 }99+ stale: true101100 }102101 for {102+ if session.stale {103+ // A load appends a save and a flag changes a mark: read both again.104+ every = history.all_saves(store)!105+ session.screen.on_line = on_line_set(store)106+ session.screen.flags = events.latest_flags(store)!107+ session.screen.counts = count_tabs(every, session.screen.on_line)108+ session.stale = false109+ }103110 session.screen.rows, session.screen.cols = terminal_size()104111 session.screen.items = filter_saves(every, session.screen.tab, session.screen.on_line,105112 session.screen.query)@@ -235,6 +242,7 @@235242 }236243 .enter {237244 if session.typed != '' {245+ session.stale = true238246 session.screen.status = act(store, Action{239247 kind: .flag240248 save_id: session.target.id@@ -259,6 +267,7 @@259267 match key.text {260268 'l' {261269 if ask_confirm('load [${session.target.id}]') {270+ session.stale = true262271 session.screen.status = act(store, Action{263272 kind: .load264273 save_id: session.target.id@@ -273,6 +282,7 @@273282 }274283 }275284 if state.flagged {285+ session.stale = true276286 session.screen.status = act(store, Action{277287 kind: .clear_flag278288 save_id: session.target.id@@ -299,7 +309,7 @@299309 mut out := []history.Save{}300310 needle := query.to_lower()301311 for save in items {302- live := save.id in on_line && save.replayed_from == ''312+ live := save.id in on_line303313 if tab == .saves && !live {304314 continue305315 }@@ -466,8 +476,8 @@466476 if save.restores != '' {467477 tags << 'back to ${save.restores}'468478 }469- if save.replayed_from != '' {470- tags << 'replayed'479+ if save.id !in screen.on_line {480+ tags << 'replayed away'471481 }472482 if tags.len > 0 {473483 line += ' ${fmt.dim(tags.join(' '))}'@@ -497,6 +507,10 @@497507 }498508 499509 fn save_mark(save history.Save, screen Screen) string {510+ // Replayed away: still in history, no longer on the line.511+ if save.id !in screen.on_line {512+ return fmt.dim('↪')513+ }500514 if save.is_checkpoint {501515 return fmt.amber('◐')502516 }@@ -506,9 +520,6 @@506520 }507521 }).flagged {508522 return fmt.red('⚑')509- }510- if save.replayed_from != '' {511- return fmt.dim('↪')512523 }513524 return ' '514525 }src/view/view_test.v+25 −7
@@ -1,6 +1,7 @@11 module view22 33 import src.events4+import src.fmt45 import src.history56 import src.snapshot67 @@ -23,15 +24,32 @@2324 }2425 2526 fn test_tabs_filter_by_what_is_on_the_line() {26- on_the_line := line_map(['aaa', 'ccc'])27- mut replayed := save('bbb', 'replayed away', false)28- replayed.replayed_from = 'aaa'29- items := [save('aaa', 'first', false), replayed, save('ccc', 'before things', true)]27+ // A combine replays the local save `bbb` as `ddd`: the copy is on the line,28+ // the original is not.29+ on_the_line := line_map(['aaa', 'ccc', 'ddd'])30+ mut copy := save('ddd', 'my work, replayed', false)31+ copy.replayed_from = 'bbb'32+ items := [save('aaa', 'first', false), save('bbb', 'my work', false),33+ save('ccc', 'before things', true), copy]3034 31- assert filter_saves(items, .all, on_the_line, '').len == 332- // A replayed-away save exists in history but is on neither live list.33- assert filter_saves(items, .saves, on_the_line, '').map(it.id) == ['aaa', 'ccc']35+ assert filter_saves(items, .all, on_the_line, '').len == 436+ // The replayed-away original exists in history but is on neither live list;37+ // the copy that replaced it is an ordinary save on the line.38+ assert filter_saves(items, .saves, on_the_line, '').map(it.id) == ['aaa', 'ccc', 'ddd']3439 assert filter_saves(items, .checkpoints, on_the_line, '').map(it.id) == ['ccc']40+}41+42+fn test_only_the_replayed_away_original_is_marked() {43+ on_the_line := line_map(['ddd'])44+ mut copy := save('ddd', 'my work, replayed', false)45+ copy.replayed_from = 'bbb'46+ screen := Screen{47+ on_line: on_the_line48+ }49+ assert fmt.strip_ansi(save_mark(save('bbb', 'my work', false), screen)) == '↪'50+ assert fmt.strip_ansi(save_mark(copy, screen)) == ' '51+ assert entry_line(save('bbb', 'my work', false), false, screen).contains('replayed away')52+ assert !entry_line(copy, false, screen).contains('replayed away')3553 }3654 3755 fn test_search_is_case_insensitive_across_the_fields() {v.mod+1 −1
@@ -1,6 +1,6 @@11 Module {22 name: 'koh3'3- version: '3.0.7'3+ version: '3.0.8'44 description: 'One SQLite file per repo, one line of history.'55 license: 'MIT'66 }