From c1be2face7174883b356d69e0a5e8285cca12bd1 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 7 Sep 2026 21:18:49 +0100 Subject: [PATCH 1/5] v0.6.2: order sources by most recent message (recency) - Sidebar sources ordered by Max(created_at) desc instead of alphabetically; "All sources" stays pinned at top (rendered separately). - Live updates move a source to the top when it gets a new message, so the recency order holds without a reload. 152 tests pass; JS re-validated with node --check. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 10 +++++++++- artifacts/views.py | 8 +++++--- daybook_project/__init__.py | 2 +- templates/artifacts/feed.html | 26 ++++++++++++++++---------- 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d6c6c6..1366216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,13 @@ former name, "Daybook".) _Nothing yet._ +## [0.6.2] — 2026-09-07 + +### Changed +- Source sidebar is now ordered by **most recent message** (Telegram-style) + instead of alphabetically; "All sources" stays pinned at the top. Live + updates move a source to the top when it receives a new message. + ## [0.6.1] — 2026-09-07 ### Changed @@ -214,7 +221,8 @@ First tagged release. - Dockerised (Gunicorn + whitenoise), SQLite, `git`-based deploy with a runbook (`deploy/cloud_agent_deploy.md`) and health check. -[Unreleased]: https://git.cygnus.systems/peter/daybook/compare/v0.6.1...HEAD +[Unreleased]: https://git.cygnus.systems/peter/daybook/compare/v0.6.2...HEAD +[0.6.2]: https://git.cygnus.systems/peter/daybook/compare/v0.6.1...v0.6.2 [0.6.1]: https://git.cygnus.systems/peter/daybook/compare/v0.6.0...v0.6.1 [0.6.0]: https://git.cygnus.systems/peter/daybook/compare/v0.5.1...v0.6.0 [0.5.1]: https://git.cygnus.systems/peter/daybook/compare/v0.5.0...v0.5.1 diff --git a/artifacts/views.py b/artifacts/views.py index 1d2aff0..055c861 100644 --- a/artifacts/views.py +++ b/artifacts/views.py @@ -19,7 +19,7 @@ from django.contrib.auth import views as auth_views from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User from django.db import OperationalError, transaction -from django.db.models import Count, Q, Sum +from django.db.models import Count, Max, Q, Sum from django.http import FileResponse, Http404, HttpResponse, HttpResponseForbidden, JsonResponse from django.shortcuts import get_object_or_404, redirect, render from django.template.defaultfilters import date as date_filter, slugify @@ -586,10 +586,12 @@ def feed(request): source_scoped = apply_feed_filters( live, "", selected_label, selected_kind, title_query ) + # Sources ordered by most recent message first (Telegram-style); "All + # sources" is rendered separately and stays pinned at the top. source_counts = ( source_scoped.values("source") - .annotate(count=Count("id", filter=Q(read_at__isnull=True))) - .order_by("source") + .annotate(count=Count("id", filter=Q(read_at__isnull=True)), latest=Max("created_at")) + .order_by("-latest") ) keep_count = live.filter(is_kept=True).count() trash_count = Artifact.objects.filter(owner=request.user, deleted_at__isnull=False).count() diff --git a/daybook_project/__init__.py b/daybook_project/__init__.py index 6227cb2..28b8174 100644 --- a/daybook_project/__init__.py +++ b/daybook_project/__init__.py @@ -1,4 +1,4 @@ # Single source of truth for the app version. Bump this, commit, then tag the # commit `vX.Y.Z` and deploy. Surfaced on Settings -> About and in # GET /api/capabilities ("version"). -__version__ = "0.6.1" +__version__ = "0.6.2" diff --git a/templates/artifacts/feed.html b/templates/artifacts/feed.html index 54b3d20..a8e9cca 100644 --- a/templates/artifacts/feed.html +++ b/templates/artifacts/feed.html @@ -443,15 +443,17 @@ `${escapeHtml(name)}` + `${count}`; - // Insert alphabetically among the per-source links (after "All sources"). + // Newest activity goes to the top of the per-source list (just under + // the pinned "All sources"), matching the recency ordering. const list = document.querySelector(".source-list"); - const others = sourceLinks().filter( - (l) => l.querySelector(".source-name")?.textContent.trim() !== "All sources", - ); - const after = others.find( - (l) => (l.querySelector(".source-name")?.textContent.trim() || "") > name, - ); - list.insertBefore(link, after || null); + const firstSource = list.querySelector(".source-link:not(.all-sources-link)"); + list.insertBefore(link, firstSource); + }; + + const moveSourceToTop = (link) => { + const list = document.querySelector(".source-list"); + const firstSource = list.querySelector(".source-link:not(.all-sources-link)"); + if (firstSource !== link) list.insertBefore(link, firstSource); }; // From the newly-arrived rows: bump each source's unread count (adding a @@ -466,8 +468,12 @@ Object.entries(counts).forEach(([source, n]) => { total += n; const existing = findSourceLink(source); - if (existing) bumpSourceBadge(existing, n); - else addSourceLink(source, n); + if (existing) { + bumpSourceBadge(existing, n); + moveSourceToTop(existing); // most-recent activity -> top (recency order) + } else { + addSourceLink(source, n); + } }); const allLink = findSourceLink("All sources"); if (allLink && total) bumpSourceBadge(allLink, total); From 7b161f12768dbf7627df2f23188a8ebb9ba9c1b7 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 7 Sep 2026 21:37:08 +0100 Subject: [PATCH 2/5] v0.6.3: UI wording -> "messages"; folder spacing + tint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - User-facing "artifact(s)" -> "messages" across the UI (feed count, empty states, preview, About, Storage). Model/API/skill keep "artifact" internally — no agent-contract change. - Sidebar: Keep/Deleted folders get spacing between them and share the "All sources" bold + blue-tint styling (special folders). - Fix: About page points to Settings -> API Keys (was stale "Settings -> Account / API token"). 152 tests pass; inline JS re-validated with node --check. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 17 ++++++++++++- daybook_project/__init__.py | 2 +- static/agentfeed.css | 17 ++++++++----- templates/artifacts/feed.html | 28 ++++++++++----------- templates/artifacts/settings_about.html | 8 +++--- templates/artifacts/settings_demo_data.html | 2 +- templates/artifacts/settings_storage.html | 10 ++++---- 7 files changed, 52 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1366216..318a2a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,20 @@ former name, "Daybook".) _Nothing yet._ +## [0.6.3] — 2026-09-07 + +### Changed +- **User-facing wording: "artifact(s)" → "messages"** throughout the UI (feed + count, empty states, preview, About, Storage). The model, API + (`/api/artifacts`), and posting skill keep "artifact" as the internal term — + no contract change for agents. +- Sidebar: the Keep/Deleted folders now have spacing between them and share the + "All sources" bold+blue-tint styling (they're special folders). + +### Fixed +- About page now points to Settings → API Keys (was the stale "Settings → + Account / personal API token"). + ## [0.6.2] — 2026-09-07 ### Changed @@ -221,7 +235,8 @@ First tagged release. - Dockerised (Gunicorn + whitenoise), SQLite, `git`-based deploy with a runbook (`deploy/cloud_agent_deploy.md`) and health check. -[Unreleased]: https://git.cygnus.systems/peter/daybook/compare/v0.6.2...HEAD +[Unreleased]: https://git.cygnus.systems/peter/daybook/compare/v0.6.3...HEAD +[0.6.3]: https://git.cygnus.systems/peter/daybook/compare/v0.6.2...v0.6.3 [0.6.2]: https://git.cygnus.systems/peter/daybook/compare/v0.6.1...v0.6.2 [0.6.1]: https://git.cygnus.systems/peter/daybook/compare/v0.6.0...v0.6.1 [0.6.0]: https://git.cygnus.systems/peter/daybook/compare/v0.5.1...v0.6.0 diff --git a/daybook_project/__init__.py b/daybook_project/__init__.py index 28b8174..b040cb3 100644 --- a/daybook_project/__init__.py +++ b/daybook_project/__init__.py @@ -1,4 +1,4 @@ # Single source of truth for the app version. Bump this, commit, then tag the # commit `vX.Y.Z` and deploy. Surfaced on Settings -> About and in # GET /api/capabilities ("version"). -__version__ = "0.6.2" +__version__ = "0.6.3" diff --git a/static/agentfeed.css b/static/agentfeed.css index d282868..ca19527 100644 --- a/static/agentfeed.css +++ b/static/agentfeed.css @@ -1139,20 +1139,25 @@ kbd { display: flex; flex-direction: column; flex-shrink: 0; - padding: 0.4rem 0.6rem 0.2rem; + gap: 0.25rem; /* space Keep / Deleted apart */ + padding: 0.4rem 0.6rem 0.3rem; } -/* "All sources" — the primary entry, visually distinct from individual sources: - bold, with a subtle blue tint (works over both light and dark surfaces). */ -.all-sources-link { +/* "Special" sidebar entries — "All sources" and the Keep/Deleted folders — + share a distinct look (bold + subtle blue tint) to set them apart from the + individual sources. */ +.all-sources-link, +.folder-link { font-weight: 600; } -.all-sources-link:not(.selected) { +.all-sources-link:not(.selected), +.folder-link:not(.selected) { background: rgba(74, 110, 168, 0.14); } -.all-sources-link:not(.selected):hover { +.all-sources-link:not(.selected):hover, +.folder-link:not(.selected):hover { background: rgba(74, 110, 168, 0.22); } diff --git a/templates/artifacts/feed.html b/templates/artifacts/feed.html index a8e9cca..daa2182 100644 --- a/templates/artifacts/feed.html +++ b/templates/artifacts/feed.html @@ -73,7 +73,7 @@ {% if selected_folder == 'deleted' %} {% else %} - {{ artifacts|length }} artifact{{ artifacts|length|pluralize }} + {{ artifacts|length }} message{{ artifacts|length|pluralize }} {% endif %} @@ -82,7 +82,7 @@ {% include "artifacts/_day_groups.html" with has_more=older_count next_before=window_start|date:"Y-m-d" %} {% else %}
-

No artifacts

+

No messages

Nothing in the last {{ 7 }} days for these filters.

{% endif %} @@ -94,7 +94,7 @@
- {% if selected_artifact %}{{ selected_artifact.title }}{% else %}No artifact loaded{% endif %} + {% if selected_artifact %}{{ selected_artifact.title }}{% else %}No message loaded{% endif %}
- {% if keep_count or trash_count %} - - {% endif %} + @@ -152,6 +146,18 @@ + {% if label_palette %} + +
+ {% for label in label_palette %} + + {% endfor %} +
+ {% endif %}