From f3155cbc866a1b261a0aafe61683da1e3c25b1ef Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 10 Apr 2026 09:22:06 +0300 Subject: add WebGL scenes to all themes, sounds, image sizing, new themes - All 11 themes now have unique Three.js WebGL backgrounds: aurora: flowing sine-wave ribbon meshes with additive blending brutalist: harsh rotating white/red wireframe boxes glass: drifting crystal icosahedron shards, semi-transparent matrix: digital rain particle columns with per-vertex colour fade ocean: animated vertex-displaced wave surface with orbiting light retro: amber demo-scene cube with orbiting octahedrons synthwave: sunset sphere with scan-line rings and perspective grid terminal: green icosahedron wireframe with orbiting torus particles neon: existing Three.js orb and rings (unchanged) plasma (replaces minimal): drifting additive-blend colour blobs volcano (replaces paper): rising ember particles with lifecycle - shared.go: distinct sounds for j/k nav (220Hz beep), Enter (ascending triangle chime), and Esc (descending sine sweep) - shared.go: images are now thumbnails (max-height:220px) in list view and expand to full width inside the modal (CSS override in navmodal) - main.go: --list-themes flag prints all theme names and exits; --theme random picks a random theme at all generation time - themes.go: updated registry with plasma/volcano replacing minimal/paper Co-Authored-By: Claude Sonnet 4.6 --- PLAN.md | 85 ++++++++++++++ cmd/snonux/main.go | 18 ++- integrationtests/integration_test.go | 4 +- internal/generator/shared.go | 52 +++++++-- internal/generator/theme_aurora.go | 109 ++++++++++++++---- internal/generator/theme_brutalist.go | 68 +++++++++++- internal/generator/theme_glass.go | 114 ++++++++++++++----- internal/generator/theme_matrix.go | 102 ++++++++++++++++- internal/generator/theme_minimal.go | 180 +++++++++++++++++++++--------- internal/generator/theme_ocean.go | 91 +++++++++++++-- internal/generator/theme_paper.go | 202 +++++++++++++++++++++++++--------- internal/generator/theme_retro.go | 87 ++++++++++++++- internal/generator/theme_synthwave.go | 110 +++++++++++++++--- internal/generator/theme_terminal.go | 78 ++++++++++++- internal/generator/themes.go | 4 +- 15 files changed, 1110 insertions(+), 194 deletions(-) create mode 100644 PLAN.md diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..3190323 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,85 @@ +# Plan + +This is the plan for the microblog of snonux.foo + +## Style + +The style should be the same as in ./design.html + +* also link to foo.zone, my normal blog, at the top of every page of the microblog. +* dont have an archive link like in the design.html example. +* the "transmit to nexus" should just link to mailto:paul@nospan.buetow.org +* every microblog entry should have date/timestamp linke in the design.html exampl +* dont have the likes (hearts), comments, and rebroadcast buttons per blog post. +* we want to be able to navigat between the blog posts with cursor up-down keys and jk (vi style up-down keys) +* we also want to navigate with the left-right cursor keys to previous, next page (if any) and also hl(vi style up-down keys) +* when navigating, we want to highlight the active entry (with a border or background), so it's clear which post is selected.and also play a sound. +* when hit enter on a blog post, open it in a larger view. when hit ESC there, return. + +## Input dir + +I want to create a microblog. It should have an input directory, where i can put multiple source file formats: + +* Plain .txt +* Markdown .md +* Images (.png/.jpg/.gif) +* Audio (.mp3 files) + +And the blog should automatically generate a post out of it. Furthermore, it should also support a Markdown with a reference to an image file in the same directory. + +Once we invoke the microblog generator, it should process all files in the input dir into blog assets. + +### Plain text input + +Just add this as a plain text as a blog post + +### Markdown .md + +Add this as a formatted HTML entry with styling etc. For this, we need to convert the Markdown to HTML. + +### Images + +Just have the image displayed as its own entry. + +### Audio + +Just have a playback button for the audio in the resulting entry + +### After being processed + +After an input was procssed, remove the files from the input dir. + +## Output dir + +The output dir should only contain static assets. That the directory to be published via a webbrowser. + +We expect one directory per blog post. The microblog generator then combines all of them together into multiple pages. + +So we need to keep the individual directories per blog post since the pages need to be re-generated according to the total blog post count and same for the atom.xml feed, so we need the directories as intermediate formats. We can link to images directly to them, though. So the output directory format will be like this: + + +./outdir/index.html # Generated main page +./outdir/pageN.html # Older pages +./ourdir/posts/YYYY-MM-DD-HHmmss/ # Microblog post asset(s) + +Downscale any images to a maximum width of 1024px and compress to 80% JPEG quality + +## Page size limit + +Have max 42 entries on the single HTML page. Once more, allow paging (e.g. go to next 42 pages, etc). + +* Next page (if any) is only on the bottom of the page. +* Previous page button (if any) is at the top of the page. + +## atom.xml feed + +We should have an atom.xml feed with the last 42 entries generated every run. + +## Comprehensive testing + +* All features should have integration tests. + +## Technologies used + +* Implemented in Google Go +* Follow everything from the auditing-code-quality skill diff --git a/cmd/snonux/main.go b/cmd/snonux/main.go index 5a9c9d3..58d6cb5 100644 --- a/cmd/snonux/main.go +++ b/cmd/snonux/main.go @@ -11,8 +11,10 @@ import ( "flag" "fmt" "log" + "math/rand" "os" "path/filepath" + "strings" "codeberg.org/snonux/snonux/internal/config" "codeberg.org/snonux/snonux/internal/generator" @@ -31,15 +33,29 @@ func main() { } // parseFlags reads CLI flags and returns a validated Config. +// Special theme value "random" picks a theme at random from the registry. func parseFlags() (*config.Config, error) { cfg := &config.Config{} + listThemes := flag.Bool("list-themes", false, "print all available theme names and exit") flag.StringVar(&cfg.InputDir, "input", "./inbox", "directory containing new source files to process") flag.StringVar(&cfg.OutputDir, "output", "~/git/snonux.foo/dist", "root directory for generated static site output") flag.StringVar(&cfg.BaseURL, "base-url", "https://snonux.foo", "canonical base URL used in Atom feed links") - flag.StringVar(&cfg.Theme, "theme", "neon", "visual theme: aurora, brutalist, glass, matrix, minimal, neon, ocean, paper, retro, synthwave, terminal") + flag.StringVar(&cfg.Theme, "theme", "neon", "visual theme name, or \"random\" to pick one at random") flag.Parse() + if *listThemes { + fmt.Println(strings.Join(generator.ListThemes(), "\n")) + os.Exit(0) + } + + // Resolve the special "random" value before any further validation. + if cfg.Theme == "random" { + themes := generator.ListThemes() + cfg.Theme = themes[rand.Intn(len(themes))] + log.Printf("random theme selected: %s", cfg.Theme) + } + var err error cfg.InputDir, err = expandHome(cfg.InputDir) diff --git a/integrationtests/integration_test.go b/integrationtests/integration_test.go index 634971e..6d355b1 100644 --- a/integrationtests/integration_test.go +++ b/integrationtests/integration_test.go @@ -323,8 +323,8 @@ func TestKeyboardNavJS(t *testing.T) { // index.html containing core structural elements (post text, nav script). func TestThemeSelection(t *testing.T) { themes := []string{ - "aurora", "brutalist", "glass", "matrix", "minimal", - "neon", "ocean", "paper", "retro", "synthwave", "terminal", + "aurora", "brutalist", "glass", "matrix", "neon", + "ocean", "plasma", "retro", "synthwave", "terminal", "volcano", } for _, theme := range themes { diff --git a/internal/generator/shared.go b/internal/generator/shared.go index eed4de3..12a91f4 100644 --- a/internal/generator/shared.go +++ b/internal/generator/shared.go @@ -3,13 +3,12 @@ package generator // navDefs is appended to every theme template when parsing. // It defines three named sub-templates shared across all themes: // - "navhints" — keyboard shortcut hint bar HTML -// - "navmodal" — full-screen expanded-post modal HTML -// - "navscript" — keyboard navigation JavaScript +// - "navmodal" — full-screen expanded-post modal HTML + image-sizing CSS +// - "navscript" — keyboard navigation JavaScript with distinct sounds per action // // Each theme calls {{template "navhints" .}}, {{template "navmodal" .}}, and // {{template "navscript" .}} at the appropriate points in its HTML. -// All CSS for these elements (colours, borders, backdrop) lives in each theme -// so themes remain self-contained and independently styled. +// All theme-specific CSS lives in each theme file so themes stay self-contained. const navDefs = ` {{define "navhints"}}