From 49b7a2920a4967a010547f1d748b0b7e26cf9a57 Mon Sep 17 00:00:00 2001 From: Patrick Mulligan Date: Tue, 28 Jul 2026 19:09:59 +0200 Subject: [PATCH] feat(deck): drop-in image gallery with City/Farm/Festival sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New Gallery slide (after the entity deep-dives) with a tabbed masonry grid and a full-screen lightbox (prev/next, counter, ESC/arrows/swipe) styled after the webapp marketplace viewer. Images auto-populate via import.meta.glob — drop files into src/deck/gallery/{city,farm,festival}/ and they appear, sorted by filename, with a graceful empty state. Seeded placeholder SVGs (clearly marked) so the gallery is populated in the demo. Lightbox keys are handled in the capture phase so they don't trigger the deck's slide navigation. Deck is now 17 slides. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/deck/Deck.vue | 2 + src/deck/components/Icon.vue | 4 + src/deck/components/Lightbox.vue | 125 +++++++++++++++++++ src/deck/gallery.ts | 82 ++++++++++++ src/deck/gallery/README.md | 18 +++ src/deck/gallery/city/01-placeholder.svg | 15 +++ src/deck/gallery/city/02-placeholder.svg | 15 +++ src/deck/gallery/city/03-placeholder.svg | 15 +++ src/deck/gallery/city/04-placeholder.svg | 15 +++ src/deck/gallery/city/05-placeholder.svg | 15 +++ src/deck/gallery/farm/01-placeholder.svg | 15 +++ src/deck/gallery/farm/02-placeholder.svg | 15 +++ src/deck/gallery/farm/03-placeholder.svg | 15 +++ src/deck/gallery/farm/04-placeholder.svg | 15 +++ src/deck/gallery/festival/01-placeholder.svg | 15 +++ src/deck/gallery/festival/02-placeholder.svg | 15 +++ src/deck/gallery/festival/03-placeholder.svg | 15 +++ src/deck/gallery/festival/04-placeholder.svg | 15 +++ src/deck/gallery/festival/05-placeholder.svg | 15 +++ src/deck/slides/GallerySlide.vue | 92 ++++++++++++++ 20 files changed, 533 insertions(+) create mode 100644 src/deck/components/Lightbox.vue create mode 100644 src/deck/gallery.ts create mode 100644 src/deck/gallery/README.md create mode 100644 src/deck/gallery/city/01-placeholder.svg create mode 100644 src/deck/gallery/city/02-placeholder.svg create mode 100644 src/deck/gallery/city/03-placeholder.svg create mode 100644 src/deck/gallery/city/04-placeholder.svg create mode 100644 src/deck/gallery/city/05-placeholder.svg create mode 100644 src/deck/gallery/farm/01-placeholder.svg create mode 100644 src/deck/gallery/farm/02-placeholder.svg create mode 100644 src/deck/gallery/farm/03-placeholder.svg create mode 100644 src/deck/gallery/farm/04-placeholder.svg create mode 100644 src/deck/gallery/festival/01-placeholder.svg create mode 100644 src/deck/gallery/festival/02-placeholder.svg create mode 100644 src/deck/gallery/festival/03-placeholder.svg create mode 100644 src/deck/gallery/festival/04-placeholder.svg create mode 100644 src/deck/gallery/festival/05-placeholder.svg create mode 100644 src/deck/slides/GallerySlide.vue diff --git a/src/deck/Deck.vue b/src/deck/Deck.vue index e0a9866..a836d17 100644 --- a/src/deck/Deck.vue +++ b/src/deck/Deck.vue @@ -12,6 +12,7 @@ import EntitiesSlide from './slides/EntitiesSlide.vue' import ArchipelagoSlide from './slides/ArchipelagoSlide.vue' import SolunarSlide from './slides/SolunarSlide.vue' import ChateauSlide from './slides/ChateauSlide.vue' +import GallerySlide from './slides/GallerySlide.vue' import EcosystemSlide from './slides/EcosystemSlide.vue' import RevenueSlide from './slides/RevenueSlide.vue' import InvestmentOverviewSlide from './slides/InvestmentOverviewSlide.vue' @@ -30,6 +31,7 @@ const slides = [ { id: 'archipelago', label: 'Archipelago', component: ArchipelagoSlide }, { id: 'solunar', label: 'Solunar Society', component: SolunarSlide }, { id: 'chateau', label: 'Chateau du Faune', component: ChateauSlide }, + { id: 'gallery', label: 'Gallery', component: GallerySlide }, { id: 'ecosystem', label: 'One membership', component: EcosystemSlide }, { id: 'revenue', label: 'Revenue', component: RevenueSlide }, { id: 'invest-overview', label: 'The raise', component: InvestmentOverviewSlide }, diff --git a/src/deck/components/Icon.vue b/src/deck/components/Icon.vue index 1ecde30..e46ad45 100644 --- a/src/deck/components/Icon.vue +++ b/src/deck/components/Icon.vue @@ -30,6 +30,8 @@ import { Moon, MapPin, ChevronRight, + Images, + Expand, type LucideProps, } from '@lucide/vue' import type { FunctionalComponent } from 'vue' @@ -64,6 +66,8 @@ const registry: Record> = { Moon, MapPin, ChevronRight, + Images, + Expand, } const props = defineProps<{ name: string }>() diff --git a/src/deck/components/Lightbox.vue b/src/deck/components/Lightbox.vue new file mode 100644 index 0000000..ce78a9e --- /dev/null +++ b/src/deck/components/Lightbox.vue @@ -0,0 +1,125 @@ + + + + + diff --git a/src/deck/gallery.ts b/src/deck/gallery.ts new file mode 100644 index 0000000..de2fff2 --- /dev/null +++ b/src/deck/gallery.ts @@ -0,0 +1,82 @@ +/** + * Gallery auto-population. + * + * Drop image files into these folders and they appear in the deck gallery + * automatically — no code changes, no manifest to edit: + * + * src/deck/gallery/city/ → City (Archipelago) + * src/deck/gallery/farm/ → Farm (Chateau du Faune) + * src/deck/gallery/festival/ → Festival (Solunar Society) + * + * Supported: jpg jpeg png webp avif gif svg. Images show sorted by filename, + * so prefix with 01-, 02-, … to control order. The seeded *-placeholder.svg + * files are just demo art — delete them once you add real photos. + * + * Under the hood this uses Vite's import.meta.glob, which must take a literal + * pattern (no variables) — hence one call per folder. + */ +import { entityById, type Accent } from './data' + +type GlobMap = Record + +// NB: import.meta.glob is statically analysed by Vite — its options must be an +// inline object literal (not a shared const), so the object is repeated below. +const city = import.meta.glob('./gallery/city/*.{jpg,jpeg,png,webp,avif,gif,svg}', { + eager: true, + query: '?url', + import: 'default', +}) as GlobMap +const farm = import.meta.glob('./gallery/farm/*.{jpg,jpeg,png,webp,avif,gif,svg}', { + eager: true, + query: '?url', + import: 'default', +}) as GlobMap +const festival = import.meta.glob('./gallery/festival/*.{jpg,jpeg,png,webp,avif,gif,svg}', { + eager: true, + query: '?url', + import: 'default', +}) as GlobMap + +export type GalleryImage = { src: string; name: string } + +function toImages(map: GlobMap): GalleryImage[] { + return Object.entries(map) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([path, src]) => ({ src, name: path.split('/').pop()!.replace(/\.[^.]+$/, '') })) +} + +export type GallerySection = { + id: 'city' | 'farm' | 'festival' + label: string + entityName: string + accent: Accent + folder: string + images: GalleryImage[] +} + +export const gallerySections: GallerySection[] = [ + { + id: 'city', + label: 'City', + entityName: entityById('archipelago').name, + accent: 'pine', + folder: 'src/deck/gallery/city/', + images: toImages(city), + }, + { + id: 'farm', + label: 'Farm', + entityName: entityById('chateau').name, + accent: 'clay', + folder: 'src/deck/gallery/farm/', + images: toImages(farm), + }, + { + id: 'festival', + label: 'Festival', + entityName: entityById('solunar').name, + accent: 'indigo', + folder: 'src/deck/gallery/festival/', + images: toImages(festival), + }, +] diff --git a/src/deck/gallery/README.md b/src/deck/gallery/README.md new file mode 100644 index 0000000..4706557 --- /dev/null +++ b/src/deck/gallery/README.md @@ -0,0 +1,18 @@ +# Deck gallery — just drop images in + +Each subfolder feeds one section of the Gallery slide. **Drop image files in and +they appear automatically** — no code to touch. + +| Folder | Section | Entity | +| ----------- | -------- | ----------------- | +| `city/` | City | Archipelago | +| `farm/` | Farm | Chateau du Faune | +| `festival/` | Festival | Solunar Society | + +- Supported formats: `jpg` `jpeg` `png` `webp` `avif` `gif` `svg`. +- Images display **sorted by filename** — prefix `01-`, `02-`, … to order them. +- The `*-placeholder.svg` files are demo art. **Delete them** once you add real photos. +- Wiring lives in `src/deck/gallery.ts` (Vite `import.meta.glob`). If you add a + brand-new format, extend the glob patterns there. + +Recommended: web-optimized `.webp` under ~500 KB each for fast loading. diff --git a/src/deck/gallery/city/01-placeholder.svg b/src/deck/gallery/city/01-placeholder.svg new file mode 100644 index 0000000..08db74e --- /dev/null +++ b/src/deck/gallery/city/01-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + City + Photo 01 + PLACEHOLDER · 800×1040 + diff --git a/src/deck/gallery/city/02-placeholder.svg b/src/deck/gallery/city/02-placeholder.svg new file mode 100644 index 0000000..9b3bb39 --- /dev/null +++ b/src/deck/gallery/city/02-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + City + Photo 02 + PLACEHOLDER · 800×620 + diff --git a/src/deck/gallery/city/03-placeholder.svg b/src/deck/gallery/city/03-placeholder.svg new file mode 100644 index 0000000..fe463cb --- /dev/null +++ b/src/deck/gallery/city/03-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + City + Photo 03 + PLACEHOLDER · 800×800 + diff --git a/src/deck/gallery/city/04-placeholder.svg b/src/deck/gallery/city/04-placeholder.svg new file mode 100644 index 0000000..7af5625 --- /dev/null +++ b/src/deck/gallery/city/04-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + City + Photo 04 + PLACEHOLDER · 800×560 + diff --git a/src/deck/gallery/city/05-placeholder.svg b/src/deck/gallery/city/05-placeholder.svg new file mode 100644 index 0000000..b2000a4 --- /dev/null +++ b/src/deck/gallery/city/05-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + City + Photo 05 + PLACEHOLDER · 800×900 + diff --git a/src/deck/gallery/farm/01-placeholder.svg b/src/deck/gallery/farm/01-placeholder.svg new file mode 100644 index 0000000..6becd7a --- /dev/null +++ b/src/deck/gallery/farm/01-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + Farm + Photo 01 + PLACEHOLDER · 800×620 + diff --git a/src/deck/gallery/farm/02-placeholder.svg b/src/deck/gallery/farm/02-placeholder.svg new file mode 100644 index 0000000..bd737ad --- /dev/null +++ b/src/deck/gallery/farm/02-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + Farm + Photo 02 + PLACEHOLDER · 800×900 + diff --git a/src/deck/gallery/farm/03-placeholder.svg b/src/deck/gallery/farm/03-placeholder.svg new file mode 100644 index 0000000..04911bb --- /dev/null +++ b/src/deck/gallery/farm/03-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + Farm + Photo 03 + PLACEHOLDER · 800×560 + diff --git a/src/deck/gallery/farm/04-placeholder.svg b/src/deck/gallery/farm/04-placeholder.svg new file mode 100644 index 0000000..2f3a624 --- /dev/null +++ b/src/deck/gallery/farm/04-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + Farm + Photo 04 + PLACEHOLDER · 800×800 + diff --git a/src/deck/gallery/festival/01-placeholder.svg b/src/deck/gallery/festival/01-placeholder.svg new file mode 100644 index 0000000..73a7b1f --- /dev/null +++ b/src/deck/gallery/festival/01-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + Festival + Photo 01 + PLACEHOLDER · 800×800 + diff --git a/src/deck/gallery/festival/02-placeholder.svg b/src/deck/gallery/festival/02-placeholder.svg new file mode 100644 index 0000000..b6c7456 --- /dev/null +++ b/src/deck/gallery/festival/02-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + Festival + Photo 02 + PLACEHOLDER · 800×560 + diff --git a/src/deck/gallery/festival/03-placeholder.svg b/src/deck/gallery/festival/03-placeholder.svg new file mode 100644 index 0000000..3951739 --- /dev/null +++ b/src/deck/gallery/festival/03-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + Festival + Photo 03 + PLACEHOLDER · 800×1040 + diff --git a/src/deck/gallery/festival/04-placeholder.svg b/src/deck/gallery/festival/04-placeholder.svg new file mode 100644 index 0000000..46ef5c8 --- /dev/null +++ b/src/deck/gallery/festival/04-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + Festival + Photo 04 + PLACEHOLDER · 800×700 + diff --git a/src/deck/gallery/festival/05-placeholder.svg b/src/deck/gallery/festival/05-placeholder.svg new file mode 100644 index 0000000..e9ce0cc --- /dev/null +++ b/src/deck/gallery/festival/05-placeholder.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + Festival + Photo 05 + PLACEHOLDER · 800×600 + diff --git a/src/deck/slides/GallerySlide.vue b/src/deck/slides/GallerySlide.vue new file mode 100644 index 0000000..b059a62 --- /dev/null +++ b/src/deck/slides/GallerySlide.vue @@ -0,0 +1,92 @@ + + +