Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
488a333
update rfd checks to show task number
darwin67 Aug 28, 2026
f80c9b4
perf(epub): paginate the current chapter first
darwin67 Aug 28, 2026
917742d
test(epub): cover staged pagination
darwin67 Aug 28, 2026
cf2e388
perf(dev): optimize debug builds
darwin67 Aug 28, 2026
fb23400
fix(library): complete activity progress
darwin67 Aug 28, 2026
39c73f8
perf(library): load bounded pages immediately
darwin67 Aug 28, 2026
3525143
perf(library): cache decoded cover thumbnails
darwin67 Aug 28, 2026
b4be125
perf(reader): open documents off the UI thread
darwin67 Aug 28, 2026
24da91b
perf(search): debounce interactive queries
darwin67 Aug 28, 2026
232a61a
perf(bookmarks): run mutations asynchronously
darwin67 Aug 28, 2026
124ccd8
perf(reader): coalesce library progress writes
darwin67 Aug 28, 2026
2daf44e
test(perf): enforce EPUB latency budgets
darwin67 Aug 28, 2026
3425c47
security(cbz): bound archive and image resources
darwin67 Aug 28, 2026
6f915dd
perf(epub): inspect metadata without building reader state
darwin67 Aug 28, 2026
c19798f
perf(import): reuse reviewed file fingerprints
darwin67 Aug 28, 2026
de7cc19
perf(startup): load preferences in one query
darwin67 Aug 28, 2026
4a728ef
perf(startup): defer legacy fingerprint backfill
darwin67 Aug 28, 2026
b9b51e4
perf(library): index common sort orders
darwin67 Aug 28, 2026
91a4b6a
perf(epub): decode nearby images asynchronously
darwin67 Aug 28, 2026
126d03c
perf(library): paint metadata before cover decoding
darwin67 Aug 28, 2026
97c69eb
perf(cbz): resolve page dimensions off the UI thread
darwin67 Aug 28, 2026
430d1cd
perf(reader): index continuous measurement targets
darwin67 Aug 28, 2026
a00d6eb
perf(reader): reuse continuous measurement indexes
darwin67 Aug 28, 2026
eb7743a
perf(epub): bound image decode batches
darwin67 Aug 28, 2026
c934ddc
fix(reader): delay neutral document loading notice
darwin67 Aug 28, 2026
765aaa4
test(perf): skip unsupported sample spread transition
darwin67 Aug 28, 2026
f8c89e7
feat(reader): center book loading preview
darwin67 Aug 28, 2026
9cd8c6f
fix(reader): avoid stale content before loading preview
darwin67 Aug 28, 2026
e3e6330
fix(reader): retain loading preview cover
darwin67 Aug 28, 2026
e2524a6
fix(epub): render SVG-wrapped cover images
darwin67 Aug 28, 2026
af312fc
fix(reader): pin EPUB page numbers to footer
darwin67 Aug 28, 2026
ee1696c
perf(core): render PDF covers at thumbnail size
darwin67 Aug 31, 2026
1acb8d8
perf(epub): make caption offset tracking linear
darwin67 Aug 31, 2026
f987292
perf(app): reuse EPUB table placements
darwin67 Aug 31, 2026
7379757
fix(ci): compile table helpers only in tests
darwin67 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
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
- "packaging/**"
- "rfd/**"
- "scripts/**"
- "benchmarks/**"
- "Cargo.toml"
- "Cargo.lock"
- "Makefile"
Expand All @@ -23,6 +24,7 @@ on:
- "packaging/**"
- "rfd/**"
- "scripts/**"
- "benchmarks/**"
- "Cargo.toml"
- "Cargo.lock"
- "Makefile"
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ anyhow = "1"
thiserror = "2"
bytes = "1"
percent-encoding = "2"

[profile.dev]
opt-level = 1
15 changes: 8 additions & 7 deletions benchmarks/epub-page-turn/2026-08-17/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ stabilization, discards five operation warmups, then records 50 samples.
The generated books contain 16 chapters so one run can select stable
within-chapter and chapter-boundary pairs. Generated archives are byte-stable.
The runner fails if a run reports an error, never produces pages, omits one of
the 16 summaries, or records a different sample count.
the 15 summaries, records a different sample count, or exceeds an operation's
p50 or p95 budget below.

## Initial budgets

Expand All @@ -57,7 +58,6 @@ All values are milliseconds.
|---|---|---|---:|---:|
| `sample.epub` | Single | Chapter transition | 0.581 | 1.618 |
| `sample.epub` | Single | Relayout | 0.432 | 0.662 |
| `sample.epub` | Spread | Chapter transition | 0.684 | 1.699 |
| `sample.epub` | Spread | Relayout | 0.366 | 0.598 |
| Generated large text | Single | Warm page turn | 1.603 | 2.906 |
| Generated large text | Single | Chapter transition | 1.370 | 3.465 |
Expand All @@ -72,8 +72,9 @@ All values are milliseconds.
| Generated large image | Spread | Chapter transition | 0.846 | 2.204 |
| Generated large image | Spread | Relayout | 1.223 | 2.010 |

The small checked-in fixture has no within-chapter warm pair, so that cell is
intentionally omitted. Large-text relayout is the dominant native cost but
remains within the initial budget. These numbers are a renderer-comparison
baseline, not a universal guarantee. Repeat the same protocol on released
platforms and under representative power and display conditions.
The small checked-in fixture has no within-chapter warm pair, and its two
chapters can share one spread, so those cells are intentionally omitted.
Large-text relayout is the dominant native cost but remains within the initial
budget. These numbers are a renderer-comparison baseline, not a universal
guarantee. Repeat the same protocol on released platforms and under
representative power and display conditions.
6 changes: 5 additions & 1 deletion benchmarks/epub-page-turn/2026-08-17/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ run() {
# The checked-in sample is intentionally small, so it covers chapter transitions
# and relayout. Generated fixtures add stable warm-turn pairs and text/image load.
for width in 700 1000; do
run "$root/crates/shosai-core/tests/fixtures/sample.epub" chapter "$width"
# The sample's two chapters can share one spread, so only single-page mode has
# a stable transition between page-turn destinations.
if [[ "$width" == 700 ]]; then
run "$root/crates/shosai-core/tests/fixtures/sample.epub" chapter "$width"
fi
run "$root/crates/shosai-core/tests/fixtures/sample.epub" relayout "$width"
for fixture in "$fixtures/large-text.epub" "$fixtures/large-image.epub"; do
run "$fixture" warm "$width"
Expand Down
20 changes: 20 additions & 0 deletions benchmarks/epub-page-turn/2026-08-17/tests/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,26 @@ def test_validator_rejects_reported_errors(self):
with self.assertRaisesRegex(ValueError, "reported an error"):
validator.validate(self.complete_log() + "\nperf-error fixture=sample.epub", 50)

def test_validator_rejects_a_latency_budget_regression(self):
content = self.complete_log().replace(
"operation=warm-page-turn fixture=large-image.epub samples=50 p50_ms=1 p95_ms=2",
"operation=warm-page-turn fixture=large-image.epub samples=50 p50_ms=1 p95_ms=17",
1,
)
with self.assertRaisesRegex(
ValueError, "p95_ms=17ms budget=16.7ms"
):
validator.validate(content, 50)

def test_validator_rejects_missing_or_non_finite_latency(self):
missing = self.complete_log().replace(" p95_ms=2", "", 1)
with self.assertRaisesRegex(ValueError, "invalid p95_ms"):
validator.validate(missing, 50)

non_finite = self.complete_log().replace("p50_ms=1", "p50_ms=nan", 1)
with self.assertRaisesRegex(ValueError, "invalid p50_ms"):
validator.validate(non_finite, 50)


if __name__ == "__main__":
unittest.main()
24 changes: 23 additions & 1 deletion benchmarks/epub-page-turn/2026-08-17/validate-results.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"""Validate that the 2026-08-17 EPUB performance matrix completed."""

import argparse
import math
from pathlib import Path
import shlex

Expand All @@ -13,6 +14,11 @@
"chapter": "chapter-transition",
"relayout": "relayout",
}
OPERATION_BUDGETS_MS = {
"warm-page-turn": {"p50_ms": 8.0, "p95_ms": 16.7},
"chapter-transition": {"p50_ms": 16.7, "p95_ms": 33.3},
"relayout": {"p50_ms": 50.0, "p95_ms": 100.0},
}


def fields(line: str) -> dict[str, str]:
Expand All @@ -22,7 +28,8 @@ def fields(line: str) -> dict[str, str]:
def expected_runs() -> set[tuple[str, str, str]]:
expected = set()
for width in WIDTHS:
expected.add(("sample.epub", "chapter", width))
if width == "700":
expected.add(("sample.epub", "chapter", width))
expected.add(("sample.epub", "relayout", width))
for fixture in FIXTURES:
for action in ACTION_OPERATION:
Expand Down Expand Up @@ -77,6 +84,21 @@ def validate(content: str, requested_samples: int) -> None:
raise ValueError(f"summary does not match run {key}: {summary}")
if summary.get("samples") != expected_samples:
raise ValueError(f"summary sample count does not match for {key}: {summary}")
budgets = OPERATION_BUDGETS_MS[expected_operation]
for metric, budget in budgets.items():
try:
value = float(summary.get(metric, ""))
except ValueError as error:
raise ValueError(
f"summary has invalid {metric} for {key}: {summary.get(metric)!r}"
) from error
if not math.isfinite(value) or value < 0:
raise ValueError(f"summary has invalid {metric} for {key}: {value}")
if value > budget:
raise ValueError(
f"performance budget exceeded for {key}: "
f"{metric}={value:g}ms budget={budget:g}ms"
)

expected = expected_runs()
if actual != expected:
Expand Down
1 change: 1 addition & 0 deletions crates/shosai-app/locales/en-US/main.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ add-first-books = + Add your first books
continue-reading = Continue reading
search-results = Search results
loading-more = Loading more…
opening-document = Opening document…
unknown-author = Unknown author
not-started = Not started
percent = { $percentage }%
Expand Down
1 change: 1 addition & 0 deletions crates/shosai-app/locales/ja/main.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ add-first-books = + 最初の本を追加
continue-reading = 読書を続ける
search-results = 検索結果
loading-more = さらに読み込み中…
opening-document = ドキュメントを開いています…
unknown-author = 著者不明
not-started = 未読
percent = { $percentage }%
Expand Down
Loading
Loading