Skip to content

Commit 85cf012

Browse files
committed
Merge develop into main: release v1.5.9
2 parents 81e1750 + 718a1ba commit 85cf012

7 files changed

Lines changed: 41 additions & 49 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## [1.5.9] - 2026-05-17
4+
5+
### Fixed
6+
7+
- **Migaku popup scroll eaten by reader** - The reader's window-level wheel listener intercepted every wheel and blocked scrolling inside Migaku/Yomitan popup overlays. Wheel events are now only intercepted inside the reader content (#214)
8+
- **Migaku sentence capture stopped at line breaks** - "Capture sentence" / "read sentence" only saw the hovered OCR line because `<br/>` separators were treated as sentence boundaries. OCR lines now use a CSS-generated newline so DOM walkers see one continuous text node per speech bubble. Yomitan behaviour unchanged (#214)
9+
310
## [1.5.8] - 2026-05-16
411

512
### Added

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mokuro-reader",
3-
"version": "1.5.8",
3+
"version": "1.5.9",
44
"private": true,
55
"scripts": {
66
"dev": "vite dev",

src/lib/components/Reader/Reader.svelte

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -542,20 +542,19 @@
542542
}
543543
}
544544
545-
// Wheel handler wrapper that excludes settings drawer, popovers, and modals
545+
// Wheel handler wrapper.
546+
// We only intercept wheel events that originate inside our reader content
547+
// (the Panzoom wrapper marked with data-mokuro-reader). Anything else —
548+
// settings drawer, popovers, dialogs, and extension overlays like Migaku
549+
// and Yomitan popups (which inject into <body>, often inside shadow DOM) —
550+
// is left alone so the browser's default scroll handling can apply.
546551
function handleWheelEvent(e: WheelEvent) {
547552
// In continuous scroll mode, let ContinuousScrollReader handle wheel events
548553
if ($settings.continuousScroll) return;
549554
550555
const target = e.target as HTMLElement;
551-
// Don't capture wheel events from settings drawer, popovers, or modals
552-
if (
553-
target.closest('#settings') ||
554-
target.closest('[data-popover]') ||
555-
target.closest('dialog')
556-
) {
557-
return;
558-
}
556+
if (!target.closest('[data-mokuro-reader]')) return;
557+
559558
panzoomHandleWheel(e);
560559
}
561560

src/lib/components/Reader/TextBoxes.svelte

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@
553553
{contenteditable}
554554
>
555555
<p>
556-
{#each lines as line, i}{line}{#if i < lines.length - 1}<br />{/if}{/each}
556+
{#each lines as line}<span class="ocr-line">{line}</span>{/each}
557557
</p>
558558
</div>
559559
{/each}
@@ -624,4 +624,12 @@
624624
.textBox.originalMode p {
625625
white-space: nowrap;
626626
}
627+
628+
/* Use CSS-generated newline instead of <br/> so DOM walkers
629+
(Migaku/Yomitan) see one continuous text node per textbox
630+
and don't treat line breaks as sentence boundaries. */
631+
.textBox .ocr-line:not(:last-child)::after {
632+
content: '\A';
633+
white-space: pre;
634+
}
627635
</style>

src/lib/panzoom/Panzoom.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
let { children }: Props = $props();
99
</script>
1010

11-
<div use:initPanzoom>
11+
<div use:initPanzoom data-mokuro-reader>
1212
{@render children?.()}
1313
</div>

src/lib/panzoom/util.ts

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ export function initPanzoom(node: HTMLElement) {
2222
minZoom: 0.1,
2323
zoomDoubleClickSpeed: 1,
2424
enableTextSelection: true,
25+
// We handle all keyboard shortcuts at the window level in Reader.svelte, so
26+
// we don't need panzoom's built-in keyboard handling. Disabling it also
27+
// prevents the library from adding `tabindex="0"` to the panzoom parent
28+
// (see node_modules/panzoom/lib/domController.js), which otherwise turned
29+
// the reader wrapper into a focus target and caused a stray focus-visible
30+
// outline to appear as thin white lines around the content (issue #65).
31+
disableKeyboardInteraction: true,
2532
onDoubleClick: () => false, // Allow dblclick events to propagate to Reader's onDoubleTap handler
2633
beforeMouseDown: (e) => {
2734
const target = e.target as HTMLElement;
@@ -33,39 +40,7 @@ export function initPanzoom(node: HTMLElement) {
3340
},
3441
// Disable library's wheel zoom - we handle it ourselves for symmetric zoom
3542
beforeWheel: () => true,
36-
onTouch: (e) => e.touches.length > 1,
37-
// Panzoom typing is wrong here
38-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
39-
// @ts-expect-error
40-
filterKey: (e: KeyboardEvent) => {
41-
// Filter (ignore) keys that shouldn't be handled by panzoom
42-
const target = e.target as HTMLElement;
43-
44-
// Always filter left/right arrows (page navigation)
45-
if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
46-
return true;
47-
}
48-
49-
// Filter all keys when in text inputs, settings, popovers, or textboxes
50-
if (
51-
target.tagName === 'INPUT' ||
52-
target.tagName === 'TEXTAREA' ||
53-
target.isContentEditable ||
54-
target.closest('#settings') ||
55-
target.closest('[data-popover]') ||
56-
target.closest('.textBox')
57-
) {
58-
return true;
59-
}
60-
61-
// Filter nav keys when modifier keys are pressed (for text selection, etc.)
62-
const isNavKey = ['ArrowUp', 'ArrowDown', 'Home', 'End'].includes(e.key);
63-
if (isNavKey && (e.ctrlKey || e.altKey || e.metaKey || e.shiftKey)) {
64-
return true;
65-
}
66-
67-
return false;
68-
}
43+
onTouch: (e) => e.touches.length > 1
6944
});
7045

7146
panzoomStore.set(pz);
@@ -256,7 +231,10 @@ export function keepInBounds() {
256231
let newY = y;
257232

258233
if (forceCenterX) {
259-
newX = (innerWidth - width) / 2;
234+
// Round to integer to avoid sub-pixel CSS transforms, which can produce
235+
// a 1-px white compositor seam along the content edge in Chrome when the
236+
// image is smaller than the viewport (issue #65).
237+
newX = Math.round((innerWidth - width) / 2);
260238
} else {
261239
if (x < minX) {
262240
newX = minX;
@@ -267,7 +245,7 @@ export function keepInBounds() {
267245
}
268246

269247
if (forceCenterY) {
270-
newY = (innerHeight - height) / 2;
248+
newY = Math.round((innerHeight - height) / 2);
271249
} else {
272250
if (y < minY) {
273251
newY = minY;

0 commit comments

Comments
 (0)