| Age | Commit message (Collapse) | Author |
|
Amp-Thread-ID: https://ampcode.com/threads/T-019fc31d-668a-769a-8dc8-554ecbd6a274
Co-authored-by: Amp <amp@ampcode.com>
|
|
Amp-Thread-ID: https://ampcode.com/threads/T-019fc31d-668a-769a-8dc8-554ecbd6a274
Co-authored-by: Amp <amp@ampcode.com>
|
|
Amp-Thread-ID: https://ampcode.com/threads/T-019fbeb1-4c14-7775-baa1-5cd8579b1dfd
Co-authored-by: Amp <amp@ampcode.com>
|
|
Amp-Thread-ID: https://ampcode.com/threads/T-019fbeb1-4c14-7775-baa1-5cd8579b1dfd
Co-authored-by: Amp <amp@ampcode.com>
|
|
Amp-Thread-ID: https://ampcode.com/threads/T-019fbeb1-4c14-7775-baa1-5cd8579b1dfd
Co-authored-by: Amp <amp@ampcode.com>
|
|
|
|
|
|
Injects an explicit "With music" / "Without music" choice into the splash
overlay at runtime (uniformly across all themes) instead of only letting
visitors discover the 'p' ambient shortcut after they're already inside.
Clicking a button saves the preference, starts/pauses the ambient engine,
and dismisses the splash. Re-injected after the two spots that replace
#splash-overlay's innerHTML wholesale (theme switch / theme-meta swap) so
the buttons survive. Also fixes a keyboard-focus bug this introduced: the
splash's document-level Enter/Space handler was swallowing native button
activation for a Tab-focused choice button.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
The default sync target directory was still /var/www/html/snonux/, but that
directory was renamed to snonux.foo/ on both pi0 and pi1 as part of a
bozohttpd vhost fix. Also add --chmod=D755,F644 to the rsync invocation:
locally-generated output is mode 600, which lighttpd apparently tolerated
but bozohttpd's separate _httpd service user does not (403 Forbidden).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
Amp-Thread-ID: https://ampcode.com/threads/T-019e8f2a-b58e-72b3-b572-28d1fb1d9a1d
Co-authored-by: Amp <amp@ampcode.com>
|
|
Removed the mobile-only display:none gate so the share button is visible
on all viewports. On desktop, navigator.share is typically unavailable,
so the existing JS handler falls through to clipboard.writeText() and
shows the 'Link copied' toast. The mobile @media rule still bumps the
touch target to 40x40.
Amp-Thread-ID: https://ampcode.com/threads/T-019e8f2a-b58e-72b3-b572-28d1fb1d9a1d
Co-authored-by: Amp <amp@ampcode.com>
|
|
Each post now exposes a share button in its header that surfaces only on
mobile. Clicking it invokes navigator.share() with a permalink of the
form "<page>#post-<ID>", which the existing deep-link handler in
shared.js already opens directly in the modal.
When navigator.share is unavailable the link is copied to the clipboard
and a brief toast confirms the action; very old browsers fall back to
window.prompt.
Amp-Thread-ID: https://ampcode.com/threads/T-019e8f2a-b58e-72b3-b572-28d1fb1d9a1d
Co-authored-by: Amp <amp@ampcode.com>
|
|
|
|
Amp-Thread-ID: https://ampcode.com/threads/T-019dfbe0-b5ea-77bb-be19-11decfbbea69
Co-authored-by: Amp <amp@ampcode.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Task: 7b
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- 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>
|
|
(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>
|
|
Made-with: Cursor
|
|
|
|
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.
|
|
- 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).
|
|
|
|
- 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.
|
|
|
|
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 ./...
|
|
- 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/).
|
|
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.
|
|
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.
|
|
|
|
|
|
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)
|