Skip to content

Commit 2ae75eb

Browse files
committed
Merge develop into main: release v1.5.10
2 parents 85cf012 + 1a50b3d commit 2ae75eb

7 files changed

Lines changed: 145 additions & 15 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.10] - 2026-05-22
4+
5+
### Fixed
6+
7+
- **QuickActions "+" ignored Anki card mode** - The QuickActions "+" → image → text-box picker flow always opened the "Create new card" modal, ignoring the `cardMode` setting. It now uses the same update/create dispatch logic as the double-click and context-menu flows, including the 5-minute window check for updates. The `{page_num}` template variable also resolves correctly in this flow (#216)
8+
- **Page swipes during Anki modal** - The reader no longer flips pages while the Anki Update/Create Card modal is open, preventing accidental flips when dragging the crop box on touchscreens (#189)
9+
310
## [1.5.9] - 2026-05-17
411

512
### Fixed

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.9",
3+
"version": "1.5.10",
44
"private": true,
55
"scripts": {
66
"dev": "vite dev",

src/lib/components/Reader/QuickActions.svelte

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,23 @@
2121
volumeUuid: string;
2222
page1?: Page; // First page data for Anki card creation
2323
page2?: Page; // Second page data (when in dual mode)
24+
page1Number?: number; // 1-indexed page number for src1/page1
25+
page2Number?: number; // 1-indexed page number for src2/page2
2426
visible?: boolean;
2527
}
2628
27-
let { left, right, src1, src2, volumeUuid, page1, page2, visible = true }: Props = $props();
29+
let {
30+
left,
31+
right,
32+
src1,
33+
src2,
34+
volumeUuid,
35+
page1,
36+
page2,
37+
page1Number,
38+
page2Number,
39+
visible = true
40+
}: Props = $props();
2841
2942
let ankiTags = $derived($settings.ankiConnectSettings.tags);
3043
let volumeMetadata = $derived<VolumeMetadata>({
@@ -49,10 +62,10 @@
4962
open = false;
5063
}
5164
52-
async function onUpdateCard(src: File | undefined, page?: Page) {
65+
async function onUpdateCard(src: File | undefined, page?: Page, pageNumber?: number) {
5366
if ($settings.ankiConnectSettings.enabled && src && page) {
54-
// Show text box picker first, then cropper
55-
showTextBoxPicker(URL.createObjectURL(src), page, ankiTags, volumeMetadata);
67+
// Show text box picker first, then dispatch to create/update based on cardMode
68+
showTextBoxPicker(URL.createObjectURL(src), page, ankiTags, volumeMetadata, pageNumber);
5669
}
5770
open = false;
5871
}
@@ -69,7 +82,7 @@
6982
<div class="mb-2 flex flex-col items-center gap-2">
7083
{#if $settings.ankiConnectSettings.enabled}
7184
<button
72-
onclick={() => onUpdateCard(src1, page1)}
85+
onclick={() => onUpdateCard(src1, page1, page1Number)}
7386
class="relative flex h-12 w-12 items-center justify-center rounded-full bg-gray-700 text-gray-300 shadow-lg hover:bg-gray-600 focus:outline-none dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
7487
aria-label="Add image to Anki"
7588
>
@@ -84,7 +97,7 @@
8497
{/if}
8598
{#if $settings.ankiConnectSettings.enabled && src2}
8699
<button
87-
onclick={() => onUpdateCard(src2, page2)}
100+
onclick={() => onUpdateCard(src2, page2, page2Number)}
88101
class="relative flex h-12 w-12 items-center justify-center rounded-full bg-gray-700 text-gray-300 shadow-lg hover:bg-gray-600 focus:outline-none dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
89102
aria-label="Add image 2 to Anki"
90103
>

src/lib/components/Reader/Reader.svelte

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import MangaPage from './MangaPage.svelte';
3232
import TextBoxContextMenu from './TextBoxContextMenu.svelte';
3333
import {
34+
cropperStore,
3435
openCreateModal,
3536
openUpdateModal,
3637
sendQuickCapture,
@@ -474,6 +475,7 @@
474475
475476
function handleTouchStart(event: TouchEvent) {
476477
if (!$settings.mobile) return;
478+
if ($cropperStore?.open) return;
477479
if ($settings.continuousScroll) return; // Continuous mode handles its own touch
478480
if (event.touches.length > 1) return; // Ignore multi-touch starts
479481
@@ -494,6 +496,7 @@
494496
function handlePointerUp(event: TouchEvent) {
495497
if (!$settings.mobile) return;
496498
if ($settings.continuousScroll) return; // Continuous mode handles its own touch
499+
if ($cropperStore?.open) return; // Don't process swipes when Anki modal is open
497500
498501
// If fingers remain, this was a multi-touch gesture - mark it and wait
499502
if (event.touches.length !== 0) {
@@ -1279,6 +1282,8 @@
12791282
volumeUuid={volume.volume_uuid}
12801283
page1={pages[index]}
12811284
page2={!useSinglePage ? pages[index + 1] : undefined}
1285+
page1Number={index + 1}
1286+
page2Number={!useSinglePage ? index + 2 : undefined}
12821287
visible={overlaysVisible}
12831288
/>
12841289
<SettingsButton visible={overlaysVisible} />

src/lib/components/Reader/TextBoxPicker.svelte

Lines changed: 107 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
11
<script lang="ts">
22
import { Button, Helper, Modal, Spinner } from 'flowbite-svelte';
33
import type { Page } from '$lib/types';
4-
import { showCropper, expandTextBoxBounds, type VolumeMetadata } from '$lib/anki-connect';
4+
import {
5+
expandTextBoxBounds,
6+
extractFieldValues,
7+
getCardAgeInMin,
8+
getLastCardInfo,
9+
getModelConfig,
10+
openCreateModal,
11+
openUpdateModal,
12+
sendQuickCapture,
13+
type VolumeMetadata
14+
} from '$lib/anki-connect';
15+
import { settings } from '$lib/settings';
16+
import { showSnackbar } from '$lib/util';
517
import { onMount } from 'svelte';
618
import { textBoxPickerStore } from './text-box-picker';
719
820
let open = $state(false);
921
let image = $state<string | undefined>(undefined);
1022
let page = $state<Page | undefined>(undefined);
23+
let pageNumber = $state<number | undefined>(undefined);
1124
let tags = $state<string | undefined>(undefined);
1225
let metadata = $state<VolumeMetadata | undefined>(undefined);
1326
@@ -54,6 +67,7 @@
5467
open = value.open;
5568
image = value.image;
5669
page = value.page;
70+
pageNumber = value.pageNumber;
5771
tags = value.tags;
5872
metadata = value.metadata;
5973
@@ -72,7 +86,7 @@
7286
textBoxPickerStore.set({ open: false });
7387
}
7488
75-
function handleContinue() {
89+
async function handleContinue() {
7690
if (selectedBlockIndex === null || !page || !image) return;
7791
7892
const zone = textBoxZones.find((z) => z.blockIndex === selectedBlockIndex);
@@ -88,11 +102,100 @@
88102
const imageUrl = image;
89103
const ankiTags = tags;
90104
const volumeMetadata = metadata;
105+
const currentPageNumber = pageNumber;
106+
const pageFilename = page.img_path;
91107
92108
close();
93109
94-
// Show the cropper with the selected text and bounds
95-
showCropper(imageUrl, selectedText, selectedText, ankiTags, volumeMetadata, undefined, textBox);
110+
const cardMode = $settings.ankiConnectSettings.cardMode;
111+
112+
if (cardMode === 'update') {
113+
const lastCard = await getLastCardInfo();
114+
115+
if (!lastCard || !lastCard.noteId) {
116+
showSnackbar('No recent card found to update');
117+
return;
118+
}
119+
120+
if (!lastCard.modelName) {
121+
showSnackbar('Could not detect card note type');
122+
return;
123+
}
124+
125+
const cardAge = getCardAgeInMin(lastCard.noteId);
126+
if (cardAge >= 5) {
127+
showSnackbar(`Last card is ${cardAge} minutes old (max 5 min)`);
128+
return;
129+
}
130+
131+
const previousValues = extractFieldValues(lastCard);
132+
const modelConfig = getModelConfig(lastCard.modelName, 'update');
133+
const quickCapture = modelConfig?.quickCapture ?? false;
134+
135+
if (quickCapture) {
136+
await sendQuickCapture(
137+
'update',
138+
imageUrl,
139+
selectedText,
140+
selectedText,
141+
volumeMetadata,
142+
textBox,
143+
previousValues,
144+
lastCard.noteId,
145+
lastCard.tags,
146+
lastCard.modelName,
147+
pageFilename
148+
);
149+
} else {
150+
openUpdateModal(
151+
imageUrl,
152+
previousValues,
153+
lastCard.noteId,
154+
lastCard.modelName,
155+
lastCard.tags,
156+
selectedText,
157+
selectedText,
158+
ankiTags,
159+
volumeMetadata,
160+
undefined,
161+
textBox,
162+
currentPageNumber,
163+
pageFilename
164+
);
165+
}
166+
} else {
167+
const { selectedModel } = $settings.ankiConnectSettings;
168+
const modelConfig = getModelConfig(selectedModel, 'create');
169+
const quickCapture = modelConfig?.quickCapture ?? false;
170+
171+
if (quickCapture) {
172+
await sendQuickCapture(
173+
'create',
174+
imageUrl,
175+
selectedText,
176+
selectedText,
177+
volumeMetadata,
178+
textBox,
179+
undefined,
180+
undefined,
181+
undefined,
182+
undefined,
183+
pageFilename
184+
);
185+
} else {
186+
openCreateModal(
187+
imageUrl,
188+
selectedText,
189+
selectedText,
190+
ankiTags,
191+
volumeMetadata,
192+
undefined,
193+
textBox,
194+
currentPageNumber,
195+
pageFilename
196+
);
197+
}
198+
}
96199
}
97200
98201
// Handle backdrop mousedown - dismiss on mousedown outside content

src/lib/components/Reader/text-box-picker.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ interface TextBoxPickerState {
66
open: boolean;
77
image?: string;
88
page?: Page;
9+
pageNumber?: number;
910
tags?: string;
1011
metadata?: VolumeMetadata;
1112
}
@@ -16,7 +17,8 @@ export function showTextBoxPicker(
1617
image: string,
1718
page: Page,
1819
tags?: string,
19-
metadata?: VolumeMetadata
20+
metadata?: VolumeMetadata,
21+
pageNumber?: number
2022
) {
21-
textBoxPickerStore.set({ open: true, image, page, tags, metadata });
23+
textBoxPickerStore.set({ open: true, image, page, pageNumber, tags, metadata });
2224
}

0 commit comments

Comments
 (0)