Commit Graph
100 Commits
Author SHA1 Message Date
john-okeefe d68f72f21b feat(reader): wire up progress mode switching with four display modes
Connect the existing progress_mode setting dropdown to the reader's
progress display. Four modes are now functional:
- pages: overall percent + page/location number (default, existing)
- chapter: chapter title + page X / Y within current section
- percentage: overall percent only
- time-left: percent + estimated time remaining via reading speed API
The progress display in the bottom bar is now clickable to cycle through
modes with immediate visual feedback. The settings dropdown is bound
with x-model for persistence. Reading speed is fetched once on init
from the backend reading-speed API for time-left estimates.
2026-04-25 13:40:18 -04:00
john-okeefe d400377474 feat(types): add FoliateTocItem interface for foliate-js TOC entries
Add a typed interface for the tocItem data returned by foliate-js
relocate events, replacing untyped usage in the reader progress display.
2026-04-25 13:39:07 -04:00
john-okeefe 7cd88b6107 chore(templates): regenerate all templ generated Go files
Regenerated all _templ.go files after running templ generate. Changes
include updated FileName references (relative path normalization) and
line number adjustments from the templ code generator.
2026-04-24 14:03:16 -04:00
john-okeefe d38804e910 fix(progress): correct percentage display and add format-aware progress
Fix two bugs in progress display across book detail, progress page, reader,
and sync modal templates:

1. Percentage was stored as 0.0-1.0 fraction but displayed as-if 0-100
   (showing 0.5% instead of 50%). Multiply by 100 at the data source in
   both GetAllProgress and GetAllProgressData handlers, and in the reader
   route's ReadingProgress construction.

2. Progress bar width was never evaluated — { expr } inside style=".."
   was rendered as literal text by templ, resulting in 0% width bars for
   all items. Fixed by using templ's style={ expr } attribute syntax
   which evaluates the Go expression (uses SanitizeStyleAttributeValues).

Also add format-aware progress display:
- Reader template: shows "45% · Page 89/196" for reflowable (estimated
  pages), "127/342" for comics/PDFs (actual pages)
- Progress page: shows "Page X of Y (est.)" for reflowable, "X / Y"
  for fixed layout
- Add FormatGroup and EstimatedPages to ProgressWithMedia struct
- Remove hardcoded totalPages=200 fallback in progress handler (now 0)
- Add fmt import to progress.templ for string formatting
2026-04-24 14:03:03 -04:00
john-okeefe 192c978a38 feat(templates): expand reader and progress template types for format-aware display
Add fields to ReaderMetadata and ReadingProgress template types to support
KOReader-like progress display:

ReaderMetadata:
- TotalCharacters: from media item, used for estimated page calculation
- EstimatedPages: computed via sync.EstimatedPages()

ReadingProgress:
- Chapter: current chapter index from reading_progress
- ChapterProgress: within-chapter progress (0-100, multiplied from DB fraction)
- FormatGroup: item format for conditional display logic

These fields enable format-aware progress display (pages for comics/PDFs,
estimated pages for reflowable, percentage for all).
2026-04-24 14:02:43 -04:00
john-okeefe 44ec2f496a feat(sync): add estimated pages calculation for reflowable ebooks
Reflowable ebooks (EPUBs) don't have inherent page numbers since layout
depends on device settings. Add an EstimatedPages() function that converts
total character count to an estimated print page count using the industry
standard of 1800 characters per page.

This provides a consistent, device-independent page count for progress
display (e.g., "Page 89 of 196" for a reflowable EPUB), matching how
KOReader and similar readers handle the same problem.
2026-04-24 14:02:28 -04:00
john-okeefe d3ddecb840 fix(reader): persist chapter metadata cache to database
The DetectChapters function in reader.go was serializing chapter detection
results to JSON but then discarding the bytes with `_ = metadataBytes`
instead of writing them to the database. This meant chapter_metadata in
media_items was never populated, forcing re-detection on every request.

Replace the no-op discard with an actual UpdateMediaItemChapterMetadata()
call using the existing sqlc-generated query.
2026-04-24 14:02:13 -04:00
john-okeefe da1732285f fix(scanner): populate page count and total characters during media scanning
The media scanner never populated page_count or total_characters in
media_items, leaving progress display and reading position calculations
with no reliable data. This commit fixes data population for all formats:

Comics (CBZ/CBR/CB7/CBT):
- Add countArchiveImages() helper that walks archive entries and counts
  image files (.jpg, .jpeg, .png, .gif, .webp)
- Call it during comic metadata merge to set metadata.PageCount

PDFs:
- Extract pdfInfo.PageCount from the pdfcpu library (already available
  from PDFInfo call, just never used) and set metadata.PageCount

Reflowable EPUBs:
- Use book.AllChaptersText() to compute metadata.TotalCharacters
- Use book.ChapterCount() to set metadata.ChapterCount

Fixed-layout EPUBs (manga/comics in EPUB format):
- Merge .epub into the .cbz case in countArchiveImages since both are
  ZIP archives with images
- Detect fixed-layout EPUBs via DetectFixedLayoutEPUB() in both the
  Calibre sidecar path (mergeMetadata) and the no-sidecar path
  (extractMetadata), counting images when fixed-layout is detected

Format group on creation:
- Remove the guard condition on UpdateMediaItemFormatGroup so that
  format_group, is_reflowable, and has_fixed_layout are set immediately
  for every new item (not just items with text data)
- Use DetectFixedLayoutEPUB() instead of hardcoding all .epub as
  reflowable, correctly classifying fixed-layout EPUBs

Also pass PageCount to CreateMediaItem and add PageCount,
TotalCharacters, and ChapterCount fields to the MediaMetadata struct.
2026-04-24 14:01:57 -04:00
john-okeefe 0269403a5d feat(reader): wire up reading progress save and restore
The web reader had all the infrastructure for progress persistence
(updateReadingProgress/getReadingProgress API functions, PUT/GET
endpoints, database queries) but the reader.ts never called them.

Changes:
- Add debounced (2s) saveProgress call on every relocate event that
  PUTs percentage, current_page, total_pages, and epubcfi to the
  existing /api/media-items/:id/progress endpoint
- Replace renderer.next() with view.init({ lastLocation }) to restore
  the reader to the last saved position on load (CFI first, then
  fraction fallback, then default first page)
- Pass savedPercentage and savedCfi from server-side progress data
  through readerInitExpr config to the JS initReader function
- Add mediaItemId and saveTimeout to the Alpine data object

This fixes both the blank /progress page and the missing progress
section on book detail pages — both were empty because the
reading_progress table never received any data from the web reader.
2026-04-23 21:08:30 -04:00
john-okeefe a4962a87b2 fix: replace invalid new(expression) calls with proper pointer allocation
Go's new() builtin takes a type and allocates a zero value — it cannot
wrap an expression. All instances of new(someExpression) were compile
errors. Replace each with a local variable assignment and address-of
operator.

Affected files:
- handlers/koreader.go: progress field pointers (Chapter, Page, etc.)
- handlers/kobo.go: pagesRemaining pointer
- handlers/queue.go: uuidPtrToString and timestamptzPtrToString helpers
- router/reader.go: bookmark pageNumber and chapterNumber pointers
- services/media_scanner.go: validation error message pointers
- services/worker.go: StartedAt and CompletedAt timestamps
- sync/offline.go: GetDeviceStatus return pointer
- tests/device_test.go: SyncEnabled and SyncFrequencyMinutes pointers
2026-04-23 20:39:50 -04:00
john-okeefe 35c8ffe33e fix(reader): URL-encode file paths and JSON-encode init config to fix comics/manga loading
The reader failed to load comics and manga (and any file with special
characters in its path) for two reasons:

1. FileURL was built with raw fmt.Sprintf instead of ResolveMediaURL,
   so characters like '#' in paths (e.g. 'Annual #2') were interpreted
   as URL fragments, truncating the path and causing 404s.

2. The Alpine x-init expression used raw string interpolation for config
   values, so apostrophes in paths (e.g. "I'll Use My Appraisal Skill")
   broke JavaScript parsing with 'Unexpected identifier'.

Fix by using utils.ResolveMediaURL for proper URL path encoding and
json.Marshal for the initReader config to safely escape all special
characters.
2026-04-23 20:39:36 -04:00
john-okeefe 70d8ffd528 build: regenerate CSS after dashboard changes 2026-04-23 17:01:30 -04:00
john-okeefe 3a3fa8763e fix(dashboard): use anchor tags for client-side rendered book cards
When switching libraries via TypeScript, renderBookCard() built book
cards as <div> elements with data-action="view-book" for event
delegation, but the click handler was commented out — making books
unclickable after any library switch. The SSR path used proper <a> tags.

Now renderBookCard() wraps cards in <a href="/media/{id}"> to match the
SSR BookCard template, so book links work identically regardless of
whether content was server-rendered or client-rendered.

Also removed the dead view-book handler code and viewBook() stub.

Additionally fixed a listener re-registration bug where the input and
library-select change listeners were nested inside the click callback,
causing them to be registered N times after N clicks. Moved them to
initDashboard() scope so they register exactly once.
2026-04-23 17:01:20 -04:00
john-okeefe 4119a5e38e fix(reader): use proper templ expression for back link href and clean up formatting
The back link in ReaderChrome used literal curly braces inside the href
attribute string (href="/media-items/{ metadata.MediaItemID }") which
doesn't interpolate the variable in templ. Changed to use the correct
templ expression syntax: href={ "/media/" + metadata.MediaItemID }.

Also fixed minor formatting issues:
- Normalize whitespace in comment after closing div
- Collapse empty navigator-viewport div to single line
2026-04-23 17:01:01 -04:00
john-okeefe 86f44230c2 chore(bruno): mark environment IDs as secrets to prevent cross-machine syncing
Remove hardcoded values for database/environment IDs (media_item_id,
library IDs, collection_id, user_id, etc.) and mark them as secret so
that changing them per machine won't keep syncing to git.
2026-04-23 13:57:35 -04:00
john-okeefe 3c3c4e8bf5 fix(utils): URL-encode media paths to handle special characters in filenames
Cover image URLs with special characters like parentheses, #, ?, or
spaces would break because browsers interpret them as URL delimiters.
Apply url.PathEscape() per path segment in ResolveMediaURL so the
server can correctly resolve files like "Wonder Woman (2016) #001.cbz.cover.jpg".

Also adds a package doc comment and fixes the exported function comment.
2026-04-23 13:57:30 -04:00
john-okeefe 133ca1fdaa fix(scanner): extract metadata and covers for comic archives and kepub files
Comic archive formats (.cbz, .cbr, .cb7, .cbt) and .kepub files were
falling through to the default case in extractMetadata(), which only
set the title from the filename. This meant ComicInfo.xml was never
parsed and no cover images were extracted for comics without a Calibre
metadata.opf sidecar file.

The fix adds dedicated switch cases:
- .cbz/.cbr/.cb7/.cbt: calls mergeMetadata() with nil, which triggers
  existing ComicInfo.xml parsing (title, series, issue number, writer,
  publisher, genre, reading direction, etc.) and cover image extraction
  from the archive. Falls back to sidecar cover if no image is found.
- .kepub: treated the same as .epub since KEPUB is an EPUB variant,
  enabling full metadata and cover extraction.
2026-04-22 21:18:53 -04:00
john-okeefe c7f0eb406a fix(tests): handle 404 response for nonexistent library in search filter test
TestCollectionSearchLibraryFilter's 'invalid library_id' case was
expecting a 200 with empty results, but the search handler correctly
returns 404 when no results are found. The test also consumed the
response body for debug logging then tried to JSON-decode the same
body (causing EOF). Add expectedStatus field to the test struct and
return early when a specific non-200 status is expected.
2026-04-22 15:44:01 -04:00
john-okeefe 2fdf894216 fix(tests): correct input validation tests for processing issues endpoints
Three issues fixed in processing_issues_test.go:

- Empty UUID: handler returns 400 (uuid.Parse rejects empty string), not 404.
  Fix expectedStatus in both List and Stats validation tests.
- Path traversal: raw '../../' in URL creates extra path segments that don't
  match the route. Use url.PathEscape so the string is treated as a single
  path parameter, letting the handler reject it with 400.
- SQL injection: raw special characters (semicolons, quotes) caused
  httptest.NewRequest to panic. url.PathEscape prevents the panic and the
  handler rejects the decoded value via uuid.Parse.
- Remove unsupported 'audiobooks' library type from
  TestProcessingIssuesDifferentLibraryTypes (only ebooks/comics/manga exist
  in the database schema).
2026-04-22 15:43:54 -04:00
john-okeefe 17f2dc3120 fix(tests): initialize ProcessingIssuesHandler in test server setup
The setupTestServer() helper in test_helpers_test.go was not creating
a ProcessingIssuesHandler and not passing one to the router config,
causing a nil pointer dereference when any processing issues route was
hit during tests. Add handler creation and wire it into routerConfig
to match how cmd/server/main.go does it.
2026-04-22 15:43:45 -04:00
john-okeefe a06c85e72a fix(handlers): use correct route param name 'id' instead of 'libraryId' in processing issues
Both ListProcessingIssues and GetProcessingIssueStats were reading the
URL parameter 'libraryId', but the routes in internal/router/library.go
define the param as ':id'. This caused both endpoints to always fail with
an invalid library ID error since c.Param('libraryId') returns an empty
string that can't be parsed as a UUID.
2026-04-21 21:31:00 -04:00
john-okeefe e389df92c3 refactor: remove unnecessary type conversions and handle ignored errors across codebase
Remove redundant type conversions that Go 1.26 makes unnecessary or that
were already no-ops:

- uuid.UUID(x.Bytes) → x.Bytes (uuid.UUID is [16]byte, same as pgtype UUID Bytes)
- pgtype.UUID{Bytes: [16]byte(u), Valid: true} → pgtype.UUID{Bytes: u, Valid: true}
- (*time.Time)(&x.Time) → &x.Time
- json.RawMessage(x) → x where x is already []byte
- []byte(stringVal) → stringVal where []byte is expected
- int()/int64()/byte() casts on values already of the target type
- Decompressor(fn) → fn (type is identical)

Handle previously ignored error returns:

- collections.go: check json.Unmarshal error in GetCollection
- conversion_service.go: check fileSize.Scan() error
- app_test.go: check app.Shutdown() error in benchmark
2026-04-21 21:15:59 -04:00
john-okeefe ad27902790 fix(conflicts): use ListConflictsByUser in DismissAllResolved so resolved conflicts are found
DismissAllResolved was calling ListSyncConflictsByUser which filters to
'unresolved' conflicts only, so it could never find the user_resolved or
bulk_resolved conflicts it was trying to delete. The query always returned
an empty set, making dismiss-all a no-op.

Fix the leading space in three SQL query name annotations (ListConflictsByUser,
ListAllConflictsByUserAndStatus, CheckForProgressConflicts) that prevented
sqlc from generating their Go functions. Regenerate the query code and swap
DismissAllResolved to use ListConflictsByUser (no status filter) — the
existing Go loop already filters by resolution_status.
2026-04-21 21:15:34 -04:00
john-okeefe 855ef161d8 fix(docker): bump builder to Go 1.26, download sqlc binary, fix test-runner Go version
The builder stage was previously bumped to Go 1.26 but the test-runner stage
remained on Go 1.25, causing 'go.mod requires go >= 1.26.0' errors during
test execution.

Go 1.26 introduced stricter validation of replace directives in dependency
go.mod files, which broke 'go install sqlc@latest' (sqlc v1.31.0 has replace
directives). Replace go install with a direct binary download from GitHub
releases, matching the existing kepubify download pattern.

Bump test-runner from golang:1.25-alpine to golang:1.26-alpine to match the
go.mod requirement.
2026-04-21 21:15:18 -04:00
john-okeefe a6700f73e0 fix(tests): handle all Close() and Decode() errors across integration tests
Replace all unhandled resp.Body.Close() calls throughout the test suite:

- Deferred calls: replace 'defer VAR.Body.Close()' with a closure that explicitly
  discards the error via 'defer func(Body io.ReadCloser) { _ = Body.Close() }(VAR.Body)'
- Immediate calls: replace 'VAR.Body.Close()' with '_ = VAR.Body.Close()'

Replace all unhandled json.NewDecoder(VAR.Body).Decode(&x) calls with error capture
and require.NoError assertion. Files using httptest.ResponseRecorder (collections_preview,
processing_issues) use 'err :=' declaration; suite-style tests (scanner_integration,
dashboard_integration) use s.T() instead of t.
2026-04-21 20:33:05 -04:00
john-okeefe 8baecad379 fix(tests): use errors.Is() for error comparison and improve resource cleanup in analytics tests
Replace direct error equality check with errors.Is() in media_scanner_hash_test.

In analytics_test.go, improve defer patterns by capturing resp.Body as a named parameter
to avoid stale references, and add require.NoError() checks on all json.NewDecoder().Decode()
calls that were previously silently ignoring decode errors.
2026-04-20 21:20:38 -04:00
john-okeefe 9ccff320a1 refactor: use errors.Is()/errors.AsType() for error comparison and rename shadowed variables
Replace direct error equality checks (err == pgx.ErrNoRows, err != http.ErrServerClosed)
with the idiomatic errors.Is() function throughout handlers, services, middleware, and app
startup. This correctly handles wrapped error chains.

Also replace a raw type assertion (*HTTPError) with errors.AsType[*HTTPError]() in the
error handler middleware for consistency.

Additionally, rename shadowed variables for clarity:
- sidecar.go: config -> sidecarConfig, systemConfig (shadowed package-level vars)
- media_scanner.go: uuid -> uuidString (shadowed the uuid package import)
2026-04-20 21:20:22 -04:00
john-okeefe d8d6334052 feat(reader): add reading_mode (dark/light) to ReaderSettings type
Add the 'reading_mode' field with 'dark' | 'light' values to the
ReaderSettings TypeScript interface, preparing the frontend for a
dark/light reading mode toggle.
2026-04-20 20:45:54 -04:00
john-okeefe 5b2d105609 fix(scanner): always attempt cover extraction for EPUBs and relax manga detection
Two changes to EPUB metadata extraction:

1. Restructure extractMetadata so that fixed-layout detection and cover
   extraction always run for EPUBs, even when extractEPUBMetadata returns
   an error. Previously, a partial failure from the EPUB parser would skip
   cover and format detection entirely, leaving books without covers.

2. Remove the language restriction (ja/jpn) from manga reading direction
   detection. Manga tagged with 'manga' should default to RTL regardless
   of the language metadata, since the tag is an explicit signal from the
   user or metadata source.
2026-04-20 20:45:49 -04:00
john-okeefe 18811cea1b fix(sync): prevent nil pointer dereference when existing progress is missing
In applyProgressResolution and applyResolution, currentPage and totalPages
were unconditionally read from existingProgress even when the preceding
query returned err (no rows). This caused a nil pointer dereference when
no existing reading progress existed for a media item. Now declare the
variables as zero-value pgtype.Int4 and only populate them from
existingProgress when err is nil.
2026-04-20 20:45:41 -04:00
john-okeefe 3e73a582ba refactor(handlers): use errors.Is() for pgx error comparison in KOReader
Replace direct equality checks (err != pgx.ErrNoRows) with the idiomatic
errors.Is(err, pgx.ErrNoRows) pattern. This is the recommended Go practice
for error comparison as it correctly handles wrapped errors from error
chains, making the code more robust against future refactoring that might
wrap errors with fmt.Errorf and %w.
2026-04-20 20:45:35 -04:00
john-okeefe 8aeae33167 fix(sevenzip): add nil guard for subreader to prevent panic
When opening a sevenzip archive, the init() method calls sr :=SevenZipReader()
but never checked if sr was nil before using it. This could cause a nil
pointer dereference when processing malformed or empty archives. Add an
explicit nil check returning errFormat early if the subreader is nil.

Also fixes a minor import grouping whitespace issue.
2026-04-20 20:45:26 -04:00
john-okeefe 3cecb04e8d test(sync): rewrite conflict tests as real HTTP integration tests
Replace the previous mock/httptest-based conflict tests with
integration tests that exercise the full HTTP stack against a live
test server with a real database. Changes include:

- Add shared test helpers (setupConflictTest, createTestConflict,
  makeConflictData) to reduce boilerplate across test files
- Split monolithic TestConflictDetection and TestConflictsBulkOperations
  into focused test functions per scenario
- Test conflict detection, bulk resolution (most_recent, highest_progress,
  manual strategies), and edge cases (empty IDs, invalid UUIDs,
  unauthorized access)
- Verify actual database state after resolution, not just HTTP response
2026-04-20 20:43:16 -04:00
john-okeefe 6820208a36 test(handlers): add unit tests for conflict resolution logic
Add table-driven tests for the conflict handler's source selection
methods: GetMostRecentSource, GetHighestProgressSource, and
GetEarliestSource. Covers cases where each device wins, ties, and
missing/invalid data.
2026-04-20 20:43:04 -04:00
john-okeefe 77cbbf600b refactor(docs): replace deprecated strings.Title with cases.Title
strings.Title has been deprecated since Go 1.18 because it does not
handle Unicode properly. Replace it with cases.Title from
golang.org/x/text which correctly handles language-specific title
casing. Applied to breadcrumb generation and document title formatting.
2026-04-20 20:42:58 -04:00
john-okeefe 4e326dfc86 chore: bump Go dependencies
- github.com/andybalholm/brotli 1.2.0 -> 1.2.1
- github.com/go-playground/validator/v10 10.30.1 -> 10.30.2
- github.com/jackc/pgx/v5 5.9.1 -> 5.9.2
- github.com/labstack/echo/v5 5.0.4 -> 5.1.0
- github.com/yuin/goldmark 1.7.17 -> 1.8.2
- golang.org/x/crypto 0.49.0 -> 0.50.0
- golang.org/x/text 0.35.0 -> 0.36.0
- golang.org/x/image 0.37.0 -> 0.39.0
- golang.org/x/net 0.52.0 -> 0.53.0
- golang.org/x/sys 0.42.0 -> 0.43.0
- Various indirect dependency updates
2026-04-20 20:42:52 -04:00
john-okeefe 7f2aa5ef2d refactor(tests): replace temporary variable pointer pattern with new() builtin
Simplify device update test by using new(false) and new(int32(10)) instead
of declaring named sync variables and taking their addresses.
2026-04-20 08:59:01 -04:00
john-okeefe 416f10aa9d refactor(sync): replace temporary variable pointer pattern with new() builtin
Simplify GetDeviceStatus return by using inline new() instead of
assigning to a local variable and returning its address.
2026-04-20 08:58:36 -04:00
john-okeefe 2b139afce6 refactor(services): replace temporary variable pointer pattern with new() builtin
Simplify pointer creation in media scanner validation messages and worker
job timestamps by using inline new() instead of local variable + address-of.

In media_scanner.go this cleans up three validation error message returns
(manga/comics library format checks). In worker.go it simplifies StartedAt
and CompletedAt timestamp assignments.
2026-04-20 08:58:19 -04:00
john-okeefe 9b141d75e8 refactor(router): replace temporary variable pointer pattern with new() builtin
Simplify page number and chapter number pointer creation in reader route
registration by using inline new() instead of local variable + address-of.
2026-04-20 08:58:00 -04:00
john-okeefe 8ac6e1ac79 refactor(handlers): replace temporary variable pointer pattern with new() builtin
Simplify pointer creation across kobo, koreader, and queue handlers by
replacing the two-step pattern (assign to local, then take address) with
inline new() calls. This reduces verbosity without changing behavior:

Before:
  remaining := int(a - b)
  pagesRemaining = &remaining

After:
  pagesRemaining = new(int(a - b))

Covers page calculations, chapter/progress fields, UUID formatting,
and timestamp string conversions.
2026-04-20 08:57:42 -04:00
john-okeefe 45df899889 chore: bump Go toolchain from 1.25.0 to 1.26.0
Update go.mod directive to target Go 1.26.0.
2026-04-20 08:57:27 -04:00
john-okeefe 3825b93d22 feat(reader): expand reading theme palette with 20 named themes
Replace the original 5-theme allowlist (light, sepia, dark, night,
high-contrast) with a richer 20-theme palette organized into tonal
families: neutrals (light, paper, slate, oled), warm tones (sepia,
parchment, warm, candlelight), cool tones (azure, sky, arctic, frost),
and evening tones (dusk, sunset, twilight, forest, moss, solarized).

The backend validator in UpdateSettings now accepts all 20 theme names,
and the frontend Tailwind build is updated to include the new theme CSS
variables and preflight reset.
2026-04-19 21:50:40 -04:00
john-okeefe ba502ec750 refactor(reader): remove test font, move settings panel to right sidebar
- Remove Playwrite NZ Guides test font and all references (FONT_MAP,
  FONT_FILES, reader-fonts.css, dropdown option, font files)
- Move settings panel from left sidebar to right sidebar (left sidebar
  now only contains TOC)
- Move Restore Defaults button from top bar icon to a styled button
  inside the settings panel, side by side with Done button
2026-04-19 21:42:19 -04:00
john-okeefe 9ca8e2aa44 fix(reader): use blob URLs for fonts to bypass sandboxed iframe restrictions
The paginator renders inside a sandboxed iframe that blocks @font-face
URL fetches. Fonts were never loading — all font-family rules fell back
to the generic 'serif' system font, making every reading font identical.

Fix: fetch font files on the parent page, create blob: URLs via
URL.createObjectURL(), and use those blob URLs in the @font-face rules
injected into the iframe via setStyles(). Blob URLs are always
same-origin with the creating document, so the sandboxed iframe can
access them with allow-same-origin.

Also added Playwrite NZ Guides as a test font for verifying font
switching works.
2026-04-19 21:36:19 -04:00
john-okeefe 0007b4d1d0 fix(reader): inject reader-fonts.css into iframe, remove foliate-themes.css
Fonts weren't loading because the paginator renders inside a sandboxed
iframe. @font-face declarations in the parent page's CSS are invisible
to the iframe's document. Even injecting @font-face rules via
setStyles() may not trigger font loading in sandboxed iframes.

Fix: inject a <link> to reader-fonts.css directly into the iframe's
document on each section load, so @font-face declarations are parsed
in the iframe's own document context where font-family rules can
reference them.

Also:
- Remove foliate-themes.css entirely (no longer needed)
- Set viewport background color directly via JS using THEME_COLORS map
- Remove reading theme CSS classes from viewport element
2026-04-19 21:23:55 -04:00
john-okeefe 4f5825529a fix(reader): set background-color on html/body instead of background: none
background: none on html/body exposed the iframe's default black
background in gaps around the content. Now uses background-color
matching the theme color, and stops forcing background on body *
which caused black bars around page margins.
2026-04-19 21:16:34 -04:00
john-okeefe db33771575 fix(reader): inject @font-face rules into shadow DOM iframe for font switching
The paginator renders book content inside a sandboxed iframe within a
closed shadow DOM. @font-face declarations from the parent page's
reader-fonts.css are NOT available inside the iframe's document context.
All font-family rules fell back to the generic 'serif' system font,
making every reading font look identical.

Fix: prepend all @font-face declarations (Literata, Crimson Pro,
Source Serif 4, EB Garamond, Libertinus Serif, Noto Serif, Charis SIL,
IBM Plex Serif) into the CSS string returned by getCSS(), so they're
injected into the iframe via renderer.setStyles().
2026-04-19 21:15:10 -04:00
john-okeefe 95202f1e28 fix(reader): remove Comic Sans test font (system font not available on Linux)
Comic Sans MS is a system font not available on Linux. The cursive
fallback rendered as a script font, giving false negatives. All 8
loaded reading fonts are serif fonts loaded via @font-face, so they
intentionally look similar — font switching is confirmed working.
2026-04-19 21:12:20 -04:00
john-okeefe 0eb3a7df48 fix(reader): rewrite getCSS with concrete theme colors and aggressive !important
Studied grimmory-tools/grimmory's ebook-reader style.service.ts and adopted
their approach:

- Replace CSS custom property resolution (getComputedStyle) with a hardcoded
  THEME_COLORS map containing concrete fg/bg/link values for all 18 themes
  in both light and dark modes. No more variable resolution failures.

- Font family now targets body + body * with !important, overriding book CSS
  on every element (matching grimmory's approach).

- Colors use grimmory's aggressive pattern:
  html, body { color: ... !important; background: none !important; }
  body * { color: inherit !important; background-color: ... !important; }
  This forces reading theme colors on ALL book elements, overriding inline
  styles and book stylesheets.

- Line height uses !important on p, li, blockquote, dd to override book CSS.

- Removed fragile getComputedStyle calls entirely. getCSS() now receives
  themeName and themeMode parameters for direct color lookup.
2026-04-19 21:01:07 -04:00
john-okeefe d68879a6c6 fix(reader): add test font, expand ranges, fix colors, add restore defaults
- Add Comic Sans MS as a test font option to verify font switching works
- Add !important to background-color and color in getCSS() to prevent
  book CSS from overriding user's reading theme colors
- Expand font size range from 12-24px to 10-40px, bump default to 18px
- Expand line height range from 1.0-2.5 to 0.8-3.0
- Add restoreDefaults() method that resets reading theme, font, size,
  line height, and justify/hyphenate to sensible defaults
- Add ↩️ restore defaults button in top bar underneath the settings gear
2026-04-19 20:51:02 -04:00
john-okeefe 863c0fd9af fix(reader): scope reading theme to viewport, wire up font/size/line-height to shadow DOM
Two root causes fixed:

1. Reading theme CSS variables were on document.body, leaking font/color
   into chrome UI. Now scoped to #reader-viewport so chrome keeps its own
   theme (system font, --text-primary colors) while the reading area uses
   reading theme colors/background.

2. getCSS() never received font family, font size, or line height settings.
   The settings UI (dropdowns, sliders) saved values but they were never
   injected into the book's shadow DOM. Now getCSS() accepts all four
   settings and generates proper CSS rules for them.

Changes:
- Wrap foliate-view in #reader-viewport div (absolute positioned between
  chrome bars)
- getCSS() reads computed style from #reader-viewport, not document.body
- getCSS() params expanded: fontFamily, fontSize, lineHeight, justify,
  hyphenate (removed unused 'spacing')
- Added FONT_MAP to translate setting keys to CSS font-family values
- applyTheme() targets #reader-viewport instead of document.body
- Removed dead #reader-viewport typography rules from foliate-themes.css
  (shadow DOM doesn't inherit outer styles), kept only background-color
2026-04-19 20:36:18 -04:00
john-okeefe 96effde421 fix(reader): inset foliate-view between chrome bars and remove base typography
- Position foliate-view with absolute inset-x-0 top-[52px] bottom-[52px]
  so book content renders between the fixed header and footer bars instead
  of behind them
- Confirmed removal of foliate-themes.css base typography was correct:
  body color now comes from chrome theme's --text-primary (light for
  dark chrome themes like tokyo-night)
2026-04-19 20:19:35 -04:00
john-okeefe 36de7cfa2f fix(reader): dark text on dark themes and add toggle icons
Two fixes:

1. Remove base typography block from foliate-themes.css. The html/body
   rules were unlayered CSS that overrode the chrome theme's layered
   body styles, causing dark reading theme text colors (--reader-text)
   to apply to the outer chrome UI on dark backgrounds. These styles
   are only meant for the shadow DOM, which getCSS() already handles.

2. Move missing typography rules (img, blockquote, a, p orphans/widows)
   into getCSS() so the shadow DOM still gets them.

3. Add sun/moon emoji indicators to the light/dark toggle switch.
2026-04-19 20:12:34 -04:00
john-okeefe 48a8716a25 feat(reader): add light/dark mode toggle for reading themes
Add explicit light/dark mode toggle switch to the reading theme settings.
The reading mode defaults based on the chrome theme (dark chrome themes
like tokyo-night default to dark reading mode).

- Add readingMode property to readerShell Alpine component
- Add toggleReadingMode() method that toggles dark class on body
- Add detectChromeDarkMode() to infer default from chrome theme
- Update applyTheme() to add/remove dark class and persist reading_mode
- Add toggle switch UI in settings panel (blue pill style, next to
  Reading Theme heading)
- Add reading_mode to default settings in settings-manager
2026-04-19 20:05:42 -04:00
john-okeefe b983f2cd2e fix(reader): restore emoji icons on reading theme optgroup labels 2026-04-19 19:51:48 -04:00
john-okeefe 0589157b57 feat(reader): wire up TOC, bookmarks, and navigator panels with Alpine.js bindings
Replace dead data-action attributes with Alpine.js @click handlers and
x-ref references across all reader panels:

- TOC panel: replaced static <nav> with x-for loop over tocItems array,
  added goToTOCItem() click handler, window-shade toggle via .tocPanel
- Bookmarks panel: replaced data-action with @click.prevent handlers,
  added goToBookmarkTarget() using data-cfi attributes for navigation,
  window-shade toggle via .bookmarksPanel
- Navigator panel: replaced data-action with @click window-shade toggle
  via .navigatorPanel
- Added goToBookmarkTarget() and toggleWindowShade() methods to reader.ts
- Removed unused panel-lock buttons (lock feature not yet implemented)
- Regenerated reader_templ.go, rebuilt CSS and JS bundles
2026-04-19 18:17:42 -04:00
john-okeefe 47e0e96ab8 fix(reader): authenticate book file fetch and inject reading theme colors
Books were failing to load because foliate-js fetches the file URL
without auth headers, getting rejected by JWT middleware. Also,
reading themes were not being applied because getCSS() didn't
inject background/text colors into the book iframe.

- Fetch book file with Bearer token, pass as File (not URL) to
  view.open() so foliate-js can detect format via filename extension
- Add reading theme class to body so foliate-themes.css activates
  the correct --reader-bg/--reader-text CSS variables
- Update getCSS() to read theme colors from outer page and embed
  them in the iframe CSS string (background-color, color, link
  color, selection color)
- Fix Alpine.start() deadlock: move call outside the alpine:init
  listener so Alpine actually initializes
- Remove unused tocItem from relocate handler destructuring
- Replace apiGet/apiPut with direct fetch in settings-manager to
  fix /api prefix mismatch (reader routes are at /readers/*, not
  /api/readers/*)
2026-04-19 18:09:05 -04:00
john-okeefe 88c8fbc89d fix(reader): add viewport sizing for foliate-view and remove unused CSS
The foliate-view custom element had no height, causing its shadow DOM
content to collapse to 0px. Books were loading but invisible.

- Add h-screen overflow-hidden to body for full viewport height
- Add block w-full h-full to foliate-view element
- Replace flex-grow with Tailwind grow class on progress slider
- Remove #progress-slider CSS rule from inline style block
  (replaced by Tailwind grow utility)
2026-04-19 18:08:51 -04:00
john-okeefe 58a30b6561 chore(bruno): add multi-library dev setup with scan-all and folder requests
Update NewDevDBSetup collection to create Ebook, Comic, and Manga
libraries with separate IDs (ebook_library_id, comic_library_id,
manga_library_id) instead of a single library_id.

- Fix CreateComicLibrary and CreateMangaLibrary to use correct
  names, descriptions, and types instead of duplicating Ebook values
- Update NewDB.sh to run the full setup sequence: register user,
  create all three libraries, add folders, then scan all
- Add AddEbookLibraryFolder, AddComicLibraryFolder, and
  AddMangaLibraryFolder requests with per-type subfolder paths
- Add ScanAllLibraries request using bru.sendRequest() to scan
  each library sequentially via the /api/scanner/scan endpoint
- Update Get Libraries (Admin) to save all three library IDs
- Update List Media Items requests to use ebook_library_id
- Rename library_id to ebook_library_id in Create Library and
  Add Library Folder requests
- Add comic_library_id and manga_library_id to environment
2026-04-19 18:08:39 -04:00
john-okeefe afeb3f5b45 refactor(reader): rewrite reader module for foliate-js pan/zoom integration
Major rewrite of the web reader to properly interface with
@bookhoard/foliate-js, replacing the abandoned panel-detection
architecture with direct pan and zoom support built into the
foliate-js FixedLayout renderer.

Template (reader.templ):
- Fix critical bug: x-init config was using literal strings
  '{ readerData.X }' inside a quoted attribute, which templ
  treated as raw text and never interpolated. Values were never
  actually passed to JavaScript. Now uses fmt.Sprintf() with
  templ's expression attribute syntax ={ }.
- Pass fileUrl from server so foliate-js can open books directly.
- Redesign bottom bar with foliate-js parity: left/right navigation
  buttons, progress slider with tick marks, and zoom controls
  (zoom out, percentage display, zoom in, magnifier, pan/select
  mode toggle for PDFs).
- Remove panel editor button and enablePanelDetection config.
- Add SVG icon styles for consistent reader controls.

Go types (templates/types.go):
- Expand ReaderMetadata with FormatGroup, MangaType,
  ReadingDirection, FileURL, and LibraryID fields needed by
  the reader frontend.

Router (internal/router/reader.go):
- Populate new ReaderMetadata fields from database values.
- Construct FileURL from library ID and file path for the
  /uploads/library-{id}/* file serving route.

Reader JS (reader.ts):
- Full rewrite modeled on foliate-js Reader class, adapted for
  Alpine.js. Opens books via view.open(fileUrl), accesses
  view.renderer for zoom/pan/navigation, and wires up keyboard
  shortcuts (+/-/0 for zoom, arrows for nav, Escape for magnifier).
- Uses view.isFixedLayout instead of importing FixedLayout class,
  avoiding a TypeScript module resolution issue with the Vite alias.

Settings manager (settings-manager.ts):
- Remove dependency on deleted ReaderContext event bus.
- Export loadSettings/saveSettings/syncSettings directly as
  standalone async functions.

Cleanup:
- Delete reader-context.ts and reader-events.ts (over-engineered
  event system replaced by direct function calls).
- Remove panel_zoom_enabled from ReaderSettings type.
2026-04-19 14:27:56 -04:00
john-okeefe e2d2dbecab chore(bruno): update library API collection and add dev DB setup
Update all library API request files to the current Bruno format
with proper settings blocks, updated sequence numbers, and cleaner
YAML formatting.

Add NewDevDBSetup collection with requests for creating comic, ebook,
and manga libraries, user registration, and folder configuration to
speed up development environment setup.
2026-04-19 14:27:35 -04:00
john-okeefe c337b46bef chore: remove completed MANGA_EPUB_IMPLEMENTATION doc
The manga/EPUB implementation plan has been fully executed. Remove
the tracking document since all described features are now in place.
2026-04-19 14:27:24 -04:00
john-okeefe c4bacc76b3 chore(deps): switch @bookhoard/foliate-js to main branch
The bookhoard-panel-detection branch has been merged. Switch the
dependency back to the main branch of john-okeefe/foliate-js.
2026-04-19 14:27:13 -04:00
john-okeefe b912a037ff fix(database): correct mismatched parentheses in detect_fixed_layout_epub
The string_to_array call in detect_fixed_layout_epub() had an extra
closing parenthesis after the '<img' delimiter, causing a SQL syntax
error that prevented the database container from initializing:

  IF array_length(string_to_array(opf_content, '<img')), 1) - 1 > 50

Fixed to:

  IF array_length(string_to_array(opf_content, '<img'), 1) - 1 > 50
2026-04-19 14:27:04 -04:00
john-okeefe 4b524075a7 feat(reader): Update reader template for dynamic initialization and metadata
Update the reader template to support dynamic configuration and manga metadata:

templates/reader_templ.go:
- Remove direct foliate-js/view.js script tag (integrated into reader.js)
- Add foliate-themes.css stylesheet for theming support
- Update initReader() call to accept configuration object with:
  - mediaItemId: Unique media item identifier
  - title: Media item title
  - enablePanelDetection: Boolean for comic/manga panel detection
  - libraryType: Media type for reader initialization
  - formatGroup: Format category (ebook, comic, manga)
  - mangaType: Manga subtype for specialized handling
  - readingDirection: RTL/LTR/vertical reading direction
- Simplify theme to always use tokyo-night (theme handled in JS)

These changes enable the reader to dynamically configure itself based on
media item metadata, supporting enhanced manga reading features and
panel detection for comic formats.
2026-04-13 09:27:01 -04:00
john-okeefe 6ed1a82cbd chore(deps): Remove unused heavy AI/ML dependencies from package.json
Remove large dependencies that are not actively used in the codebase:
- jszip: Unused ZIP processing library
- pdfjs-dist: PDF rendering (handled by external library)
- @techstark/opencv-js: Computer vision operations
- @tensorflow/tfjs: TensorFlow.js machine learning framework
- @tensorflow-models/coco-ssd: COCO-SSD object detection model

These dependencies were related to experimental features that have been
replaced or moved to external processing. Removing them significantly
reduces bundle size and simplifies the dependency tree.

Retain only actively used dependencies like htmx, chart.js, lunr,
and the @bookhoard/foliate-js fork with panel detection support.
2026-04-13 09:25:54 -04:00
john-okeefe e1aef8e85f refactor(services): Modernize Go code style in collection and filters services
Apply Go 1.18+ language features and modern style:

internal/services/collection_service.go:
- Use map[string]any instead of map[string]interface{} (Go 1.18+)
- Use range clause with single variable for iteration-only loops
- Replace if-else chains with switch statements for better readability
- Remove explicit type initialization for zero values

internal/services/filters.go:
- Add Err prefix to custom error variable for error naming convention

internal/router/library.go:
- Use cfg.ProcessingIssuesHandler instead of local processingIssuesHandler variable
- Ensures proper dependency injection through router config

These changes follow current Go best practices and improve code readability.
2026-04-13 09:25:01 -04:00
john-okeefe 6287088bc1 feat(router): Add admin processing issues UI route
Add frontend route /admin/libraries/:id/issues to display processing issues
management page for a specific library.

internal/router/frontend.go:
- Register GET /admin/libraries/:id/issues with admin middleware
- Fetch processing issue stats from database
- List processing issues for the library
- Convert database models to template types
- Render AdminProcessingIssues template with issues and stats

This provides the admin UI for viewing and managing processing errors that
occur during media scanning and import workflows.
2026-04-13 09:24:25 -04:00
john-okeefe 9694475738 feat(router): Register ProcessingIssuesHandler in router configuration
Wire up the ProcessingIssuesHandler throughout the application:

cmd/server/main.go:
- Remove obsolete commented-out getTemplateUserWithTheme function
- Instantiate ProcessingIssuesHandler with database queries
- Add handler to router Config (with field alignment cleanup)

internal/router/router.go:
- Add ProcessingIssuesHandler field to router Config struct
- Reformat Config struct for better field alignment

This enables the processing issues API endpoints for listing and getting
statistics about issues within libraries, integrated with the admin UI.
2026-04-13 09:24:14 -04:00
john-okeefe 67b3282831 fix(handlers): Correct database call parameters in processing issues handler
Fix ResolveProcessingIssue and DeleteProcessingIssue methods to use proper
parameter structs instead of individual arguments.

Changes:
- ResolveProcessingIssue: Use database.ResolveProcessingIssueParams struct
  with ID and MediaItemID fields instead of separate arguments
- DeleteProcessingIssue: Wrap issueID in pgtype.UUID struct
- Use map[string]any instead of map[string]interface{} for JSON responses

These changes align with the sqlc-generated database interface and ensure
type-safe parameter passing to the database layer.
2026-04-13 09:24:01 -04:00
john-okeefe 12b07058bc feat(templates): Add admin processing issues management UI template
Add admin_processing_issues_templ.go template for managing processing issues
in the admin dashboard. This template provides:

- List view of all processing issues with filtering by severity
- Issue details display (file path, error type, description)
- Actions to resolve or dismiss issues
- Integration with the ProcessingIssuesHandler API endpoints

This UI enables administrators to monitor and address processing errors that
occur during media scanning and import workflows.
2026-04-13 09:23:39 -04:00
john-okeefe bcaa1ed98d feat(templates): Add processing issues data types
Add ProcessingIssueData and IssueStats types to templates/types.go for use
in the processing issues management UI. These types support:

- ProcessingIssueData: Individual issue details including ID, media item,
  file path, format, issue type, severity, and timestamps

- IssueStats: Aggregated counts of issues by severity (error, warning, info)

These types enable the admin UI to display processing issues from the database
and provide statistics for the issues dashboard.
2026-04-13 09:23:16 -04:00
john-okeefe 6398802d15 docs: Add package documentation for handlers and services
Add Go package documentation comments to clarify the purpose and scope of:

- internal/handlers/: HTTP request/response handlers for authentication,
  libraries, media items, reading, collections, dashboards, devices,
  analytics, and system features

- internal/services/: Core business logic layer including media scanning,
  library management, search, analytics, and conversion services

These doc comments improve code discoverability and help developers understand
the architectural separation between HTTP handling (handlers) and business
logic (services).
2026-04-13 09:23:12 -04:00
john-okeefe f6a5e49965 docs: Remove implemented reader refactoring design document
Remove READER_REFACTOR_MODULARIZATION_AND_PAGINATION.md as the modularization
and pagination refactoring has been completed and integrated into the codebase.

This document outlined:
- Modular reader architecture by format (reflowable, pdf, comic, manga)
- Page-based pagination using word count estimation
- CFI-based progress tracking for reflowable formats
- Format-agnostic UI components

The implementation has been completed, so this design document is no longer needed.
2026-04-13 09:23:02 -04:00
john-okeefe 15f4304f65 test: add integration tests for processing issues API endpoints
Added comprehensive integration tests for the new processing issues API
endpoints that track EPUB format mismatches in manga/comics libraries.

Test Coverage:
- Authentication & authorization (no auth, invalid auth, non-admin, admin)
- Input validation (malformed UUIDs, path traversal, SQL injection attempts)
- Response structure validation (fields, types, content-type)
- Cross-library isolation (ensures issues don't leak between libraries)
- All library types (ebooks, comics, manga, audiobooks)
- Edge cases and error conditions

Endpoints Tested:
- GET /api/libraries/:id/issues/list - Lists unresolved processing issues
- GET /api/libraries/:id/issues/stats - Returns error/warning/info counts

Test Implementation:
- 522 lines, 9 test functions, 30+ subtests
- Uses setupTestServer() helper for server setup
- Uses setupDeviceTest() helper for library creation
- Follows PROJECT_GUIDELINES.md requirements
- Table-driven tests with t.Run() for comprehensive coverage
- Tests all three user contexts: no user, regular user, admin

This ensures the processing issues feature is properly tested before
integration with the media scanner service.
2026-04-12 20:58:58 -04:00
john-okeefe a13d2cc3bb docs: Update manga EPUB implementation guide
- Add markdownlint disable directives for linting
- Update SQL examples for consistency
- Update templ examples for panel detection integration
- Update TypeScript examples for reader shell configuration
2026-04-12 20:44:02 -04:00
john-okeefe 03f7c15445 feat(reader): Add dynamic panel detection loading in frontend
- Add enablePanelDetection, libraryType, formatGroup state
- Add panelDetector instance for dynamic loading
- Update initReader to accept and process configuration
- Conditionally load panel detection only when enabled
- Use dynamic import for foliate-js/panel-detection.js
- Add error handling for panel detection loading
2026-04-12 20:44:00 -04:00
john-okeefe 08054ccc76 feat(reader): Update reader template for panel detection config
- Update reader initialization to pass panel detection configuration
- Include enablePanelDetection, libraryType, formatGroup params
- Add mangaType and readingDirection for proper manga rendering
- Change theme class to theme-tokyo-night for consistent styling
2026-04-12 20:43:58 -04:00
john-okeefe 492892097d feat(admin): Add processing issues management UI and API
- Add ProcessingIssuesHandler with List and GetStats methods
- Add AdminProcessingIssues template for issues dashboard
- Display error/warning/info stats cards
- Sort issues by severity and creation date
- Add dismiss functionality for warnings and info items
- Add navigate to media item functionality
- Show issue type, description, and media details
2026-04-12 20:43:57 -04:00
john-okeefe 595072c50d feat(router): Add processing issues API endpoints
- Add GET /admin/libraries/:id/issues/list for listing issues
- Add GET /admin/libraries/:id/issues/stats for issue statistics
- Integrate processing issues handler with library routes
2026-04-12 20:43:55 -04:00
john-okeefe 1ebcd3ac47 feat(reader): Add panel detection support for manga and comics
- Add shouldEnablePanelDetection to determine when to enable panel detection
- Enable for manga/comics libraries with fixed_layout or comic_archive formats
- Fetch library type info using GetLibraryWithType query
- Pass panel detection config to reader initialization
- Include format_group, manga_type, and reading_direction in reader response
2026-04-12 20:43:54 -04:00
john-okeefe f7610c6063 feat(scanner): Add fixed-layout EPUB detection for manga support
- Add DetectFixedLayoutEPUB method to identify manga-style EPUBs
- Check for rendition:layout pre-paginated metadata
- Check for RTL page-progression-direction (manga indicator)
- Check image count threshold (>50 images suggests manga/comic)
- Check subject tags for manga/comic keywords
- Enable proper format detection for manga EPUBs in libraries
2026-04-12 20:43:49 -04:00
john-okeefe 059955be72 chore(db): Regenerate database code from processing issues queries
- Add ProcessingIssues model struct
- Update Querier interface with processing issues methods
- Add generated query implementations for CreateProcessingIssue, ListProcessingIssuesByLibrary, GetProcessingIssueStats, ResolveProcessingIssue, DeleteProcessingIssue
- Add GetLibraryWithType query for fetching library with type information
2026-04-12 20:43:46 -04:00
john-okeefe 37405c5704 feat(queries): Add processing issues management queries
- Add CreateProcessingIssue with upsert for recording/renewing issues
- Add ListProcessingIssuesByLibrary with severity ordering and media item details
- Add GetProcessingIssueStats for error/warning/info counts
- Add ResolveProcessingIssue for marking issues as resolved
- Add DeleteProcessingIssue for removing resolved issues
- Add GetLibraryWithType for fetching library with type info for validation
2026-04-12 20:43:44 -04:00
john-okeefe 7ac86dafa9 feat(schema): Add processing_issues table for tracking media validation problems
- Add processing_issues table to track media items that cannot be properly processed in their assigned library
- Include fields for issue type, description, severity, and resolution status
- Add indexes for efficient querying by library and severity
- Support tracking format mismatches and other processing problems
- Unique constraint on media_item_id and issue_type to prevent duplicates
2026-04-12 20:43:42 -04:00
john-okeefe 4950f8eaf3 refactor: Simplify reading progress parameters for foliate-js integration
Remove unused Epubcfi and Percentage fields from UpdateReadingProgressParams
struct to align with the new foliate-js based reader implementation.

The foliate-js library handles CFI tracking and percentage calculation
internally, so these parameters are no longer needed in the update API.
The reader now relies on foliate-js's built-in progress tracking mechanisms.

This change aligns the database layer with the foliate-js integration completed
in commit c7a9098 (feat: Replace foliate-js submodule with npm git dependency).

Changes:
- Remove Epubcfi field from UpdateReadingProgressParams struct
- Remove Percentage field from UpdateReadingProgressParams struct
- UpdateReadingProgress function now uses simplified parameter set
2026-04-12 19:03:58 -04:00
john-okeefe 88982ec11e docs: Add comprehensive implementation plan for manga EPUB and panel detection
This document provides a complete, phased implementation plan for:
- Enabling manga EPUBs in manga library (not just CBZ/CBR)
- Detecting fixed-layout EPUBs vs reflowable EPUBs
- Processing issue tracking for format mismatches
- Universal panel detection for manga and comics libraries
- Smart panel detection that works for PDF comics but not PDF ebooks

Key features:
- All changes follow existing code patterns with exact line numbers
- 9 implementation phases in correct dependency order
- Code-around context for every change (before/after)
- Testing checklist and rollback plan
- Database schema changes, scanner enhancements, new handlers, frontend updates

Panel detection logic:
- Manga library + fixed_layout/comic_archive → panel detection ON
- Comics library + fixed_layout/comic_archive → panel detection ON
- Ebooks library + any format → panel detection OFF
- Comics library + PDF → panel detection ON
- Ebooks library + PDF → panel detection OFF

Implementation addresses the constraint that manga EPUBs live in /manga/
directory physically but must be filtered to only show fixed-layout EPUBs
in the manga library (not reflowable novels).

This is a planning document only - no code changes yet.
2026-04-12 19:03:49 -04:00
john-okeefe c7a9098c69 feat: Replace foliate-js submodule with npm git dependency
Migrate from git submodule to npm package management for better
developer experience and simplified deployment.
Changes:
- Add @bookhoard/foliate-js from GitHub fork
(john-okeefe/foliate-js#bookhoard-panel-detection)
- Update vite alias to point to node_modules instead of vendor
- Delete .gitmodules (no submodules tracked)
- Remove scripts/setup-git-hooks.sh (no longer needed)
- Delete web/vendor/foliate-js/ submodule directory
- Remove sc-commit git alias (submodule-specific)
Benefits:
- Standard npm workflow (npm install / npm update)
- No authentication issues for end users (public GitHub)
- Simpler deployment (npm ci in containers)
- foliate-js protected in node_modules (AI won't rewrite)
- Independent project management
- Cleaner git history
Technical details:
- Import remains unchanged: import "foliate-js/view.js"
- Vite alias maps "foliate-js" to "/node_modules/@bookhoard/foliate-js"
- Build verified working (reader.js includes foliate-js)
- Package installed from git branch: bookhoard-panel-detection
2026-04-12 17:09:19 -04:00
john-okeefe fd6cee0997 chore: Enhance git hook setup with executable permissions and submodule alias
- Add chmod +x to ensure pre-push hook is executable after creation
- Add global git alias 'sc-commit' for committing to all submodules at once
- Improve user feedback with detailed explanation of installed components
- Better code organization with clearer comments

This makes the setup script more robust by ensuring the hook has proper permissions and provides a convenient command for bulk submodule commits.
2026-04-12 13:31:00 -04:00
john-okeefe 9b164637d7 chore: Add git hook setup script for submodule safety
This script installs a pre-push hook that prevents pushing commits when submodules have uncommitted changes, helping avoid accidental commits with dirty submodule states.

The hook checks all submodules for uncommitted changes before allowing a push, protecting against pushing incomplete work that includes submodule modifications.
2026-04-12 13:26:57 -04:00
john-okeefe 991e04ffa3 chore: Expand gitignore patterns for PDF.js build artifacts
- 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.
2026-04-12 12:19:42 -04:00
john-okeefe 157bf734c7 feat: Create minimal reader entry point for foliate-js
Create minimal Alpine.js integration for foliate-js reader. This file
serves as the entry point that imports foliate-js and provides basic
navigation controls.

Implementation:
1. Import foliate-js/view.js:
   - Registers <foliate-view> custom element globally
   - Makes foliate reader functional when element is added to DOM
   - No explicit EPUB imports needed (foliate detects format automatically)

2. Alpine.js integration:
   - Create readerShell data object for UI state management
   - Provide nextPage() and previousPage() methods for button controls
   - Methods access <foliate-view> custom element's API (next(), prev())
   - Simple, functional approach (no OOP, follows project guidelines)

3. Init placeholder:
   - initReader() method for future initialization logic
   - Currently just logs for debugging
   - Will be extended with theme switching, progress sync, etc.

Design Choices:
- Follow project guidelines: No classes, functional/procedural style
- Use Alpine.js for UI state (consistent with rest of application)
- Defer book loading to foliate's internal format detection
- Minimal footprint: Only what's needed to make <foliate-view> work

Next Steps (Future Commits):
- Theme switching logic (apply CSS custom properties)
- Progress sync to API (listen to foliate's relocate event)
- Book loading integration (open book path, handle CFI locations)
- Settings persistence (save theme, font, spacing preferences)

File: web/src/reader/reader.ts (31 lines)
- Clean separation: Foliate handles rendering, Alpine handles UI state
- Type-safe with @ts-ignore for foliate custom element API access
2026-04-12 12:10:02 -04:00
john-okeefe db4de823f3 feat: Update reader template for foliate-js integration
Update reader page template to use foliate-js custom element and add theme selector UI.

Template Changes:
1. Add foliate-js integration:
   - Load foliate-themes.css for reading theme system
   - Load foliate-js/view.js to register <foliate-view> custom element
   - Replace <main id="reader-content"> with <foliate-view id="reader-view">
   - Foliate auto-initializes from the custom element

2. Add Reading Theme selector:
   - New section in settings panel (before Typography)
   - Single dropdown with 18 themes organized by category using <optgroup>
   - Categories: Classic Reading, Sky & Atmosphere, Sunset & Warmth, Nature & Earth, High Performance
   - Each theme shows descriptive name
   - Themes organized for easy discovery (grouped by mood/use case)

3. Remove broken references:
   - Remove ebook-content class (tied to broken CSS columns approach)
   - Clean up old reader-specific CSS class references

Reader Template Structure:
- Chrome (top/bottom bars): Back button, title, settings gear
- Bottom bar: Progress display, TOC/bookmarks/notes buttons, panel editor (comics)
- Settings panel: Chrome behavior, progress mode, reading themes, typography (fonts, spacing)
- TOC panel: Table of contents navigation
- Navigator panel: Page thumbnail with draggable viewport
- Bookmarks panel: User bookmarks with add button
- Dictionary popup: Word definition popup

Template Generator:
- Regenerated reader_templ.go via go generate
- Syncs template changes with Go backend
2026-04-12 12:09:56 -04:00
john-okeefe aaa2dff8e2 feat: Add 18 reading themes for ebook reader
Add comprehensive reading theme system with 18 themes organized into 5 categories.
All themes include light and dark mode variants, optimized for readability and
eye comfort during long reading sessions.

Theme Categories:
1. Classic Reading (6 themes)
   - Light, Paper, Sepia, Parchment, Warm, Candlelight
   - Time-tested, comfortable for general reading

2. Sky & Atmosphere (4 themes)
   - Azure, Sky, Arctic, Frost
   - Open, airy, contemplative feel with blue tones

3. Sunset & Warmth (3 themes)
   - Dusk, Sunset, Twilight
   - Warm, energizing colors for evening reading

4. Nature & Earth (3 themes)
   - Forest, Moss, Slate
   - Grounded, natural, calming greens and grays

5. High Performance (2 themes)
   - OLED, Solarized
   - Optimized for specific use cases (battery saving, precision design)

Theme Features:
- All themes pass WCAG AAA contrast standards (7:1 ratio)
- Each theme has light and dark mode variants
- CSS custom properties for dynamic theme switching
- Optimized color temperatures for different lighting conditions
- Inspired by best practices from e-readers (Kindle, Kobo) and community projects (Grimmory)

Design Principles:
- Readability first: Avoid pure black on pure white (causes eye strain)
- Color temperature: Warm tones for evening, cool tones for daytime focus
- Typography support: Works seamlessly with 9 bundled libre fonts
- Progressive enhancement: Themes work without JavaScript

File: web/static/foliate-themes.css (301 lines)
- CSS custom properties for each theme variant
- Typography base styles (font-family, font-size, line-height, margins)
- Link, selection, and image handling styles
- Orphan/widow prevention for better text flow
2026-04-12 12:09:51 -04:00
john-okeefe 649b8b79fc build: Update Vite config for foliate-js integration
Update Vite configuration to support foliate-js library integration:

1. Add import alias for foliate-js:
   - Maps 'foliate-js' imports to web/vendor/foliate-js submodule
   - Allows clean imports: import { View } from 'foliate-js/view.js'

2. Update build target to ESNext:
   - Change from 'es2020' to 'esnext' to support top-level await
   - Required by foliate-js pdf.js which uses top-level await
   - ES2022+ support is excellent in all modern browsers (Chrome 112+, Firefox 115+, Safari 16.4+)

These changes enable Vite to bundle foliate-js into reader.js without
requiring a separate build step for the library.
2026-04-12 12:09:44 -04:00
john-okeefe 231a1c64e3 refactor: Remove broken reader implementation for foliate-js migration
Remove 60+ files from the old reader implementation that relied on
CSS columns pagination, which was fundamentally broken. This includes:
- Comic/Manga format handlers (panel detection, reading direction)
- PDF rendering, bookmarks, annotations, outlines
- Reflowable content pagination (EPUB, FB2, TXT, HTML parsers)
- UI components (gestures, keyboard shortcuts, panel dock system)
- Core navigation and state management

The old implementation used CSS columns for EPUB pagination, but this
approach is fundamentally incompatible with horizontal book layouts
because CSS columns fill vertically first, then wrap horizontally.
This causes only 1 column to be created instead of the expected 92+.

This cleanup prepares the codebase for foliate-js integration, which
uses JavaScript-driven pagination with CFI-based positioning that
actually works for book reading.

Files removed:
- formats/: comic/, manga/, pdf/, reflowable/ (65 files)
- parsers/: EPUB, FB2, TXT, HTML (4 files)
- ui/: gestures, keyboard shortcuts, panel dock, progress tracker (8 files)
- core/: parser-manager, reader-navigation, reader-services, reader-state (4 files)
- reader-shell.ts: Main reader orchestrator (565 lines)

Total: 9,361 lines removed

Reader functionality will be restored via foliate-js integration.
2026-04-12 12:09:39 -04:00
john-okeefe 87cb11cef2 Update foliate-js: Fix Vite build 2026-04-12 12:06:20 -04:00
john-okeefe 562de71a2b Add foliate-js submodule with import alias 2026-04-12 10:54:25 -04:00
john-okeefe ef15fb9ab4 Remove foliate-js submodule 2026-04-12 10:47:59 -04:00