Compare commits
No commits in common. "v0.4.1" and "v0.4.0" have entirely different histories.
3 changed files with 7 additions and 81 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
|
@ -9,16 +9,6 @@ former name, "Daybook".)
|
||||||
|
|
||||||
_Nothing yet._
|
_Nothing yet._
|
||||||
|
|
||||||
## [0.4.1] — 2026-09-05
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Live feed updates now also keep the **source sidebar** current: as new posts
|
|
||||||
arrive, per-source and total unread counts bump, and a source posting for the
|
|
||||||
first time is added to the list (alphabetically) — all derived client-side
|
|
||||||
from the polled rows, no extra request. Sources added live stay
|
|
||||||
keyboard-navigable. Caveat: while filtered to a single source, *other* new
|
|
||||||
sources still only appear on reload (the poll is scoped to the selected view).
|
|
||||||
|
|
||||||
## [0.4.0] — 2026-09-04
|
## [0.4.0] — 2026-09-04
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
@ -165,8 +155,7 @@ First tagged release.
|
||||||
- Dockerised (Gunicorn + whitenoise), SQLite, `git`-based deploy with a
|
- Dockerised (Gunicorn + whitenoise), SQLite, `git`-based deploy with a
|
||||||
runbook (`deploy/cloud_agent_deploy.md`) and health check.
|
runbook (`deploy/cloud_agent_deploy.md`) and health check.
|
||||||
|
|
||||||
[Unreleased]: https://git.cygnus.systems/peter/daybook/compare/v0.4.1...HEAD
|
[Unreleased]: https://git.cygnus.systems/peter/daybook/compare/v0.4.0...HEAD
|
||||||
[0.4.1]: https://git.cygnus.systems/peter/daybook/compare/v0.4.0...v0.4.1
|
|
||||||
[0.4.0]: https://git.cygnus.systems/peter/daybook/compare/v0.3.2...v0.4.0
|
[0.4.0]: https://git.cygnus.systems/peter/daybook/compare/v0.3.2...v0.4.0
|
||||||
[0.3.2]: https://git.cygnus.systems/peter/daybook/compare/v0.3.1...v0.3.2
|
[0.3.2]: https://git.cygnus.systems/peter/daybook/compare/v0.3.1...v0.3.2
|
||||||
[0.3.1]: https://git.cygnus.systems/peter/daybook/compare/v0.3.0...v0.3.1
|
[0.3.1]: https://git.cygnus.systems/peter/daybook/compare/v0.3.0...v0.3.1
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Single source of truth for the app version. Bump this, commit, then tag the
|
# 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
|
# commit `vX.Y.Z` and deploy. Surfaced on Settings -> About and in
|
||||||
# GET /api/capabilities ("version").
|
# GET /api/capabilities ("version").
|
||||||
__version__ = "0.4.1"
|
__version__ = "0.4.0"
|
||||||
|
|
|
||||||
|
|
@ -195,8 +195,7 @@
|
||||||
const artifactList = document.querySelector("[data-artifact-list]");
|
const artifactList = document.querySelector("[data-artifact-list]");
|
||||||
// Rows grow as older pages load, so always read them live rather than caching an array.
|
// Rows grow as older pages load, so always read them live rather than caching an array.
|
||||||
const allRows = () => Array.from(document.querySelectorAll(".artifact-row"));
|
const allRows = () => Array.from(document.querySelectorAll(".artifact-row"));
|
||||||
// Live-queried so sources added by live updates are keyboard-navigable too.
|
const sourceLinks = Array.from(document.querySelectorAll(".source-link"));
|
||||||
const sourceLinks = () => Array.from(document.querySelectorAll(".source-link"));
|
|
||||||
const csrfToken = document.querySelector("[name=csrfmiddlewaretoken]")?.value || "";
|
const csrfToken = document.querySelector("[name=csrfmiddlewaretoken]")?.value || "";
|
||||||
|
|
||||||
const escapeHtml = (value) => value.replace(/[&<>"]/g, (character) => ({
|
const escapeHtml = (value) => value.replace(/[&<>"]/g, (character) => ({
|
||||||
|
|
@ -384,66 +383,6 @@
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const findSourceLink = (name) =>
|
|
||||||
sourceLinks().find((link) => link.querySelector(".source-name")?.textContent.trim() === name);
|
|
||||||
|
|
||||||
const bumpSourceBadge = (link, delta) => {
|
|
||||||
let badge = link.querySelector(".source-count");
|
|
||||||
if (!badge) {
|
|
||||||
badge = document.createElement("span");
|
|
||||||
badge.className = "source-count";
|
|
||||||
badge.textContent = "0";
|
|
||||||
link.appendChild(badge);
|
|
||||||
}
|
|
||||||
badge.textContent = String((Number(badge.textContent) || 0) + delta);
|
|
||||||
};
|
|
||||||
|
|
||||||
const addSourceLink = (name, count) => {
|
|
||||||
const params = new URLSearchParams();
|
|
||||||
params.set("source", name);
|
|
||||||
if (feed.dataset.selectedLabel) params.set("label", feed.dataset.selectedLabel);
|
|
||||||
if (feed.dataset.selectedKind) params.set("kind", feed.dataset.selectedKind);
|
|
||||||
if (feed.dataset.titleQuery) params.set("q", feed.dataset.titleQuery);
|
|
||||||
params.set("view", feed.dataset.selectedView || "rendered");
|
|
||||||
params.set("preview", "none");
|
|
||||||
|
|
||||||
const link = document.createElement("a");
|
|
||||||
link.className = "source-link";
|
|
||||||
link.href = `?${params.toString()}`;
|
|
||||||
link.innerHTML =
|
|
||||||
`<span class="source-name">${escapeHtml(name)}</span>` +
|
|
||||||
`<span class="source-count">${count}</span>`;
|
|
||||||
|
|
||||||
// Insert alphabetically among the per-source links (after "All sources").
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
// From the newly-arrived rows: bump each source's unread count (adding a
|
|
||||||
// sidebar entry for a source seen for the first time) plus the total.
|
|
||||||
const registerNewSources = (rows) => {
|
|
||||||
const counts = {};
|
|
||||||
rows.forEach((row) => {
|
|
||||||
const source = row.dataset.source;
|
|
||||||
if (source) counts[source] = (counts[source] || 0) + 1;
|
|
||||||
});
|
|
||||||
let total = 0;
|
|
||||||
Object.entries(counts).forEach(([source, n]) => {
|
|
||||||
total += n;
|
|
||||||
const existing = findSourceLink(source);
|
|
||||||
if (existing) bumpSourceBadge(existing, n);
|
|
||||||
else addSourceLink(source, n);
|
|
||||||
});
|
|
||||||
const allLink = findSourceLink("All sources");
|
|
||||||
if (allLink && total) bumpSourceBadge(allLink, total);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderPreview = (row, pushState = true) => {
|
const renderPreview = (row, pushState = true) => {
|
||||||
navigationMode = "artifacts";
|
navigationMode = "artifacts";
|
||||||
markRead(row);
|
markRead(row);
|
||||||
|
|
@ -499,13 +438,12 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const moveSource = (direction) => {
|
const moveSource = (direction) => {
|
||||||
const links = sourceLinks();
|
if (!sourceLinks.length) return;
|
||||||
if (!links.length) return;
|
|
||||||
navigationMode = "sources";
|
navigationMode = "sources";
|
||||||
const current = document.querySelector(".source-link.selected");
|
const current = document.querySelector(".source-link.selected");
|
||||||
const currentIndex = current ? links.indexOf(current) : 0;
|
const currentIndex = current ? sourceLinks.indexOf(current) : 0;
|
||||||
const nextIndex = Math.max(0, Math.min(links.length - 1, currentIndex + direction));
|
const nextIndex = Math.max(0, Math.min(sourceLinks.length - 1, currentIndex + direction));
|
||||||
const nextSource = links[nextIndex];
|
const nextSource = sourceLinks[nextIndex];
|
||||||
if (nextSource && nextSource !== current) window.location.href = nextSource.href;
|
if (nextSource && nextSource !== current) window.location.href = nextSource.href;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -685,7 +623,6 @@
|
||||||
node = node.nextElementSibling;
|
node = node.nextElementSibling;
|
||||||
}
|
}
|
||||||
markArrived(inserted);
|
markArrived(inserted);
|
||||||
registerNewSources(inserted); // update sidebar: counts + any new source
|
|
||||||
liveSince = data.newest;
|
liveSince = data.newest;
|
||||||
const dayCount = group.querySelector(".day-separator-count");
|
const dayCount = group.querySelector(".day-separator-count");
|
||||||
if (dayCount) dayCount.textContent = String((parseInt(dayCount.textContent, 10) || 0) + data.count);
|
if (dayCount) dayCount.textContent = String((parseInt(dayCount.textContent, 10) || 0) + data.count);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue