6314fa6b1c
Adds the client side of Phase 2 (People). A new "People" tab in the
Photos sub-navigation lists identity clusters from GET /api/people and
drills into a person's photos using the existing photos lightbox.
- people.js: peopleView with list/drill-in/rename, reusing .photos-grid
tiles and photosLightbox; rename via Modal.prompt + PATCH /api/people/{id}
- people.css: person grid, circular avatars, single-person header,
loading/empty states — all design tokens, no raw colors
- places.js: People tab wired into the Moments|Places sub-nav, revealed
only when GET /api/people is reachable (capability probe); _switchTab
now toggles three views
- index.html: load people.css + people.js
- en.json: photos.tab_people + people.* labels (other locales fall back
to English via i18n)
The tab stays hidden unless OXICLOUD_ENABLE_FACES is on (the API 404s
otherwise), so this is inert by default.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JW6ghFMDtnRYuYNzZhb47M
113 lines
2.1 KiB
CSS
113 lines
2.1 KiB
CSS
/* People (faces) view */
|
|
.people-container {
|
|
display: none;
|
|
}
|
|
|
|
.people-container.active {
|
|
display: block;
|
|
padding: var(--space-2);
|
|
}
|
|
|
|
/* Grid of person tiles */
|
|
.people-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
gap: var(--space-4);
|
|
padding: var(--space-2);
|
|
}
|
|
|
|
.person-tile {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: var(--space-2);
|
|
padding: var(--space-2);
|
|
background: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
border-radius: var(--radius-lg);
|
|
}
|
|
|
|
.person-tile:hover {
|
|
background: var(--color-bg-muted);
|
|
}
|
|
|
|
.person-avatar {
|
|
width: 96px;
|
|
height: 96px;
|
|
border-radius: 50%;
|
|
background-size: cover;
|
|
background-position: center;
|
|
background-color: var(--color-bg-muted);
|
|
border: 2px solid var(--color-border);
|
|
}
|
|
|
|
.person-name {
|
|
max-width: 100%;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
font-size: var(--text-sm);
|
|
font-weight: var(--weight-medium);
|
|
color: var(--color-text);
|
|
}
|
|
|
|
.person-count {
|
|
font-size: var(--text-xs);
|
|
color: var(--color-text-faint);
|
|
}
|
|
|
|
/* Single-person header */
|
|
.people-toolbar {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-3);
|
|
padding: var(--space-2);
|
|
}
|
|
|
|
.people-toolbar .people-title {
|
|
flex: 1;
|
|
margin: 0;
|
|
font-size: var(--text-lg);
|
|
font-weight: var(--weight-semibold);
|
|
color: var(--color-text);
|
|
}
|
|
|
|
.people-back,
|
|
.people-rename {
|
|
width: 36px;
|
|
height: 36px;
|
|
border: none;
|
|
border-radius: 50%;
|
|
background: none;
|
|
color: var(--color-text-subtle);
|
|
font-size: var(--text-base);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.people-back:hover,
|
|
.people-rename:hover {
|
|
background: var(--color-bg-muted);
|
|
}
|
|
|
|
/* Loading / empty states */
|
|
.people-loading,
|
|
.people-empty {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: var(--space-3);
|
|
padding: var(--space-20) var(--space-5);
|
|
color: var(--color-text-faint);
|
|
}
|
|
|
|
.people-empty i {
|
|
font-size: 48px;
|
|
color: var(--color-border-medium);
|
|
}
|
|
|
|
.people-loading i {
|
|
animation: spin 1s linear infinite;
|
|
}
|