summaryrefslogtreecommitdiff
path: root/vrsite/model.go
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-07-30 17:05:12 -0300
committerGabriel Schneider <[email protected]>2026-07-30 17:56:23 -0300
commit8ec4e135e318c7799b2576a8729493c063da4a97 (patch)
treeab37cdbb7f478eb996264cf941891f127f303c85 /vrsite/model.go
parentcb05c363045bf0e7af5858f6d7bc26f026fa9d70 (diff)
downloadnotevi-8ec4e135e318c7799b2576a8729493c063da4a97.tar.gz
notevi-8ec4e135e318c7799b2576a8729493c063da4a97.zip
Diffstat (limited to 'vrsite/model.go')
-rw-r--r--vrsite/model.go54
1 files changed, 43 insertions, 11 deletions
diff --git a/vrsite/model.go b/vrsite/model.go
index 2cafd75..678d5ee 100644
--- a/vrsite/model.go
+++ b/vrsite/model.go
@@ -74,22 +74,37 @@ type Site struct {
Title string
Repo string
Change string // the change most entries were logged at
+ Only string // the change this view is scoped to ("" = the whole log)
Agents []*Agent
Files map[string]*File // traced files
Order []string // traced paths, sorted
All []*File // every file in the repo at the site change
- Notes []*Note
- Entries []Entry
- Theme *Theme // dark
+ Notes []*Note // in scope
+ Entries []Entry // the whole log, scope or no scope
+ Theme *Theme // dark
Light *Theme
+
+ // counted over the whole log, so a scoped view can still say what it is
+ // not showing
+ NotesTotal int
+ NotesAt map[string]int // change -> notes recorded there
+ Votes map[string]int // change -> entries recorded there
+ Ranked []string // changes, most-logged first
}
// buildModel projects log entries onto agents, files and notes. It never
// touches the repo, so it is cheap enough to redo on every log change.
-func buildModel(title string, entries []Entry) *Site {
- s := &Site{Title: title, Files: map[string]*File{}, Entries: entries}
+//
+// only, when set, keeps just what was recorded at that change: a read logged
+// against another change points at line numbers that have since moved, so a
+// view of one change must not inherit them. Two things stay whole-log
+// regardless — agent colors, so they do not shuffle when the scope changes,
+// and note ids, so a link to a note survives it.
+func buildModel(title string, entries []Entry, only string) *Site {
+ s := &Site{Title: title, Only: only, Files: map[string]*File{}, Entries: entries,
+ NotesAt: map[string]int{}, Votes: map[string]int{}}
agents := map[string]*Agent{}
- changeVotes := map[string]int{}
+ noteN := 0
for _, e := range entries {
if e.Agent == "" {
e.Agent = "unknown"
@@ -105,7 +120,15 @@ func buildModel(title string, entries []Entry) *Site {
s.Agents = append(s.Agents, a)
}
if e.Change != "" {
- changeVotes[e.Change]++
+ s.Votes[e.Change]++
+ }
+ if e.Op == "note" {
+ noteN++
+ s.NotesTotal++
+ s.NotesAt[e.Change]++
+ }
+ if only != "" && e.Change != only {
+ continue
}
switch e.Op {
case "read":
@@ -125,7 +148,7 @@ func buildModel(title string, entries []Entry) *Site {
case "note":
a.Notes++
at := parseTime(e.Time)
- n := &Note{ID: len(s.Notes) + 1, NoteID: e.NoteID, Agent: a, Session: e.Session,
+ n := &Note{ID: noteN, NoteID: e.NoteID, Agent: a, Session: e.Session,
Model: e.Model, Reason: e.Reason, File: e.File, Change: e.Change,
Rev: e.Rev, Start: e.Start, End: e.End, Kind: e.Type, Text: e.Text,
At: at, When: stamp(at), Ago: ago(at)}
@@ -136,10 +159,19 @@ func buildModel(title string, entries []Entry) *Site {
}
}
}
- for c, n := range changeVotes {
- if s.Change == "" || n > changeVotes[s.Change] {
- s.Change = c
+ for c := range s.Votes {
+ s.Ranked = append(s.Ranked, c)
+ }
+ // most-logged first; ties by id so the pick is stable across rebuilds
+ sort.Slice(s.Ranked, func(i, j int) bool {
+ a, b := s.Ranked[i], s.Ranked[j]
+ if s.Votes[a] != s.Votes[b] {
+ return s.Votes[a] > s.Votes[b]
}
+ return a < b
+ })
+ if len(s.Ranked) > 0 {
+ s.Change = s.Ranked[0]
}
// drop grep-only "files" that are really path filters (dirs, non-files)
for p, f := range s.Files {