feat: delta/instant upload + frontend UI/UX polish

Bundles the backend+frontend delta-upload (content-dedup) feature with a
batch of frontend fixes from this session.

Upload / dedup:
- Client-hashed delta & instant upload (deltaUpload, hashWasm vendor shim)
- Backend dedup batch endpoint (dedup_service, dedup_handler, routes)
- session store owned-hash helpers; unit tests + upload-strategy bench

Frontend UI/UX:
- Colour file-type icons in grid/list (per-type tinted tiles + glyph hue)
- Robust thumbnail fallback; PDFs now show their type icon (backend
  generates no PDF thumbnails) instead of a blank tile
- Fix PDF preview: load via a same-origin blob: iframe — the API URL is
  blocked by the global X-Frame-Options: DENY in the browser's framed
  PDF viewer, matching the existing CSP `frame-src blob:` design
- Groups: localized virtual-group description (no DB schema-note leak),
  add nav.groups to the 15 missing locales, fix primary-button contrast
- Repoint --color-text-light → --color-on-accent (was faint grey on accent)
- Nudge the admin role badge off the user-menu header divider

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DioCrafts
2026-06-20 16:33:08 +02:00
parent f8490ad96e
commit d98e3117b2
35 changed files with 901 additions and 101 deletions
+2 -1
View File
@@ -52,7 +52,8 @@
"trash": "سلة المهملات",
"sharedwithme": "مشتركة معي",
"profile": "الملف الشخصي",
"shared_with_me": "مشتركة معي"
"shared_with_me": "مشتركة معي",
"groups": "المجموعات"
},
"photos": {
"empty_state": "لا توجد صور بعد",
+2 -1
View File
@@ -52,7 +52,8 @@
"trash": "Papierkorb",
"sharedwithme": "Mit mir geteilt",
"profile": "Profil",
"shared_with_me": "Mit mir geteilt"
"shared_with_me": "Mit mir geteilt",
"groups": "Gruppen"
},
"photos": {
"empty_state": "Noch keine Fotos",
+4 -2
View File
@@ -52,7 +52,8 @@
"trash": "Papelera",
"sharedwithme": "Compartidos conmigo",
"profile": "Perfil",
"shared_with_me": "Compartidos conmigo"
"shared_with_me": "Compartidos conmigo",
"groups": "Grupos"
},
"photos": {
"empty_state": "Aún no hay fotos",
@@ -365,7 +366,8 @@
"new_folder": "Nueva carpeta",
"share": "Compartir",
"view": "Ver",
"selected_count": "{{count}} seleccionados"
"selected_count": "{{count}} seleccionados",
"uploaded_saved": "Subida completa — {{mb}} MB deduplicados"
},
"dialogs": {
"rename_folder": "Renombrar carpeta",
+2 -1
View File
@@ -52,7 +52,8 @@
"trash": "سطل زباله",
"sharedwithme": "به اشتراک‌گذاشته شده با من",
"profile": "پروفایل",
"shared_with_me": "به اشتراک‌گذاشته شده با من"
"shared_with_me": "به اشتراک‌گذاشته شده با من",
"groups": "گروه‌ها"
},
"photos": {
"empty_state": "هنوز عکسی نیست",
+2 -1
View File
@@ -52,7 +52,8 @@
"trash": "Corbeille",
"sharedwithme": "Partages avec moi",
"profile": "Profil",
"shared_with_me": "Partages avec moi"
"shared_with_me": "Partages avec moi",
"groups": "Groupes"
},
"photos": {
"empty_state": "Pas encore de photos",
+2 -1
View File
@@ -52,7 +52,8 @@
"trash": "रद्दी",
"sharedwithme": "मेरे साथ साझा किए गए",
"profile": "प्रोफ़ाइल",
"shared_with_me": "मेरे साथ साझा किए गए"
"shared_with_me": "मेरे साथ साझा किए गए",
"groups": "समूह"
},
"photos": {
"empty_state": "अभी कोई फ़ोटो नहीं",
+2 -1
View File
@@ -52,7 +52,8 @@
"trash": "Cestino",
"sharedwithme": "Condivisi con me",
"profile": "Profilo",
"shared_with_me": "Condivisi con me"
"shared_with_me": "Condivisi con me",
"groups": "Gruppi"
},
"photos": {
"empty_state": "Nessuna foto ancora",
+2 -1
View File
@@ -52,7 +52,8 @@
"trash": "ゴミ箱",
"sharedwithme": "自分と共有",
"profile": "プロフィール",
"shared_with_me": "自分と共有"
"shared_with_me": "自分と共有",
"groups": "グループ"
},
"photos": {
"empty_state": "写真はまだありません",
+2 -1
View File
@@ -52,7 +52,8 @@
"trash": "휴지통",
"sharedwithme": "나와 공유됨",
"profile": "프로필",
"shared_with_me": "나와 공유됨"
"shared_with_me": "나와 공유됨",
"groups": "그룹"
},
"photos": {
"empty_state": "아직 사진이 없습니다",
+2 -1
View File
@@ -52,7 +52,8 @@
"trash": "Prullenbak",
"sharedwithme": "Gedeeld met mij",
"profile": "Profiel",
"shared_with_me": "Gedeeld met mij"
"shared_with_me": "Gedeeld met mij",
"groups": "Groepen"
},
"photos": {
"empty_state": "Nog geen foto's",
+2 -1
View File
@@ -52,7 +52,8 @@
"trash": "Kosz",
"sharedwithme": "Udostępnione dla mnie",
"profile": "Profil",
"shared_with_me": "Udostępnione dla mnie"
"shared_with_me": "Udostępnione dla mnie",
"groups": "Grupy"
},
"photos": {
"empty_state": "Brak zdjęć",
+2 -1
View File
@@ -52,7 +52,8 @@
"trash": "Lixeira",
"sharedwithme": "Compartilhados comigo",
"profile": "Perfil",
"shared_with_me": "Compartilhados comigo"
"shared_with_me": "Compartilhados comigo",
"groups": "Grupos"
},
"photos": {
"empty_state": "Nenhuma foto ainda",
+2 -1
View File
@@ -52,7 +52,8 @@
"trash": "Корзина",
"sharedwithme": "Доступно мне",
"profile": "Профиль",
"shared_with_me": "Доступно мне"
"shared_with_me": "Доступно мне",
"groups": "Группы"
},
"photos": {
"empty_state": "Фотографий пока нет",
+2 -1
View File
@@ -52,7 +52,8 @@
"trash": "回收站",
"sharedwithme": "與我共享",
"profile": "個人資料",
"shared_with_me": "與我共享"
"shared_with_me": "與我共享",
"groups": "群組"
},
"photos": {
"empty_state": "還沒有照片",
+2 -1
View File
@@ -52,7 +52,8 @@
"trash": "回收站",
"sharedwithme": "与我共享",
"profile": "个人资料",
"shared_with_me": "与我共享"
"shared_with_me": "与我共享",
"groups": "群组"
},
"photos": {
"empty_state": "还没有照片",