From 1b24076788868f50812468b1bf4bb0ae86214e8c Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 16 May 2026 09:35:31 +0300 Subject: Add theme background music --- internal/generator/generator.go | 31 ++- internal/generator/generator_test.go | 24 +++ internal/generator/templates/embed.go | 3 +- internal/generator/templates/shared/nav.tmpl | 61 ++++-- internal/generator/templates/shared/shared.css | 53 ++++- internal/generator/templates/shared/shared.js | 224 ++++++++++++++++++++- .../templates/themes/aurora/MUSIC_LICENSE.txt | 7 + .../generator/templates/themes/aurora/ambient.ogg | Bin 0 -> 1396111 bytes .../generator/templates/themes/aurora/sounds.json | 8 +- .../generator/templates/themes/aurora/theme.css | 2 +- .../templates/themes/biomech/MUSIC_LICENSE.txt | 7 + .../generator/templates/themes/biomech/ambient.ogg | Bin 0 -> 1317802 bytes .../generator/templates/themes/biomech/sounds.json | 8 +- .../generator/templates/themes/biomech/theme.css | 2 +- .../templates/themes/brutalist/MUSIC_LICENSE.txt | 7 + .../templates/themes/brutalist/ambient.ogg | Bin 0 -> 1428440 bytes .../templates/themes/brutalist/sounds.json | 8 +- .../generator/templates/themes/brutalist/theme.css | 2 +- .../templates/themes/cathedral/MUSIC_LICENSE.txt | 7 + .../templates/themes/cathedral/ambient.ogg | Bin 0 -> 1048852 bytes .../templates/themes/cathedral/sounds.json | 8 +- .../generator/templates/themes/cathedral/theme.css | 2 +- .../templates/themes/cosmos/MUSIC_LICENSE.txt | 7 + .../generator/templates/themes/cosmos/ambient.ogg | Bin 0 -> 2706024 bytes .../generator/templates/themes/cosmos/sounds.json | 8 +- .../generator/templates/themes/cosmos/theme.css | 2 +- .../templates/themes/dos/MUSIC_LICENSE.txt | 7 + .../generator/templates/themes/dos/ambient.ogg | Bin 0 -> 1039452 bytes .../generator/templates/themes/dos/sounds.json | 8 +- internal/generator/templates/themes/dos/theme.css | 2 +- .../templates/themes/matrix/MUSIC_LICENSE.txt | 7 + .../generator/templates/themes/matrix/ambient.ogg | Bin 0 -> 276371 bytes .../generator/templates/themes/matrix/sounds.json | 8 +- .../generator/templates/themes/matrix/theme.css | 2 +- .../templates/themes/neon/MUSIC_LICENSE.txt | 7 + .../generator/templates/themes/neon/ambient.ogg | Bin 0 -> 3565740 bytes .../generator/templates/themes/neon/sounds.json | 8 +- .../templates/themes/noir/MUSIC_LICENSE.txt | 7 + .../generator/templates/themes/noir/ambient.ogg | Bin 0 -> 2440360 bytes .../generator/templates/themes/noir/sounds.json | 8 +- internal/generator/templates/themes/noir/theme.css | 2 +- .../templates/themes/nukem/MUSIC_LICENSE.txt | 7 + .../generator/templates/themes/nukem/ambient.ogg | Bin 0 -> 691008 bytes .../generator/templates/themes/nukem/sounds.json | 8 +- .../generator/templates/themes/nukem/theme.css | 2 +- .../templates/themes/ocean/MUSIC_LICENSE.txt | 7 + .../generator/templates/themes/ocean/ambient.ogg | Bin 0 -> 2313143 bytes .../generator/templates/themes/ocean/sounds.json | 8 +- .../generator/templates/themes/ocean/theme.css | 2 +- .../templates/themes/plasma/MUSIC_LICENSE.txt | 7 + .../generator/templates/themes/plasma/ambient.ogg | Bin 0 -> 1949772 bytes .../generator/templates/themes/plasma/sounds.json | 8 +- .../generator/templates/themes/plasma/theme.css | 2 +- .../templates/themes/retro/MUSIC_LICENSE.txt | 7 + .../generator/templates/themes/retro/ambient.ogg | Bin 0 -> 1937080 bytes .../generator/templates/themes/retro/sounds.json | 8 +- .../generator/templates/themes/retro/theme.css | 2 +- .../templates/themes/retrofuture/MUSIC_LICENSE.txt | 7 + .../templates/themes/retrofuture/ambient.ogg | Bin 0 -> 500879 bytes .../templates/themes/retrofuture/sounds.json | 8 +- .../templates/themes/retrofuture/theme.css | 2 +- .../templates/themes/spaceage/MUSIC_LICENSE.txt | 7 + .../templates/themes/spaceage/ambient.ogg | Bin 0 -> 477361 bytes .../templates/themes/spaceage/sounds.json | 8 +- .../generator/templates/themes/spaceage/theme.css | 2 +- .../themes/surveillance/MUSIC_LICENSE.txt | 7 + .../templates/themes/surveillance/ambient.ogg | Bin 0 -> 148158 bytes .../templates/themes/surveillance/sounds.json | 8 +- .../templates/themes/surveillance/theme.css | 2 +- .../templates/themes/synthwave/MUSIC_LICENSE.txt | 7 + .../templates/themes/synthwave/ambient.ogg | Bin 0 -> 655778 bytes .../templates/themes/synthwave/sounds.json | 8 +- .../generator/templates/themes/synthwave/theme.css | 2 +- .../templates/themes/terminal/MUSIC_LICENSE.txt | 7 + .../templates/themes/terminal/ambient.ogg | Bin 0 -> 888400 bytes .../templates/themes/terminal/sounds.json | 8 +- .../generator/templates/themes/terminal/theme.css | 2 +- .../templates/themes/tropicale/MUSIC_LICENSE.txt | 7 + .../templates/themes/tropicale/ambient.ogg | Bin 0 -> 363392 bytes .../templates/themes/tropicale/sounds.json | 8 +- .../generator/templates/themes/tropicale/theme.css | 2 +- .../templates/themes/volcano/MUSIC_LICENSE.txt | 7 + .../generator/templates/themes/volcano/ambient.ogg | Bin 0 -> 977074 bytes .../generator/templates/themes/volcano/sounds.json | 8 +- .../generator/templates/themes/volcano/theme.css | 2 +- internal/generator/theme_sounds.go | 2 + internal/version/version.go | 2 +- 87 files changed, 655 insertions(+), 83 deletions(-) create mode 100644 internal/generator/templates/themes/aurora/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/aurora/ambient.ogg create mode 100644 internal/generator/templates/themes/biomech/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/biomech/ambient.ogg create mode 100644 internal/generator/templates/themes/brutalist/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/brutalist/ambient.ogg create mode 100644 internal/generator/templates/themes/cathedral/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/cathedral/ambient.ogg create mode 100644 internal/generator/templates/themes/cosmos/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/cosmos/ambient.ogg create mode 100644 internal/generator/templates/themes/dos/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/dos/ambient.ogg create mode 100644 internal/generator/templates/themes/matrix/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/matrix/ambient.ogg create mode 100644 internal/generator/templates/themes/neon/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/neon/ambient.ogg create mode 100644 internal/generator/templates/themes/noir/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/noir/ambient.ogg create mode 100644 internal/generator/templates/themes/nukem/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/nukem/ambient.ogg create mode 100644 internal/generator/templates/themes/ocean/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/ocean/ambient.ogg create mode 100644 internal/generator/templates/themes/plasma/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/plasma/ambient.ogg create mode 100644 internal/generator/templates/themes/retro/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/retro/ambient.ogg create mode 100644 internal/generator/templates/themes/retrofuture/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/retrofuture/ambient.ogg create mode 100644 internal/generator/templates/themes/spaceage/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/spaceage/ambient.ogg create mode 100644 internal/generator/templates/themes/surveillance/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/surveillance/ambient.ogg create mode 100644 internal/generator/templates/themes/synthwave/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/synthwave/ambient.ogg create mode 100644 internal/generator/templates/themes/terminal/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/terminal/ambient.ogg create mode 100644 internal/generator/templates/themes/tropicale/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/tropicale/ambient.ogg create mode 100644 internal/generator/templates/themes/volcano/MUSIC_LICENSE.txt create mode 100644 internal/generator/templates/themes/volcano/ambient.ogg (limited to 'internal') diff --git a/internal/generator/generator.go b/internal/generator/generator.go index aae4c97..feefe18 100644 --- a/internal/generator/generator.go +++ b/internal/generator/generator.go @@ -15,6 +15,7 @@ import ( "codeberg.org/snonux/snonux/internal/generator/atom" "codeberg.org/snonux/snonux/internal/generator/templates" "codeberg.org/snonux/snonux/internal/post" + "codeberg.org/snonux/snonux/internal/version" ) // pageData holds the template variables for a single HTML page. @@ -339,7 +340,7 @@ func writeThemeAsset(dir, name string) error { return fmt.Errorf("write %s/theme.js: %w", name, err) } - meta, err := templates.ThemeMeta(name) + meta, err := themeMetaJSON(name) if err != nil { return fmt.Errorf("read %s/meta.json: %w", name, err) } @@ -376,9 +377,37 @@ func loadThemeMeta(name string) (themeMeta, error) { if err := json.Unmarshal(b, &m); err != nil { return m, fmt.Errorf("parse theme meta %q: %w", name, err) } + m.HeaderHTML = headerHTMLWithVersion(m.HeaderHTML) return m, nil } +func themeMetaJSON(name string) ([]byte, error) { + m, err := loadThemeMeta(name) + if err != nil { + return nil, err + } + b, err := json.MarshalIndent(m, "", " ") + if err != nil { + return nil, fmt.Errorf("marshal theme meta %q: %w", name, err) + } + return b, nil +} + +func headerHTMLWithVersion(header string) string { + if strings.Contains(header, "sno-version") { + return header + } + versionHTML := ` snonux v` + template.HTMLEscapeString(version.Version) + `` + hostClass := `class="logo-host"` + if idx := strings.Index(header, hostClass); idx >= 0 { + if end := strings.Index(header[idx:], "

"); end >= 0 { + pos := idx + end + return header[:pos] + versionHTML + header[pos:] + } + } + return header + versionHTML +} + // allThemesJSON returns a JS array literal of all theme names. func allThemesJSON() (template.JS, error) { names, err := templates.ThemeNames() diff --git a/internal/generator/generator_test.go b/internal/generator/generator_test.go index 7e2742b..a718c95 100644 --- a/internal/generator/generator_test.go +++ b/internal/generator/generator_test.go @@ -12,6 +12,7 @@ import ( "codeberg.org/snonux/snonux/internal/config" "codeberg.org/snonux/snonux/internal/post" + "codeberg.org/snonux/snonux/internal/version" ) var ctx = context.Background() //nolint:gochecknoglobals // test-only top-level helper used by every test in the file @@ -94,6 +95,23 @@ func TestJSONStringOrNull(t *testing.T) { } } +func TestLoadThemeMetaInjectsVersion(t *testing.T) { + t.Parallel() + + for name := range getThemeSet() { + meta, err := loadThemeMeta(name) + if err != nil { + t.Fatalf("loadThemeMeta(%q): %v", name, err) + } + if !strings.Contains(meta.HeaderHTML, "snonux v"+version.Version) { + t.Errorf("theme %q header missing snonux version", name) + } + if strings.Count(meta.HeaderHTML, `class="sno-version"`) != 1 { + t.Errorf("theme %q header has duplicate version markup", name) + } + } +} + func TestThemeSoundsJSONNonEmpty(t *testing.T) { t.Parallel() j := themeSoundsJSON("neon") @@ -181,6 +199,12 @@ func TestThemeSoundPresetsAmbientValuesBounded(t *testing.T) { if a.Gain <= 0 || a.Gain > 0.15 { t.Errorf("theme %q ambient.%s gain=%f; want (0, 0.15]", name, mode, a.Gain) } + if a.File != "ambient.ogg" { + t.Errorf("theme %q ambient.%s file=%q; want ambient.ogg", name, mode, a.File) + } + if a.Volume <= 0 || a.Volume > 0.8 { + t.Errorf("theme %q ambient.%s volume=%f; want (0, 0.8]", name, mode, a.Volume) + } if a.BPM <= 0 || a.BPM > 400 { t.Errorf("theme %q ambient.%s bpm=%f; want (0, 250]", name, mode, a.BPM) } diff --git a/internal/generator/templates/embed.go b/internal/generator/templates/embed.go index 56fc673..191f4ad 100644 --- a/internal/generator/templates/embed.go +++ b/internal/generator/templates/embed.go @@ -20,6 +20,7 @@ import ( // theme.js — required, always present // meta.json — required, always present // sounds.json — required, always present +// ambient.ogg — optional, background music loop // .woff2 — optional, one or more self-hosted web fonts // .woff — optional, fallback / non-woff2 web fonts // FONT_LICENSE.txt — required iff any font file is present; @@ -34,7 +35,7 @@ import ( // match at least one file. When you introduce a new font extension // that no other theme uses yet, append the matching glob here. // -//go:embed shell.tmpl shared/*.tmpl shared/shared.css shared/shared.js themes/*/theme.css themes/*/theme.js themes/*/meta.json themes/*/sounds.json themes/*/*.woff themes/*/*.woff2 themes/*/FONT_LICENSE.txt +//go:embed shell.tmpl shared/*.tmpl shared/shared.css shared/shared.js themes/*/theme.css themes/*/theme.js themes/*/meta.json themes/*/sounds.json themes/*/*.ogg themes/*/*.woff themes/*/*.woff2 themes/*/FONT_LICENSE.txt themes/*/MUSIC_LICENSE.txt var FS embed.FS // themeStandardFiles lists the per-theme files that have dedicated diff --git a/internal/generator/templates/shared/nav.tmpl b/internal/generator/templates/shared/nav.tmpl index b04b160..9759761 100644 --- a/internal/generator/templates/shared/nav.tmpl +++ b/internal/generator/templates/shared/nav.tmpl @@ -44,38 +44,65 @@ {{end}} {{define "navhints"}} -