β All Skills Knowledge History
gallery-apps: image-only lightbox on the Picsum gallery
image lightboxgalleryPicsumVue Teleport overlayVueUse keyboard + scroll lockunplugin-vue-components auto-import
Request
- Clicking a gallery thumbnail should open an image-only popup: a full-screen dark overlay with the large image centered, closable by backdrop click or Esc.
- Add caption (author + dimensions), prev/next navigation, and a source/full-res link.
- Run the Mono Skills read+save workflow around the task.
Summary
gallery-apps β image-only lightbox on the Picsum gallery
What was built
Clicking a thumbnail on /gallery now opens an image-only lightbox instead of navigating away
to the Unsplash source page. The lightbox is a full-screen dark overlay (bg-black/90) with the
large image centered; it closes on backdrop click, the β button, or Esc. It also carries minimal
overlaid chrome: a caption (author + widthΓheight), prev/next navigation across the loaded photos
(on-screen arrows + β/β keys, hidden at the ends), and links to the source page (p.url) and
full-resolution image (p.download_url).
Files
src/components/GalleryLightbox.vue(new) β the lightbox.<Teleport to="body">+v-if, propsphotos: PicsumPhoto[]andindex: number | null(v-model:index), emitsupdate:index+close. Keyboard via VueUseonKeyStroke(Escape/ArrowLeft/ArrowRight, guarded to only act while open); background scroll frozen viauseScrollLock(document.body)toggled by awatchon the open photo. Backdrop uses@click.self="close"; chrome/image wrapped with@click.stop.src/datas/tables/gallery.tsβ addedlargeUrl(p, w=1200, h=800)that reusesthumbUrl's/id/{id}/{w}/{h}form, so the lightbox serves a bounded image rather than the multi-MB original.src/pages/gallery/index.vueβ addedactiveIndex = ref<number|null>(null); the grid item keeps its<a :href="p.url">(right-click/no-JS still works) but intercepts left click with@click.prevent="activeIndex = i"(v-for="(p, i) in store.photos"); renders<GalleryLightbox :photos="store.photos" v-model:index="activeIndex" @close="activeIndex = null" />.
Key facts for future sessions
src/components/is theunplugin-vue-componentsauto-import root β components there register globally with no manual import in the page (verified: Vite injected the import automatically).- VueUse is auto-imported project-wide (
onKeyStroke,useScrollLock,useIntersectionObserverall resolve from@vueuse/corewith no explicit import). PicsumPhotoalready carries everything the lightbox needs (author,width,height,url,download_url) β no store changes were required; the lightbox pages throughstore.photos.mono-modal(mono-helper/ui/modal) exists and is the project's modal convention, but it always renders a padded card β not suitable for an edge-to-edge image-only lightbox (see decisions).- Pre-existing unrelated typecheck error:
.mono/apps/mono-host/app/composables/use-utils.tsreferencesimport.meta.client(Nuxt) βTS2339under this app's vue-tsc. Not caused here.
Outcome
shippedAdded src/components/GalleryLightbox.vue (Teleport overlay, keyboard nav, scroll lock). Added largeUrl() helper to src/datas/tables/gallery.ts (reuses thumbUrl). Wired lightbox into src/pages/gallery/index.vue via v-model:index and @click.prevent. vue-tsc clean for the changed files; Vite compiled all three modules (HTTP 200), VueUse auto-imports resolved.
Decisions
Custom Teleport lightbox instead of mono-modal
Built a custom Teleport overlay component (GalleryLightbox.vue) instead of reusing the mono-modal web component.
Why: mono-modal always renders a padded modal card; the requested design is an edge-to-edge, image-only lightbox. A small dedicated Teleport overlay is cleaner and gives full control over backdrop, arrows, and caption. All needed behavior (keyboard, scroll-lock) is already available via auto-imported VueUse.
Keep the anchor, intercept left-click with @click.prevent
Kept the grid item as an <a :href="p.url"> but intercept the left click with @click.prevent to open the lightbox.
Why: Preserves right-click 'open in new tab', middle-click, and no-JS graceful degradation to the source page, while a normal left click opens the in-app lightbox.
Bounded 1200x800 largeUrl over the full original
Added a largeUrl() helper that reuses thumbUrl's /id/{id}/{w}/{h} form (1200x800) rather than loading p.download_url (the full original).
Why: Picsum originals are multi-megapixel/multi-MB; a bounded 1200x800 render is crisp enough for the lightbox and much faster. The full-res download_url is still offered as an explicit link.
Guard the global VueUse key handlers on open state
Registered global VueUse onKeyStroke handlers but guarded each to only act while a photo is open.
Why: The component is always mounted (rendered in the page), so the key listeners are always live; guarding on the open state prevents Esc/arrows from firing when the lightbox is closed.
Files changed
| File | Operation | Note |
|---|---|---|
src/components/GalleryLightbox.vue | added | |
src/datas/tables/gallery.ts | modified | |
src/pages/gallery/index.vue | modified |
Commands
mono skills check
mono skills read --app gallery-apps --type knowledge
mono skills search --app gallery-apps --query "gallery lightbox image popup"
npx vue-tsc --noEmit
npm run devValidation
Tests: not run Build: passed