From ecb4d7ddc2fae354c804be859bd517437c366fdc Mon Sep 17 00:00:00 2001 From: Seismix Date: Fri, 21 Nov 2025 14:25:49 +0100 Subject: [PATCH 1/8] fix: refactor enableJump handling in defaults and migrations --- src/entrypoints/popup/Popup.svelte | 14 +++++++------- src/lib/utils/storage-utils.ts | 29 ++++++++++++++--------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/entrypoints/popup/Popup.svelte b/src/entrypoints/popup/Popup.svelte index 101d69f..a9f352a 100644 --- a/src/entrypoints/popup/Popup.svelte +++ b/src/entrypoints/popup/Popup.svelte @@ -1,17 +1,17 @@

Recap Settings

@@ -79,25 +74,20 @@ Scroll behavior - {#if userPrefersReducedMotion} + {#if userPrefersReducedMotion && settings.scrollBehavior === "smooth"}

- Scroll behavior was set to instant for you because you have - motion reduction enabled in your system settings. + System Reduced Motion Detected

- This means the scroll will jump instantly. If you want to use - smooth scrolling instead, turn off motion reduction in your - accessibility settings. + Your system has reduced motion enabled, so "Auto" will use + instant scrolling. If you prefer not to jump at all, disable + "Enable jump to recap" above.

{/if} diff --git a/src/entrypoints/background.ts b/src/entrypoints/background.ts index 257ac95..e8a65e6 100644 --- a/src/entrypoints/background.ts +++ b/src/entrypoints/background.ts @@ -5,10 +5,9 @@ import { restoreSelectors, setSettings } from "~/lib/utils/storage-utils" export default defineBackground(() => { browser.runtime.onInstalled.addListener(async (details) => { if (details.reason === "install") { - // Use conservative defaults - first-time detection happens on UI side + // Use defaults await setSettings({ ...DEFAULTS, - hasDetectedReducedMotion: false, // Flag to trigger first-time detection }) } @@ -27,10 +26,9 @@ export default defineBackground(() => { if (typeof message !== "object" || message === null) return if ("request" in message && message.request === "getDefaultSettings") { - // Return basic defaults - first-time detection happens on UI side + // Return basic defaults return Promise.resolve({ ...DEFAULTS, - hasDetectedReducedMotion: false, // Flag to trigger first-time detection }) } diff --git a/src/lib/config/defaults.ts b/src/lib/config/defaults.ts index 61e4223..5115747 100644 --- a/src/lib/config/defaults.ts +++ b/src/lib/config/defaults.ts @@ -17,14 +17,14 @@ export const DEFAULT_SELECTORS: ExtensionSelectors = { const DEFAULTS: ExtensionSettings = { wordCount: 250, - enableJump: false, + enableJump: true, scrollBehavior: "smooth" as ScrollBehavior, autoExpand: false, ...DEFAULT_SELECTORS, } /** - * Get defaults with prefers-reduced-motion detection for fresh installs only + * Get defaults * For existing users, their settings are preserved completely */ export function getDefaults(existingSettings?: Partial) { @@ -36,23 +36,7 @@ export function getDefaults(existingSettings?: Partial) { } } - // Fresh install: detect reduced motion preference - const reducedMotion = prefersReducedMotion() - devLog.log("Fresh install: prefersReducedMotion", reducedMotion) - - if (!reducedMotion) { - // No reduced motion preference - safe to enable jump functionality - return { - ...DEFAULTS, - enableJump: true, - scrollBehavior: "smooth" as ScrollBehavior, - } - } - - // Default case (reduced motion or detection failed) - disable animations - return { - ...DEFAULTS, - } + return { ...DEFAULTS } } /** diff --git a/src/lib/utils/storage-utils.ts b/src/lib/utils/storage-utils.ts index 82c7bdd..fe92067 100644 --- a/src/lib/utils/storage-utils.ts +++ b/src/lib/utils/storage-utils.ts @@ -17,7 +17,6 @@ export const settingsStore = storage.defineItem( enableJump: true, // Jump is a core feature, always enabled by default scrollBehavior: "smooth" as ScrollBehavior, autoExpand: false, - hasDetectedReducedMotion: false, // Fresh installs should trigger detection ...DEFAULT_SELECTORS, }, version: 2, @@ -31,8 +30,6 @@ export const settingsStore = storage.defineItem( scrollBehavior: (oldSettings.smoothScroll ? "smooth" : "instant") as ScrollBehavior, - // Migration: mark as having been detected so we don't override user's choice - hasDetectedReducedMotion: true, } as ExtensionSettings // Remove the old property @@ -53,52 +50,9 @@ export const settingsStore = storage.defineItem( /** * Get current settings from storage - * Automatically performs first-time reduced motion detection if needed */ export async function getSettings(): Promise { - const settings = await settingsStore.getValue() - - // Skip if we've already done detection - if (settings?.hasDetectedReducedMotion) { - return settings - } - - try { - const reducedMotion = prefersReducedMotion() - devLog.log( - "First time detection - prefersReducedMotion:", - reducedMotion, - ) - - let updatedSettings = { - ...settings, - enableJump: true, // Jump is a core feature, always enabled - hasDetectedReducedMotion: true, - } - - // Adjust scroll behavior based on reduced motion preference - if (!reducedMotion) { - updatedSettings.scrollBehavior = "smooth" as ScrollBehavior - devLog.log("No reduced motion detected, using smooth scroll") - } else { - // Respect reduced motion by using instant scrolling - updatedSettings.scrollBehavior = "instant" as ScrollBehavior - devLog.log("Reduced motion detected, using instant scroll") - } - - await settingsStore.setValue(updatedSettings) - return updatedSettings - } catch (error) { - devLog.log("Could not detect reduced motion preference:", error) - } - - // Fallback: just mark as detected - const fallbackSettings = { - ...settings, - hasDetectedReducedMotion: true, - } - await settingsStore.setValue(fallbackSettings) - return fallbackSettings + return await settingsStore.getValue() } /** diff --git a/src/types/types.ts b/src/types/types.ts index 76c8199..cd7bcd1 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -15,7 +15,6 @@ export type ExtensionSettings = { enableJump: boolean scrollBehavior: ScrollBehavior autoExpand: boolean - hasDetectedReducedMotion?: boolean } & ExtensionSelectors export type ExtensionSettingsKeys = keyof ExtensionSettings From c53d85cc0ac9dca3d3ed6ef398ceed5bb3e3db5e Mon Sep 17 00:00:00 2001 From: Seismix Date: Tue, 20 Jan 2026 16:06:30 +0100 Subject: [PATCH 5/8] feat: update version to 1.6.0 and add release notes for enhanced scrolling options and accessibility improvements --- package.json | 2 +- src/assets/patches/v1.6.0.json | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 src/assets/patches/v1.6.0.json diff --git a/package.json b/package.json index 694a990..1890c52 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "royalrefresh", - "version": "1.5.0", + "version": "1.6.0", "description": "A web extension for royalroad.com. For people who juggle multiple stories", "main": "background.js", "directories": { diff --git a/src/assets/patches/v1.6.0.json b/src/assets/patches/v1.6.0.json new file mode 100644 index 0000000..6da5d13 --- /dev/null +++ b/src/assets/patches/v1.6.0.json @@ -0,0 +1,12 @@ +{ + "version": "1.6.0", + "releasedOn": "2026-01-20", + "summary": "Enhanced scrolling options and accessibility improvements.", + "new": [ + "You can now choose between 'Auto' and 'Instant' scrolling speeds when jumping to recaps" + ], + "fixes": [ + "Clearer explanations when your device's Reduced Motion settings affect animations", + "Improved reliability when jumping to the recap section" + ] +} From cc00600f7f4707680a1cc84d73614d94793c8e2a Mon Sep 17 00:00:00 2001 From: Seismix Date: Tue, 20 Jan 2026 16:35:36 +0100 Subject: [PATCH 6/8] feat: refactor migration from v1 to v2 for smoothScroll settings and add corresponding tests --- src/lib/utils/logger.ts | 2 +- src/lib/utils/migrations.ts | 22 ++++++++ src/lib/utils/storage-utils.ts | 27 +--------- src/tests/migrations.test.ts | 96 ++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 26 deletions(-) create mode 100644 src/lib/utils/migrations.ts create mode 100644 src/tests/migrations.test.ts diff --git a/src/lib/utils/logger.ts b/src/lib/utils/logger.ts index 461059b..9931c5f 100644 --- a/src/lib/utils/logger.ts +++ b/src/lib/utils/logger.ts @@ -1,6 +1,6 @@ export const devLog = { log: (...args: any[]) => { - if (import.meta.env.DEV) { + if (import.meta.env?.DEV) { console.log(...args) } }, diff --git a/src/lib/utils/migrations.ts b/src/lib/utils/migrations.ts new file mode 100644 index 0000000..1087af2 --- /dev/null +++ b/src/lib/utils/migrations.ts @@ -0,0 +1,22 @@ +import type { ExtensionSettings } from "~/types/types" +import { devLog } from "./logger" + +export function migrateV1toV2(oldSettings: any): ExtensionSettings { + // Migration from v1 to v2: smoothScroll -> enableJump & scrollBehavior + if ("smoothScroll" in oldSettings) { + const { smoothScroll, ...rest } = oldSettings + const migrated: ExtensionSettings = { + ...rest, + enableJump: smoothScroll === true, // Preserve user's choice + scrollBehavior: (smoothScroll ? "smooth" : "instant") as ScrollBehavior, + } + + devLog.log("WXT Migration v1→v2: smoothScroll ->", { + enableJump: migrated.enableJump, + scrollBehavior: migrated.scrollBehavior, + }) + + return migrated + } + return oldSettings as ExtensionSettings +} diff --git a/src/lib/utils/storage-utils.ts b/src/lib/utils/storage-utils.ts index fe92067..80eb03f 100644 --- a/src/lib/utils/storage-utils.ts +++ b/src/lib/utils/storage-utils.ts @@ -1,8 +1,7 @@ import { storage } from "wxt/utils/storage" import { DEFAULT_SELECTORS, getDefaults } from "~/lib/config/defaults" import type { ExtensionSettings } from "~/types/types" -import { devLog } from "./logger" -import { prefersReducedMotion } from "./platform" +import { migrateV1toV2 } from "./migrations" /** * WXT storage utilities for extension settings with migration support @@ -21,29 +20,7 @@ export const settingsStore = storage.defineItem( }, version: 2, migrations: { - 2: (oldSettings: any) => { - // Migration from v1 to v2: smoothScroll -> enableJump & scrollBehavior - if ("smoothScroll" in oldSettings) { - const migrated = { - ...oldSettings, - enableJump: oldSettings.smoothScroll === true, // Preserve user's choice - scrollBehavior: (oldSettings.smoothScroll - ? "smooth" - : "instant") as ScrollBehavior, - } as ExtensionSettings - - // Remove the old property - delete (migrated as any).smoothScroll - - devLog.log("WXT Migration v1→v2: smoothScroll ->", { - enableJump: migrated.enableJump, - scrollBehavior: migrated.scrollBehavior, - }) - - return migrated - } - return oldSettings as ExtensionSettings - }, + 2: migrateV1toV2, }, }, ) diff --git a/src/tests/migrations.test.ts b/src/tests/migrations.test.ts new file mode 100644 index 0000000..951b0c1 --- /dev/null +++ b/src/tests/migrations.test.ts @@ -0,0 +1,96 @@ +import { test, expect } from '@playwright/test'; +import { migrateV1toV2 } from '../lib/utils/migrations'; + +// Helper to simulate the full migration chain +// As you add more versions, update this function +function migrateToLatest(settings: any, startVersion: number) { + let migrated = { ...settings }; + + // Chain: v1 -> v2 + if (startVersion < 2) { + migrated = migrateV1toV2(migrated); + } + + // Future: v2 -> v3 + // if (startVersion < 3) { + // migrated = migrateV2toV3(migrated); + // } + + return migrated; +} + +test.describe('Settings Migrations', () => { + + // Test Scenarios + // Add new test cases here instead of writing new test blocks + const scenarios = [ + { + name: 'v1 to Latest: smoothScroll=true', + fromVersion: 1, + input: { wordCount: 250, smoothScroll: true, autoExpand: false }, + expected: { + enableJump: true, + scrollBehavior: 'smooth', + wordCount: 250 + }, + shouldNotHave: ['smoothScroll'] + }, + { + name: 'v1 to Latest: smoothScroll=false', + fromVersion: 1, + input: { wordCount: 250, smoothScroll: false, autoExpand: false }, + expected: { + enableJump: false, + scrollBehavior: 'instant' + }, + shouldNotHave: ['smoothScroll'] + }, + { + name: 'v1 to Latest: preserves unknown properties', + fromVersion: 1, + input: { wordCount: 500, smoothScroll: true, customProp: 'kept' }, + expected: { + wordCount: 500, + customProp: 'kept' + }, + shouldNotHave: ['smoothScroll'] + }, + // Example for future v3 test: + // { + // name: 'v2 to Latest: simple update', + // fromVersion: 2, + // input: { ...v2State }, + // expected: { ...v3State } + // } + ]; + + for (const scenario of scenarios) { + test(scenario.name, () => { + const result = migrateToLatest(scenario.input, scenario.fromVersion); + + // 1. Verify expected values match + expect(result).toMatchObject(scenario.expected); + + // 2. Verify cleaned up keys + if (scenario.shouldNotHave) { + for (const key of scenario.shouldNotHave) { + expect(result).not.toHaveProperty(key); + } + } + }); + } + + test('should return input unchanged if already at latest version schema (idempotency check)', () => { + const v2Settings = { + wordCount: 250, + enableJump: true, + scrollBehavior: 'smooth' + }; + + // If we are already at v2 (or logically check the schema), + // migrateV1toV2 returns input if 'smoothScroll' is missing. + const result = migrateV1toV2(v2Settings); + + expect(result).toBe(v2Settings); + }); +}); From 83155f9b8e4c9a589a7cc8edbb837722a83e511b Mon Sep 17 00:00:00 2001 From: Seismix Date: Tue, 20 Jan 2026 16:38:05 +0100 Subject: [PATCH 7/8] chore: format --- src/components/settings/BasicSettings.svelte | 4 +- src/lib/utils/migrations.ts | 4 +- src/tests/migrations.test.ts | 59 ++++++++++---------- 3 files changed, 33 insertions(+), 34 deletions(-) diff --git a/src/components/settings/BasicSettings.svelte b/src/components/settings/BasicSettings.svelte index 7bbf76f..98e7848 100644 --- a/src/components/settings/BasicSettings.svelte +++ b/src/components/settings/BasicSettings.svelte @@ -72,9 +72,7 @@ {#if settings.enableJump}