feat(gallery): 原图查看与下载——仓外 originals store 双轨制

- git 仓只进压缩版(构建用);原图落 ~/docker_apps/blog/photos-originals/
  (迁移器/ingest 双入口写入,幂等),build.sh 独立 rsync 到
  staging/photos-originals/(同一静态根,零 nginx 改动)
- lightbox 增「原图」(新标签全尺寸)+「下载」(download 属性带文件名)
- 原图持久保障:store + Phase 0 备份 tar 双留档;存量 21 张已回填(196M)

验证: astro build 11 页 exit 0;dist/gallery 含 lb-original×2 与
      photos-originals 引用;staging/photos-originals 21 张实测

Spec: none (用户功能补req:原图查看/下载,跨 Phase 独立小特性)

Co-authored-by: GLM-5.3
This commit is contained in:
wait4xandGLM-5.3 committed 2026-10-25 09:12:26 +08:00
1 parent 382e56431f
commit 44bfb23efb
4 files changed
+50 -4

No files matched your search

+37 -1
View File
@@ -47,6 +47,8 @@ const cards = await Promise.all(
image: photo.data.image,
exif: formatExif(photo.data.exif),
fullSrc: full.src,
// 原图走仓外 store(构建时独立 rsync 到 /photos-originals/),git 不背 196M
original: `/photos-originals/${photo.id}.jpg`,
w,
h,
s3: spanFor(w, h, 3),
@@ -84,6 +86,7 @@ const first = cards[0];
data-title={card.title}
data-exif={card.exif}
data-full={card.fullSrc}
data-original={card.original}
aria-label={`查看大图:${card.title}${card.exif ? `(${card.exif})` : ''}`}
>
<Image
@@ -119,6 +122,10 @@ const first = cards[0];
<div class="lb-meta">
<strong id="lb-title">{first?.title}</strong>
<span id="lb-exif">{first?.exif}</span>
<span class="lb-orig">
<a id="lb-original" href={first?.original} target="_blank" rel="noopener">原图</a>
<a id="lb-download" href={first?.original} download>下载</a>
</span>
</div>
<button id="lb-next" class="tap lbnav" type="button" aria-label="下一张(→)">
<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
@@ -150,6 +157,11 @@ const first = cards[0];
img.alt = btn.ariaLabel ?? '';
titleEl.textContent = btn.dataset.title ?? '';
exifEl.textContent = btn.dataset.exif ?? '';
const orig = btn.dataset.original ?? '';
(document.getElementById('lb-original') as HTMLAnchorElement).href = orig;
const dl = document.getElementById('lb-download') as HTMLAnchorElement;
dl.href = orig;
dl.setAttribute('download', btn.dataset.title ?? 'photo');
if (!reduced.matches) {
img.style.opacity = '0';
requestAnimationFrame(() => {
@@ -315,6 +327,18 @@ const first = cards[0];
font-family: var(--font-mono);
font-size: 12.5px;
}
.lb-orig {
display: inline-flex;
gap: var(--space-3);
margin-left: var(--space-2);
}
.lb-orig a {
font-family: var(--font-mono);
font-size: 12px;
color: var(--accent);
text-decoration: underline;
text-underline-offset: 0.2em;
}
.lb-meta {
flex: 1;
min-width: 0;
@@ -352,6 +376,18 @@ const first = cards[0];
}
@media (max-width: 560px) {
.lb-bottom { flex-wrap: wrap; }
.lb-meta { order: 3; flex-basis: 100%; }
.lb-orig {
display: inline-flex;
gap: var(--space-3);
margin-left: var(--space-2);
}
.lb-orig a {
font-family: var(--font-mono);
font-size: 12px;
color: var(--accent);
text-decoration: underline;
text-underline-offset: 0.2em;
}
.lb-meta { order: 3; flex-basis: 100%; }
}
</style>