summaryrefslogtreecommitdiff
AgeCommit message (Collapse)Author
2026-05-01add more fontsv0.14.2Paul Buetow
2026-05-01test: cover scroll-driven post selection for task bPaul Buetow
2026-05-01Handle scroll-driven post selection for task bPaul Buetow
2026-05-01Document brutalist system fonts for task aPaul Buetow
2026-05-01task 9: self-host nukem Web437 fontPaul Buetow
2026-05-01Task 8: self-host volcano theme fontsPaul Buetow
2026-05-01Task 7: cover tropicale font assets in integration testsPaul Buetow
2026-05-01Task 7: self-host tropicale Quicksand fontsPaul Buetow
2026-05-01Task 5b: self-host aurora Manrope fontsPaul Buetow
2026-05-01za: self-host neon Orbitron webfontsPaul Buetow
2026-05-01xa: self-host surveillance webfontPaul Buetow
2026-05-01wa self-host Space Mono for spaceagePaul Buetow
2026-05-01va self-host retrofuture webfontsPaul Buetow
2026-05-01ua self-host noir webfontsPaul Buetow
2026-05-01ta self-host cathedral webfontsPaul Buetow
2026-05-01Add self-hosted Outfit font for ocean themePaul Buetow
Task: 7b
2026-05-01Self-host Exo 2 for cosmos theme (task 6b)Paul Buetow
2026-05-01task 2b: self-host terminal JetBrains MonoPaul Buetow
2026-05-01Self-host Rajdhani for plasma theme (task 8b)Paul Buetow
2026-05-01Add retro VT323 webfont for task 1bPaul Buetow
2026-05-01Add synthwave self-hosted webfonts for task yaPaul Buetow
2026-05-01Add self-hosted VT323 matrix font for task 0bPaul Buetow
2026-05-01biomech theme: self-host Oxanium 400/700 webfont (task sa)Paul Buetow
- Add Oxanium-Regular.woff2 and Oxanium-Bold.woff2 (latin + latin-ext subset, ~22 KB total) by Tyler Finck, fetched 2026-05-01 from https://gwfh.mranftl.com/fonts/oxanium (mirrors Google Fonts); released under SIL OFL 1.1. - biomech/theme.css: add @font-face blocks pointing at the local files. The CSS already declared 'Oxanium' as the body font but the family was never loaded, so it silently fell back to system sans-serif. Now the geometric techy face actually renders. - biomech/FONT_LICENSE.txt: full attribution + OFL link + source URL + fetch date so future maintainers can re-fetch deterministically. - templates/embed.go: append themes/*/*.woff2 to the //go:embed glob (deferred from task ra because go:embed requires every pattern to match at least one file, which biomech now satisfies). - README.md: append biomech entry to the bundled-fonts bullet list. Self-hosted, no third-party CDN requests at page load; verified via grep -RnE 'googleapis|gstatic|@import url\(http' against both source and generated dist. Amp-Thread-ID: https://ampcode.com/threads/T-019de000-c062-73c5-9afc-c9af422473cf Co-authored-by: Amp <amp@ampcode.com>
2026-05-01webfonts: bundle dos theme font + document per-theme webfont conventions ↵Paul Buetow
(task ra) - dos theme: bundle 'WebPlus IBM VGA 8x16' (.woff) by VileR from the Ultimate Oldschool PC Font Pack v2.2 (CC BY-SA 4.0); replace the never-loaded VT323 reference with @font-face + a self-hosted bitmap face matching the BBS/DOS aesthetic. - templates/embed.go: add ThemeExtraFiles + extend the //go:embed glob with themes/*/*.woff and themes/*/FONT_LICENSE.txt so any theme can ship its own font assets without further code changes; add a NOTE comment block documenting the per-theme directory convention and the //go:embed pattern-matching constraint. - generator/writeThemeAsset: copy any extra files (fonts, license notices) verbatim from the embedded FS into dist/themes/<name>/. - README.md: rewrite the 'Third-party assets' section into a bullet list with self-hosting policy, attribution for the bundled dos font, and a step-by-step recipe for adding new bundled fonts. - dos/FONT_LICENSE.txt: full attribution + CC BY-SA 4.0 notice. Self-hosted, no third-party CDN requests at page load. Amp-Thread-ID: https://ampcode.com/threads/T-019de000-c062-73c5-9afc-c9af422473cf Co-authored-by: Amp <amp@ampcode.com>
2026-04-29Bump version to 0.14.10.14.1Paul Buetow
Made-with: Cursor
2026-04-27Bump version to 0.14.0v0.14.0Paul Buetow
2026-04-27fix(processor): handle os.Remove errors for markdown inbox extrasPaul Buetow
When removing markdown inbox extras (embedded local images), os.Remove errors were silently ignored. If removal failed, the image remained in the inbox and was later published as a standalone image post. - Check os.Remove errors and return a wrapped error. - Rollback the already-saved post directory when extra removal fails. - Deduplicate extras to avoid failing on duplicate references (e.g. same image referenced twice in one markdown). - Add a negative test that verifies the error is returned and no post is persisted when removing an embedded image fails. Fixes duplicate image posts caused by leftover inbox extras.
2026-04-27integrationtests: skip TestThemeSelection when -short is setPaul Buetow
The test loops over all registered themes and runs the full pipeline for each one, which makes it too slow for quick test cycles. Add a `testing.Short()` guard so it returns early when -short is passed.
2026-04-27+code-quality Add slice capacity pre-allocation in loadAllPosts and paginatePaul Buetow
- loadAllPosts: pre-allocate posts slice with capacity len(entries) since each directory entry may become a post. - paginate: pre-allocate pages slice with capacity (len(posts)+pageSize-1)/pageSize. The concat function no longer exists in the codebase (removed when theme sound data was moved to embedded JSON files in commit 06dd860).
2026-04-27+code-quality Remove dead/unimplemented midi.go (YAGNI violation)Paul Buetow
2026-04-27refactor(generator): move theme sound data from Go code to embedded JSON filesPaul Buetow
- Generate sounds.json for each theme inside templates/themes/<name>/ - Load sound data at runtime from embedded FS via templates.ThemeSounds() - Replace ~1100 lines of hardcoded builder functions and presets with a 145-line loader (soundCache, initSoundCache, loadThemeSounds) - Update embed.go to include themes/*/sounds.json in the embedded FS - Update tests to use loadThemeSounds() instead of themeSoundPresets - Eliminates duplication between themeSoundPresets and JSON output Improves cohesion and DRY by keeping theme assets (CSS, JS, meta, sounds) in a single location per theme directory.
2026-04-27generator: place Run entry point before private helpersPaul Buetow
2026-04-27Add context.Context to I/O-bound public APIs (generator.Run, processor.Run, ↵Paul Buetow
atom.Generate, syncOutput) - generator.Run(ctx, cfg) – ctx passed through to atom.Generate - processor.Run(ctx, cfg) – signature updated for cancellation propagation - atom.Generate(ctx, posts, cfg) – accepts ctx for future cancellation - syncOutput(ctx, cfg) – rsync subprocesses now use exec.CommandContext - Updated all call sites in tests, cmd/snonux/main.go, and integration tests - All call sites pass context.Background() / context.TODO() All tests pass: go test ./...
2026-04-27Externalize sync targets from hardcoded constants to env/configPaul Buetow
- Added SyncTargets and SyncRemoteDir to internal/config.Config. - Replaced the package-level syncTargets and syncRemoteDir constants in cmd/snonux/sync.go with default values, populated lazily via resolveSyncConfig(). - Accept --sync-targets and --sync-remote-dir CLI flags; fall back to SNONUX_SYNC_TARGETS and SNONUX_SYNC_REMOTE_DIR env vars; then fall back to the previous hardcoded defaults (pi0/pi1, /var/www/html/snonux/).
2026-04-27processor: introduce PostBuilder registry to replace hardcoded switchPaul Buetow
Replace the large, duplicate switch statements in planPost and commitPlan with a PostBuilder interface registered per file extension. - PostBuilder interface: Plan(srcPath, ext) (postPlan, error) and Commit(plan, postDir, id, now) (*post.Post, []string, error) - Per-type builders: txtBuilder, mdBuilder, imageBuilder, audioBuilder. - Each builder self-registers via init() into a map[string]PostBuilder. - Core processor loops are now extension-agnostic, satisfying OCP/DIP. - All existing tests pass.
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