The RequestHandler import sat commented out while all four handlers were
annotated with it, so every destructured parameter was an implicit any --
24 of the 30 svelte-check errors. deleteFn returned `{}`, which void is not
assignable to.
no-useless-assignment joined the recommended set and does not understand runes,
reading every `$bindable()` prop default as a dead store; off for Svelte files.
Carries @eslint/js, @eslint/compat, globals and @types/node with it.
SvelteKit 2.53 to 2.70 renamed the remote form enhance callback's `form` to
`element`, which would have thrown on reset in the signup, device, library and
cover forms.
Every push runs ruff, prettier, pytest and svelte-check; a tagged release runs the
blocking half again before it publishes an image. eslint and svelte-check report
without failing, since 87 and 30 findings predate the workflow.
Applies ruff format, ruff check --fix and prettier, so CI can gate on all three.
The surviving unused imports were all re-exports in __init__.py, now covered by a
per-file ignore; the pdf.js viewer and the generated openapi types join the
vendored code that eslint and prettier already skip.
Tagging v* builds both images on Gitea Actions and pushes them to the instance
registry, then smoke-tests the published stack. The frontend read its backend
URL through import.meta.env, which Vite resolves at build time, so an image
could only point at whatever the build host had; it now reads it at runtime.
Reads metadata.db and copies the books into a library — from a zip uploaded on
the library settings page, or from a path with `litestar calibre-import`. The
source is never touched, and re-running only picks up what is new.
Also names the formats mimetypes does not know: a Calibre library is full of
MOBI and AZW3, and a null content type used to fail the book endpoint.
A workbench with the folded records in a rail, the survivor's fields live and
editable beside them, and per-field actions chosen by what kind of field it is.
Fields the records agree on stay out of the way. Reachable from a duplicate
group and from the selection toolbar, which is the only way to merge a pair the
detector never proposed.
Adds the per-library review screen, a muted note in the upload tray for a book
that may already be held, and the remote functions behind them. The tray note
is deliberately weaker than the file-level one, which means something stronger.
Storing the same bytes twice splits reading progress and shelves across two
records that can never converge. The skipped row still names the book that
holds the file; allow_duplicates stays on the API for a wrong verdict.
Full-bleed serif title on a woven ground, drawn in the browser rather than
stored, sized with container queries so one component works from 36px to full
page. Colour is hashed into a list of bookcloth tones instead of the hue wheel.
The grid and search were falling back to default_cover.jpg because a missing
cover made the src "/api/undefined"; they now check for one first.
One request per book instead of one for everything, queued in state above the
dialog so closing it no longer cancels the import. A docked tray reports each
book and offers a retry; it clears itself after a clean run, stays put if
anything failed, and holds while hovered.
Also fixes the dialog overflowing on long filenames — Dialog.Content is a grid
and its body needed min-w-0 to shrink below the content's intrinsic width.
The picker could only choose folders: webkitdirectory switches the OS dialog
into folder mode rather than filtering, so one input cannot offer both. Adds
a drop zone with two browse buttons behind two inputs.
Dropping a folder never worked either — dataTransfer.files does not descend
into directories, so it arrived as one typeless entry and was rejected. Walks
dataTransfer.items instead.
Also collects skipped files into one expandable line rather than a toast each,
which a folder of covers and notes made unusable, and shows the file count and
total size before uploading.
Drops the three tabs for a wide dialog: cover and files in a fixed rail,
all fourteen metadata fields grouped beside them, Save in the footer.
Adds file management, which the Files tab never had — it could only upload,
never list or remove. Files now show size and format with download and
delete, the latter asking whether to remove it from disk too.
Adds a reader section covering the vendored tree, the $foliate alias and the
traps around it, and refreshes the stale stack line, oklch claim and rough
edges. Records why CSP is not enabled in TODO.md.
Nothing imports it since the foliate rewrite. Also note the one-shot purge of
its locations cache in TODO.md, so the temporary code gets removed later.
Key events do not cross document boundaries, so shortcuts bound on the window
never fired while the book had focus and the browser default won instead —
ctrl+B opened bookmarks rather than the chapter sidebar. Replay them on the
window, cancelling the original only if a handler claimed it so ctrl+C still
copies.
Typography was in the stylesheet foliate prepends, which the book overrides;
moved to the appended one. The window keydown handler was never bound.
Also: wider reading area, page on a card, arrows moved onto it, spread gutter,
and the sidebar stays open on chapter clicks.
shadcn-svelte generates the track with `data-horizontal:` / `data-vertical:`
variants, which Tailwind compiles to `[data-horizontal]` — an attribute
nothing sets, since the orientation is carried as data-orientation. The track
got no height, leaving only the thumb on screen. Use the `data-[orientation=…]`
form the rest of ui/ already uses.
Typeface, size, weight, line height, letter spacing, justify and hyphenate,
plus flow, columns, line width, gap and margins — persisted in localStorage
and applied live.
Fixed-layout books hide the typography and column controls: they render
through foliate-fxl, which has no setStyles and ignores the paginator's
attributes, so those sliders would do nothing.
Drops epub.js's 1600-location pre-pass and its localStorage cache: foliate
hands back a CFI and an overall fraction on every relocate, so first open no
longer freezes.
Also fixes resuming (pre-resolves the stored CFI, falling back to the stored
percentage rather than silently resetting to page one), progress saving
(missing Content-Type, and no flush on the way out), and arrow keys inside
the book iframe.
Settings are validated on read, not only on write: several go straight onto
foliate's renderer as custom-element attributes, where a bad value wedges the
layout silently rather than throwing. Unknown keys are stripped and missing
ones filled from the defaults, so settings added later do not invalidate what
a reader already has stored.
The stylesheet is built as foliate's [before, after] pair. The paginator
prepends the first to the section's <head> and appends the second, so the
book's own CSS overrides the first and loses to the second. Typography goes
in the first — a book that styles its own headings still gets to, and font
size lands on <html> without !important so rem/em headings scale rather than
being flattened. Colour goes in the second, because most EPUBs set a body
background and would otherwise leave the page white against a dark UI.
Font choices are reused from the app theme's FONT_STACKS rather than
duplicated, so the reader cannot drift from the rest of the UI.
Rename the ambient declaration to foliate-js.d.ts: as foliate.d.ts beside
foliate.ts, TypeScript takes it for that file's emitted declaration, drops it
from the program, and every $foliate import fails with TS2307.
$foliate is a Vite-only alias, deliberately absent from kit.alias and
tsconfig paths: TypeScript cannot resolve it, so the ambient declaration in
src/lib/reader/foliate.d.ts is the only candidate and svelte-check never
walks the ~11k lines of untyped vendored JS. The generated tsconfig includes
src/**/*.js and checkJs is on, so src/lib/vendor is excluded as well —
`exclude` replaces rather than merges, hence the repeated service-worker
entries.
optimizeDeps.include for construct-style-sheets-polyfill: it sits behind a
dynamic import in fixed-layout.js, so Vite would otherwise discover it
mid-session and force a page reload on the first fixed-layout EPUB.
Verified with a temporary import that the graph resolves: view.js and
paginator.js are emitted as chunks, and the bundled pdf.js is our stub
rather than upstream's bare `import '@pdfjs/pdf.min.mjs'`.
foliate-js has no npm release and no build step; upstream recommends a git
submodule. Copy it instead: this repo has no submodules and already vendors
pdf.js the same way under static/pdfjs, and a submodule would pull in 231
files / 13 MB of which 191 files / 12 MB is a bundled pdf.js build that
Chitai does not use.
Only the import closure reachable from view.js is vendored — 15 files, 584K.
pdf.js is replaced by a stub that throws: view.js reaches it through a
static-string dynamic import inside makeBook, which Rollup resolves at build
time even though Chitai serves PDFs from static/pdfjs/web/viewer.html, and
upstream's version opens with a bare `import '@pdfjs/pdf.min.mjs'` that does
not resolve here.
scripts/vendor-foliate.sh pins the commit and makes the next update a one-line
change. fixed-layout.js needs construct-style-sheets-polyfill, so add it.
prettier-plugin-tailwindcss 0.6.14 is incompatible with prettier 3.8.1 and
threw "getVisitorKeys is not a function" on every .svelte file, so `pnpm
lint` could not run at all. 0.8.1 fixes it.
No reformatting here: the files prettier now reports are mostly vendored
shadcn components, and rewriting those is unrelated to any current work.
The load function read progress.epub_loc and progress.progress, neither of
which BookProgressRead carries, so resuming never worked and every open
started at page one. Read epub_cfi and percentage instead.
Fail loudly when the book cannot be fetched. The previous version caught
everything and returned {status, error} that nothing consumed, so a missing
book produced a reader sitting on its spinner forever. Check response.ok
before parsing too: fetch resolves on a 404, and arrayBuffer() happily
returns the error page, which only surfaced later as an opaque epub parse
error.
Give the reader a working sidebar, a theme that survives the book's own
CSS, and an error state with a retry. The theme is registered rather than
applied as bare overrides because most EPUBs ship their own body colours
and win otherwise, which is why the page stayed white against a dark UI.
The PDF reader pointed an iframe at the vendored pdf.js viewer and had no
way to report a failure: the viewer renders its own errors inside the
frame, so a missing or unreadable file showed an empty grey panel. Probe
the file with a request first and render an error card with a retry when
that fails, matching the chrome the EPUB reader uses.
Add the missing +page.ts for the PDF route so the tab gets the book title
rather than a bare "Reader".
Drop chapter-sidebar.svelte and its import. It rendered a list of chapters
whose buttons had no click handler, and read/+layout@.svelte imported it
without ever rendering it. The reader's own sidebar replaces it.
The reader and login pages use +layout@ breakouts to escape the (root)
shell, so anything mounted in (root)/+layout.svelte never reaches them.
ModeWatcher was mounted there and duplicated in login/+layout@.svelte,
which left the reader's theme toggle inert: it flipped the store but
nothing wrote .dark onto <html>.
Mount both once at the top-level +layout.svelte instead, which every
route inherits regardless of breakouts.