Continue Working widget shows pixelated thumbnails
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Idoneità per principianti
- 72/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Tranquilla
- Stack tecnologico
- javascript
- Ambito
- frontend
Direzione di ricerca
Inizia in components/continue-working/index.js, in getProjectThumbnail(), e verifica come vengono risolte le miniature dei canvas IIIF e i servizi di immagini per Presentation 2 e 3. Esegui jekyll s, accedi e visita / per verificare miniature nitide di circa 200 CSS px, richieste IIIF da 400 px e l’assenza di 404 dovuti a URL malformati nel Network panel.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
The "Continue Working" widget on the home page renders project thumbnails at very low resolution. Images appear visibly pixelated, especially on HiDPI displays.
Reproduction
- Sign in and visit the home page (
/). - Observe the project thumbnails in the "Continue Working" card.
- Thumbnails render blurry/pixelated rather than crisp at the ~200 CSS px display size.
Reproduced against the Italian Paleography manuscript hosted on iiif.library.utoronto.ca (IIIF Image API 2 / Presentation API 2.1).
Root cause
In components/continue-working/index.js → getProjectThumbnail(), two stacked bugs combine to deliver a tiny image:
Bug 1 — canvas.thumbnail trusted blindly without checking declared size
Line 194:
let thumbnailUrl = canvas.thumbnail?.id ?? canvas.thumbnail?.['@id'] ?? canvas.thumbnail
if (!thumbnailUrl) {
// …construct a sized URL from the IIIF service…
}
For the Toronto manifest the canvas advertises:
"thumbnail": {
"@id": ".../full/80,/0/default.jpg",
"width": 80, "height": 120
}
So thumbnailUrl is set to an 80-pixel-wide image and the entire fallback path is skipped. The widget displays it in a flex: 1 1 200px; aspect-ratio: 1; object-fit: cover section → stretched from 80 → ~200 CSS px (2.5×, worse on HiDPI). That's the pixelation.
The IIIF Presentation spec lets canvas.thumbnail be any size — it's metadata, not a sizing constraint. Trusting it without checking declared width/height is the bug.
Bug 2 — Latent: malformed URLs in the fallback path
Even if canvas.thumbnail were missing, the constructed fallback URLs are malformed. Lines 209–213 build:
`${baseUrl}/full/${isV3 ? 'max' : 'full'}/200,/0/default.jpg`
That produces e.g. …/full/full/200,/0/default.jpg — three size-related segments where IIIF wants region/size/rotation. Verified against the Toronto server:
GET …/full/full/200,/0/default.jpg → 404
GET …/full/200,/0/default.jpg → 200
The ${isV3 ? 'max' : 'full'}/ insert stuffs a size token in front of the actual size 200,, pushing rotation/quality into invalid path positions. Both the square and full attempts 404, hitting the final .catch(() => thumbnailUrl = imageUrl) — which on Toronto is …/full/512,/0/default.jpg (still small).
Bug 3 — Latent: thumbnail as array not handled
IIIF Presentation 3.0 §5.3.1 requires thumbnail to be an array of objects. The current canvas.thumbnail?.id ?? canvas.thumbnail?.['@id'] chain returns undefined for arrays and falls through to canvas.thumbnail itself (the array) as the URL — which renders as [object Object] in the <img src>.
Suggested fix
Always derive a sized thumbnail from the IIIF Image service when one is available; only fall back to canvas.thumbnail when its declared width is sufficient; correctly construct the IIIF URL.
async getProjectThumbnail(project, annotationPageId) {
try {
if (!annotationPageId) return this.generateProjectPlaceholder(project)
const annotationPage = await vault.get(
`${TPEN.servicesURL}/project/${project._id}/page/${annotationPageId}`,
'annotationpage', true
)
if (!annotationPage?.target) return this.generateProjectPlaceholder(project)
const canvas = await vault.getWithFallback(annotationPage.target, 'canvas', project.manifest)
if (!canvas) return this.generateProjectPlaceholder(project)
const isV3 = Array.isArray(canvas.items) || canvas.type === "Canvas"
const annotation = isV3 ? canvas.items?.[0]?.items?.[0] : canvas.images?.[0]
const rawService = isV3 ? annotation?.body?.service : annotation?.resource?.service
const service = Array.isArray(rawService) ? rawService[0] : rawService
const serviceBase = service?.id ?? service?.['@id']
// Prefer service-derived sized thumbnail (sharp at ~200 CSS px on HiDPI)
if (serviceBase) {
const base = serviceBase.replace(/\/$/, '')
return `${base}/full/400,/0/default.jpg`
}
// canvas.thumbnail can be array (P3) or object/string (P2/P3)
const t = Array.isArray(canvas.thumbnail) ? canvas.thumbnail[0] : canvas.thumbnail
const tWidth = t?.width ?? 0
if (tWidth >= 200) {
return t?.id ?? t?.['@id'] ?? (typeof t === 'string' ? t : null) ?? this.generateProjectPlaceholder(project)
}
// Last resort — the painting body's image URL
const imageUrl = isV3
? annotation?.body?.id ?? annotation?.body?.['@id']
: annotation?.resource?.['@id'] ?? annotation?.resource?.id
return imageUrl ?? this.generateProjectPlaceholder(project)
} catch (error) {
console.error('Error getting thumbnail:', error)
return this.generateProjectPlaceholder(project)
}
}
What this changes:
- Always asks the IIIF service for a 400 px-wide rendition first (sharp on HiDPI 2× displays at 200 CSS px).
- Only uses
canvas.thumbnailwhen no service is available and the declared thumbnail is at least 200 wide. - Drops the broken
square/max-prefix attempt chain. - Handles
thumbnailas an array (Presentation 3.0 shape).
Verification
jekyll s, sign in, visit/.- Confirm "Continue Working" thumbnails render crisply (no visible pixelation) at the ~200 CSS px display size.
- Confirm Network panel shows
…/full/400,/0/default.jpgrequests, no 404s on…/full/full/200,/…or…/square/full/200,/….
- Lingua principale
- JavaScript
- Stelle
- 2
- Fork
- 3
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di CenterForDigitalHumanities/TPEN-interfaces
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
-
enhancement
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
-
License link borkedAperta
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 68/100
Tutte le issue di CenterForDigitalHumanities/TPEN-interfaces
Issue simili
-
0. Needs triage 35-feedback bug
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 62/100
I maintainer di solito rispondono entro 1 giorno
-
A-linter
Difficoltà 2/5 1-3 ore Idoneità per principianti 73/100
oxc-project/oxc#28783 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
osmlab/name-suggestion-index#13056 ·
I maintainer di solito rispondono entro 2 giorni
-
[aw] Upgrade availableApertaagentic-workflows
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 68/100
githubnext/gh-cao#20168 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
Sagargupta16/awesome-mcp-servers#297 ·
I maintainer di solito rispondono entro 1 giorno