summaryrefslogtreecommitdiffstats
path: root/views/people.erb
diff options
context:
space:
mode:
authorKen D'Ambrosio <ken@jots.org>2026-06-08 19:00:02 +0000
committerKen D'Ambrosio <ken@jots.org>2026-06-08 19:00:02 +0000
commit01f52565f460a0107679999588b73b770f01a98c (patch)
tree806c723ad62221f176fd97d5fdcaadac5d8da2d4 /views/people.erb
parent625b3d5176f2c274e91fcf28bda8e45cc0477722 (diff)
Add people/face clustering feature
- scripts/cluster_faces.py: greedy centroid clustering (numpy) with 3 refinement passes; preserves existing UUID/name mappings across re-runs; writes MEDIA_ROOT/people.json atomically. - app.rb: GET /face/* serves cropped+padded face thumbnails (100x100, cached under cache/faces/); GET|POST /admin/people for cluster management; POST /admin/people/recluster runs cluster_faces.py as a background job; POST /admin/people/:uuid saves names+slugs; GET /people public grid of named people; GET /people/:slug photos for one person. - views/admin/people.erb: lists all clusters (named first, then by size), face crop samples, inline name form, re-cluster button with live log. - views/people.erb: public grid of named people. - views/person.erb: photo grid for one person, linking back to album lightbox for each photo. - views/layout.erb: People link in nav (conditional on FACES_ENABLED). - public/css/style.css: styles for people admin list and public tiles. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'views/people.erb')
-rw-r--r--views/people.erb25
1 files changed, 25 insertions, 0 deletions
diff --git a/views/people.erb b/views/people.erb
new file mode 100644
index 0000000..4d47769
--- /dev/null
+++ b/views/people.erb
@@ -0,0 +1,25 @@
+<div class="album-header">
+ <h1>People</h1>
+</div>
+
+<% if @people.empty? %>
+ <p class="empty-album">
+ No named people yet.
+ <% if admin? %>
+ <a href="/admin/people">Go to Manage People</a> to name face clusters.
+ <% end %>
+ </p>
+<% else %>
+ <div class="grid people-grid">
+ <% @people.each do |p| %>
+ <a href="/people/<%= ERB::Util.url_encode(p[:slug]) %>" class="person-tile">
+ <div class="thumb-wrap">
+ <img src="/face/<%= ERB::Util.html_escape(p[:face][:rel]) %>?box=<%= ERB::Util.html_escape(p[:face][:box].join(',')) %>"
+ alt="<%= ERB::Util.html_escape(p[:name]) %>" loading="lazy">
+ </div>
+ <div class="person-name"><%= ERB::Util.html_escape(p[:name]) %></div>
+ <div class="person-count"><%= p[:count] %> photo<%= p[:count] == 1 ? '' : 's' %></div>
+ </a>
+ <% end %>
+ </div>
+<% end %>