Every one of these was silent at the code level and loud in a real
browser; each fix carries its own tripwire (render pin, unit pin, or
CSP-safe rewrite) so the class cannot quietly return.
1. Theme picker dead since the CSP migration (eb57b720): the Appearance
panel's theme buttons emitted `@click="selectTheme(opt.Name)"` as
LITERAL text (templ gotcha #1 — quoted attribute values are never
interpolated; `opt` is a Go loop var), so Alpine threw "Undefined
variable: opt" on every click and no PUT /api/auth/theme ever fired.
The wood-paneling sibling always used the correct expression form,
which is why the panel half-worked. Fixed to the interpolated form;
TestThemePickerHandlersAreInterpolated pins that real theme names
reach the HTML and the raw identifier never does.
2. Filter-builder op dropdown "does nothing": the field ingest
hardcoded `ops: []` into every field's meta while only mirroring the
ops into the global label registry — opsFor() returned [] for EVERY
field, so the op menu rendered zero items: an invisible menu, no
console error (user tried author/publisher/title/subtitle, all
equally dead). FieldMeta.ops is now the wire's op objects, opsFor()
is a standalone exported helper, and opsForField unit tests pin the
round-trip. Verified live: the field menu opens/closes while the op
menu stayed shut, both with identical wiring — the difference was
the data, not the wiring.
3. AND/OR connective chip dead: the chip carried an `if` STATEMENT in
the Alpine attribute (`@click="if (row.chipClickable) ..."`) which
the CSP build's evaluator rejects as a statement — the whole
expression silently never ran. Moved the clickability check into a
toggleConnective(row) component method; the attribute is now a plain
method call.
4. Pencil vs card navigation race: navigateToCollection guarded with
`event.target instanceof HTMLElement`, which is FALSE for the
pencil glyph's SVGElement — a click landing on the glyph sailed past
the guard and navigated while the htmx edit-modal request still
fired (the reported "modal flashes, then the builder page"). The
guard now uses target.closest("button") (works on SVGElement) and
takes $event explicitly instead of the deprecated window.event.
.icon-btn is already a 36px square, so no hit-area change needed.
5. Add Items modal under the sidebar + taller than the window: the
picker overlay sat at z-50 UNDER the app-sidebar's z-60 (its left
edge rendered beneath the nav), and the card had no height bound —
header + tabs + filters + grid + footer overflow short windows with
the footer unreachable (fixed overlay, no page scroll). The picker
and create-modal overlays move to z-[65], both confirm dialogs to
z-[70], and the picker card is now a flex column capped at
max-h-[85vh] whose grids flex-fill (they grow on tall windows,
shrink on short ones). The ladder is documented at .app-sidebar in
input.css: sidebar 60 / overlays 65 / confirm 70 / toasts 9999.
Found by clicking every control the way a user does (panel clicks +
computed-style reads + screenshots) after the previous sweep trusted
DOM-text snapshots, which show hidden menus' text and cannot see
occlusion, stacking, or geometry at all.
Integration tests require the containerized dev DB and bind-mounted
paths — a bare host `go test ./cmd/server/tests` fails on database
auth on every machine not running the compose stack (the old doc
prescribed exactly that flow). development.md now documents the
container runner and the iteration rule: run ONLY your own tests via
a filtered container invocation while working, and reserve full-suite
runs for pre-commit validation — several tests read live shared state
(system_settings, real library folders, the periodic backup scan's
websocket broadcasts) and flap on data or timing a change never
touched.
Binary media fixtures are never committed: internal/services/
testdata/ is gitignored by policy (the audio metadata tests read
real-format binaries that ffmpeg cannot synthesize — Audible freeform
atoms — so they live untracked on the machines that run the suite).
The new "Media fixtures" section carries the ffmpeg recipes for every
supported container, tag writing, embedded covers and ffmetadata
chapters, plus the manual end-to-end recipe for the live system
(one book per subfolder — a directory with ≥2 audio files ingests as
one folder book).
The export/ directory holds database extracts (annotations, reading
positions, book metadata) pulled from production for debugging. The
contents are personal data and must never be committed.
Remove 185 binary files (169 CMaps + 16 standard fonts) from git
tracking. These are build artifacts copied from
node_modules/@bookhoard/foliate-js at build time and should not be
version-controlled.
Changes:
- Remove web/static/vendor/pdfjs/ from git (169 cmap files + 16
standard font files)
- Add web/static/vendor/ to .gitignore
- Drop CJK cmap copying from build scripts — the app is English-only
and CJK support can be re-added later if needed (saves ~1.7MB in
the container image)
- Update all three build scripts (build:ts, build:ts:dev,
build:ts:watch) to copy only standard_fonts/ from node_modules
- Remove cMapUrl from reader.ts PDF config since we no longer ship
cmaps
- Keep standardFontDataUrl pointing to the build-copied fonts which
are needed for PDFs with non-embedded standard fonts (Helvetica,
Times, Courier, etc.)
- Add web/static/*.mjs to ignore compiled JavaScript modules
- Add web/static/text_layer_builder*.css for PDF.js text layer CSS files
- Add web/static/annotation_layer_builder*.css for PDF.js annotation layer CSS
These files are generated during the PDF.js build process and should not be tracked in version control.
- Add reader.ts as second entry point alongside main.ts in vite.config.ts
- Set assetsDir to empty string to output chunks to /static/ root
- Remove reader-shell import from main.ts (loaded separately via reader.js)
- Update .gitignore to exclude web/static/assets/*.js chunks
- Update Bruno environment with new test IDs
Add *.map pattern to .gitignore to exclude JavaScript sourcemap files
from version control. These files are generated during the build process
and are not needed in the repository, matching the existing pattern for
TypeScript declaration maps (*.d.ts.map).
This prevents accidentally committing generated sourcemap files like
collections.js.map that provide debugging information but are not
necessary for deployment or source control.
- Add .zed/ to gitignore (Zed editor config)
- Add .opencode/ to gitignore (opencode tool data)
- Remove .zed/settings.json from version control
- Keeps repository clean of IDE-specific files
Change 'server' to '/server' to make pattern more explicit.
This prevents editors from confusing the ignored server binary
with the tracked cmd/server/ source code directory.
Pattern now only matches:
- /server (binary at root, ignored)
- NOT cmd/server/ (source directory, tracked)
- Added htmx.min.js to git (previously only downloaded during build)
- Updated .gitignore to explicitly allow documentation dependencies
- Clarified compiled vs downloaded JS in gitignore comments
Remove exception for htmx.min.js so all compiled JavaScript in
web/static/ is ignored and generated during Docker build.
The Dockerfile already handles downloading HTMX via:
- npm postinstall script
- Downloads from unpkg.com during container build
This keeps the repository clean and lets the container build
process generate all static assets consistently.
- Add Kobo sync handler with markup, bookmark, analytics, and initialization endpoints
- Add Kobo integration tests and Bruno API test collection
- Move device approve/reject routes from public to protected routes
- Enhance test infrastructure with DATABASE_URL support and helper functions
- Fix device GetDevice handler nil pointer handling
- Clean up test reports and session files
- Add node_modules/ and npm debug logs
- Add TypeScript build artifacts (*.tsbuildinfo)
- Ignore compiled JS files in web/static/
- Keep htmx.min.js (third-party library)
- Add IDE ignores (.idea, .vscode)
- Add OS ignores (DS_Store, Thumbs.db)
- Add database and uploads directories
- Ignore package-lock.json (use npm shrinkwrap for production if needed)
Standard ignore patterns for modern web development with Go backend.
- Remove unnecessary Node.js and build tool entries
- Keep only relevant entries for Go web application
- Preserve Bruno collection, static assets, and Go template files
- Add clarifying comments for project-specific entries
- Move migrations/ to database/schema/ for clarity on database schema definitions
- Move sqlc.yaml to internal/database/ to group with database code
- Move static/ to cmd/server/static/ to co-locate with server
- Update all configuration files and documentation
- Follow Go project conventions for better organization
- Rename project from 'bookmann' to 'shelf'
- Move all backend/ contents to root level (flatten structure)
- Update Go module name from 'bookmann' to 'shelf'
- Update all import paths to use new 'shelf' module
- Update Dockerfile to work without backend/ subdirectory
- Update docker-compose.yml to use new structure and rename containers
- Update .gitignore for new file paths
- Update README.md with new project name and structure
- Regenerate database code with new module imports