Commit Diff


commit - 7da8ef9eb7516961a7acaedbe448cde8a6e66380
commit + 906e3938298d8ab91936cc7800fd7f0e8cfd7f52
blob - dd5eb25c031c1b4f1578aa089476ced1fc81d51a
blob + facdbe6734a9193f10c74f61f62ebae8fb2b0c59
--- README.md
+++ README.md
@@ -40,8 +40,9 @@ ninja tui INPUT=demo
 ## Keys
 
 ```text
-m  months       w  weeks       l  labels
-a  artists      s  search      r  random play
+d  dailies      m  months      w  weeks
+l  labels       a  artists     s  search
+r  random play
 p  play release  q  queue release
 pa play buffer   qa queue buffer
 nn next page    pp prev page   b  back
@@ -49,6 +50,10 @@ nn next page    pp prev page   b  back
 x  exit
 ```
 
+Chronological views show the newest entries first. The dailies view shows one
+effective source per date after applying the daily, fallback, and session
+precedence rules. Days, weeks, and months with no counted releases are omitted.
+
 Enter a row number to open it. Commands such as `:month 2026-08`,
 `:day 2026-08-11`, `:label Warp`, `:artist Autechre`, `:release Label/Artist/Release`, and
 `:search Autechre`
blob - 72778315b5c77ca53c9db9d0b0f203f3b95d0211
blob + b1b0d3dc2027753401c52f08c856ed8549983ffd
--- doc/mcol.1
+++ doc/mcol.1
@@ -1,4 +1,4 @@
-.TH MCOL 1 "2026-08-23" "mcol" "User Commands"
+.TH MCOL 1 "2026-08-26" "mcol" "User Commands"
 .SH NAME
 mcol \- browse an MPD release archive
 .SH SYNOPSIS
@@ -60,11 +60,20 @@ Fail on malformed historical filenames.
 Print usage.
 .SH KEYS
 .TP
+.B d
+Show effective daily sources, newest first.  There is one row per date after
+applying the daily, fallback, and session precedence rules.
+.TP
 .BR m ,
 .BR w ,
 .BR l ,
 .B a
 Show months, weeks, labels, or artists.
+.PP
+Chronological views and addition-date lists are shown newest first.  Releases
+within a label or artist view are ordered by their latest known daily addition;
+entries known only from weekly sources follow dated releases.
+Days, weeks, and months with no counted release additions are omitted.
 .TP
 .B s
 Search artists, labels, releases, and paths.
blob - 48c89f1ba7d2310515cb27fa3f798c06fe4257fd
blob + 39864a15674a715bec0f17379fb8d6e7f5781c4b
--- src/mcol/core.sls
+++ src/mcol/core.sls
@@ -254,6 +254,8 @@
    (vector->list (hashtable-keys table)))
  (define (sorted-keys table)
    (sort string<? (table-keys table)))
+ (define (reverse-sorted-keys table)
+   (sort string>? (table-keys table)))
  (define (unique-entries entries)
    (let ([seen (make-string-table)])
      (filter (lambda (item)
@@ -348,7 +350,9 @@
                (sources-of-kind database 'daily))
      table))
  (define (canonical-days canonical)
-   (map (lambda (date) (file-summary (hashtable-ref canonical date #f))) (sorted-keys canonical)))
+   (filter (lambda (day) (> (field day 'additions) 0))
+           (map (lambda (date) (file-summary (hashtable-ref canonical date #f)))
+                (reverse-sorted-keys canonical))))
  (define (dated-catalog canonical database)
    (append (fold-left (lambda (entries date)
                         (append entries
@@ -395,7 +399,7 @@
                                                            'observedDays)
                                                     (max 1 expected))))
                                        10.0))))))
-          (sorted-keys table))))
+          (reverse-sorted-keys table))))
  (define (label-summary canonical)
    (let ([counts (make-string-table)])
      (for-each
@@ -420,23 +424,26 @@
            (map (lambda (key) (object (cons 'name key) (cons 'count (hashtable-ref counts key 0))))
                 (table-keys counts)))))
  (define (weekly-summary database days)
-   (map (lambda (item)
-          (let* ([row (file-summary item)]
-                 [daily-total (fold-left (lambda (sum day)
-                                           (if (and (string<=? (source-period-start item)
-                                                             (field day
-                                                                    'date))
-                                                    (string<=? (field day
-                                                                    'date)
-                                                             (source-period-end item)))
-                                               (+ sum
-                                                  (field day
-                                                         'additions))
-                                               sum))
-                                         0
-                                         days)])
-            (append row (object (cons 'dailyAdditions daily-total)))))
-        (sources-of-kind database 'weekly)))
+   (filter (lambda (week) (> (field week 'additions) 0))
+           (sort (lambda (left right)
+                   (string>? (field left 'periodStart) (field right 'periodStart)))
+                 (map (lambda (item)
+                        (let* ([row (file-summary item)]
+                               [daily-total (fold-left (lambda (sum day)
+                                                         (if (and (string<=? (source-period-start item)
+                                                                           (field day
+                                                                                  'date))
+                                                                  (string<=? (field day
+                                                                                  'date)
+                                                                           (source-period-end item)))
+                                                             (+ sum
+                                                                (field day
+                                                                       'additions))
+                                                             sum))
+                                                       0
+                                                       days)])
+                          (append row (object (cons 'dailyAdditions daily-total)))))
+                      (sources-of-kind database 'weekly)))))
  (define (summarize database)
    (let* ([canonical (canonical-sources database)]
           [days (canonical-days canonical)]
blob - 9a40b996cbc13bbaaaee79c3852e729732b11e17
blob + 00d8a6fcb98ec3f6a3e827342da62bfc5bd52fcc
--- src/mcol/tui.sls
+++ src/mcol/tui.sls
@@ -259,8 +259,29 @@
      (define (dates-for path)
        (let ([dates (hashtable-ref dates-by-path path #f)])
          (if dates
-             (sort string<? (vector->list (hashtable-keys dates)))
+             (sort string>? (vector->list (hashtable-keys dates)))
              '())))
+     (define (items-newest-first items)
+       (map cdr
+            (sort (lambda (left right)
+                    (let ([left-date (car left)]
+                          [right-date (car right)]
+                          [left-item (cdr left)]
+                          [right-item (cdr right)])
+                      (cond
+                        [(and left-date right-date)
+                         (or (string>? left-date right-date)
+                             (and (string=? left-date right-date)
+                                  (string-ci<? (field left-item 'path)
+                                               (field right-item 'path))))]
+                        [left-date #t]
+                        [right-date #f]
+                        [else (string-ci<? (field left-item 'path)
+                                          (field right-item 'path))])))
+                  (map (lambda (item)
+                         (let ([dates (dates-for (field item 'path))])
+                           (cons (and (pair? dates) (car dates)) item)))
+                       items))))
      (define (find-day date)
        (find (lambda (day)
                (string=? date
@@ -526,6 +547,22 @@
                                              'date)))))
                       month-days))))
 
+     (define (view-dailies)
+       (values (list (format "~a effective daily sources" (length days)))
+               (map (lambda (day)
+                      (link (format "~a  ~5a additions  ~a"
+                                    (field day
+                                           'date)
+                                    (field day
+                                           'additions)
+                                    (field day
+                                           'source))
+                            (lambda ()
+                              (open 'day
+                                    (field day
+                                           'date)))))
+                    days)))
+
      (define (view-day)
        (let ([day (find-day value)])
          (if (not day)
@@ -614,7 +651,9 @@
                               (equal? value
                                       (field item
                                              'label))))]
-              [items (unique-by-path (append (filter matches? catalog) (filter matches? events)))]
+              [items (items-newest-first
+                      (unique-by-path (append (filter matches? catalog)
+                                              (filter matches? events))))]
               [artists (unique-strings (map (lambda (item)
                                               (field item
                                                      'artist))
@@ -647,11 +686,12 @@
                       visible))))
 
      (define (view-artist)
-       (let ([items (unique-by-path (filter (lambda (item)
-                                              (equal? value
-                                                      (field item
-                                                             'artist)))
-                                            (append catalog events)))])
+       (let ([items (items-newest-first
+                     (unique-by-path (filter (lambda (item)
+                                               (equal? value
+                                                       (field item
+                                                              'artist)))
+                                             (append catalog events))))])
          (values (list (format "~a releases" (length items)))
                  (map (lambda (item)
                         (release-link (item-line item
@@ -726,7 +766,7 @@
 
      (define (view-help)
        (values (list "number   open row        nn/pp next/prev page"
-                     "b        back            m    months"
+                     "b        back            m    months        d    dailies"
                      "w        weeks           l    labels"
                      "a        artists         s    search        r    random play"
                      "p [N...] play release(s) q [N...] queue release(s)"
@@ -743,6 +783,7 @@
        (case screen
          [(months) (view-months)]
          [(month) (view-month)]
+         [(dailies) (view-dailies)]
          [(day) (view-day)]
          [(weeks) (view-weeks)]
          [(week) (view-week)]
@@ -775,7 +816,7 @@
        (display (muted "  release database // all sources"))
        (newline)
        (display
-        (muted "[m]onths [w]eeks [l]abels [a]rtists [s]earch [p]lay [q]ueue [r]random [?]help [x]exit"))
+        (muted "[m]onths [d]ailies [w]eeks [l]abels [a]rtists [s]earch [p]lay [q]ueue [r]random [?]help [x]exit"))
        (newline)
        (rule)
        (display (muted (string-append "~/" (crumbs))))
@@ -868,6 +909,7 @@
                [(string=? input "?") (open 'help)]
                [(string=? input "b") (back)]
                [(string=? input "m") (root 'months)]
+               [(string=? input "d") (root 'dailies)]
                [(string=? input "w") (root 'weeks)]
                [(string=? input "l") (root 'labels)]
                [(string=? input "a") (root 'artists)]
blob - 6cf5db2f0f80ac83a7a8d40781efa3c9ed6a0393
blob + c7d3e10f7d41880ae6f0249229d9b36d3cfd8257
--- test/test.ss
+++ test/test.ss
@@ -68,6 +68,9 @@
     (write-lines root "2026/may/dailies/2026-05-20.txt" '("Future/Album"))
     (write-lines root "2026/dailies/stdin_playlist_20260521_120000.txt" '("Session/One"))
     (write-lines root "2026/dailies/stdin_playlist_20260521_130000.txt" '("Session/Two"))
+    (write-lines root "2026/may/dailies/2026-05-22.txt" '(""))
+    (write-lines root "2026/june/dailies/2026-06-01.txt" '(""))
+    (write-lines root "2026/may/2_05_2026.txt" '(""))
     (write-lines root "2026/may/not-a-week.txt" '("Malformed/Source"))
     (let* ([database (scan-database root)]
            [report (summarize database)]
@@ -87,21 +90,29 @@
                 (field current
                        'single))
              "no undated catalog-only records")
-      (check (= 3 (length days)) "all dated sources scanned")
+      (check (= 3 (length days)) "empty daily sources omitted")
+      (check (= 1 (length (field report 'weeks))) "empty weekly sources omitted")
+      (check (equal? (map (lambda (month) (field month 'period))
+                          (field report 'months))
+                     '("2026-05"))
+             "empty months omitted")
+      (check (equal? (map (lambda (day) (field day 'date)) days)
+                     '("2026-05-21" "2026-05-20" "2026-05-02"))
+             "days newest first")
       (check (string=? "2026-05-20"
                        (field (cadr days)
                               'date))
              "later date retained")
       (check (= 2
-                (field (car days)
+                (field (caddr days)
                        'additions))
              "parents excluded")
-      (check (string=? (field (car days)
+      (check (string=? (field (caddr days)
                               'source)
                        "2026/may/dailies/2026-05-02.txt")
              "month-local source precedence")
       (check (= 2
-                (length (field (car days)
+                (length (field (caddr days)
                                'items)))
              "day exposes selectable releases")
       (check (= 5
@@ -109,11 +120,11 @@
                                'catalog)))
              "catalog is derived from dated records")
       (check (= 2
-                (field (caddr days)
+                (field (car days)
                        'additions))
              "same-day session sources merged")
       (check (string=? "2 session playlists"
-                       (field (caddr days)
+                       (field (car days)
                               'source))
              "session fallback source shown"))
     (check (guard (condition (else #t)) (scan-database root #t) #f)
@@ -137,7 +148,17 @@
     (check (= 6 (length (field report 'days))) "demo days")
     (check (= 3 (length (field report 'months))) "demo months")
     (check (= 2 (length (field report 'weeks))) "demo weeks")
-    (check (= 9 (length (field report 'labels))) "demo labels")))
+    (check (= 9 (length (field report 'labels))) "demo labels")
+    (check (equal? (map (lambda (day) (field day 'date)) (field report 'days))
+                   '("2026-08-21" "2026-08-20" "2026-08-19"
+                     "2026-08-18" "2025-08-12" "2024-12-05"))
+           "demo days newest first")
+    (check (equal? (map (lambda (month) (field month 'period)) (field report 'months))
+                   '("2026-08" "2025-08" "2024-12"))
+           "demo months newest first")
+    (check (equal? (map (lambda (week) (field week 'periodStart)) (field report 'weeks))
+                   '("2026-08-01" "2025-08-01"))
+           "demo weeks newest first")))
 
 (define (tui-test)
   ;; remove-at removes a single element at the given index