- Move the Keep/Deleted system folders out of the scrolling source list into a
pinned section at the bottom of the sidebar (only sources scroll now), so the
folders stay reachable with many sources. Renamed .source-folders ->
.system-folders; .source-list flexes to fill.
- "All sources" is visually distinct (bold + subtle blue tint) as the primary
entry.
Template/CSS only. Feed/trash rendering tests pass; feed renders in all views.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A ⋯ button on row hover and right-click on a feed row open the same floating
menu: Keep / Open / Copy link / Delete. Dismisses on click-away/Escape,
edge-flips to stay on-screen. Standalone one-click Delete icon stays; the
Trash view keeps its dedicated Undelete.
- Row markup: actions grouped in a .row-actions container (delete + ⋯ in the
feed/keep views; undelete in trash). Reuses the existing keep endpoint.
- Menu only in the normal/keep views, not trash. Keep-toggle syncs the row's
kept badge + the preview Keep button, and (in the Keep folder) drops the row
when unkept.
- Frontend-only (no backend change); inline JS validated with node --check;
152 tests pass; feed renders in all views.
Share + Add-label join the menu in later slices.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Delete now moves an artifact to a recoverable Trash instead of hard-removing.
- Model: Artifact.deleted_at (nullable, indexed) = in Trash.
- Soft-delete everywhere: DELETE /api/artifacts/{id} sets deleted_at
(idempotent 204); session POST /a/{id}/delete for the feed's hover Delete
icon; POST /a/{id}/undelete restores; POST /feed/empty-trash purges.
- Trashed artifacts are off-quota (deleting frees quota) and eviction-protected
(never evicted early); hidden from feed, sources, counts, and all API reads
(list/detail/raw).
- Sidebar: "Deleted" and "Keep" system folders at the bottom (counts, hidden
when empty); ?folder=deleted / ?folder=keep views reuse the row rendering.
Deleted view has hover Undelete + an Empty Trash button. Keep = view over
is_kept. Live-poll disabled in folder views.
- Docs (api.md delete section, CLAUDE.md), 10 new/updated tests. 150 passing;
API soft-delete lifecycle verified live.
Purge is manual (Empty Trash) this release; auto purge-on-delete (background,
~3-day retention) is the planned fast-follow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extends v0.4.0 without a second request: the sidebar is updated client-side
from the rows the existing /feed/updates poll already returns.
- Each new row's data-source drives the sidebar: per-source + "All sources"
unread counts bump; a first-time source is inserted into the list
(alphabetically). Reuses the existing count-badge pattern.
- sourceLinks is now live-queried (was cached at load) so sources added live
stay keyboard-navigable.
- No endpoint/model change. 142 tests pass; feed renders.
Caveat: while filtered to a single source, other new sources appear only on
reload (the poll is scoped to the selected view).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New artifacts appear at the top of the feed without a manual refresh.
- GET /feed/updates (session-authed): new artifacts since a timestamp marker,
owner-scoped and filter-aware (source/label/kind/q), returned as row HTML.
- Feed JS polls every 5s, paused while the tab is hidden (immediate poll on
refocus). Prepends new rows into today's group without touching the selected
row or the preview iframe — what you're reading stays put and selected.
- Fresh rows show "now", aging into their real time after ~3 min; brief
highlight on arrival.
- Extracted _artifact_rows.html partial, shared by the day-group render and the
updates endpoint, so live rows match exactly.
- Polling chosen over SSE/WebSockets: zero infra on sync-WSGI + SQLite (see
planning/next_steps for the SSE upgrade path). 142 tests pass; feed page +
endpoint verified.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Removes the "Daybook" name from everything user- and agent-facing, since no
agents are configured yet and this is the cheap moment to do it.
- UI brand, docs, README, CLAUDE.md -> AgentFeed.
- Ingest env vars DAYBOOK_URL/TOKEN/SOURCE/DATA_DIR/BIND_ADDR/HOST_DATA_DIR/
KEY_PROVISIONING_API -> AGENTFEED_* (token *values* unchanged).
- Skill: skills/daybook-posting -> skills/agentfeed-posting, daybook_post.py
-> agentfeed_post.py (+ scripts/ wrapper and its test).
- static/daybook.css -> static/agentfeed.css; localStorage daybook.* ->
agentfeed.*; templatetag daybook_extras -> agentfeed_extras; multipart
boundaries; json/csv viewer element ids; daybook_exception_handler ->
agentfeed_exception_handler; compose service/container -> agentfeed.
- Key-provisioning API now OFF by default (independent of DEBUG); enable with
AGENTFEED_KEY_PROVISIONING_API=1 on test installs only.
- Posting helper now points at Settings -> API Keys.
Deferred on purpose (a later, separate step): the Django package
`daybook_project`, the `/opt/daybook` install path, and the Forgejo repo name.
The sibling AGENT_DAYBOOK__LLM_TEST name is intentionally untouched.
Full suite: 129 passing; live server verified serving under the new name.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When a single source is selected the row's source column is hidden, so the
sidebar now renders 112px (7rem) narrower and the reclaimed space goes to
the preview pane — instead of the sidebar staying the same width and just
widening the title. One stored "base" width drives both modes (wide =
stored, source-selected = stored - 112, floored at 150px); dragging the
splitter in either mode adjusts that base directly, so the width stays
user-settable in whichever mode and the modes track each other by the fixed
offset. Avoids having to re-resize every time you switch modes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Generated a transparent PNG set from the source icon (a JPEG with a baked-in
checkerboard) by keying out the background on saturation — the arrow is
strongly saturated (0.47-0.68) while the grey/white background is 0.00, so
the cutout is clean. Produced static/logo.png (top-bar, natural aspect),
static/favicon.png (32px), and static/apple-touch-icon.png (180px).
Wired favicon + apple-touch links into all three page heads (feed, settings,
login) and placed the logo next to the "AgentFeed" title in the top bar.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When a single source is selected, every row in the middle sidebar repeats
that same source, so the source column is pure waste — the pane header
already names the source. The column now only shows under "All sources"
(where it's needed to tell rows apart); selecting a source hides it and
gives the width back to the artifact title. Also drops the now-dangling
row-main border-right in that state.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The UI stylesheet already centralized its palette in a :root block, so this
mostly meant (1) migrating the ~30 colors still hardcoded in rules into
variables so they flip in one place, and (2) adding a [data-theme="dark"]
override palette. Artifact rendering is untouched — those use the separate
static/themes/*.css and the preview backdrop stays white by design, since
artifacts (charts, reports) are authored light.
- Toggle lives in the gear-menu dropdown (Dark mode / Light mode), persists
to localStorage.
- A tiny inline script in each page <head> (feed, settings, login) applies
the stored theme before first paint, so dark-mode users never see a white
flash on load.
- color-scheme: dark on the dark palette makes native form controls
(inputs, buttons) render dark too.
- Semantic accents (kept=green, shared=blue, today, danger) got
dark-appropriate variants; status/data-viz colors (usage bar, tag chips)
and the white artifact backdrop are intentionally left as-is.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Any artifact can now be made publicly viewable via a share link, no login
required - opt-in per artifact, toggled from the feed UI next to Keep.
- Artifact.share_token: nullable, unique, high-entropy (secrets.token_urlsafe(32)).
NULL = not shared; SQL treats each NULL as distinct so uniqueness still
holds for the many not-shared rows.
- New public route GET /s/<token> (shared_artifact_view), completely
unauthenticated, serving the same rendered/raw content as the owner
route via a shared serve_artifact_file() helper.
- toggle_artifact_share: generates/clears the token: mirrors
toggle_artifact_keep's pattern.
- Fixed a real gap this surfaced: embedded images in a shared artifact's
content would otherwise break for an anonymous visitor, since asset_view
was owner-only. It now also allows access when the asset's parent
artifact is currently shared - unattached (orphaned) assets stay private
regardless.
- serialize_artifact now reports is_shared/share_url so the API reflects
sharing status (read-only for now; toggling stays UI-only).
- Feed UI: Share button (mirrors Keep's toggle interaction), a copyable
share-link box that appears when shared, and a "shared" badge on the
artifact row - distinct blue accent from Keep's green so both can be
visible on the same row without confusion.
Verified live end-to-end (toggle on/off, public access, fake-token 404,
old-link-dies-on-unshare, embedded-asset access before/after sharing)
before writing the 12 automated tests in tests/views/test_sharing.py.
Also fixed two unrelated stale spots noticed in docs/api.md while adding
the Sharing section: the example response was missing is_kept/size_bytes,
and the raw-content table didn't list json/csv (added earlier this
session but never documented there).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Daybook: a self-hosted, multi-user Django + DRF app where agents, scripts,
or people publish artifacts and each logged-in user browses their own
time-ordered, label-filterable feed.
This squashes prior local development history (kept privately on a separate
branch) into a single clean starting point for the public repository.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>