Compare commits
No commits in common. "v0.2.1" and "v0.2.0" have entirely different histories.
8 changed files with 12 additions and 134 deletions
16
CHANGELOG.md
16
CHANGELOG.md
|
|
@ -8,19 +8,6 @@ the git tags (`vX.Y.Z`) and `daybook_project.__version__`.
|
||||||
|
|
||||||
_Nothing yet._
|
_Nothing yet._
|
||||||
|
|
||||||
## [0.2.1] — 2026-09-04
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Top-bar **avatar**: a round initials badge (deterministic per-user color)
|
|
||||||
replaces the gear/username button; lays the groundwork for profile photos.
|
|
||||||
- Django admin now registers `ApiKey` and `TagColor`, so `/admin/` is complete
|
|
||||||
as an operator break-glass tool (key value shown read-only).
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Settings nav regrouped into **You** (Account, API Keys, Storage, Demo Data)
|
|
||||||
and a superuser-only **Admin** (Users) section, with About on its own — the
|
|
||||||
personal/admin split now mirrors who each setting affects.
|
|
||||||
|
|
||||||
## [0.2.0] — 2026-09-04
|
## [0.2.0] — 2026-09-04
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
@ -98,8 +85,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.2.1...HEAD
|
[Unreleased]: https://git.cygnus.systems/peter/daybook/compare/v0.2.0...HEAD
|
||||||
[0.2.1]: https://git.cygnus.systems/peter/daybook/compare/v0.2.0...v0.2.1
|
|
||||||
[0.2.0]: https://git.cygnus.systems/peter/daybook/compare/v0.1.1...v0.2.0
|
[0.2.0]: https://git.cygnus.systems/peter/daybook/compare/v0.1.1...v0.2.0
|
||||||
[0.1.1]: https://git.cygnus.systems/peter/daybook/compare/v0.1.0...v0.1.1
|
[0.1.1]: https://git.cygnus.systems/peter/daybook/compare/v0.1.0...v0.1.1
|
||||||
[0.1.0]: https://git.cygnus.systems/peter/daybook/releases/tag/v0.1.0
|
[0.1.0]: https://git.cygnus.systems/peter/daybook/releases/tag/v0.1.0
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import ApiKey, Artifact, Asset, TagColor, UserProfile
|
from .models import Artifact, Asset, UserProfile
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Artifact)
|
@admin.register(Artifact)
|
||||||
|
|
@ -21,18 +21,3 @@ class AssetAdmin(admin.ModelAdmin):
|
||||||
list_display = ("id", "owner", "artifact", "content_type", "size_bytes", "created_at")
|
list_display = ("id", "owner", "artifact", "content_type", "size_bytes", "created_at")
|
||||||
list_filter = ("owner", "content_type")
|
list_filter = ("owner", "content_type")
|
||||||
search_fields = ("id",)
|
search_fields = ("id",)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(ApiKey)
|
|
||||||
class ApiKeyAdmin(admin.ModelAdmin):
|
|
||||||
list_display = ("name", "user", "key", "created_at", "last_used_at")
|
|
||||||
list_filter = ("user",)
|
|
||||||
search_fields = ("name", "key", "user__username")
|
|
||||||
# The key value and timestamps are system-managed; show but don't hand-edit.
|
|
||||||
readonly_fields = ("key", "created_at", "last_used_at")
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(TagColor)
|
|
||||||
class TagColorAdmin(admin.ModelAdmin):
|
|
||||||
list_display = ("name", "color")
|
|
||||||
search_fields = ("name",)
|
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
from django import template
|
|
||||||
|
|
||||||
register = template.Library()
|
|
||||||
|
|
||||||
# A small fixed palette. A user's avatar color is picked deterministically from
|
|
||||||
# their username, so it stays stable across sessions and page loads.
|
|
||||||
_AVATAR_COLORS = [
|
|
||||||
"#4a6ea8",
|
|
||||||
"#3f8f7b",
|
|
||||||
"#a3712a",
|
|
||||||
"#8a4f9e",
|
|
||||||
"#a3312a",
|
|
||||||
"#2f7d95",
|
|
||||||
"#6b7280",
|
|
||||||
"#7b6a3f",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
|
||||||
def avatar_color(value):
|
|
||||||
text = str(value or "")
|
|
||||||
if not text:
|
|
||||||
return _AVATAR_COLORS[0]
|
|
||||||
return _AVATAR_COLORS[sum(ord(c) for c in text) % len(_AVATAR_COLORS)]
|
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
|
||||||
def avatar_initial(value):
|
|
||||||
text = str(value or "").strip()
|
|
||||||
return text[0].upper() if text else "?"
|
|
||||||
|
|
@ -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.2.1"
|
__version__ = "0.2.0"
|
||||||
|
|
|
||||||
|
|
@ -128,42 +128,6 @@ body {
|
||||||
border-color: var(--control-border);
|
border-color: var(--control-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-button {
|
|
||||||
background: transparent;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 50%;
|
|
||||||
cursor: pointer;
|
|
||||||
line-height: 0;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-button:hover .avatar,
|
|
||||||
.avatar-button:focus-visible .avatar {
|
|
||||||
box-shadow: 0 0 0 2px var(--content-bg), 0 0 0 4px var(--control-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 50%;
|
|
||||||
color: #fff;
|
|
||||||
display: inline-flex;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: 600;
|
|
||||||
height: 2rem;
|
|
||||||
justify-content: center;
|
|
||||||
line-height: 1;
|
|
||||||
width: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-menu-name {
|
|
||||||
border-bottom: 1px solid var(--control-border);
|
|
||||||
color: var(--muted);
|
|
||||||
font-size: 0.8rem;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
padding: 0.5rem 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-menu-dropdown {
|
.user-menu-dropdown {
|
||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
border: 1px solid var(--control-border);
|
border: 1px solid var(--control-border);
|
||||||
|
|
@ -1004,21 +968,6 @@ kbd {
|
||||||
padding-bottom: 0.7rem !important;
|
padding-bottom: 0.7rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-nav-group {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-bottom: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-nav-heading {
|
|
||||||
color: var(--muted);
|
|
||||||
font-size: 0.7rem;
|
|
||||||
font-weight: 700;
|
|
||||||
letter-spacing: 0.06em;
|
|
||||||
padding: 0.3rem 0.7rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings-panel {
|
.settings-panel {
|
||||||
max-width: 42rem;
|
max-width: 42rem;
|
||||||
padding: 1.6rem 2rem;
|
padding: 1.6rem 2rem;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
{% load daybook_extras %}
|
|
||||||
<header class="topbar">
|
<header class="topbar">
|
||||||
<div class="brandline">
|
<div class="brandline">
|
||||||
<a class="brand-link" href="{% url 'feed' %}"><img class="brand-logo" src="/static/logo.png" alt=""><h1>AgentFeed</h1></a>
|
<a class="brand-link" href="{% url 'feed' %}"><img class="brand-logo" src="/static/logo.png" alt=""><h1>AgentFeed</h1></a>
|
||||||
|
|
@ -7,12 +6,12 @@
|
||||||
{% if topbar_toolbar %}{% include topbar_toolbar %}{% endif %}
|
{% if topbar_toolbar %}{% include topbar_toolbar %}{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="topbar-actions">
|
<div class="topbar-actions">
|
||||||
<button class="avatar-button" type="button" data-user-menu-toggle aria-haspopup="true" aria-expanded="false" title="{{ request.user.username }}" aria-label="{{ request.user.username }} menu">
|
<button class="icon-button" type="button" data-user-menu-toggle aria-haspopup="true" aria-expanded="false" title="{{ request.user.username }}">
|
||||||
<span class="avatar" style="background: {{ request.user.username|avatar_color }};">{{ request.user.username|avatar_initial }}</span>
|
⚙ {{ request.user.username }}
|
||||||
</button>
|
</button>
|
||||||
<div class="user-menu-dropdown" hidden data-user-menu>
|
<div class="user-menu-dropdown" hidden data-user-menu>
|
||||||
<div class="user-menu-name">{{ request.user.username }}</div>
|
|
||||||
<a href="{% url 'settings-account' %}">Settings</a>
|
<a href="{% url 'settings-account' %}">Settings</a>
|
||||||
|
<a href="{% url 'settings-demo-data' %}">Demo Data</a>
|
||||||
<a href="{% url 'settings-about' %}">About</a>
|
<a href="{% url 'settings-about' %}">About</a>
|
||||||
<button type="button" data-theme-toggle>Dark mode</button>
|
<button type="button" data-theme-toggle>Dark mode</button>
|
||||||
<form method="post" action="{% url 'logout' %}">{% csrf_token %}<button type="submit">Log out</button></form>
|
<form method="post" action="{% url 'logout' %}">{% csrf_token %}<button type="submit">Log out</button></form>
|
||||||
|
|
|
||||||
|
|
@ -24,25 +24,14 @@
|
||||||
<main class="settings-layout">
|
<main class="settings-layout">
|
||||||
<nav class="settings-nav">
|
<nav class="settings-nav">
|
||||||
<a class="settings-nav-back" href="{% url 'feed' %}">← Back to Feed</a>
|
<a class="settings-nav-back" href="{% url 'feed' %}">← Back to Feed</a>
|
||||||
|
|
||||||
<div class="settings-nav-group">
|
|
||||||
<span class="settings-nav-heading">You</span>
|
|
||||||
<a class="{% if active_section == 'account' %}selected{% endif %}" href="{% url 'settings-account' %}">Account</a>
|
<a class="{% if active_section == 'account' %}selected{% endif %}" href="{% url 'settings-account' %}">Account</a>
|
||||||
<a class="{% if active_section == 'api-keys' %}selected{% endif %}" href="{% url 'settings-api-keys' %}">API Keys</a>
|
<a class="{% if active_section == 'api-keys' %}selected{% endif %}" href="{% url 'settings-api-keys' %}">API Keys</a>
|
||||||
<a class="{% if active_section == 'storage' %}selected{% endif %}" href="{% url 'settings-storage' %}">Storage</a>
|
<a class="{% if active_section == 'storage' %}selected{% endif %}" href="{% url 'settings-storage' %}">Storage</a>
|
||||||
<a class="{% if active_section == 'demo-data' %}selected{% endif %}" href="{% url 'settings-demo-data' %}">Demo Data</a>
|
<a class="{% if active_section == 'demo-data' %}selected{% endif %}" href="{% url 'settings-demo-data' %}">Demo Data</a>
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if request.user.is_superuser %}
|
{% if request.user.is_superuser %}
|
||||||
<div class="settings-nav-group">
|
|
||||||
<span class="settings-nav-heading">Admin</span>
|
|
||||||
<a class="{% if active_section == 'users' %}selected{% endif %}" href="{% url 'settings-users' %}">Users</a>
|
<a class="{% if active_section == 'users' %}selected{% endif %}" href="{% url 'settings-users' %}">Users</a>
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="settings-nav-group">
|
|
||||||
<a class="{% if active_section == 'about' %}selected{% endif %}" href="{% url 'settings-about' %}">About</a>
|
<a class="{% if active_section == 'about' %}selected{% endif %}" href="{% url 'settings-about' %}">About</a>
|
||||||
</div>
|
|
||||||
</nav>
|
</nav>
|
||||||
<section class="settings-panel">
|
<section class="settings-panel">
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue