Desktop comic reader for local libraries. Flutter owns the desktop UI; Rust owns filesystem discovery, archives, SQLite metadata, progress, bookmarks, history, backup/import, and the reader image pipeline.
Technical details: docs/technical.md.
- Library source selection + fast top-level listing + explicit full scan
- Folder, ZIP/CBZ, and RAR/CBR support; nested folder pages to depth 3
- JPEG, PNG, WebP, GIF, BMP, and AVIF pages
- Library/history/bookmark tabs; grid/list modes; search, sort, unread/reading filters
- Chapters with progress, page counts, and natural ordering (
06.5after06) - Vertical reader: keyboard, fullscreen, zoom, page gap, stable scroll, auto-save progress, prev/next chapter
- On-demand page bytes with bounded prefetch/cache; on-demand thumbnails (200 MB LRU disk cache)
- Comic detail: selectable title/path, open-folder action, covers
- SQLite settings/progress/bookmarks/history; backup export/import; Optimize Data (purge stale + VACUUM + report)
- Auto-update via GitHub Releases; Linux packaging/AUR; Windows installer
Main app flows are done: library listing/scan, chapters, reader, progress, bookmarks, history, settings, backup/import, thumbnails, and Optimize Data. Reader loads page bytes on demand with 2048px tiling and a stable scrollbar. Smoke tested on Windows and Linux with Linux packaging available. macOS target exists but still needs a native smoke test.
paru -S comicrd-bin
# or: yay -S comicrd-binDownload *-setup.exe from GitHub Releases. Per-user install, no admin needed.
tar -xzf comicrd-2.8.1-linux-x86_64.tar.gz
./comicrd-2.8.1-linux-x86_64/opt/comicrd/ComicRD./scripts/package-arch-local.sh 2.8.1
sudo pacman -U dist/arch/comicrd-bin-2.8.1-1-x86_64.pkg.tar.zst- Flutter desktop SDK (3.47+, Dart ^3.12.1)
- Rust toolchain (
rustc 1.98) flutter_rust_bridge_codegen2.13.0,cargo-expand- Platform desktop build tools
Uses Flutter's default renderer (Impeller since 3.47), no extra config.
Material symbols come from material_ui / cupertino_ui (SDK copies deprecated Nov 2026).
Arch/CachyOS:
sudo pacman -S --needed base-devel clang cmake dav1d gtk3 ninja pkgconfUbuntu:
sudo apt-get install -y build-essential clang cmake libdav1d-dev libgtk-3-dev ninja-build pkg-configmacOS:
brew install dav1d pkg-config
export PKG_CONFIG_PATH="$(brew --prefix dav1d)/lib/pkgconfig:$(brew --prefix)/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"Windows (Visual Studio build tools required):
# Option A — vcpkg
scoop install vcpkg pkg-config
vcpkg install dav1d:x64-windows
setx PKG_CONFIG_PATH "$env:VCPKG_ROOT\installed\x64-windows\lib\pkgconfig"# Option B — meson, or run scripts/setup-dav1d.ps1 (installs to %LOCALAPPDATA%\dav1d)
scoop install meson nasm
git clone --depth 1 --branch 1.5.4 https://code.videolan.org/videolan/dav1d.git C:\Users\<you>\dav1d-build
meson setup build --prefix=C:/Users/<you>/dav1d-install --default-library=static -Denable_tools=false -Denable_tests=false -Denable_docs=false
meson compile -C build
meson install -C build
setx PKG_CONFIG_PATH "C:\Users\<you>\dav1d-install\lib\pkgconfig"After setx, open a new terminal. Alternative (Windows-only, do not commit):
local .cargo/config.toml (already gitignored):
[env]
PKG_CONFIG_PATH = { value = "C:/Users/<you>/dav1d-install/lib/pkgconfig", force = true }Install tooling:
cargo install flutter_rust_bridge_codegen --version 2.13.0
cargo install cargo-expandcargo test
flutter analyze
flutter test
flutter run -d linux
flutter pub get
cargo build -p comicrd_bridge --releaseflutter pub get
flutter run -d linuxCMake calls scripts/build-native-bridge.sh automatically. Hot reload is
Dart-only — restart the app fully for Rust changes.
Manual bridge rebuild (missing bridge at startup / force fresh copy):
./scripts/build-native-bridge.sh --platform linux --configuration Debug --destination app_flutter/build/linux/x64/debug/bundle/lib
./scripts/build-native-bridge.sh --platform linux --configuration Release --destination app_flutter/build/linux/x64/release/bundle/libWindows uses scripts/build-native-bridge.ps1; macOS copies
libcomicrd_bridge.dylib via Xcode.
When crates/comicrd_bridge/src/api.rs changes:
flutter_rust_bridge_codegen generate --config-file flutter_rust_bridge.yamlNo codegen needed for comicrd_core-only changes, but still restart the app.
flutter build linux --release
flutter build windows --release
flutter build macos --release
ISCC.exe /D"AppVersion=2.8.1" app_flutter\windows\installer\comicrd-setup.iss
./scripts/package-linux.sh 2.8.1comicrd_flutter/
├── app_flutter/ # Flutter desktop UI
│ ├── lib/
│ │ ├── api/ # Dart facade over generated bridge APIs
│ │ ├── pages/
│ │ ├── routes/
│ │ ├── state/
│ │ ├── widgets/
│ │ └── bridge_generated.dart
│ └── pubspec.yaml
├── crates/
│ ├── comicrd_core/ # Reusable Rust core
│ └── comicrd_bridge/ # flutter_rust_bridge API crate
├── docs/
├── scripts/
└── flutter_rust_bridge.yaml
No Tauri/React/WebView — Flutter desktop + Rust core/bridge only.
Flutter: routes, Riverpod state, theme, localization, desktop behavior, rendering. Rust: source checks/scanning, chapter discovery, SQLite, progress/bookmarks/history, backup, page/image caching, MIME + dimension probing.
Flutter UI → ComicRdApi facade → FRB bindings → comicrd_bridge → comicrd_core
UI code calls app_flutter/lib/api/comicrd_api.dart, not generated functions directly.
Filesystem is the source of truth (list_library_comics_raw: depth-1 walk, one
top-level folder/archive = one comic, 30s entry cache, sort by name/date).
DB stores scan results and reader state only — unlisted/unscanned comics report
0/0/0 counts. Explicit scan upserts to SQLite; opening a comic discovers
chapters on demand. Chapters are natural-sorted by stem (06.5 after 06).
Settings → Optimize Data: deletes missing comics/chapters + orphaned
progress/bookmarks/favorites/covers, skips unavailable library roots, runs
VACUUM + WAL checkpoint, reports size before/after and freed space.
Thumbnail cache (app_data_dir/thumbnails) trims on write; Optimize Data purges
orphans immediately.
rawLibraryComicsProvider fetches from Rust; filteredLibraryComicsProvider
applies query/view-mode sync; libraryComicsProvider + libraryPaginationProvider
handle pagination. Search is debounced, scroll offsets throttled.
Metadata-first, bytes-on-demand: Rust lists pages, probes width/height, splits tall pages into ≤2048px tiles; Flutter renders flattened tiles with stable extents and an exact-total sliver (no scrollbar jumps); tile bytes load only on build/prefetch. Progress/bookmarks stay page-based.
- Folder: depth 3, ignores dotfiles/
__MACOSX/thumbs.db/desktop.ini - ZIP/CBZ: read entry bytes on demand
- RAR/CBR: extract-once session dir under app-data, served from disk, LRU (max 2), swept on startup
- Natural sort (
2.pngbefore10.png); bounded memory (prefetch ±2 tiles, 2 page sources, 16 raw tiles)
Boundary: crates/comicrd_bridge/src/api.rs. Generated files are committed
(frb_generated.rs, api.dart, frb_generated.dart, frb_generated.io.dart).
Keep the bridge minimal: no duplicate/constant/unused fields.
cargo test
flutter analyze
flutter testRust tests live in crates/comicrd_core/tests/ (per concern); Dart tests in app_flutter/test/.
MIT. See LICENSE.