release: v0.13.0 (#535)
Twenty-nine commits since v0.12.0. See CHANGELOG.md for the full list and the upgrade notes. Ten additions — the SEO package (site URL, robots.txt, sitemap.xml, JSON-LD), slideshow mode, opt-in original downloads, copy-link, previous/next album navigation, alt text from the Immich description, per-album location precision, the update notice, a Help tab, and a subpage cover grid separate from its photo grid. Two fixes: CJK album slugs, and the lightbox control bar. Photo of the Day (#476) was built on `dev` and reverted as not planned; it never appears in a release. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01DmsHVRL9yQvckUB1ZpVWre --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: ralksta <ralksta@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -65,6 +65,23 @@
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* The photograph enters with a fade and a slight scale. Under a slideshow
|
||||
* that repeats every few seconds, which is exactly the motion this preference
|
||||
* is about — so it simply appears instead. It still fades in from nothing, so
|
||||
* a half-loaded image is never shown.
|
||||
*/
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.image {
|
||||
transform: none;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.imageLoaded {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.close {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
@@ -127,11 +144,51 @@
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.counter {
|
||||
/* ── Bottom bar ───────────────────────────────────── */
|
||||
|
||||
/*
|
||||
* One row rather than five separately anchored controls.
|
||||
*
|
||||
* Each button used to carry its own fixed offset, which collided in two ways:
|
||||
* the info button widens when its label becomes "Hide info" and grew into the
|
||||
* favourite button beside it, and the left-hand controls ran into the centred
|
||||
* counter once the viewport was narrow enough. A flex row cannot collide with
|
||||
* itself, and a control that is conditionally absent now leaves no hole.
|
||||
*/
|
||||
.bottomBar {
|
||||
position: fixed;
|
||||
bottom: 24px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1001;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 16px 24px;
|
||||
/* The bar spans the viewport, so it must not swallow clicks on the photo. */
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.bottomBarGroup {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
/* Equal flex on both sides is what keeps the counter centred. */
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.bottomBarLeft {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.bottomBarRight {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.counter {
|
||||
flex: 0 0 auto;
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-muted);
|
||||
font-weight: 300;
|
||||
@@ -140,9 +197,6 @@
|
||||
|
||||
/* ── EXIF Info Panel ──────────────────────────────── */
|
||||
.infoToggle {
|
||||
position: fixed;
|
||||
bottom: 24px;
|
||||
right: 24px;
|
||||
/* Icon and label sit on one line — the buttons carried an emoji before, which
|
||||
needed no alignment of its own. */
|
||||
display: inline-flex;
|
||||
@@ -169,9 +223,71 @@
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* Proofing favourite button — sits left of the info toggle. */
|
||||
.favToggle {
|
||||
right: 6.5rem;
|
||||
/* Anchors are not buttons; strip the underline so it matches its neighbours. */
|
||||
.downloadToggle {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Shown only when the clipboard API is unavailable. */
|
||||
.copyManual {
|
||||
position: fixed;
|
||||
bottom: 76px;
|
||||
left: 24px;
|
||||
z-index: 1001;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
max-width: min(28rem, calc(100vw - 48px));
|
||||
padding: 12px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.copyManualLabel {
|
||||
font-family: var(--font-sans);
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.copyManualInput {
|
||||
width: 100%;
|
||||
padding: 6px 8px;
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--text-primary);
|
||||
font-family: var(--font-sans);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
/*
|
||||
* Four controls, a counter and the close button do not fit on one line at
|
||||
* phone width. Wrapping is the honest answer: the bottom edge already
|
||||
* belongs to the EXIF strip in some presets, so there is nowhere to move to.
|
||||
*/
|
||||
.bottomBar {
|
||||
padding: 12px 16px;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.bottomBarGroup {
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
/* Must not grow — a stretched counter pushes the row into the close button. */
|
||||
.counter {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.copyManual {
|
||||
left: 16px;
|
||||
bottom: 66px;
|
||||
}
|
||||
}
|
||||
|
||||
.exifPanel {
|
||||
@@ -187,7 +303,7 @@
|
||||
--lb-panel-muted: var(--text-muted);
|
||||
--lb-panel-border: var(--border-subtle);
|
||||
position: fixed;
|
||||
bottom: 60px;
|
||||
bottom: 76px;
|
||||
right: 24px;
|
||||
z-index: 1001;
|
||||
background: rgba(20, 20, 20, 0.92);
|
||||
@@ -277,7 +393,7 @@
|
||||
--lb-panel-muted: var(--text-muted);
|
||||
--lb-panel-border: var(--border-subtle);
|
||||
position: fixed;
|
||||
bottom: 62px;
|
||||
bottom: 76px;
|
||||
left: 24px;
|
||||
z-index: 1001;
|
||||
animation: exif-slide-up var(--transition-normal) ease forwards;
|
||||
@@ -463,11 +579,22 @@
|
||||
}
|
||||
|
||||
/* Counter → top left, monospace */
|
||||
:global([data-preset='studio-modern']) .counter {
|
||||
top: 20px;
|
||||
/*
|
||||
* This preset puts the viewer's chrome along the top edge. That is now said
|
||||
* once, on the bar, instead of by each control: when every button carried its
|
||||
* own offset, a button that changed width walked into its neighbour.
|
||||
*/
|
||||
:global([data-preset='studio-modern']) .bottomBar {
|
||||
top: 0;
|
||||
bottom: auto;
|
||||
left: 32px;
|
||||
transform: none;
|
||||
/* The close button owns the top-right corner, so the bar stops short of it.
|
||||
That is the 76px the individual controls used to reserve by hand. */
|
||||
padding: 20px 76px 20px 32px;
|
||||
}
|
||||
|
||||
/* Counter first, so it keeps the left edge it had in this preset. */
|
||||
:global([data-preset='studio-modern']) .counter {
|
||||
order: -1;
|
||||
font-family: var(--font-caption);
|
||||
font-size: 0.6875rem;
|
||||
letter-spacing: 0.15em;
|
||||
@@ -520,9 +647,6 @@
|
||||
|
||||
/* Info + favourite toggles move up into the topbar */
|
||||
:global([data-preset='studio-modern']) .infoToggle {
|
||||
top: 20px;
|
||||
bottom: auto;
|
||||
right: 76px;
|
||||
background: none;
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
@@ -542,10 +666,6 @@
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
:global([data-preset='studio-modern']) .favToggle {
|
||||
right: 176px;
|
||||
}
|
||||
|
||||
/* The EXIF strip owns the bottom edge in this preset, so the panel opens from
|
||||
the top instead. */
|
||||
:global([data-preset='studio-modern']) .shortcutsPanel {
|
||||
@@ -638,12 +758,9 @@
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
/* No room next to the info toggle — park it in the opposite corner. */
|
||||
:global([data-preset='studio-modern']) .favToggle {
|
||||
top: auto;
|
||||
bottom: 76px;
|
||||
right: auto;
|
||||
left: 16px;
|
||||
:global([data-preset='studio-modern']) .bottomBar {
|
||||
/* Same reserved corner as on desktop: the close button sits there. */
|
||||
padding: 16px 76px 16px 16px;
|
||||
}
|
||||
|
||||
:global([data-preset='studio-modern']) .navPrev {
|
||||
|
||||
Reference in New Issue
Block a user