Skip to content
Merged

10.3 #593

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
f76ad67
Add webui search
luisangelsm Aug 9, 2026
e475661
Update webui to use the same sorting used in the other apps
luisangelsm Aug 9, 2026
1c51839
Unify folders and comics in the grid view
luisangelsm Aug 13, 2026
2a418e3
Fix drag&drop to reorder comics in lists
luisangelsm Aug 13, 2026
e8e8499
Fix back/forward mouse buttons propagation
luisangelsm Aug 13, 2026
5568a55
Add state restoration when navigating back and forth through history
luisangelsm Aug 13, 2026
5022e35
Preserve scroll when editing comics and switching view modes
luisangelsm Aug 14, 2026
0874d3e
Fix info panel in the grid view not getting updates when the select c…
luisangelsm Aug 14, 2026
67ebff5
Update CHANGELOG
luisangelsm Aug 14, 2026
52ff3d1
Update the 10.3 what's new message
luisangelsm Aug 14, 2026
722b0fe
Fix compilation error on linux/macos
luisangelsm Aug 14, 2026
5406871
Merge pull request #587 from YACReader/better-gridview-and-navigation
luisangelsm Aug 14, 2026
51d3e93
Restore scroll position on history navigation in the webui
luisangelsm Aug 14, 2026
6d8f083
Fix rating context menu in the grid view
luisangelsm Aug 14, 2026
cfec262
Missing changes related to reset rating
luisangelsm Aug 14, 2026
492ba3e
Fix check tick recoloring
luisangelsm Aug 15, 2026
eb96e6c
Build optimizations
luisangelsm Aug 19, 2026
ddf3ef3
Skip non-comic epubs completely
luisangelsm Aug 19, 2026
4189aca
Update GridComicsView.qml
Luke-239 Aug 16, 2026
034c8e9
Merge pull request #589 from Luke-239/wayland-scroll-fix
luisangelsm Aug 19, 2026
02cab88
Fix scroll in scrollables inside the grid view
luisangelsm Aug 20, 2026
3e64c60
Add per-library continue reading list to the webui
luisangelsm Aug 21, 2026
9c7614a
Add support for renaming folders inside YACReaderLibrary
luisangelsm Aug 22, 2026
a6b7442
Disable test for now
luisangelsm Aug 22, 2026
935f0be
First pass on file organization
Jun 15, 2026
7d6f7ff
Organize files relative to the library root
Jul 20, 2026
9177876
Add preview dialog and padded enumeration
Jul 27, 2026
e95e03e
Organize files relative to the library root
Jul 20, 2026
1402d9a
Add a feature flag to control the organize features
luisangelsm Aug 22, 2026
70177d3
Merge pull request #567 from Andrey-Raspopov/organize_files
luisangelsm Aug 22, 2026
310116b
Extract the files organization coordination logic to it's own file
luisangelsm Aug 22, 2026
2da3db6
Extract comic file operations from LibraryWindow
luisangelsm Aug 22, 2026
6b46445
Extract database maintenance and backups to its own file
luisangelsm Aug 22, 2026
1b64724
Extract db repair coordination from LibraryWindow
luisangelsm Aug 22, 2026
8d56e8a
Extract library management
luisangelsm Aug 22, 2026
7e7f0b4
Extra folder management logic
luisangelsm Aug 22, 2026
8c8c24d
Move folder cover management to the folder manager
luisangelsm Aug 22, 2026
23affeb
Unify comic operations in ComicManagementCoordinator
luisangelsm Aug 22, 2026
3618169
Remove folder management wrapping methods from library window
luisangelsm Aug 22, 2026
3c12523
Move more methods to comic management coordinator
luisangelsm Aug 22, 2026
aa6deb2
Move more folder management logic to its coordinator
luisangelsm Aug 22, 2026
df30065
Move the rest of the organize* methods out of LibraryWindow
luisangelsm Aug 22, 2026
7f9b00f
Extract menus creation to its own class
luisangelsm Aug 22, 2026
b6f29aa
Move more logic out of LibraryWindow
luisangelsm Aug 22, 2026
7b4c78b
Extract search coordination logic
luisangelsm Aug 22, 2026
6beb2be
Move more comics management logic out of library window
luisangelsm Aug 22, 2026
b10d395
Extract lists coordination
luisangelsm Aug 22, 2026
e70c336
Put more library handling logic into LibraryManagementCoordinator
luisangelsm Aug 22, 2026
6f5f706
Move more folder operations to its coordinator
luisangelsm Aug 22, 2026
a1484f2
Make method private
luisangelsm Aug 22, 2026
fcf2791
Remove unused code
luisangelsm Aug 22, 2026
9420efc
Fix root folder updates
luisangelsm Aug 22, 2026
ab041df
Preserve folder selection semantics
luisangelsm Aug 22, 2026
f26640d
Initialize comic destination folder ID
luisangelsm Aug 22, 2026
f2cb1e5
Merge pull request #591 from YACReader/library_window_overhaul
luisangelsm Aug 22, 2026
2daffa0
Fix renaming library
luisangelsm Aug 22, 2026
996967c
Fix drag icon not being themed
luisangelsm Aug 23, 2026
bb01b76
Improve usability and security of the organization feature
luisangelsm Aug 23, 2026
892f715
Update translations
luisangelsm Aug 23, 2026
e1826f1
Update CHANGELOG
luisangelsm Aug 23, 2026
9afe632
Format
luisangelsm Aug 23, 2026
418e88b
Merge pull request #592 from YACReader/update_organize_content
luisangelsm Aug 23, 2026
f6252df
Improve organize/rename presets and saving
luisangelsm Aug 24, 2026
e36492e
Update AGENTS and CLAUDE files
luisangelsm Aug 24, 2026
1332c56
Fix folder navigation from the grid view when the folder is already s…
luisangelsm Aug 24, 2026
e24ed4d
Fix {year} and {month} rename/organization templates
luisangelsm Aug 24, 2026
cf9bdc6
Add rename/organize menu entries to the folder grid context menu
luisangelsm Aug 25, 2026
f46ae13
Keep track of the current loaded folder so refreshes don't cause cont…
luisangelsm Aug 25, 2026
b9b061a
Add rename/organize entries to the comic context menu
luisangelsm Aug 25, 2026
b6b448c
Improve final report in the organize/rename dialog
luisangelsm Aug 26, 2026
2b74feb
Show information about selected items in the side info view
luisangelsm Aug 27, 2026
be71c98
Disable selection when clicking outside items in the grid view
luisangelsm Aug 27, 2026
aa3ff2d
Add support for dropping image to set custom covers on folders and co…
luisangelsm Aug 28, 2026
33fa9da
Typos/grammar
luisangelsm Aug 29, 2026
8946c6d
Fix compiler warnings
luisangelsm Aug 29, 2026
b9d3b84
Fix what's new dialog drop shadow on macos
luisangelsm Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ jobs:

- name: Build
env:
MACOSX_DEPLOYMENT_TARGET: "11"
MACOSX_DEPLOYMENT_TARGET: "12"
run: |
VERSION="$(tr -d '\r\n' < VERSION)"
SKIP_CODESIGN="${{ env.IS_FORK }}"
Expand Down Expand Up @@ -228,7 +228,7 @@ jobs:
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
set PATH=C:\Qt\6.9.3\msvc2022_64\bin;%PATH%
set PATH=%GITHUB_WORKSPACE%\dependencies\pdfium\win\x64;C:\Qt\6.9.3\msvc2022_64\bin;%PATH%
ctest --test-dir build --output-on-failure

- name: Upload executables for signing
Expand Down Expand Up @@ -683,4 +683,3 @@ jobs:
files: staging/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ Out-of-source builds are required. In-source builds will be rejected by CMake.

```bash
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel
cmake --build build --parallel 16
```

Build options:
- `DECOMPRESSION_BACKEND`: `unarr` | `7zip` | `libarchive` (default: 7zip on Windows/macOS, libarchive on Linux)
- `PDF_BACKEND`: `pdfium` | `poppler` | `pdfkit` | `no_pdf` (default: pdfium on Windows, pdfkit on macOS, poppler on Linux)
- `BUILD_SERVER_STANDALONE=ON`: builds only `YACReaderLibraryServer` (headless), requires only Qt 6.4+
- `BUILD_TESTS=ON` (default): enables the test suite
- Always use the highest parallelism allowed by the CPU.

## Translations

Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@

Version counting is based on semantic versioning (Major.Feature.Patch)

## 10.3.0

### YACReaderLibrary
* Unify folder and comic browsing in the grid view. The side information panel can show information about folders, lists and selected items. There are settings to decide if folders should be displayed alongside comics and if folders and comics should be kept visually separated.
* Fix drag & drop for sorting comics in lists.
* Add state restoration when going back and forth through the navigation history.
* Add scroll and current item restoration when switching between content views.
* Keep current scroll position when editing comics.
* Fix info panel in the grid view not getting updates when the selected comic metadata changes.
* Fix rating context menu in the grid view.
* Add reset rating to the comic context menu.
* Add support for renaming folders inside the app. This preserves the state of the folder and its subfolders (completed, read, dates, etc.) rather than creating a new folder like updating the library does if you rename the folder directly on the file system.
* Add organizing functionalities for renaming files and creating folder structures based on metadata. Highly experimental.
* Support for dropping images onto folders and comics to change their covers.

### WebUI
* Add per-library search.
* Use the same sorting as the rest of the apps.
* Restore the previous scroll position when navigating back through library folders.
* Add per-library continue reading list.

## 10.2.0

### YACReader
Expand Down
106 changes: 105 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1 +1,105 @@
See AGENTS.md for project context.
# AGENTS GUIDANCE

This file provides guidance to AI agents when working with code in this repository.

## Build

Out-of-source builds are required. In-source builds will be rejected by CMake.

```bash
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel 16
```

Build options:
- `DECOMPRESSION_BACKEND`: `unarr` | `7zip` | `libarchive` (default: 7zip on Windows/macOS, libarchive on Linux)
- `PDF_BACKEND`: `pdfium` | `poppler` | `pdfkit` | `no_pdf` (default: pdfium on Windows, pdfkit on macOS, poppler on Linux)
- `BUILD_SERVER_STANDALONE=ON`: builds only `YACReaderLibraryServer` (headless), requires only Qt 6.4+
- `BUILD_TESTS=ON` (default): enables the test suite
- Always use the highest parallelism allowed by the CPU.

## Translations

Use CMake translation targets (Qt LinguistTools integration), not ad-hoc `lupdate` calls.

Update `.ts` files from source code (C++ + QML):

```bash
cmake --build build --target update_translations
```

On multi-config generators (Visual Studio / Ninja Multi-Config), include config:

```bash
cmake --build build --config Release --target update_translations
```

Build `.qm` files:

```bash
cmake --build build --target release_translations
```

Multi-config variant:

```bash
cmake --build build --config Release --target release_translations
```

Important:
- Do not run `lupdate` only on a hand-picked subset of QML files, because that can mark unrelated translations as obsolete.
- In `YACReaderLibrary`, `qt_add_translations(...)` is configured to scan full target sources and include the QML files directly.
- `update_translations` updates both locale TS files and `*_source.ts` template files for all apps.
- `*_source.ts` files are translator base templates and must not be treated as shipped locales.

## Tests

```bash
ctest --test-dir build --output-on-failure
```

Tests live in `tests/` and are built as Qt Test executables (`compressed_archive_test`, `concurrent_queue_test`).

## Code Formatting

CI enforces `clang-format`. Run it before committing. There are multiple `.clang-format` files — subdirectories for third-party code have their own to opt out of reformatting. Always run recursively from the repo root via the provided scripts:

- Linux: `scripts/clang-format-linux.sh`
- macOS: `scripts/clang-format-macos.sh`
- Windows: `scripts\clang-format-windows.cmd` (or `.ps1`)

Style is WebKit-based with custom brace wrapping (braces on same line for control flow, new line after functions/classes), no column limit, and `SortIncludes: false`.

## Architecture

The repo builds three applications that share a common set of static libraries:

| App | Description |
|-----|-------------|
| `YACReader` | Comic viewer |
| `YACReaderLibrary` | Comic library manager (GUI) |
| `YACReaderLibraryServer` | Headless HTTP server |

### Static library dependency layers (bottom to top)

1. **`yr_global`** — version/global constants, no GUI, used by everything
2. **`naturalsort`, `concurrent_queue`, `worker`** — utilities
3. **`common_all`** — shared non-GUI: `ComicDB`, `Folder`, `Bookmarks`, HTTP helpers, cover utils
4. **`comic_backend`** — comic file abstraction + PDF backend (source varies by `PDF_BACKEND`)
5. **`cbx_backend`** — compressed archive abstraction (in `compressed_archive/`)
6. **`db_helper`** — SQLite database layer: schema management, reading lists, query parser
7. **`library_common`** — library scanning, bundle creation, XML metadata parsing; shared between `YACReaderLibrary` and `YACReaderLibraryServer`
8. **`common_gui`** — GUI widgets, themes infrastructure, version check (not built in `BUILD_SERVER_STANDALONE`)
9. **`rhi_flow_reader` / `rhi_flow_library`** — RHI-based 3D coverflow widget, compiled twice with different defines (`YACREADER` vs `YACREADER_LIBRARY`); shaders compiled via `qt_add_shaders()`

### Key design notes

- **Theme system**: `theme_manager.h/cpp` is NOT part of `common_gui` because it depends on app-specific `Theme` structs. Each app (`YACReader/themes/`, `YACReaderLibrary/themes/`) defines its own `theme.h` and includes `theme_manager` directly.
- **Compile-time app identity**: `YACREADER` or `YACREADER_LIBRARY` defines distinguish shared source compiled into different apps.
- **PDF backend**: resolved at configure time into an `INTERFACE` target `pdf_backend_iface` (see `cmake/PdfBackend.cmake`); `comic_backend` and `common_all` link against this interface.
- **Third-party code**: `third_party/` contains QsLog, KDToolBox, QtWebApp, QrCode — each has its own `.clang-format` to prevent reformatting.
- **Runtime dependencies**: Qt binaries must be in `PATH`; third-party DLLs/dylibs must be next to the executable. Check an existing YACReader installation for the required files.

### PRs

Target branch is always `develop`.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ if(BUILD_SERVER_STANDALONE)
Sql
)
else()
find_package(Qt6 6.7 REQUIRED COMPONENTS
find_package(Qt6 6.9 REQUIRED COMPONENTS
Core
Core5Compat
Gui
Expand Down Expand Up @@ -173,6 +173,7 @@ add_subdirectory(YACReaderLibrary/server)

if(NOT BUILD_SERVER_STANDALONE)
add_subdirectory(YACReaderLibrary/comic_vine)
add_subdirectory(YACReaderLibrary/organize_files)
endif()

# Always add YACReaderLibrary: defines library_common and db_helper (shared with server)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Contributions are not restricted to coding; you can help the project by bringing
If you can't do it yourself please don't open PRs based on ideas developed using AI. If you are a software engineer it's ok to use AI as long as you know what you are doing, understanding and reviewing agent generated code before opening a PR is a must. Code that just works won't be enough to ensure I'll accept your contribution.

#### Dev Setup
YACReader is developed in *C++/Qt* and built with *CMake*. You need a *C++20* compiler and *Qt 6.7+*. In *Windows* I use *Visual Studio 2022* and in *macOS* I use Xcode, but I do all the coding using *QtCreator*.
YACReader is developed in *C++/Qt* and built with *CMake*. You need a *C++20* compiler and *Qt 6.9+*. In *Windows* I use *Visual Studio 2022* and in *macOS* I use Xcode, but I do all the coding using *QtCreator*.

The repo includes binaries for the dependencies needed for *Windows* (MSVC compiler) and *macOS* (clang). The *7zip* decompression backend source is downloaded automatically by CMake during configuration.

Expand Down Expand Up @@ -86,4 +86,4 @@ YACReader is free but it needs money to keep being alive, so please, if you like
If you are interested in YACReader, please contact me so we can discuss your next steps.

## Sponsors
Free code signing on Windows provided by [SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/)
Free code signing on Windows provided by [SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/)
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
10.2.0
10.3.0
37 changes: 20 additions & 17 deletions YACReader/render.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include <algorithm>
#include <cmath>
#include <utility>

template<class T>
inline const T &kClamp(const T &x, const T &low, const T &high)
Expand Down Expand Up @@ -382,14 +383,14 @@ Render::Render()

Render::~Render()
{
for (auto *pr : pageRenders) {
for (auto *pr : std::as_const(pageRenders)) {
if (pr != nullptr && pr->wait()) {
delete pr;
}
}

// TODO move to share_ptr
for (auto *filter : filters) {
for (auto *filter : std::as_const(filters)) {
delete filter;
}

Expand Down Expand Up @@ -693,23 +694,24 @@ void Render::load(const QString &path, const ComicDB &comicDB)
{
// TODO prepare filters
for (int i = 0; i < filters.count(); i++) {
if (typeid(*filters[i]) == typeid(BrightnessFilter)) {
auto *filter = filters.at(i);
if (typeid(*filter) == typeid(BrightnessFilter)) {
if (comicDB.info.brightness == -1)
filters[i]->setLevel(0);
filter->setLevel(0);
else
filters[i]->setLevel(comicDB.info.brightness);
filter->setLevel(comicDB.info.brightness);
}
if (typeid(*filters[i]) == typeid(ContrastFilter)) {
if (typeid(*filter) == typeid(ContrastFilter)) {
if (comicDB.info.contrast == -1)
filters[i]->setLevel(100);
filter->setLevel(100);
else
filters[i]->setLevel(comicDB.info.contrast);
filter->setLevel(comicDB.info.contrast);
}
if (typeid(*filters[i]) == typeid(GammaFilter)) {
if (typeid(*filter) == typeid(GammaFilter)) {
if (comicDB.info.gamma == -1)
filters[i]->setLevel(100);
filter->setLevel(100);
else
filters[i]->setLevel(comicDB.info.gamma);
filter->setLevel(comicDB.info.gamma);
}
}
createComic(path);
Expand Down Expand Up @@ -1128,12 +1130,13 @@ void Render::reload()
void Render::updateFilters(int brightness, int contrast, int gamma)
{
for (int i = 0; i < filters.count(); i++) {
if (typeid(*filters[i]) == typeid(BrightnessFilter))
filters[i]->setLevel(brightness);
if (typeid(*filters[i]) == typeid(ContrastFilter))
filters[i]->setLevel(contrast);
if (typeid(*filters[i]) == typeid(GammaFilter))
filters[i]->setLevel(gamma);
auto *filter = filters.at(i);
if (typeid(*filter) == typeid(BrightnessFilter))
filter->setLevel(brightness);
if (typeid(*filter) == typeid(ContrastFilter))
filter->setLevel(contrast);
if (typeid(*filter) == typeid(GammaFilter))
filter->setLevel(gamma);
}

reload();
Expand Down
36 changes: 16 additions & 20 deletions YACReader/yacreader_de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,27 +188,23 @@
<context>
<name>FileComic</name>
<message>
<location filename="../common/comic.cpp" line="633"/>
<location filename="../common/comic.cpp" line="610"/>
<location filename="../common/comic.cpp" line="626"/>
<source>Format not supported</source>
<translation>Format wird nicht unterstützt</translation>
</message>
<message>
<location filename="../common/comic.cpp" line="627"/>
<location filename="../common/comic.cpp" line="604"/>
<source>7z not found</source>
<translation>7z nicht gefunden</translation>
</message>
<message>
<location filename="../common/comic.cpp" line="520"/>
<location filename="../common/comic.cpp" line="497"/>
<source>Unknown error opening the file</source>
<translation>Unbekannter Fehler beim Öffnen des Files</translation>
</message>
<message>
<location filename="../common/comic.cpp" line="647"/>
<source>Unsupported EPUB: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../common/comic.cpp" line="513"/>
<location filename="../common/comic.cpp" line="490"/>
<source>CRC error on page (%1): some of the pages will not be displayed correctly</source>
<translation>CRC Error auf Seite (%1): Einige Seiten werden nicht korrekt dargestellt</translation>
</message>
Expand Down Expand Up @@ -371,7 +367,7 @@
<translation>Löschen</translation>
</message>
<message>
<location filename="options_dialog.cpp" line="329"/>
<location filename="options_dialog.cpp" line="327"/>
<source>Comics directory</source>
<translation>Comics-Verzeichnis</translation>
</message>
Expand Down Expand Up @@ -774,48 +770,48 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.</transla
<context>
<name>Viewer</name>
<message>
<location filename="viewer.cpp" line="1704"/>
<location filename="viewer.cpp" line="1773"/>
<source>Page not available!</source>
<translation>Seite nicht verfügbar!</translation>
</message>
<message>
<location filename="viewer.cpp" line="95"/>
<location filename="viewer.cpp" line="1676"/>
<location filename="viewer.cpp" line="122"/>
<location filename="viewer.cpp" line="1745"/>
<source>Press &apos;O&apos; to open comic.</source>
<translation>&apos;O&apos; drücken, um Comic zu öffnen.</translation>
</message>
<message>
<location filename="viewer.cpp" line="326"/>
<location filename="viewer.cpp" line="353"/>
<source>Error opening comic</source>
<translation>Fehler beim Öffnen des Comics</translation>
</message>
<message>
<location filename="viewer.cpp" line="1927"/>
<location filename="viewer.cpp" line="2121"/>
<source>Cover!</source>
<translation>Titelseite!</translation>
</message>
<message>
<location filename="viewer.cpp" line="332"/>
<location filename="viewer.cpp" line="359"/>
<source>CRC Error</source>
<translation>CRC Fehler</translation>
</message>
<message>
<location filename="viewer.cpp" line="320"/>
<location filename="viewer.cpp" line="347"/>
<source>Comic not found</source>
<translation>Comic nicht gefunden</translation>
</message>
<message>
<location filename="viewer.cpp" line="320"/>
<location filename="viewer.cpp" line="347"/>
<source>Not found</source>
<translation>Nicht gefunden</translation>
</message>
<message>
<location filename="viewer.cpp" line="1941"/>
<location filename="viewer.cpp" line="2135"/>
<source>Last page!</source>
<translation>Letzte Seite!</translation>
</message>
<message>
<location filename="viewer.cpp" line="1693"/>
<location filename="viewer.cpp" line="1762"/>
<source>Loading...please wait!</source>
<translation>Ladevorgang... Bitte warten!</translation>
</message>
Expand Down
Loading
Loading