From c3fe83b9e01617550fbe425af6d776131aae387f Mon Sep 17 00:00:00 2001 From: Gnathonic Date: Sun, 9 Nov 2025 12:41:52 -0700 Subject: [PATCH 01/10] feat: Add text box editor for mokuro files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements a dedicated editing interface for modifying text box positions, sizes, text content, and font sizes with automatic optimization. Core Features: - Dedicated edit page route (/[manga]/[volume]/edit/[pageIndex]) - Visual manipulation with 5 handles per selected box: - Move handle (top-left): Drag to reposition - Delete handle (top-right): Remove box - Clone handle (bottom-left): Duplicate and place - Resize handle (bottom-right): Adjust dimensions - Auto-size handle (right side): Automatically optimize font size Versioning System: - Added edited_pages field to VolumeData for non-destructive editing - Original mokuro data preserved in pages field - Reader automatically uses edited version via getCurrentPages() utility - Save edits to IndexedDB, export .mokuro JSON file UI/UX: - Zoom modes: Fit to Screen, Fit to Width, Original Size - Page navigation with unsaved changes warning - Real-time font size preview - Auto-size algorithm: increases until overflow, then decreases to fit - Text editing: Click selected box to edit content inline Entry Point: - Edit button added to QuickActions floating menu - Page selector modal for dual-page view - Seamless navigation between reader and editor Closes #145 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/lib/catalog/pages.ts | 23 + src/lib/components/Editor/EditCanvas.svelte | 130 ++++++ src/lib/components/Editor/EditToolbar.svelte | 101 +++++ src/lib/components/Editor/EditableBox.svelte | 393 ++++++++++++++++++ src/lib/components/Reader/QuickActions.svelte | 51 ++- src/lib/components/Reader/Reader.svelte | 5 +- src/lib/types/index.ts | 1 + .../[volume]/edit/[pageIndex]/+page.svelte | 246 +++++++++++ 8 files changed, 946 insertions(+), 4 deletions(-) create mode 100644 src/lib/catalog/pages.ts create mode 100644 src/lib/components/Editor/EditCanvas.svelte create mode 100644 src/lib/components/Editor/EditToolbar.svelte create mode 100644 src/lib/components/Editor/EditableBox.svelte create mode 100644 src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte diff --git a/src/lib/catalog/pages.ts b/src/lib/catalog/pages.ts new file mode 100644 index 00000000..53dab9ff --- /dev/null +++ b/src/lib/catalog/pages.ts @@ -0,0 +1,23 @@ +import type { VolumeData, Page } from '$lib/types'; + +/** + * Get the current working version of pages. + * Returns edited version if it exists, otherwise original. + */ +export function getCurrentPages(volumeData: VolumeData): Page[] { + return volumeData.edited_pages ?? volumeData.pages; +} + +/** + * Check if volume has edits + */ +export function hasEdits(volumeData: VolumeData): boolean { + return volumeData.edited_pages !== undefined; +} + +/** + * Get original pages (for reference in editor) + */ +export function getOriginalPages(volumeData: VolumeData): Page[] { + return volumeData.pages; +} diff --git a/src/lib/components/Editor/EditCanvas.svelte b/src/lib/components/Editor/EditCanvas.svelte new file mode 100644 index 00000000..9a37c7ce --- /dev/null +++ b/src/lib/components/Editor/EditCanvas.svelte @@ -0,0 +1,130 @@ + + +
+
+ + Manga page + + + {#each workingBlocks as block, index (index)} + handleSelect(index)} + onUpdate={(updatedBlock) => updateBlock(index, updatedBlock)} + onDelete={() => deleteBlock(index)} + onClone={() => cloneBlock(index)} + bind:selectedIndex + /> + {/each} + + + +
+
diff --git a/src/lib/components/Editor/EditToolbar.svelte b/src/lib/components/Editor/EditToolbar.svelte new file mode 100644 index 00000000..23fb13e9 --- /dev/null +++ b/src/lib/components/Editor/EditToolbar.svelte @@ -0,0 +1,101 @@ + + + + + + +
+ Page {pageIndex + 1} / {totalPages} +
+ + + + + +
+ + {:else} +
+ {#each block.lines as line} +

{line}

+ {/each} +
+ {/if} + + {#if isSelected} + + + + + + + + + + + + + + + {/if} +
+ + diff --git a/src/lib/components/Reader/QuickActions.svelte b/src/lib/components/Reader/QuickActions.svelte index d30e4bcb..2abd258f 100644 --- a/src/lib/components/Reader/QuickActions.svelte +++ b/src/lib/components/Reader/QuickActions.svelte @@ -2,14 +2,15 @@ import { goto } from '$app/navigation'; import { page } from '$app/stores'; import { toggleFullScreen, zoomFitToScreen } from '$lib/panzoom'; - import { SpeedDial, SpeedDialButton } from 'flowbite-svelte'; + import { SpeedDial, SpeedDialButton, Modal, Button } from 'flowbite-svelte'; import { settings } from '$lib/settings'; import { ArrowLeftOutline, ArrowRightOutline, CompressOutline, ImageOutline, - ZoomOutOutline + ZoomOutOutline, + EditOutline } from 'flowbite-svelte-icons'; import { imageToWebp, showCropper, updateLastCard } from '$lib/anki-connect'; import { promptConfirmation } from '$lib/util'; @@ -19,11 +20,14 @@ right: (_e: any, ingoreTimeOut?: boolean) => void; src1: File | undefined; src2: File | undefined; + currentPage: number; + showSecondPage: boolean; } - let { left, right, src1, src2 }: Props = $props(); + let { left, right, src1, src2, currentPage, showSecondPage }: Props = $props(); let open = $state(false); + let showPageSelector = $state(false); function handleZoom() { zoomFitToScreen(); @@ -40,6 +44,25 @@ open = false; } + function handleEditPage() { + open = false; + + if (showSecondPage) { + // Show modal to select which page to edit + showPageSelector = true; + } else { + // Navigate directly to edit the current page + navigateToEditPage(currentPage - 1); // Convert to 0-based index + } + } + + function navigateToEditPage(pageIndex: number) { + const manga = $page.params.manga; + const volume = $page.params.volume; + goto(`/${manga}/${volume}/edit/${pageIndex}`); + showPageSelector = false; + } + async function onUpdateCard(src: File | undefined) { if ($settings.ankiConnectSettings.enabled && src) { if ($settings.ankiConnectSettings.cropImage) { @@ -85,5 +108,27 @@ + + + {/if} + + +
+

+ Which page do you want to edit? +

+
+ + +
+
+ +
+
+
diff --git a/src/lib/components/Reader/Reader.svelte b/src/lib/components/Reader/Reader.svelte index 13474f40..b0c77cdd 100644 --- a/src/lib/components/Reader/Reader.svelte +++ b/src/lib/components/Reader/Reader.svelte @@ -2,6 +2,7 @@ import { run } from 'svelte/legacy'; import { currentSeries, currentVolume, currentVolumeData } from '$lib/catalog'; + import { getCurrentPages } from '$lib/catalog/pages'; import { Panzoom, panzoomStore, @@ -360,7 +361,7 @@ } }); - let pages = $derived(volumeData?.pages || []); + let pages = $derived(volumeData ? getCurrentPages(volumeData) : []); let page = $derived($progress?.[volume?.volume_uuid || 0] || 1); let index = $derived(page - 1); @@ -543,6 +544,8 @@ {right} src1={volumeData.files ? Object.values(volumeData.files)[index] : undefined} src2={!useSinglePage && volumeData.files ? Object.values(volumeData.files)[index + 1] : undefined} + currentPage={page} + showSecondPage={showSecondPage()} /> diff --git a/src/lib/types/index.ts b/src/lib/types/index.ts index edc1e34c..4075489c 100644 --- a/src/lib/types/index.ts +++ b/src/lib/types/index.ts @@ -42,5 +42,6 @@ export interface VolumeMetadata { export interface VolumeData { volume_uuid: string; pages: Page[]; + edited_pages?: Page[]; files?: Record; } diff --git a/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte b/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte new file mode 100644 index 00000000..2f0d8852 --- /dev/null +++ b/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte @@ -0,0 +1,246 @@ + + + + Edit Page {pageIndex + 1} + + +{#if isLoading} +
+ +
+{:else if volumeData && pageData} + navigatePage(pageIndex - 1)} + onNext={() => navigatePage(pageIndex + 1)} + onSave={saveEdits} + onExport={exportMokuro} + onExit={exitToReader} + onZoomChange={(mode) => (zoomMode = mode)} + /> + + + + +
+

+ You have unsaved changes. Save before leaving? +

+
+ + + +
+
+
+{:else} +
+

Failed to load page data

+
+{/if} From 3f5666df1119339e834355825ed1ab8403536398 Mon Sep 17 00:00:00 2001 From: Gnathonic Date: Sun, 9 Nov 2025 12:55:16 -0700 Subject: [PATCH 02/10] fix: Resolve DataCloneError when saving edits by serializing Svelte state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed DataCloneError that occurred when saving text box edits to IndexedDB. The issue was caused by attempting to store Svelte $state proxies and Dexie IndexedDB objects directly, which cannot be cloned by the structured clone algorithm. ## Changes - Changed from `structuredClone()` to `JSON.parse(JSON.stringify())` for cloning pages - Added serialization of `workingBlocks` before assignment to strip Svelte reactivity - Updated comments to clarify the serialization is needed for both IndexedDB objects and Svelte proxies ## Technical Details The error occurred in two places: 1. `currentPages` from Dexie had properties that couldn't be cloned 2. `workingBlocks` as a `$state` variable was wrapped in Svelte proxies JSON serialization strips all non-serializable properties and proxies, leaving only plain data that IndexedDB can store. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../[volume]/edit/[pageIndex]/+page.svelte | 50 +++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte b/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte index 2f0d8852..e008a2d7 100644 --- a/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte +++ b/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte @@ -5,7 +5,8 @@ import { getCurrentPages, getOriginalPages } from '$lib/catalog/pages'; import type { VolumeData, Page, Block } from '$lib/types'; import { onMount } from 'svelte'; - import { Button, Modal, Spinner } from 'flowbite-svelte'; + import { Button, Modal, Spinner, Toast } from 'flowbite-svelte'; + import { CheckCircleSolid, CloseCircleSolid } from 'flowbite-svelte-icons'; import EditToolbar from '$lib/components/Editor/EditToolbar.svelte'; import EditCanvas from '$lib/components/Editor/EditCanvas.svelte'; @@ -23,6 +24,9 @@ let showUnsavedWarning = $state(false); let pendingNavigation: (() => void) | null = null; let zoomMode = $state('fit-screen'); + let showSaveSuccess = $state(false); + let showSaveError = $state(false); + let saveErrorMessage = $state(''); let pageData = $derived(volumeData ? getCurrentPages(volumeData)[pageIndex] : undefined); let totalPages = $derived(volumeData ? getCurrentPages(volumeData).length : 0); @@ -90,8 +94,9 @@ const currentPages = getCurrentPages(volumeData); // Create a copy and update the current page's blocks - const updatedPages = structuredClone(currentPages); - updatedPages[pageIndex].blocks = workingBlocks; + // Use JSON parse/stringify to avoid cloning issues with IndexedDB objects and Svelte proxies + const updatedPages = JSON.parse(JSON.stringify(currentPages)); + updatedPages[pageIndex].blocks = JSON.parse(JSON.stringify(workingBlocks)); // Save to edited_pages field await db.volumes_data.update(volumeUuid, { @@ -102,9 +107,22 @@ volumeData.edited_pages = updatedPages; hasUnsavedChanges = false; + // Show success toast + showSaveSuccess = true; + setTimeout(() => { + showSaveSuccess = false; + }, 3000); + console.log('Edits saved successfully'); } catch (error) { console.error('Failed to save edits:', error); + + // Show error toast + saveErrorMessage = error instanceof Error ? error.message : 'Unknown error'; + showSaveError = true; + setTimeout(() => { + showSaveError = false; + }, 5000); } } @@ -239,6 +257,32 @@ + + + + + + + Edits saved successfully + + + + + + + + Failed to save: {saveErrorMessage} + {:else}

Failed to load page data

From b315dd15c5121107c22e96e1c9a4206f13a9f9f4 Mon Sep 17 00:00:00 2001 From: Gnathonic Date: Sun, 9 Nov 2025 12:58:36 -0700 Subject: [PATCH 03/10] feat: Add edit indicators and revert functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added visual indicators and the ability to revert edits to original mokuro data. ## Changes **Visual Indicators:** - Added yellow pencil badge to VolumeItem component (both list and grid views) - Badge appears when a volume has edited pages - Uses `hasEdits()` utility to check for `edited_pages` field **Revert Functionality:** - Added "Revert to Original" button in EditToolbar - Button only appears when volume has edits - Shows confirmation dialog before reverting - Removes `edited_pages` field from database - Reloads page data to show original content - Shows success/error toasts **Component Updates:** - VolumeItem.svelte: Added `volumeHasEdits` state and pencil badge - EditToolbar.svelte: Added `hasEdits` prop and revert button - edit/+page.svelte: Added `volumeHasEdits` derived, `revertToOriginal()` function 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/lib/components/Editor/EditToolbar.svelte | 14 ++++++- src/lib/components/VolumeItem.svelte | 24 ++++++++++-- .../[volume]/edit/[pageIndex]/+page.svelte | 38 ++++++++++++++++++- 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/src/lib/components/Editor/EditToolbar.svelte b/src/lib/components/Editor/EditToolbar.svelte index 23fb13e9..292dd80f 100644 --- a/src/lib/components/Editor/EditToolbar.svelte +++ b/src/lib/components/Editor/EditToolbar.svelte @@ -5,7 +5,8 @@ CaretRightSolid, FloppyDiskSolid, DownloadSolid, - XSolid + XSolid, + ArrowRotateBackwardSolid } from 'flowbite-svelte-icons'; type ZoomMode = 'fit-screen' | 'fit-width' | 'original'; @@ -14,12 +15,14 @@ pageIndex: number; totalPages: number; hasUnsavedChanges: boolean; + hasEdits: boolean; zoomMode: ZoomMode; onPrev: () => void; onNext: () => void; onSave: () => void; onExport: () => void; onExit: () => void; + onRevert: () => void; onZoomChange: (mode: ZoomMode) => void; } @@ -27,12 +30,14 @@ pageIndex, totalPages, hasUnsavedChanges, + hasEdits, zoomMode = $bindable(), onPrev, onNext, onSave, onExport, onExit, + onRevert, onZoomChange }: Props = $props(); @@ -93,6 +98,13 @@ Save + {#if hasEdits} + + {/if} + {#if hasEdits} + + +
+ + + {/if} {:else}

Failed to load page data

From 79f956c54f568357858e7fc4dd0170dae0defa04 Mon Sep 17 00:00:00 2001 From: Gnathonic Date: Sun, 9 Nov 2025 13:02:37 -0700 Subject: [PATCH 05/10] fix: Use correct Flowbite icon for revert button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed from ArrowRotateBackwardSolid (doesn't exist) to UndoOutline. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/lib/components/Editor/EditToolbar.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/components/Editor/EditToolbar.svelte b/src/lib/components/Editor/EditToolbar.svelte index 48592966..e952fb8c 100644 --- a/src/lib/components/Editor/EditToolbar.svelte +++ b/src/lib/components/Editor/EditToolbar.svelte @@ -6,7 +6,7 @@ FloppyDiskSolid, DownloadSolid, XSolid, - ArrowRotateBackwardSolid, + UndoOutline, EyeSolid } from 'flowbite-svelte-icons'; @@ -108,7 +108,7 @@ {/if} From 3106b590cfa8d3d80ce4e278f9ee1bd7a21d4a1f Mon Sep 17 00:00:00 2001 From: Gnathonic Date: Sun, 9 Nov 2025 13:06:08 -0700 Subject: [PATCH 06/10] feat: Replace comparison view with add textbox feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed the comparison modal and added the ability to create new text boxes. ## Changes **Add Textbox Button:** - Added purple "Add Textbox" button in EditToolbar - Creates new empty text box in center of page - Default size: 200x100px, font size 20px - Auto-selects new box for immediate editing - Marks page as having unsaved changes **Removed Features:** - Removed comparison modal (original vs edited view) - Removed onCompare callback and related code - Removed unused imports (getOriginalPages, Modal) **Component Updates:** - EditToolbar.svelte: Replaced Compare button with Add Textbox - edit/+page.svelte: Added addTextbox() function, removed compare modal This makes it easier to add custom text annotations to manga pages. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/lib/components/Editor/EditToolbar.svelte | 16 +-- .../[volume]/edit/[pageIndex]/+page.svelte | 114 +++++------------- 2 files changed, 38 insertions(+), 92 deletions(-) diff --git a/src/lib/components/Editor/EditToolbar.svelte b/src/lib/components/Editor/EditToolbar.svelte index e952fb8c..c4d8ddf2 100644 --- a/src/lib/components/Editor/EditToolbar.svelte +++ b/src/lib/components/Editor/EditToolbar.svelte @@ -7,7 +7,7 @@ DownloadSolid, XSolid, UndoOutline, - EyeSolid + PlusSolid } from 'flowbite-svelte-icons'; type ZoomMode = 'fit-screen' | 'fit-width' | 'original'; @@ -24,7 +24,7 @@ onExport: () => void; onExit: () => void; onRevert: () => void; - onCompare: () => void; + onAddBox: () => void; onZoomChange: (mode: ZoomMode) => void; } @@ -40,7 +40,7 @@ onExport, onExit, onRevert, - onCompare, + onAddBox, onZoomChange }: Props = $props(); @@ -101,12 +101,12 @@ Save - {#if hasEdits} - + + {#if hasEdits} -
- - - {/if} {:else}

Failed to load page data

From 88a47de7c197211819b966b28aa95efac2b9b048 Mon Sep 17 00:00:00 2001 From: Gnathonic Date: Sun, 9 Nov 2025 13:08:36 -0700 Subject: [PATCH 07/10] fix: Correct icon name and separate revert toast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed two issues: 1. Changed PlusSolid to CirclePlusSolid (correct Flowbite icon name) 2. Added separate toast for revert operations to avoid confusion ## Changes - EditToolbar.svelte: Use CirclePlusSolid instead of PlusSolid - edit/+page.svelte: Added showRevertSuccess state and toast - Revert now shows "Reverted to original" instead of "Edits saved successfully" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/lib/components/Editor/EditToolbar.svelte | 4 ++-- .../[volume]/edit/[pageIndex]/+page.svelte | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/lib/components/Editor/EditToolbar.svelte b/src/lib/components/Editor/EditToolbar.svelte index c4d8ddf2..d5820f57 100644 --- a/src/lib/components/Editor/EditToolbar.svelte +++ b/src/lib/components/Editor/EditToolbar.svelte @@ -7,7 +7,7 @@ DownloadSolid, XSolid, UndoOutline, - PlusSolid + CirclePlusSolid } from 'flowbite-svelte-icons'; type ZoomMode = 'fit-screen' | 'fit-width' | 'original'; @@ -102,7 +102,7 @@ diff --git a/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte b/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte index 9ad4aff6..8d3281ee 100644 --- a/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte +++ b/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte @@ -28,6 +28,7 @@ let showSaveSuccess = $state(false); let showSaveError = $state(false); let saveErrorMessage = $state(''); + let showRevertSuccess = $state(false); let pageData = $derived(volumeData ? getCurrentPages(volumeData)[pageIndex] : undefined); let totalPages = $derived(volumeData ? getCurrentPages(volumeData).length : 0); @@ -220,9 +221,9 @@ await loadVolumeData(); // Show success toast - showSaveSuccess = true; + showRevertSuccess = true; setTimeout(() => { - showSaveSuccess = false; + showRevertSuccess = false; }, 3000); } catch (error) { console.error('Failed to revert edits:', error); @@ -349,6 +350,19 @@ Failed to save: {saveErrorMessage} + + + + + + + Reverted to original + {:else}

Failed to load page data

From a7c530a5c981f90556698a0f2cfd618c71762760 Mon Sep 17 00:00:00 2001 From: Gnathonic Date: Sun, 9 Nov 2025 22:38:01 -0700 Subject: [PATCH 08/10] feat: Auto-resize font during textbox resize and remove manual button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implemented automatic font sizing during resize operations and removed the redundant manual auto-size button. ## Changes **Auto-resize during drag:** - Font size now automatically adjusts as you resize a text box - Throttled to every 10th frame for smooth performance - Final auto-size runs when resize completes for perfect fitting - Uses fast overflow detection to increase/decrease font size **Removed manual button:** - Removed auto-size button (now redundant) - Removed TextSizeOutline icon import - Removed handle-font-auto CSS styles - Updated resize handle tooltip to indicate auto-sizing **Bug fixes:** - Added data-block-index attribute for DOM queries - Added text-container class for overflow detection - Fixed page navigation not updating text boxes - Added $effect to reload workingBlocks on page change - Removed transition-all for instant position updates - Fixed Modal import that was accidentally removed - Fixed toast showing on page load with isMounted guard **Technical improvements:** - Used JSON serialization instead of structuredClone for Svelte state - Throttled auto-size to prevent blocking resize operations - requestAnimationFrame for non-blocking font updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/lib/components/Editor/EditableBox.svelte | 88 ++++++++++++++----- .../[volume]/edit/[pageIndex]/+page.svelte | 22 ++++- 2 files changed, 82 insertions(+), 28 deletions(-) diff --git a/src/lib/components/Editor/EditableBox.svelte b/src/lib/components/Editor/EditableBox.svelte index a5e6a847..8dc63f17 100644 --- a/src/lib/components/Editor/EditableBox.svelte +++ b/src/lib/components/Editor/EditableBox.svelte @@ -1,6 +1,6 @@
-
- - Manga page - - - {#each workingBlocks as block, index (index)} - handleSelect(index)} - onUpdate={(updatedBlock) => updateBlock(index, updatedBlock)} - onDelete={() => deleteBlock(index)} - onClone={() => cloneBlock(index)} - bind:selectedIndex - /> - {/each} - - - -
+
+ + Manga page + + + {#each workingBlocks as block, index (index)} + handleSelect(index)} + onUpdate={(updatedBlock) => updateBlock(index, updatedBlock)} + onDelete={() => deleteBlock(index)} + onClone={() => cloneBlock(index)} + bind:selectedIndex + /> + {/each} + + + +
diff --git a/src/lib/components/Editor/EditToolbar.svelte b/src/lib/components/Editor/EditToolbar.svelte index ca399557..568a55f3 100644 --- a/src/lib/components/Editor/EditToolbar.svelte +++ b/src/lib/components/Editor/EditToolbar.svelte @@ -1,121 +1,121 @@ - - + + -
- Page {pageIndex + 1} / {totalPages} -
+
+ Page {pageIndex + 1} / {totalPages} +
- + - + -
- +
+
- - {#if hasUnsavedChanges} - Unsaved changes - {/if} + + {#if hasUnsavedChanges} + Unsaved changes + {/if} - + - + - {#if hasEdits} - - {/if} + {#if hasEdits} + + {/if} - - + +
diff --git a/src/lib/components/Editor/EditableBox.svelte b/src/lib/components/Editor/EditableBox.svelte index 8dc63f17..a4a349c2 100644 --- a/src/lib/components/Editor/EditableBox.svelte +++ b/src/lib/components/Editor/EditableBox.svelte @@ -1,433 +1,448 @@
- - {#if isEditingText} - - {:else} -
- {#each block.lines as line} -

{line}

- {/each} -
- {/if} - - {#if isSelected} - - - - - - - - - - - - {/if} + + {#if isEditingText} + + {:else} +
+ {#each block.lines as line} +

{line}

+ {/each} +
+ {/if} + + {#if isSelected} + + + + + + + + + + + + {/if}
diff --git a/src/lib/components/Reader/QuickActions.svelte b/src/lib/components/Reader/QuickActions.svelte index b2f546b4..c52ccca3 100644 --- a/src/lib/components/Reader/QuickActions.svelte +++ b/src/lib/components/Reader/QuickActions.svelte @@ -1,186 +1,186 @@ {#if $settings.quickActions} -
- - {#if open} -
- {#if $settings.ankiConnectSettings.enabled} - - {/if} - {#if $settings.ankiConnectSettings.enabled && src2} - - {/if} - - - - - -
- {/if} +
+ + {#if open} +
+ {#if $settings.ankiConnectSettings.enabled} + + {/if} + {#if $settings.ankiConnectSettings.enabled && src2} + + {/if} + + + + + +
+ {/if} - - -
+ + +
{/if} -
-

- Which page do you want to edit? -

-
- - -
-
- -
-
+
+

+ Which page do you want to edit? +

+
+ + +
+
+ +
+
diff --git a/src/lib/components/VolumeItem.svelte b/src/lib/components/VolumeItem.svelte index 8d550a96..b36ec888 100644 --- a/src/lib/components/VolumeItem.svelte +++ b/src/lib/components/VolumeItem.svelte @@ -402,7 +402,7 @@ {/if}
{#if volumeHasEdits} - + {/if} diff --git a/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte b/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte index 4c992416..9feb33c1 100644 --- a/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte +++ b/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte @@ -1,387 +1,372 @@ - Edit Page {pageIndex + 1} + Edit Page {pageIndex + 1} {#if isLoading} -
- -
+
+ +
{:else if volumeData && pageData} - navigatePage(pageIndex - 1)} - onNext={() => navigatePage(pageIndex + 1)} - onSave={saveEdits} - onExport={exportMokuro} - onExit={exitToReader} - onRevert={revertToOriginal} - onAddBox={addTextbox} - onZoomChange={(mode) => (zoomMode = mode)} - /> - - - - -
-

- You have unsaved changes. Save before leaving? -

-
- - - -
-
-
- - - {#if isMounted && showSaveSuccess} - - {#snippet icon()} - - {/snippet} - Edits saved successfully - - {/if} - - - {#if isMounted && showSaveError} - - {#snippet icon()} - - {/snippet} - Failed to save: {saveErrorMessage} - - {/if} - - - {#if isMounted && showRevertSuccess} - - {#snippet icon()} - - {/snippet} - Reverted to original - - {/if} + navigatePage(pageIndex - 1)} + onNext={() => navigatePage(pageIndex + 1)} + onSave={saveEdits} + onExport={exportMokuro} + onExit={exitToReader} + onRevert={revertToOriginal} + onAddBox={addTextbox} + onZoomChange={(mode) => (zoomMode = mode)} + /> + + + + +
+

+ You have unsaved changes. Save before leaving? +

+
+ + + +
+
+
+ + + {#if isMounted && showSaveSuccess} + + {#snippet icon()} + + {/snippet} + Edits saved successfully + + {/if} + + + {#if isMounted && showSaveError} + + {#snippet icon()} + + {/snippet} + Failed to save: {saveErrorMessage} + + {/if} + + + {#if isMounted && showRevertSuccess} + + {#snippet icon()} + + {/snippet} + Reverted to original + + {/if} {:else} -
-

Failed to load page data

-
+
+

Failed to load page data

+
{/if} From 362aa0273b6fc85dca7dab69a3e7f57bb7ecdf5c Mon Sep 17 00:00:00 2001 From: Gnathonic Date: Mon, 24 Nov 2025 20:52:13 -0700 Subject: [PATCH 10/10] fix: Replace structuredClone with JSON parse/stringify for ESLint compatibility --- src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte b/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte index 9feb33c1..aa61792c 100644 --- a/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte +++ b/src/routes/[manga]/[volume]/edit/[pageIndex]/+page.svelte @@ -65,7 +65,7 @@ volumeData = data; // Initialize working blocks with current page's blocks (edited or original) const currentPages = getCurrentPages(data); - workingBlocks = structuredClone(currentPages[pageIndex]?.blocks || []); + workingBlocks = JSON.parse(JSON.stringify(currentPages[pageIndex]?.blocks || [])); } } catch (error) { console.error('Failed to load volume data:', error); @@ -88,7 +88,7 @@ } function cloneBlock(index: number): number { - const clonedBlock = structuredClone(workingBlocks[index]); + const clonedBlock = JSON.parse(JSON.stringify(workingBlocks[index])); // Offset the cloned box slightly const offset = 20; clonedBlock.box = [