Files
pivoine.art/layouts/_default/baseof.html

139 lines
4.0 KiB
HTML
Raw Normal View History

2025-11-29 17:51:00 +01:00
<!DOCTYPE html>
<html lang="{{ .Site.LanguageCode | default "en" }}" class="dark">
<head>
{{- partial "head/meta.html" . -}}
{{- partial "head/opengraph.html" . -}}
{{- partial "head/twitter.html" . -}}
{{- partial "head/json-ld.html" . -}}
{{- partial "head/preload.html" . -}}
{{- partial "head/favicon.html" . -}}
{{/* CSS - built by Tailwind CLI to static folder */}}
<link rel="stylesheet" href="/css/main.css">
</head>
<body
x-data
hx-boost="true"
hx-target="#main-content"
hx-select="#main-content"
hx-swap="innerHTML show:top"
hx-push-url="true"
class="text-text-primary min-h-screen flex flex-col"
>
{{/* WebGL Background Canvas (preserved across navigation) */}}
<canvas
id="webgl-bg"
hx-preserve="true"
class="fixed inset-0 -z-10 pointer-events-none"
aria-hidden="true"
></canvas>
{{- partial "header.html" . -}}
<main id="main-content" class="flex-1">
{{- block "main" . }}{{- end -}}
</main>
{{- partial "footer.html" . -}}
{{/* Persistent Audio Player (preserved across navigation) */}}
<div id="audio-player-container" hx-preserve="true" class="fixed bottom-0 left-0 right-0 z-player">
{{- partial "player.html" . -}}
</div>
{{/* WebGL Visualizer Canvas (preserved) */}}
<canvas
id="visualizer"
hx-preserve="true"
class="fixed inset-0 pointer-events-none z-visualizer"
aria-hidden="true"
></canvas>
{{/* Alpine.js - data and stores defined before CDN loads */}}
<script>
// Define Alpine stores and components BEFORE Alpine loads
document.addEventListener('alpine:init', () => {
// Global audio store
Alpine.store('audio', {
currentTrack: null,
isPlaying: false,
progress: 0,
duration: 0,
volume: 0.8
});
// Player UI component
Alpine.data('playerUI', () => ({
togglePlay() {
window.__pivoine?.audioManager?.toggle();
},
seek(time) {
window.__pivoine?.audioManager?.seek(parseFloat(time));
},
setVolume(v) {
const volume = parseFloat(v);
Alpine.store('audio').volume = volume;
window.__pivoine?.audioManager?.setVolume(volume);
localStorage.setItem('pivoine-volume', volume);
},
toggleMute() {
const store = Alpine.store('audio');
if (store.volume > 0) {
this._previousVolume = store.volume;
this.setVolume(0);
} else {
this.setVolume(this._previousVolume || 0.8);
}
},
formatTime(seconds) {
if (!seconds || isNaN(seconds)) return '0:00';
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs.toString().padStart(2, '0')}`;
},
_previousVolume: 0.8
}));
});
</script>
{{/* htmx */}}
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
{{/* Alpine.js */}}
<script defer src="https://unpkg.com/alpinejs@3.14.8/dist/cdn.min.js"></script>
{{/* Main JS - audio manager and visualizer */}}
{{- $js := resources.Get "js/main.js" -}}
{{- if $js -}}
{{- $jsOpts := dict "format" "esm" -}}
{{- if hugo.IsProduction -}}
{{- $jsOpts = merge $jsOpts (dict "minify" true) -}}
{{- end -}}
{{- $js = $js | js.Build $jsOpts -}}
{{- if hugo.IsProduction -}}
{{- $js = $js | fingerprint -}}
{{- end -}}
<script type="module" src="{{ $js.RelPermalink }}"></script>
{{- end -}}
{{/* Analytics */}}
{{- if and .Site.Params.umami.enabled hugo.IsProduction -}}
{{- partial "analytics.html" . -}}
{{- end -}}
<script>
// htmx config
document.addEventListener('DOMContentLoaded', function() {
if (typeof htmx !== 'undefined') {
htmx.config.globalViewTransitions = true;
}
});
// Re-init components after htmx swap
document.body.addEventListener('htmx:afterSwap', function() {
window.dispatchEvent(new CustomEvent('page:loaded'));
});
</script>
</body>
</html>