Add gallery admin and video media support.

This updates gallery handling to support video playback with generated poster thumbnails, adds authenticated admin upload/delete flows, and improves dev/runtime behavior including reliable thumbnail generation and media-safe response handling.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-02 23:01:02 +12:00
parent 509e7ccb43
commit 45b31be9a7
22 changed files with 1002 additions and 217 deletions

View File

@@ -1,9 +1,6 @@
package templates
import (
"fmt"
"technical.kiwi/website/internal/gallery"
)
import "technical.kiwi/website/internal/gallery"
templ Home(images []gallery.Image, hero gallery.Image, hasHero bool, contactEnabled bool) {
@Layout("Technical Kiwi Limited", heroPreload(hero, hasHero), homeContent(images, hero, hasHero, contactEnabled))
@@ -20,10 +17,10 @@ templ homeContent(images []gallery.Image, hero gallery.Image, hasHero bool, cont
<section class="hero">
<div class="hero-copy">
<p class="eyebrow">Technical Kiwi Limited</p>
<h1>Electronic engineering, DevOps, and interactive art</h1>
<h1>Electronic engineering and interactive art</h1>
<p class="lead">
We design and build lighting installations, embedded systems, and the infrastructure
that keeps creative technology running — from workshop bench to stage.
We design and build lighting installations, embedded systems, and interactive
experiences — from workshop bench to stage.
</p>
<div class="hero-actions">
<a href="#gallery" class="btn btn-primary">View our work</a>
@@ -49,105 +46,20 @@ templ homeContent(images []gallery.Image, hero gallery.Image, hasHero bool, cont
<h3>Electronic engineering</h3>
<p>Custom LED systems, control electronics, PCB design, and fabrication for installations and products.</p>
</article>
<article class="service-card">
<h3>DevOps &amp; infrastructure</h3>
<p>Servers, CI/CD, monitoring, and reliable deployments — so your systems stay up when the show goes live.</p>
</article>
<article class="service-card">
<h3>Interactive art</h3>
<p>Lighting, sound-reactive visuals, and experiential tech for events, venues, and public spaces.</p>
</article>
</div>
</section>
<section id="code" class="code-section">
<div class="section-head">
<h2>Code</h2>
<p>
All repositories on
<a href="https://git.technical.kiwi/" rel="noopener noreferrer" target="_blank">git.technical.kiwi</a>
are written and maintained by Technical Kiwi — firmware, lighting control, web services,
and the infrastructure behind them.
</p>
</div>
<a
href="https://git.technical.kiwi/"
class="code-card"
rel="noopener noreferrer"
target="_blank"
>
<div class="code-card-copy">
<h3>git.technical.kiwi</h3>
<p>
Source for installations, internal tools, and open components we ship or deploy
{ "for" } clients. Browse projects, history, and releases in one place.
</p>
</div>
<span class="btn btn-primary">Browse repositories</span>
</a>
</section>
<section id="gallery" class="gallery-section">
<div class="section-head">
<h2>Gallery</h2>
<p>Installations, prototypes, and behind-the-scenes work.</p>
</div>
@GalleryControls(images)
@GalleryCollectionControls(images)
<div id="gallery-grid" class="gallery-grid">
@GalleryGrid(images)
</div>
</section>
@ContactForm(contactEnabled)
}
templ GalleryControls(images []gallery.Image) {
<div class="gallery-controls">
<button
type="button"
class="filter-btn active"
hx-get="/gallery"
hx-target="#gallery-grid"
hx-swap="innerHTML"
>
All
</button>
for _, album := range gallery.Albums(images) {
<button
type="button"
class="filter-btn"
hx-get={ fmt.Sprintf("/gallery?album=%s", album) }
hx-target="#gallery-grid"
hx-swap="innerHTML"
>
{ gallery.AlbumLabel(album) }
</button>
}
</div>
}
templ GalleryCollectionControls(images []gallery.Image) {
if len(gallery.Collections(images, "templeoftechno")) > 0 {
<div class="gallery-controls gallery-controls-collections">
<span class="gallery-controls-label">Temple of techno</span>
<button
type="button"
class="filter-btn"
hx-get="/gallery?album=templeoftechno"
hx-target="#gallery-grid"
hx-swap="innerHTML"
>
All events
</button>
for _, key := range gallery.Collections(images, "templeoftechno") {
<button
type="button"
class="filter-btn"
hx-get={ fmt.Sprintf("/gallery?album=templeoftechno&collection=%s", key) }
hx-target="#gallery-grid"
hx-swap="innerHTML"
>
{ gallery.CollectionLabel(key, images) }
</button>
}
</div>
}
}