2525### Task 1: ` pagedGap ` setting
2626
2727** Files:**
28+
2829- Modify: ` src/lib/settings/settings.ts:166 ` (type), ` src/lib/settings/settings.ts:313 ` (default)
2930- Test: ` src/lib/settings/settings.test.ts `
3031
3132** Interfaces:**
33+
3234- Produces: ` Settings.pagedGap: number ` (default ` 0 ` ) — read by Tasks 4, 5, 7 as ` $settings.pagedGap ` .
3335
3436- [ ] ** Step 1: Write the failing test**
@@ -55,8 +57,8 @@ Expected: FAIL — `expected undefined to be 0`
5557In ` src/lib/settings/settings.ts ` , after the ` scrollGap ` type field (line ~ 166):
5658
5759``` typescript
58- scrollGap : number ; // Pixels of padding between pages in scroll modes
59- pagedGap : number ; // Gap between a paged-mode spread's two pages (image px, scales with zoom)
60+ scrollGap : number ; // Pixels of padding between pages in scroll modes
61+ pagedGap : number ; // Gap between a paged-mode spread's two pages (image px, scales with zoom)
6062```
6163
6264In ` defaultSettings ` after ` scrollGap: 0, ` (line ~ 313):
@@ -83,10 +85,12 @@ git commit -m "feat(settings): add pagedGap setting"
8385### Task 2: ` spreadContentSize ` helper
8486
8587** Files:**
88+
8689- Modify: ` src/lib/reader/paged-zoom-layout.ts ` (append after ` panEdgeState ` )
8790- Test: ` src/lib/reader/paged-zoom-layout.test.ts `
8891
8992** Interfaces:**
93+
9094- Consumes: ` Size ` (already exported from this module).
9195- Produces: ` spreadContentSize(first: Size, second: Size | null, gap: number): Size ` — used by Task 4 in ` Reader.svelte ` .
9296
@@ -159,11 +163,13 @@ git commit -m "feat(reader): spreadContentSize includes the pair gap in fit math
159163### Task 3: Gap wheel intent helpers
160164
161165** Files:**
166+
162167- Modify: ` src/lib/reader/zoom-math.ts ` (after ` normalizeWheelDelta ` , line ~ 120)
163168- Test: ` src/lib/reader/zoom-math.test.ts `
164169- Modify: ` docs/superpowers/specs/2026-07-05-page-gap-234-design.md ` (amend helper section)
165170
166171** Interfaces:**
172+
167173- Consumes: ` normalizeWheelDelta ` , ` WheelAccumulator ` (same module).
168174- Produces (used by Tasks 5–7):
169175 - ` MAX_PAGE_GAP = 100 ` (const)
@@ -212,7 +218,10 @@ describe('gapWheelSteps', () => {
212218 const acc = new WheelAccumulator (GAP_WHEEL_STEP_SIZE );
213219 let total = 0 ;
214220 for (let i = 0 ; i < 4 ; i ++ ) {
215- total += gapWheelSteps ({ deltaX: 0 , deltaY: - 6 , deltaMode: 0 , timeStamp: 1000 + i * 16 }, acc );
221+ total += gapWheelSteps (
222+ { deltaX: 0 , deltaY: - 6 , deltaMode: 0 , timeStamp: 1000 + i * 16 },
223+ acc
224+ );
216225 }
217226 expect (total ).toBe (1 ); // 24 wheel px accumulated -> one 20px step
218227 });
@@ -302,9 +311,11 @@ git commit -m "feat(reader): gap-adjust wheel intent helpers"
302311### Task 4: Paged rendering + fit math
303312
304313** Files:**
314+
305315- Modify: ` src/lib/components/Reader/Reader.svelte:376-389 ` (pagedContentSize), ` :1204-1211 ` (flex row), imports at top of ` <script> ` .
306316
307317** Interfaces:**
318+
308319- Consumes: ` spreadContentSize ` (Task 2), ` $settings.pagedGap ` (Task 1).
309320- Produces: paged spread renders with ` column-gap ` ; ` pagedContentSize ` includes the gap (PagedViewport re-fits automatically via its ` contentSize ` signature).
310321
@@ -367,10 +378,12 @@ git commit -m "feat(reader): render pagedGap between spread pages (#234)"
367378### Task 5: Paged surface wheel chord + toast
368379
369380** Files:**
381+
370382- Modify: ` src/lib/components/Reader/PagedViewport.svelte ` (Props, imports, ` handleWheel ` )
371383- Modify: ` src/lib/components/Reader/Reader.svelte ` (` handleGapChange ` near ` showNotification ` line ~ 887; ` onGapChange ` prop at the ` <PagedViewport ` usage line ~ 1171)
372384
373385** Interfaces:**
386+
374387- Consumes: ` wheelIntentIsGapAdjust ` , ` gapWheelSteps ` , ` WheelAccumulator ` , ` GAP_WHEEL_STEP_SIZE ` , ` MAX_PAGE_GAP ` (Task 3); ` updateSetting ` from ` $lib/settings ` .
375388- Produces: ` PagedViewport ` prop ` onGapChange?: (px: number) => void ` ; Reader's ` handleGapChange(px: number) ` (reused by Task 6).
376389
@@ -406,34 +419,34 @@ and add `onGapChange` to the destructuring.
406419Above ` handleWheel ` , add:
407420
408421``` typescript
409- const gapAccumulator = new WheelAccumulator (GAP_WHEEL_STEP_SIZE );
422+ const gapAccumulator = new WheelAccumulator (GAP_WHEEL_STEP_SIZE );
410423
411- function adjustGap(delta : number ) {
412- if (delta === 0 ) return ;
413- const next = Math .max (0 , Math .min (MAX_PAGE_GAP , ($settings .pagedGap ?? 0 ) + delta ));
414- updateSetting (' pagedGap' , next );
415- onGapChange ?.(next );
416- }
424+ function adjustGap(delta : number ) {
425+ if (delta === 0 ) return ;
426+ const next = Math .max (0 , Math .min (MAX_PAGE_GAP , ($settings .pagedGap ?? 0 ) + delta ));
427+ updateSetting (' pagedGap' , next );
428+ onGapChange ?.(next );
429+ }
417430```
418431
419432At the TOP of ` handleWheel ` (before ` const modifier = … ` ):
420433
421434``` typescript
422- if (wheelIntentIsGapAdjust (e )) {
423- e .preventDefault ();
424- adjustGap (gapWheelSteps (e , gapAccumulator ));
425- return ;
426- }
435+ if (wheelIntentIsGapAdjust (e )) {
436+ e .preventDefault ();
437+ adjustGap (gapWheelSteps (e , gapAccumulator ));
438+ return ;
439+ }
427440```
428441
429442- [ ] ** Step 2: Wire the toast in Reader**
430443
431444In ` src/lib/components/Reader/Reader.svelte ` , after the ` showNotification ` function (line ~ 901):
432445
433446``` typescript
434- function handleGapChange(px : number ) {
435- showNotification (` Page gap: ${px }px ` , ' page-gap' );
436- }
447+ function handleGapChange(px : number ) {
448+ showNotification (` Page gap: ${px }px ` , ' page-gap' );
449+ }
437450```
438451
439452At the ` <PagedViewport ` usage (line ~ 1171), add the prop:
@@ -468,11 +481,13 @@ git commit -m "feat(reader): ctrl+shift+scroll adjusts pagedGap in paged mode"
468481### Task 6: Scroll surfaces wheel chord
469482
470483** Files:**
484+
471485- Modify: ` src/lib/components/Reader/HorizontalScrollReader.svelte ` (Props line ~ 21, imports, ` handleWheel ` line ~ 346)
472486- Modify: ` src/lib/components/Reader/VerticalScrollReader.svelte ` (Props line ~ 21, imports, ` handleWheel ` line ~ 343)
473487- Modify: ` src/lib/components/Reader/Reader.svelte ` (pass ` onGapChange={handleGapChange} ` to both usages, lines ~ 1140-1166)
474488
475489** Interfaces:**
490+
476491- Consumes: Task 3 helpers; Reader's ` handleGapChange ` (Task 5); ` updateSetting ` from ` $lib/settings ` (both files already import ` settings ` ; extend that import).
477492- Produces: both scroll readers accept ` onGapChange?: (px: number) => void ` ; the chord writes ` scrollGap ` and syncs ` pageDividers = gap > 0 ` .
478493
@@ -492,28 +507,28 @@ Add to `interface Props` (after `onOverlayToggle?`) and to the `$props()` destru
492507Add above ` handleWheel ` :
493508
494509``` typescript
495- const gapAccumulator = new WheelAccumulator (GAP_WHEEL_STEP_SIZE );
496-
497- // The chord drives the continuous-mode dividers: gap > 0 means dividers on,
498- // reaching 0 turns them off (the M toggle keeps working independently).
499- function adjustGap(delta : number ) {
500- if (delta === 0 ) return ;
501- const next = Math .max (0 , Math .min (MAX_PAGE_GAP , $settings .scrollGap + delta ));
502- updateSetting (' scrollGap' , next );
503- const dividers = next > 0 ;
504- if ($settings .pageDividers !== dividers ) updateSetting (' pageDividers' , dividers );
505- onGapChange ?.(next );
506- }
510+ const gapAccumulator = new WheelAccumulator (GAP_WHEEL_STEP_SIZE );
511+
512+ // The chord drives the continuous-mode dividers: gap > 0 means dividers on,
513+ // reaching 0 turns them off (the M toggle keeps working independently).
514+ function adjustGap(delta : number ) {
515+ if (delta === 0 ) return ;
516+ const next = Math .max (0 , Math .min (MAX_PAGE_GAP , $settings .scrollGap + delta ));
517+ updateSetting (' scrollGap' , next );
518+ const dividers = next > 0 ;
519+ if ($settings .pageDividers !== dividers ) updateSetting (' pageDividers' , dividers );
520+ onGapChange ?.(next );
521+ }
507522```
508523
509524In ` handleWheel ` , directly after the ` if (!scrollContainer) return; ` guard (both files):
510525
511526``` typescript
512- if (wheelIntentIsGapAdjust (e )) {
513- e .preventDefault ();
514- adjustGap (gapWheelSteps (e , gapAccumulator ));
515- return ;
516- }
527+ if (wheelIntentIsGapAdjust (e )) {
528+ e .preventDefault ();
529+ adjustGap (gapWheelSteps (e , gapAccumulator ));
530+ return ;
531+ }
517532```
518533
519534(In VerticalScrollReader the ` preventDefault ` matters doubly: the surface scrolls natively, and without it the browser would treat ctrl+wheel as page zoom.)
@@ -541,9 +556,11 @@ git commit -m "feat(reader): ctrl+shift+scroll adjusts dividers in continuous mo
541556### Task 7: Settings UI
542557
543558** Files:**
559+
544560- Modify: ` src/lib/components/Settings/Reader/ReaderSettings.svelte ` (isPaged block line ~ 90-104; divider slider label line ~ 127)
545561
546562** Interfaces:**
563+
547564- Consumes: ` $settings.pagedGap ` , ` updateSetting ` (already imported), ` MAX_PAGE_GAP ` (Task 3), ` Range ` /` Label ` (already imported).
548565
549566- [ ] ** Step 1: Add the paged gap slider**
@@ -574,10 +591,10 @@ Inside the `{#if isPaged}` block, after the page-view-mode `</div>` (line ~103),
574591Change the divider-size label (line ~ 127) to:
575592
576593``` svelte
577- <Label class="text-gray-900 dark:text-white">
578- Divider size: {$settings.scrollGap}px
579- <span class="ml-2 text-xs text-gray-500 dark:text-gray-400">(Ctrl+Shift+Scroll)</span>
580- </Label>
594+ <Label class="text-gray-900 dark:text-white">
595+ Divider size: {$settings.scrollGap}px
596+ <span class="ml-2 text-xs text-gray-500 dark:text-gray-400">(Ctrl+Shift+Scroll)</span>
597+ </Label>
581598```
582599
583600and change that slider's ` max={100} ` to ` max={MAX_PAGE_GAP} ` .
@@ -599,14 +616,15 @@ git commit -m "feat(settings): page gap slider + chord hints"
599616### Task 8: Input contracts doc + full verification
600617
601618** Files:**
619+
602620- Modify: ` docs/INPUT-CONTRACTS.md:115 ` (wheel row) + new paragraph after the matrix
603621
604622- [ ] ** Step 1: Update the contracts doc**
605623
606624Change the wheel row of the per-surface policy matrix:
607625
608626``` markdown
609- | Wheel | zoom, gap, or camera glide | zoom, gap, or (native/strip) scroll |
627+ | Wheel | zoom, gap, or camera glide | zoom, gap, or (native/strip) scroll |
610628```
611629
612630After the matrix table, add:
0 commit comments