summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
2026-04-27refactor(snonux): separate CLI parsing from I/O and theme logicPaul Buetow
Split the overloaded parseFlags into focused helpers: - parseFlags now does *only* CLI flag parsing. - resolvePaths handles ~ expansion. - validateDirs performs filesystem creation/checks. - resolveTheme selects the random theme using rng. This removes the SRP violation in parseFlags and makes each function independently testable. Tests updated accordingly. Fixes: z8
2026-04-27processor: refactor to two-phase commit for inbox processingPaul Buetow
Introduce postPlan to capture everything validated in Phase 1 before any filesystem mutation occurs. Phase 1 scans the inbox, validates all source files (parses text, markdown, image, audio), checks markdown image claims for conflicts, and collects a plan per item. Phase 2 commits mutations only after every plan is validated: creates post directories, writes assets, persists post.json, and removes sources. If Phase 1 fails (e.g. unsupported file, missing markdown image, claim conflict), no mutations occur and the inbox is left untouched. Roll back the partial post directory if a mutation fails during commit. Also refactor image and audio sub-processors into validation-only and write-only parts (validateImage/writeImageAsset, validateAudio/copyFile) so that Phase 1 is strictly read-only. All existing tests pass.
2026-04-27generator: lazy-init themeSet and navDefs with sync.OncePaul Buetow
2026-04-27Move post.Load constructor before methods in post.goPaul Buetow
2026-04-27processor: reject two markdown files claiming the same inbox imagePaul Buetow
The pre-scan claimedByMarkdown used to mark an image as claimed by any markdown that referenced it, but it did not prevent two different markdown files from referencing the same image. When that happened, the first file processed would copy the image into its post directory and then delete it from the inbox. The second file then failed because the source image was already gone. Fix: make image claiming exclusive. claimedByMarkdown now tracks the owning markdown filename for each claimed image and returns an error immediately during the pre-scan if a conflict is detected. This way no sources are removed and no partial posts are created. Also add tests: - twoMarkdownsClaimingSameImageFails (negative) - duplicateImageClaimsInSameMarkdownAllowed (positive)
2026-04-27processor: reject markdown image refs with path separators or parent traversalPaul Buetow
findLocalImages used filepath.Base(ref) after stat succeeded, which caused subdirectory or parent-directory references to pass the scan but then fail during copy because the basename was looked up in the wrong directory. Fix: add isSimpleImageRef helper that accepts only flat filenames (no path separators, no .. traversal). findLocalImages now returns the ref unchanged, matching what copyLocalImages and claimedByMarkdown expect. Added tests for isSimpleImageRef and negative findLocalImages cases for subdirectory and parent-directory references.
2026-04-27processor: fix uniqueID to return error instead of infinite loop on Stat errorsPaul Buetow
Previously, uniqueID(postsDir, t) returned only a string and looped forever when os.Stat returned an error other than IsNotExist (e.g. permission denied). Change the signature to (string, error), propagate the error, and update the caller in processFile to handle it. Add positive and negative tests covering new ID generation, suffix collision, and permission-based stat failure.
2026-04-27fix: seed rand source for --theme random to avoid deterministic outputPaul Buetow
2026-04-27fix(generator): write to temp file, check close error, rename on successPaul Buetow
Fixes data loss risk in writePage: - Write to a temp file instead of the target path. - Explicitly close the temp file and check the error (was ignored). - Rename to the final path only after successful close. - Remove the temp file on any error so truncated output is never left on disk. Added TestWritePage (happy path + template error) and TestWritePage_tempFileCleanedOnError to verify no corruption of existing file.
2026-04-27Add blank mode toggle (b key) and theme-hot-swap + Nukem sounds reworkPaul Buetow
- New blank mode hides all UI except the WebGL canvas; toggled via the ‘blank’ nav button or the b key. - Theme switching is now in-place (no page reload), avoiding the Web Audio context reset that used to kill ambient music. - Nukem ambient/riff rebuilt around the classic Grabbag riff (E5 E5 G5 A5 …).
2026-04-27Add nukem theme: action-hero hard rock with explosive WebGL effectsPaul Buetow
- New theme directory: templates/themes/nukem/ (meta.json, theme.css, theme.js) - Red/gold/black color palette with monospace typography - WebGL: rising sparks, falling debris, rubble blocks, explosion glow - Sound preset: 140 BPM E-minor power riffs with fanfare stabs (wild: 180 BPM thrash) - All nav/open/close/scroll/wild/page effects implemented Amp-Thread-ID: https://ampcode.com/threads/T-019dcd42-8558-748b-8c28-3f848400026d Co-authored-by: Amp <amp@ampcode.com>
2026-04-26audio: make ambient music hard, fast, and aggressivePaul Buetow
Complete rewrite of all 19 theme ambient presets to eliminate the slow waltz/lo-fi feel. Every preset now targets a driving rock/ industrial/hardstyle vibe with: - **Much faster BPM**: normal 120–180, wild 180–260 (was 60–70 / 120–140) - **Power chords** replace major/minor triads — aggressive distortion-ready - **Straight eighth-note drive** replaces swung waltz melodies — punchy, rhythmic, no floaty legato - **More active drum patterns**: double-kick, snare backbeats, sixteenth-note hats, clap stabs on the 16th - **Shorter attack / release** on normal (0.3→0.02, 0.5→0.15) so everything hits harder - **Higher filter cutoffs** for a brighter, more aggressive timbre - **Three drone voices** (was 4) with lower fundamental octave, fewer frequencies, more focused. Sub-bass added via buildRockSong octave divider. - **buildRockSong** replaces buildSong: adds sub-bass (/2 voice) and bass hits per measure, making each bar feel like a down-beat impact - **buildDriveMelody** replaces buildSwingMelody: straight eighth notes, no swing - **buildStabMelody** for wild mode: short 16th-note bursts with gaps Also bumped test BPM cap from 250→400 to accommodate the new range.
2026-04-26chore: bump version to 0.13.1v0.13.1Paul Buetow
2026-04-26fix: mobile UX across all 19 themesPaul Buetow
Audit found universal mobile issues in every theme + shared CSS: - body/overlay height:100vh had no 100dvh dynamic-viewport fallback on any theme - @media(max-width:640px) was too minimal; 3 themes had no .content/.modal-inner reduction - .modal-close buttons had zero padding → tap target well below 44×44px - .transmit-btn and .page-nav a vertical height too small on most themes Fixes applied: Go/CSS common (shared.css): - Add .modal-close padding + min 44×44px touch target - Add overflow:hidden to #sno-wild-banner to prevent wild-mode banner overflow - Add mobile @media override block with !important for .modal-inner padding, .transmit-btn, and .page-nav a min-height to guarantee 44px touch targets All 19 themes: - Add height:100dvh fallback after height:100vh on body and .overlay - Expand @media(max-width:640px) with missing .content/.modal-inner padding, .transmit-btn min-height, and .modal-close touch-target padding where absent Specific themes: - DOS: enlarge .transmit-btn base padding from 4px to 8px - Spaceage: bump .post-text from 0.86rem (~13.8px) to 0.9rem (~14.4px) - Brutalist / Retrofuture / Synthwave: these previously had no .content or .modal-inner reduction in their media queries; now filled in
2026-04-26chore: bump version to 0.13.0v0.13.0Paul Buetow
2026-04-26audio: make ambient music polyphonic with real songs, fix engine silencePaul Buetow
Rewrite all 19 theme ambient presets with chord progressions, bass drones, overlapping chord pads (dur > step on melodyNote), and swing melodies. Add per-theme 16-step drum patterns (kick, snare, hat, clap) synthesized in the Web Audio engine. Go (theme_sounds.go): - Add Step field to melodyNote for sequencer timing independent of duration - Add Drums []string field to ambientPreset for 16-step patterns - Add buildMeasure, buildSong, buildSwingMelody, and pat helpers - Rewrite every sounds*() func with I–IV–V–I progressions, bass, and melody JS (shared.js): - Fix startEngine() to await ctx.resume() before scheduling - Make masterGain a binary gate (fade to 1.0 in ~50 ms), not preset.gain - Fix startDrones() to scale by preset.gain instead of 1.0/numDrones - Add twin-oscillator detuned timbre in playMelodyNote() - Add synthesized drum engine: playDrum(), scheduleDrums(), stopDrums() - Keep splash sounds bypassing masterGain so they never get gated out
2026-04-26ambient: polyphonic songs, fix cascaded gain and resume bugsPaul Buetow
Rewrite all 19 theme melodies into proper ~10-16 second looping songs with bass drones, chord pads, and scalar melody lines using the new 'step' field for polyphonic overlap on a monophonic step sequencer. Fix four bugs that caused total silence: 1. masterGain was faded to preset.gain while each note was ALSO scaled by preset.gain*0.6, squaring volume to ~-68 dB. 2. fadeMasterTo ramped the gate for 0.5-1.5s, swallowing first notes. 3. startDrones set per-osc gain to 1.0/numDrones, clipping and burying the melody. 4. AudioContext resume was not awaited; notes fired into a suspended context and were lost. Shared.js: - startEngine() waits for ctx.resume() before scheduling anything. - masterGain acts as a binary gate (fades to 1.0 over 50ms). - playMelodyNote uses twin detuned oscillators for richer timbre. - startDrones scales by preset.gain so drones match melody level. Theme_sounds.go: - Add 'step' field to melodyNote struct. - Add chord-building helpers (major/minor triads). - Add buildSong() / buildMeasure() for structured 4-bar loops. - All 19 themes: audible bass (130-220Hz), chord pads (260-520Hz), melodies (330-1046Hz), real chord progressions, proper BPM.
2026-04-26Add unique ambient melodies for all 19 themesPaul Buetow
Each theme now has a distinctive looping melodic phrase (normal + wild variants) played by the Web Audio ambient engine. This replaces the generic random-pulse behavior that made all themes sound the same. Go changes: - Add melodyNote struct with freq/dur/gain fields to ambientPreset - Add n() helper for concise melody construction - Rewrite all 19 theme presets with unique melodic phrases: neon=glassy arpeggio, terminal=sparse CRT beeps, synthwave=warm minor, plasma=tritone shimmer, brutalist=concrete rumble, volcano=phrygian, aurora=high shimmer, matrix=digital ostinato, ocean=wave flow, dos=8-bit arpeggio, retro=VHS warmth, cosmos=vast fifths, retrofuture=lounge minor-7, spaceage=pure ascending, tropicale=pentatonic, noir=smoky minor-7, cathedral=organ thirds, surveillance=fourth alert, biomech=diminished unease JS changes: - Add melodyTimer/melodyIndex state to ambient engine - Add playMelodyNote() with per-note gain and filter support - Add scheduleMelodyNote() with phrase-rest gaps - schedulePulse() now routes to melody player when melody is present - Add sawtooth support to snonuxWaveType() - Reset melody index on startEngine() and stopAll() Test changes: - TestThemeSoundPresetsAmbientPopulated now accepts melody as valid content - TestThemeSoundPresetsAmbientValuesBounded validates melody note freq/dur
2026-04-25Fix splash screen Enter/Space key not playing chime soundPaul Buetow
The splash overlay played its chime on pointerdown (click) but not when dismissing via Enter or Space. Expose playSplashChime on window so the global keydown handler can trigger the same sound before dismissal. Escape continues to dismiss silently.
2026-04-25Add ambient music and wild-mode testsPaul Buetow
- generator_test.go: - TestThemeSoundPresetsAmbientPopulated: every theme has Ambient.Normal and Ambient.Wild with at least one drone or pulse frequency. - TestThemeSoundPresetsAmbientValuesBounded: gains/durations/intervals are positive and below conservative max values. - TestThemeSoundsJSON_neonAmbientRoundTrip: themeSoundsJSON unmarshals and contains ambient.normal and ambient.wild. - integration_test.go: - TestKeyboardNavJS: verify p=ambient, f=flash hotkeys and nav hints. - TestIndexHTMLBakesSounds: verify index.html bakes window.SNONUX_SOUNDS. - TestThemeSelection: dynamically test all registered themes and assert generated sounds.json includes ambient data for each.
2026-04-25Refine normal and wild ambient presets for all 19 themesPaul Buetow
Replace generic ambient() helper calls with detailed ambientPreset literals for every registered theme. Normal presets are now subtle (gain 0.02-0.03), loopable, and low-fatigue. Wild presets are recognizably the same theme but faster, denser, and more dramatic (gain 0.05-0.065). Theme-specific changes: - neon: glass drone with filter sweep; wild adds brighter arps - terminal: CRT hum + sparse ticks; wild becomes rapid process storm - synthwave: warm minor pad; wild doubles pulse with octave jumps - plasma: tritone shimmer; wild adds aggressive sweep and noise - brutalist: concrete rumble; wild adds blunt sub impacts with rhythm - volcano: magma drone; wild adds tremor pulses and heat noise - aurora: airy fifths; wild adds solar-storm detune shimmer - matrix: digital rain ostinato; wild speeds code-rain pulses - ocean: tidal swell + surf noise; wild adds storm surge - dos: PC speaker hum; wild becomes IRQ panic at 200 BPM - retro: amber chip tones; wild adds tape-eat jitter and noise - cosmos: wide fifth/octave drone; wild adds supernova pulse - retrofuture: atomic lounge pad; wild adds Geiger-like rhythm - spaceage: theremin sines; wild adds re-entry alarm pings - tropicale: dusk pentatonic surf; wild adds category-5 percussion - noir: smoky minor drone; wild adds siren-like intervals - cathedral: organ stacked sines; wild adds bell/requiem intensity - surveillance: monitor hum/scanner; wild adds alert cascade - biomech: organic asymmetric drone; wild adds nervous wet pulses All generator tests pass.
2026-04-25Make ambient music react to wild modePaul Buetow
- When wild mode turns on, automatically start ambient playback using the theme's wild ambient variant, even if normal ambience was off. - Track wild-forced ambient starts via _snonuxAmbientWildForced so that deactivation can fade to silence when the user never opted in, or crossfade back to normal when they did. - Do not persist forced-wild playback as a normal ambient preference; only user toggles (p key / ambient button) are saved. - Enhance wild ambient playback: denser pulses (1.5x bpm), increased noise texture, slightly raised gain (capped at 0.15), and deeper lowpass filter sweeps on pulses. - Re-sync FX button states after ambient start/stop so the ambient button accurately reflects the current state.
2026-04-25Add opt-in ambient music control (task o8)Paul Buetow
- Change ambient playback hotkey from 'm' to 'p' - Move flash effect hotkey from 'p' to 'f' - Update nav button labels: '<kbd>p</kbd> music' and '<kbd>f</kbd> flash' - Update splash hint to advertise 'p' music alongside 'w' wild - Add 'p' and 'f' hotkey support in splash and modal states - Persist ambient enabled/disabled state in localStorage (snonuxAmbientEnabled) - Default off; restore preference on page load without overwriting via wild mode - Improve accessibility with explicit aria-labels and aria-pressed state sync
2026-04-25Implement shared Web Audio ambient enginePaul Buetow
Adds a reusable ambient controller in shared.js with: - One shared AudioContext + master gain node for all ambient sound - Long-running drone oscillators from SNONUX_SOUNDS.ambient droneFreqs - Scheduled short pulse oscillators with BPM-derived interval jitter - Looped buffer-source noise when noiseGain is present (no AudioWorklet) - Fade in/out (~0.3-0.8s) using preset attack/release on start/pause/switch - snonuxAmbientStart/Pause/Toggle/SetWild/SyncPreset/IsPlaying window APIs Integrates with existing UI: - Ambient toggle button (<kbd>m</kbd> ambient) in nav-fx-buttons - m hotkey toggles ambient in both splash and main keyboard handlers - w key starts wild ambient and crossfades preset when switching wild state - Theme sounds.json fetch completion triggers snonuxAmbientSyncPreset() - One-shot UI sounds (splash/nav/open/close/bounce) remain unchanged and route through their own AudioContexts, never through the ambient master.
2026-04-25Add ambient preset schema to theme soundsPaul Buetow
Extend themeSounds with Ambient field containing Normal and Wild variants. Each ambientPreset carries numeric/string fields for generative background audio: bpm, pulseInterval, gain, wave, droneFreqs, pulseFreqs, cutoffMin/cutoffMax, noiseGain, attack, release, detuneCents, and rhythm. Populate all 19 existing themes with normal and wild ambient presets using a concise ambient() helper. Existing splash/nav/open/close/bounce fields remain unchanged for backwards compatibility. Add TestThemeSoundsJSON_ambientSchema to verify every theme emits valid JSON containing both ambient variants alongside the legacy sound fields.
2026-04-25can swap themes dynamicallyv0.12.0Paul Buetow
2026-04-23Bump version to 0.11.0v0.11.0Paul Buetow
2026-04-23Bump version to 0.10.0v0.10.0Paul Buetow
2026-04-23Bump version to 0.9.0v0.9.0Paul Buetow
2026-04-23newPaul Buetow
2026-04-22v0.8.0: modal drift, flying emoji, random flips, hue drift, cursor sparkle, ↵v0.8.0Paul Buetow
post afterimage Amp-Thread-ID: https://ampcode.com/threads/T-019db698-f351-7161-a397-1cce1fdab440 Co-authored-by: Amp <amp@ampcode.com>
2026-04-22v0.7.0: vertical bounce, hover ripple, modal scroll-end, idle breathing, ↵v0.7.0Paul Buetow
first-visit burst Amp-Thread-ID: https://ampcode.com/threads/T-019db698-f351-7161-a397-1cce1fdab440 Co-authored-by: Amp <amp@ampcode.com>
2026-04-22v0.6.0: bounce effect on page boundary + wild bouncev0.6.0Paul Buetow
Amp-Thread-ID: https://ampcode.com/threads/T-019db698-f351-7161-a397-1cce1fdab440 Co-authored-by: Amp <amp@ampcode.com>
2026-04-20feat(nav): add sno-wild visual effects and theme support to nav.tmplPaul Buetow
2026-04-20forgot to addv0.5.0Paul Buetow
2026-04-20new themePaul Buetow
2026-04-18v0.4.3Paul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18v0.4.2: boost wild mode intensity across all 14 themesPaul Buetow
DOS: camera FOV/Z/X lunge + per-char X jitter (corrupted RAM look) Spaceage: station scale pulse, pods fly to expanded/oscillating radius, full camera X/Y/Z/FOV chaos (was time-offset only) Matrix: camera plunges into the rain with Z/X sway + FOV warp Plasma: blobs pulse in scale + camera sucked into vortex (was no camera) Synthwave: camera warp-drive rush X/Y/Z/FOV (was tiny X drift only) Retro: main cube scale pulse, orbiters expand to larger radius, camera frenetic X/Y/Z/FOV sway Retrofuture: rings tumble in 3D (X/Y tilt), full camera spiral + FOV pulse All themes now have multi-dimensional wild effects; weakest now ≥4/5. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18feat(cosmos): make wild mode dramatically more intensePaul Buetow
- Speed multiplier raised from 10× to 22× - Planet rings now counter-spin and tilt independently in wild mode - Asteroid orbits breathe in/out and scatter vertically (radial + y sine waves) - Camera swings 20 units left/right, bobs 10 units, dives ±14 units in z - FOV pulses 42°–78° giving a dramatic zoom-in/zoom-out feel Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18v0.4.1: add scroll/wild effects, post translucency, aurora wild boostPaul Buetow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18feat(aurora): make wild mode dramatically more intensePaul Buetow
- Time offset multiplier raised from 7× to 18× (waves churn ~19× faster) - Wave amplitude boosted 3.2× in wild mode (tall, violent ribbon swings) - Camera sways left/right and bobs up/down while wild is active - Ribbons drift vertically independently so they cross and tangle mid-air Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18feat(themes): make non-active posts translucent to reveal WebGL backgroundPaul Buetow
Non-selected posts now sit at 0.55 opacity so the animated background bleeds through clearly. Hovering a non-active post raises it to 0.85, and the active/highlighted post is always fully opaque (1.0). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18feat(themes): add PgUp/PgDn scroll effects, wild mode lightning & badge fixPaul Buetow
- PgUp/PgDn now calls snonuxScrollEffect(dir) in all 14 themes - Each theme gets a unique directional colour strip (theme-matched gradient) sliding across the screen in the scroll direction; thicker when wild - Wild mode activation fires dramatic multi-flash lightning overlay plus a persistent storm overlay that flickers intermittently while active - Wild mode deactivation shows a brief dark veil then removes the storm - Fixed WILD MODE badge: animation now only runs under .sno-wild-on so the badge stays fully hidden (opacity:0, no pulsing) when wild is off Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18feat(themes): add per-theme special effects, wild mode & w hotkeyPaul Buetow
Each of the 14 themes now has three interactive effect hooks: - Navigation (j/k): brief thematic flash/shake/glitch per theme - Page change (h/l): zoom/warp/tunnel transition per theme - Wild mode (w key): toggles background animation into overdrive Wild-mode effects per theme: - neon: rings spin 14x, particles vortex - matrix: rain falls 10x faster - volcano: embers/smoke 8x/5x, lava glows brighter - brutalist: boxes 15x + random jitter - aurora/ocean/plasma/cosmos/dos/synthwave/terminal/retro/spaceage/retrofuture: time-offset approach accelerates all sine-based animations 8-11x Shared infrastructure added to nav.tmpl: - sno-shake/sno-zoom-fwd/sno-glitch CSS keyframes - #sno-wild-badge pulsing red overlay badge - selectPost() calls snonuxNavEffect() - h/l navigation calls snonuxPageEffect() - w key calls snonuxWildToggle() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18fix(generator): match markdown typography between list and zoomed-in modal viewsPaul Buetow
The zoomed-in post modal (opened with Enter/click) used #modal-content to display post HTML, but the CSS typography rules (.post-text p/ul/ol/li margin and padding) only targeted the .post-text class used in the list view. This caused visually different formatting — missing paragraph spacing and unindented lists — in the expanded modal compared to the feed. Fix: extend all .post-text typography selectors in nav.tmpl to also cover #modal-content, so both views apply identical spacing rules. Also adds a missing spaceage theme sound preset to satisfy the TestThemeSoundPresetsMatchRegistry test that was already failing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18Add retrofuture themev0.4.0Paul Buetow
2026-04-16v0.3.1: refactor themes & shared HTML into embedded templatesv0.3.1Paul Buetow
Made-with: Cursor
2026-04-16refactor(generator): load themes & shared HTML from embedded template files ↵Paul Buetow
(task s4) Move the 12 theme HTML templates out of theme_*.go (~185KB of raw-string literals) and the shared navDefs/faviconHeadHTML fragments out of shared.go/favicon.go into separate .tmpl files under internal/generator/templates/{themes,shared}/. A new templates subpackage owns a //go:embed FS and exposes Theme(), Shared() and ThemeNames() helpers so the binary still ships fully self-contained. The hand-maintained themeRegistry map is replaced by on-startup enumeration of the embedded FS; getTheme/ListThemes now derive theme names from the template files themselves, and navDefs/faviconHeadHTML are loaded once at package init. Rendered output is byte-equivalent — all unit tests and integration tests pass, go vet is clean. Made-with: Cursor
2026-04-16docs: add comprehensive README with posting instructionsPaul Buetow
Amp-Thread-ID: https://ampcode.com/threads/T-019d94cc-99a9-74af-8f3d-9521cd73324f Co-authored-by: Amp <amp@ampcode.com>
2026-04-16v0.3.0: add DOS theme with VT323 webfontv0.3.0Paul Buetow
Amp-Thread-ID: https://ampcode.com/threads/T-019d94cc-99a9-74af-8f3d-9521cd73324f Co-authored-by: Amp <amp@ampcode.com>