diff --git a/backend/Cargo.lock b/backend/Cargo.lock
index 332ac16..77e9e38 100644
--- a/backend/Cargo.lock
+++ b/backend/Cargo.lock
@@ -1517,7 +1517,7 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
[[package]]
name = "mangalord"
-version = "0.88.0"
+version = "0.89.0"
dependencies = [
"anyhow",
"argon2",
diff --git a/backend/Cargo.toml b/backend/Cargo.toml
index 88ade25..765d388 100644
--- a/backend/Cargo.toml
+++ b/backend/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "mangalord"
-version = "0.88.0"
+version = "0.89.0"
edition = "2021"
default-run = "mangalord"
diff --git a/frontend/e2e/reader-mode.spec.ts b/frontend/e2e/reader-mode.spec.ts
index b31cd8b..012bc90 100644
--- a/frontend/e2e/reader-mode.spec.ts
+++ b/frontend/e2e/reader-mode.spec.ts
@@ -66,6 +66,15 @@ async function mockReaderApis(page: Page) {
body: JSON.stringify({ error: { code: 'unauthenticated', message: '' } })
})
);
+ // Anonymous reader: read-progress reads/writes are unauthenticated. Without
+ // this the GET/PUT fall through to the (absent) backend and stall the load.
+ await page.route('**/api/v1/me/read-progress/**', (route) =>
+ route.fulfill({
+ status: 401,
+ contentType: 'application/json',
+ body: JSON.stringify({ error: { code: 'unauthenticated', message: '' } })
+ })
+ );
await page.route(`**/api/v1/mangas/${mangaId}`, (route) =>
route.fulfill({
status: 200,
@@ -126,6 +135,7 @@ test.beforeEach(async ({ context }) => {
try {
localStorage.removeItem('mangalord-reader-mode');
localStorage.removeItem('mangalord-reader-gap');
+ localStorage.removeItem('mangalord-reader-brightness');
} catch {
// ignore — about:blank doesn't expose localStorage yet.
}
@@ -181,6 +191,64 @@ test('gap select updates the inline gap on the continuous container', async ({ p
await expect(container).toHaveAttribute('style', /gap:\s*64px/);
});
+test('desktop reader nav exposes a brightness control that dims and undims the reader', async ({
+ page
+}) => {
+ // The brightness slider used to live only in the mobile settings sheet,
+ // so desktop users couldn't dim — and could be stuck dimmed by a value a
+ // phone had persisted. The desktop nav now carries its own slider.
+ await mockReaderApis(page);
+ await page.goto(`/manga/${mangaId}/chapter/${chapterId}`);
+
+ const slider = page.getByTestId('reader-brightness-desktop');
+ await expect(slider).toBeVisible();
+
+ const dim = () =>
+ page.evaluate(() =>
+ document.documentElement.style.getPropertyValue('--reader-dim')
+ );
+
+ // Default brightness is full → no dim overlay.
+ await expect.poll(async () => Number(await dim())).toBe(0);
+
+ // Dimming raises the overlay opacity…
+ await slider.fill('0.3');
+ await expect.poll(async () => Number(await dim())).toBeGreaterThan(0);
+
+ // …and restoring it clears the dim, so a desktop user is never stuck.
+ await slider.fill('1');
+ await expect.poll(async () => Number(await dim())).toBe(0);
+});
+
+test('a brightness value persisted by a phone is recoverable on desktop', async ({
+ page,
+ context
+}) => {
+ // Seed a dimmed value as if set on mobile, then open the reader on a
+ // desktop viewport: the dim applies and the desktop control reflects it.
+ await context.addInitScript(() => {
+ try {
+ localStorage.setItem('mangalord-reader-brightness', '0.3');
+ } catch {
+ // about:blank — ignore
+ }
+ });
+ await mockReaderApis(page);
+ await page.goto(`/manga/${mangaId}/chapter/${chapterId}`);
+
+ const slider = page.getByTestId('reader-brightness-desktop');
+ await expect(slider).toHaveValue('0.3');
+ await expect
+ .poll(async () =>
+ Number(
+ await page.evaluate(() =>
+ document.documentElement.style.getPropertyValue('--reader-dim')
+ )
+ )
+ )
+ .toBeGreaterThan(0);
+});
+
test('reader-mode preference set on one page is honored when the reader opens', async ({
page,
context
diff --git a/frontend/package.json b/frontend/package.json
index 18e80b3..50639c3 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,6 +1,6 @@
{
"name": "mangalord-frontend",
- "version": "0.88.0",
+ "version": "0.89.0",
"private": true,
"type": "module",
"scripts": {
diff --git a/frontend/src/routes/manga/[id]/chapter/[chapter_id]/+page.svelte b/frontend/src/routes/manga/[id]/chapter/[chapter_id]/+page.svelte
index cd2b865..c59893f 100644
--- a/frontend/src/routes/manga/[id]/chapter/[chapter_id]/+page.svelte
+++ b/frontend/src/routes/manga/[id]/chapter/[chapter_id]/+page.svelte
@@ -1094,6 +1094,28 @@
{/if}
+
+
+