forked from akai/readest
* feat(reader): make fixed-layout scroll gap configurable (foliate-js bump) (#3647) * feat(reader): add webtoonMode view setting + scroll-gap helper (#3647) * feat(reader): Webtoon Mode toggle in the fixed-layout view menu (#3647) * feat(reader): apply Webtoon Mode gap on fixed-layout book open (#3647) * fix(reader): clear Webtoon Mode + reset gap when Shift+J leaves scrolled (#3647) * chore(i18n): translate Webtoon Mode string across locales (#3647) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(deps): bump foliate-js to merged readest/foliate-js#30 (#3647) --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1906,5 +1906,6 @@
|
||||
"biometrics": "البيومترية",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "أظهر تلميحًا قصيرًا بلغتك الأم فوق الكلمات الصعبة. الكلمات التي تفوق مستواك تحصل على تلميح.",
|
||||
"Level": "المستوى",
|
||||
"CEFR level": "مستوى CEFR"
|
||||
"CEFR level": "مستوى CEFR",
|
||||
"Webtoon Mode": "وضع الويبتون"
|
||||
}
|
||||
|
||||
@@ -1774,5 +1774,6 @@
|
||||
"biometrics": "বায়োমেট্রিক্স",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "কঠিন শব্দের উপরে আপনার মাতৃভাষায় একটি সংক্ষিপ্ত ইঙ্গিত দেখান। আপনার স্তরের উপরের শব্দগুলো একটি ইঙ্গিত পায়।",
|
||||
"Level": "স্তর",
|
||||
"CEFR level": "CEFR স্তর"
|
||||
"CEFR level": "CEFR স্তর",
|
||||
"Webtoon Mode": "ওয়েবটুন মোড"
|
||||
}
|
||||
|
||||
@@ -1741,5 +1741,6 @@
|
||||
"biometrics": "ལུས་རྟགས་ངོ་སྤྲོད།",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "ཚིག་དཀའ་མོའི་སྟེང་དུ་རང་གི་སྐད་ཡིག་ཐོག་ནས་བརྡ་སྟོན་ཐུང་ངུ་ཞིག་སྟོན། ཁྱེད་ཀྱི་རིམ་པ་ལས་མཐོ་བའི་ཚིག་ལ་བརྡ་སྟོན་ཐོབ།",
|
||||
"Level": "རིམ་པ།",
|
||||
"CEFR level": "CEFR རིམ་པ།"
|
||||
"CEFR level": "CEFR རིམ་པ།",
|
||||
"Webtoon Mode": "Webtoon སྤྱོད་ཚུལ།"
|
||||
}
|
||||
|
||||
@@ -1774,5 +1774,6 @@
|
||||
"biometrics": "Biometrie",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Zeigt einen kurzen Hinweis in deiner Muttersprache über schwierigen Wörtern an. Wörter über deinem Niveau erhalten einen Hinweis.",
|
||||
"Level": "Niveau",
|
||||
"CEFR level": "CEFR-Niveau"
|
||||
"CEFR level": "CEFR-Niveau",
|
||||
"Webtoon Mode": "Webtoon-Modus"
|
||||
}
|
||||
|
||||
@@ -1774,5 +1774,6 @@
|
||||
"biometrics": "βιομετρικά",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Εμφανίζει μια σύντομη υπόδειξη στη μητρική σου γλώσσα πάνω από δύσκολες λέξεις. Οι λέξεις πάνω από το επίπεδό σου λαμβάνουν υπόδειξη.",
|
||||
"Level": "Επίπεδο",
|
||||
"CEFR level": "Επίπεδο CEFR"
|
||||
"CEFR level": "Επίπεδο CEFR",
|
||||
"Webtoon Mode": "Λειτουργία webtoon"
|
||||
}
|
||||
|
||||
@@ -1807,5 +1807,6 @@
|
||||
"biometrics": "biometría",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Muestra una breve pista en tu idioma nativo encima de las palabras difíciles. Las palabras por encima de tu nivel reciben una pista.",
|
||||
"Level": "Nivel",
|
||||
"CEFR level": "Nivel CEFR"
|
||||
"CEFR level": "Nivel CEFR",
|
||||
"Webtoon Mode": "Modo webtoon"
|
||||
}
|
||||
|
||||
@@ -1774,5 +1774,6 @@
|
||||
"biometrics": "بیومتریک",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "یک راهنمای کوتاه به زبان مادریتان بالای واژههای دشوار نشان میدهد. واژههای بالاتر از سطح شما راهنما میگیرند.",
|
||||
"Level": "سطح",
|
||||
"CEFR level": "سطح CEFR"
|
||||
"CEFR level": "سطح CEFR",
|
||||
"Webtoon Mode": "حالت وبتون"
|
||||
}
|
||||
|
||||
@@ -1807,5 +1807,6 @@
|
||||
"biometrics": "biométrie",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Affiche une courte indication dans votre langue maternelle au-dessus des mots difficiles. Les mots au-dessus de votre niveau reçoivent une indication.",
|
||||
"Level": "Niveau",
|
||||
"CEFR level": "Niveau CEFR"
|
||||
"CEFR level": "Niveau CEFR",
|
||||
"Webtoon Mode": "Mode webtoon"
|
||||
}
|
||||
|
||||
@@ -1807,5 +1807,6 @@
|
||||
"biometrics": "ביומטריה",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "מציג רמז קצר בשפת האם שלך מעל מילים קשות. מילים מעל הרמה שלך מקבלות רמז.",
|
||||
"Level": "רמה",
|
||||
"CEFR level": "רמת CEFR"
|
||||
"CEFR level": "רמת CEFR",
|
||||
"Webtoon Mode": "מצב ובטון"
|
||||
}
|
||||
|
||||
@@ -1774,5 +1774,6 @@
|
||||
"biometrics": "बायोमेट्रिक्स",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "कठिन शब्दों के ऊपर आपकी मातृभाषा में एक छोटा संकेत दिखाता है। आपके स्तर से ऊपर के शब्दों को संकेत मिलता है।",
|
||||
"Level": "स्तर",
|
||||
"CEFR level": "CEFR स्तर"
|
||||
"CEFR level": "CEFR स्तर",
|
||||
"Webtoon Mode": "वेबटून मोड"
|
||||
}
|
||||
|
||||
@@ -1774,5 +1774,6 @@
|
||||
"biometrics": "biometria",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Rövid anyanyelvi tippet jelenít meg a nehéz szavak fölött. A szintednél nehezebb szavak kapnak tippet.",
|
||||
"Level": "Szint",
|
||||
"CEFR level": "CEFR-szint"
|
||||
"CEFR level": "CEFR-szint",
|
||||
"Webtoon Mode": "Webtoon mód"
|
||||
}
|
||||
|
||||
@@ -1741,5 +1741,6 @@
|
||||
"biometrics": "biometrik",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Menampilkan petunjuk singkat dalam bahasa ibu Anda di atas kata-kata sulit. Kata-kata di atas level Anda mendapat petunjuk.",
|
||||
"Level": "Level",
|
||||
"CEFR level": "Level CEFR"
|
||||
"CEFR level": "Level CEFR",
|
||||
"Webtoon Mode": "Mode webtoon"
|
||||
}
|
||||
|
||||
@@ -1807,5 +1807,6 @@
|
||||
"biometrics": "biometria",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Mostra un breve suggerimento nella tua lingua madre sopra le parole difficili. Le parole sopra il tuo livello ricevono un suggerimento.",
|
||||
"Level": "Livello",
|
||||
"CEFR level": "Livello CEFR"
|
||||
"CEFR level": "Livello CEFR",
|
||||
"Webtoon Mode": "Modalità webtoon"
|
||||
}
|
||||
|
||||
@@ -1741,5 +1741,6 @@
|
||||
"biometrics": "生体認証",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "難しい単語の上に母語の短いヒントを表示します。あなたのレベルを超える単語にヒントが付きます。",
|
||||
"Level": "レベル",
|
||||
"CEFR level": "CEFRレベル"
|
||||
"CEFR level": "CEFRレベル",
|
||||
"Webtoon Mode": "ウェブトゥーンモード"
|
||||
}
|
||||
|
||||
@@ -1741,5 +1741,6 @@
|
||||
"biometrics": "생체 인증",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "어려운 단어 위에 모국어로 된 짧은 힌트를 표시합니다. 내 수준보다 높은 단어에 힌트가 표시됩니다.",
|
||||
"Level": "수준",
|
||||
"CEFR level": "CEFR 수준"
|
||||
"CEFR level": "CEFR 수준",
|
||||
"Webtoon Mode": "웹툰 모드"
|
||||
}
|
||||
|
||||
@@ -1741,5 +1741,6 @@
|
||||
"biometrics": "biometrik",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Menunjukkan petunjuk ringkas dalam bahasa ibunda anda di atas perkataan sukar. Perkataan melebihi tahap anda akan mendapat petunjuk.",
|
||||
"Level": "Tahap",
|
||||
"CEFR level": "Tahap CEFR"
|
||||
"CEFR level": "Tahap CEFR",
|
||||
"Webtoon Mode": "Mode webtoon"
|
||||
}
|
||||
|
||||
@@ -1774,5 +1774,6 @@
|
||||
"biometrics": "biometrie",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Toont een korte hint in je moedertaal boven moeilijke woorden. Woorden boven jouw niveau krijgen een hint.",
|
||||
"Level": "Niveau",
|
||||
"CEFR level": "CEFR-niveau"
|
||||
"CEFR level": "CEFR-niveau",
|
||||
"Webtoon Mode": "Webtoon-modus"
|
||||
}
|
||||
|
||||
@@ -1840,5 +1840,6 @@
|
||||
"biometrics": "biometria",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Pokazuje krótką wskazówkę w Twoim języku ojczystym nad trudnymi słowami. Słowa powyżej Twojego poziomu otrzymują wskazówkę.",
|
||||
"Level": "Poziom",
|
||||
"CEFR level": "Poziom CEFR"
|
||||
"CEFR level": "Poziom CEFR",
|
||||
"Webtoon Mode": "Tryb webtoon"
|
||||
}
|
||||
|
||||
@@ -1807,5 +1807,6 @@
|
||||
"biometrics": "biometria",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Mostra uma breve dica no seu idioma nativo acima de palavras difíceis. Palavras acima do seu nível recebem uma dica.",
|
||||
"Level": "Nível",
|
||||
"CEFR level": "Nível CEFR"
|
||||
"CEFR level": "Nível CEFR",
|
||||
"Webtoon Mode": "Modo webtoon"
|
||||
}
|
||||
|
||||
@@ -1807,5 +1807,6 @@
|
||||
"biometrics": "biometria",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Mostra uma breve dica na sua língua materna acima de palavras difíceis. Palavras acima do seu nível recebem uma dica.",
|
||||
"Level": "Nível",
|
||||
"CEFR level": "Nível CEFR"
|
||||
"CEFR level": "Nível CEFR",
|
||||
"Webtoon Mode": "Modo webtoon"
|
||||
}
|
||||
|
||||
@@ -1807,5 +1807,6 @@
|
||||
"biometrics": "biometrie",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Afișează un scurt indiciu în limba ta maternă deasupra cuvintelor dificile. Cuvintele peste nivelul tău primesc un indiciu.",
|
||||
"Level": "Nivel",
|
||||
"CEFR level": "Nivel CEFR"
|
||||
"CEFR level": "Nivel CEFR",
|
||||
"Webtoon Mode": "Mod webtoon"
|
||||
}
|
||||
|
||||
@@ -1840,5 +1840,6 @@
|
||||
"biometrics": "биометрия",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Показывает короткую подсказку на вашем родном языке над сложными словами. Слова выше вашего уровня получают подсказку.",
|
||||
"Level": "Уровень",
|
||||
"CEFR level": "Уровень CEFR"
|
||||
"CEFR level": "Уровень CEFR",
|
||||
"Webtoon Mode": "Режим вебтуна"
|
||||
}
|
||||
|
||||
@@ -1774,5 +1774,6 @@
|
||||
"biometrics": "ජෛව මිතික",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "අසීරු වචන මත ඔබේ මව් භාෂාවෙන් කෙටි ඉඟියක් පෙන්වයි. ඔබේ මට්ටමට වඩා ඉහළ වචනවලට ඉඟියක් ලැබේ.",
|
||||
"Level": "මට්ටම",
|
||||
"CEFR level": "CEFR මට්ටම"
|
||||
"CEFR level": "CEFR මට්ටම",
|
||||
"Webtoon Mode": "Webtoon ආකාරය"
|
||||
}
|
||||
|
||||
@@ -1840,5 +1840,6 @@
|
||||
"biometrics": "biometrija",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Prikaže kratek namig v tvojem maternem jeziku nad težkimi besedami. Besede nad tvojo ravnjo dobijo namig.",
|
||||
"Level": "Raven",
|
||||
"CEFR level": "Raven CEFR"
|
||||
"CEFR level": "Raven CEFR",
|
||||
"Webtoon Mode": "Tryb webtoon"
|
||||
}
|
||||
|
||||
@@ -1774,5 +1774,6 @@
|
||||
"biometrics": "biometri",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Visar en kort ledtråd på ditt modersmål ovanför svåra ord. Ord över din nivå får en ledtråd.",
|
||||
"Level": "Nivå",
|
||||
"CEFR level": "CEFR-nivå"
|
||||
"CEFR level": "CEFR-nivå",
|
||||
"Webtoon Mode": "Webtoon-läge"
|
||||
}
|
||||
|
||||
@@ -1774,5 +1774,6 @@
|
||||
"biometrics": "உயிரியல் அடையாளம்",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "கடினமான சொற்களுக்கு மேலே உங்கள் தாய்மொழியில் ஒரு சிறு குறிப்பைக் காட்டும். உங்கள் நிலைக்கு மேலான சொற்களுக்கு குறிப்பு கிடைக்கும்.",
|
||||
"Level": "நிலை",
|
||||
"CEFR level": "CEFR நிலை"
|
||||
"CEFR level": "CEFR நிலை",
|
||||
"Webtoon Mode": "வெப்டூன் பயன்முறை"
|
||||
}
|
||||
|
||||
@@ -1741,5 +1741,6 @@
|
||||
"biometrics": "ข้อมูลไบโอเมตริก",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "แสดงคำใบ้สั้น ๆ ในภาษาแม่ของคุณเหนือคำที่ยาก คำที่สูงกว่าระดับของคุณจะมีคำใบ้",
|
||||
"Level": "ระดับ",
|
||||
"CEFR level": "ระดับ CEFR"
|
||||
"CEFR level": "ระดับ CEFR",
|
||||
"Webtoon Mode": "โหมดเว็บตูน"
|
||||
}
|
||||
|
||||
@@ -1774,5 +1774,6 @@
|
||||
"biometrics": "biyometri",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Zor kelimelerin üzerinde ana dilinizde kısa bir ipucu gösterir. Seviyenizin üzerindeki kelimeler ipucu alır.",
|
||||
"Level": "Seviye",
|
||||
"CEFR level": "CEFR seviyesi"
|
||||
"CEFR level": "CEFR seviyesi",
|
||||
"Webtoon Mode": "Webtoon modu"
|
||||
}
|
||||
|
||||
@@ -1840,5 +1840,6 @@
|
||||
"biometrics": "біометрія",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Показує коротку підказку вашою рідною мовою над складними словами. Слова, вищі за ваш рівень, отримують підказку.",
|
||||
"Level": "Рівень",
|
||||
"CEFR level": "Рівень CEFR"
|
||||
"CEFR level": "Рівень CEFR",
|
||||
"Webtoon Mode": "Режим вебтуна"
|
||||
}
|
||||
|
||||
@@ -1774,5 +1774,6 @@
|
||||
"biometrics": "biometrik",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Qiyin so‘zlar ustida ona tilingizda qisqa maslahat ko‘rsatadi. Darajangizdan yuqori so‘zlarga maslahat beriladi.",
|
||||
"Level": "Daraja",
|
||||
"CEFR level": "CEFR darajasi"
|
||||
"CEFR level": "CEFR darajasi",
|
||||
"Webtoon Mode": "Webtoon rejimi"
|
||||
}
|
||||
|
||||
@@ -1741,5 +1741,6 @@
|
||||
"biometrics": "sinh trắc học",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "Hiển thị gợi ý ngắn bằng tiếng mẹ đẻ của bạn phía trên những từ khó. Những từ trên trình độ của bạn sẽ có gợi ý.",
|
||||
"Level": "Cấp độ",
|
||||
"CEFR level": "Cấp độ CEFR"
|
||||
"CEFR level": "Cấp độ CEFR",
|
||||
"Webtoon Mode": "Chế độ webtoon"
|
||||
}
|
||||
|
||||
@@ -1741,5 +1741,6 @@
|
||||
"biometrics": "生物识别",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "在生词上方显示一条母语简短提示。高于你水平的单词会显示提示。",
|
||||
"Level": "级别",
|
||||
"CEFR level": "CEFR 级别"
|
||||
"CEFR level": "CEFR 级别",
|
||||
"Webtoon Mode": "条漫模式"
|
||||
}
|
||||
|
||||
@@ -1741,5 +1741,6 @@
|
||||
"biometrics": "生物辨識",
|
||||
"Show a short native-language hint above difficult words. Words above your level get a hint.": "在難詞上方顯示一則母語簡短提示。高於你程度的單字會顯示提示。",
|
||||
"Level": "等級",
|
||||
"CEFR level": "CEFR 等級"
|
||||
"CEFR level": "CEFR 等級",
|
||||
"Webtoon Mode": "條漫模式"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { scrollGapToCss } from 'foliate-js/fixed-layout.js';
|
||||
|
||||
describe('scrollGapToCss', () => {
|
||||
it('maps a non-negative number string to a px length', () => {
|
||||
expect(scrollGapToCss('0')).toBe('0px');
|
||||
expect(scrollGapToCss('4')).toBe('4px');
|
||||
expect(scrollGapToCss('12')).toBe('12px');
|
||||
});
|
||||
|
||||
it('returns null for empty / invalid / negative / null so the CSS fallback (4px) applies', () => {
|
||||
expect(scrollGapToCss('')).toBeNull();
|
||||
expect(scrollGapToCss('abc')).toBeNull();
|
||||
expect(scrollGapToCss('-2')).toBeNull();
|
||||
expect(scrollGapToCss(null)).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { getScrollGapAttr } from '@/utils/webtoon';
|
||||
|
||||
describe('getScrollGapAttr', () => {
|
||||
it('returns 0 for Webtoon Mode (seamless, no gap)', () => {
|
||||
expect(getScrollGapAttr(true)).toBe('0');
|
||||
});
|
||||
|
||||
it('returns the default 4 otherwise', () => {
|
||||
expect(getScrollGapAttr(false)).toBe('4');
|
||||
});
|
||||
});
|
||||
@@ -74,6 +74,7 @@ import { getLocale } from '@/utils/misc';
|
||||
import { isMetered } from '@/utils/network';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { isFontType } from '@/utils/font';
|
||||
import { getScrollGapAttr } from '@/utils/webtoon';
|
||||
import { ParagraphControl } from './paragraph';
|
||||
import Spinner from '@/components/Spinner';
|
||||
import KOSyncConflictResolver from './KOSyncResolver';
|
||||
@@ -702,6 +703,7 @@ const FoliateViewer: React.FC<{
|
||||
view.renderer.setAttribute('zoom', viewSettings.zoomMode);
|
||||
view.renderer.setAttribute('spread', viewSettings.spreadMode);
|
||||
view.renderer.setAttribute('scale-factor', viewSettings.zoomLevel);
|
||||
view.renderer.setAttribute('scroll-gap', getScrollGapAttr(viewSettings.webtoonMode));
|
||||
} else {
|
||||
view.renderer.setAttribute('max-column-count', maxColumnCount);
|
||||
view.renderer.setAttribute('max-inline-size', `${maxInlineSize}px`);
|
||||
|
||||
@@ -21,6 +21,7 @@ import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { getStyles } from '@/utils/style';
|
||||
import { navigateToLogin } from '@/utils/nav';
|
||||
import { getScrollGapAttr } from '@/utils/webtoon';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { getMaxInlineSize } from '@/utils/config';
|
||||
import dayjs from 'dayjs';
|
||||
@@ -54,6 +55,7 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
|
||||
|
||||
const { themeMode, isDarkMode, setThemeMode } = useThemeStore();
|
||||
const [isScrolledMode, setScrolledMode] = useState(viewSettings!.scrolled);
|
||||
const [webtoonMode, setWebtoonMode] = useState(viewSettings!.webtoonMode ?? false);
|
||||
const [isParagraphMode, setParagraphMode] = useState(
|
||||
viewSettings?.paragraphMode?.enabled ?? false,
|
||||
);
|
||||
@@ -70,6 +72,7 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
|
||||
const zoomOut = () => setZoomLevel((prev) => Math.max(prev - ZOOM_STEP, MIN_ZOOM_LEVEL));
|
||||
const resetZoom = () => setZoomLevel(100);
|
||||
const toggleScrolledMode = () => setScrolledMode(!isScrolledMode);
|
||||
const toggleWebtoonMode = () => setWebtoonMode(!webtoonMode);
|
||||
const toggleParagraphMode = () => {
|
||||
setParagraphMode(!isParagraphMode);
|
||||
eventDispatcher.dispatch('toggle-paragraph-mode', { bookKey });
|
||||
@@ -119,6 +122,7 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
|
||||
useEffect(() => {
|
||||
if (isScrolledMode === viewSettings!.scrolled) return;
|
||||
viewSettings!.scrolled = isScrolledMode;
|
||||
if (!isScrolledMode && webtoonMode) setWebtoonMode(false);
|
||||
getView(bookKey)?.renderer.setAttribute('flow', isScrolledMode ? 'scrolled' : 'paginated');
|
||||
getView(bookKey)?.renderer.setAttribute(
|
||||
'max-inline-size',
|
||||
@@ -129,6 +133,23 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isScrolledMode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (webtoonMode === viewSettings.webtoonMode) return;
|
||||
viewSettings.webtoonMode = webtoonMode;
|
||||
getView(bookKey)?.renderer.setAttribute('scroll-gap', getScrollGapAttr(webtoonMode));
|
||||
if (webtoonMode) {
|
||||
// Webtoon Mode implies scrolled flow + fit-width (scale-factor 100) so pages
|
||||
// fill the width without horizontal overflow/clipping. Reuse the existing
|
||||
// scrolled / zoomLevel effects rather than duplicating their renderer wiring.
|
||||
if (!isScrolledMode) setScrolledMode(true);
|
||||
if (zoomLevel !== 100) setZoomLevel(100);
|
||||
saveViewSettings(envConfig, bookKey, 'scrolled', true, false, false);
|
||||
}
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
saveViewSettings(envConfig, bookKey, 'webtoonMode', webtoonMode, false, false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [webtoonMode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (zoomLevel === viewSettings.zoomLevel) return;
|
||||
saveViewSettings(envConfig, bookKey, 'zoomLevel', zoomLevel, true, true);
|
||||
@@ -290,6 +311,7 @@ const ViewMenu: React.FC<ViewMenuProps> = ({
|
||||
onClick={() => setKeepCoverSpread(!keepCoverSpread)}
|
||||
disabled={spreadMode === 'none'}
|
||||
/>
|
||||
<MenuItem label={_('Webtoon Mode')} toggled={webtoonMode} onClick={toggleWebtoonMode} />
|
||||
</>
|
||||
<hr aria-hidden='true' className='border-base-300 my-1' />
|
||||
</>
|
||||
|
||||
@@ -11,6 +11,7 @@ import { eventDispatcher } from '@/utils/event';
|
||||
import { setShortcutsDialogVisible } from '@/components/KeyboardShortcutsHelp';
|
||||
import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL, ZOOM_STEP } from '@/services/constants';
|
||||
import { getParagraphActionForKey } from '@/utils/paragraphPresentation';
|
||||
import { getScrollGapAttr } from '@/utils/webtoon';
|
||||
import { viewPagination } from './usePagination';
|
||||
import useShortcuts from '@/hooks/useShortcuts';
|
||||
import useBooksManager from './useBooksManager';
|
||||
@@ -53,6 +54,12 @@ const useBookShortcuts = ({ sideBarBookKey, bookKeys }: UseBookShortcutsProps) =
|
||||
const viewSettings = getViewSettings(sideBarBookKey ?? '');
|
||||
if (viewSettings && sideBarBookKey) {
|
||||
viewSettings.scrolled = !viewSettings.scrolled;
|
||||
// Webtoon Mode requires scrolled flow; leaving scrolled exits Webtoon Mode
|
||||
// and restores the default page gap (mirror the View menu's behavior).
|
||||
if (!viewSettings.scrolled && viewSettings.webtoonMode) {
|
||||
viewSettings.webtoonMode = false;
|
||||
getView(sideBarBookKey)?.renderer.setAttribute('scroll-gap', getScrollGapAttr(false));
|
||||
}
|
||||
setViewSettings(sideBarBookKey, viewSettings!);
|
||||
const flowMode = viewSettings.scrolled ? 'scrolled' : 'paginated';
|
||||
getView(sideBarBookKey)?.renderer.setAttribute('flow', flowMode);
|
||||
|
||||
@@ -241,6 +241,7 @@ export const DEFAULT_BOOK_LAYOUT: BookLayout = {
|
||||
compactMarginRightPx: 16,
|
||||
gapPercent: 5,
|
||||
scrolled: false,
|
||||
webtoonMode: false,
|
||||
noContinuousScroll: false,
|
||||
disableClick: false,
|
||||
disableSwipe: false,
|
||||
|
||||
@@ -184,6 +184,7 @@ export interface BookLayout {
|
||||
compactMarginPx?: number; // deprecated
|
||||
gapPercent: number;
|
||||
scrolled: boolean;
|
||||
webtoonMode: boolean;
|
||||
noContinuousScroll: boolean;
|
||||
disableClick: boolean;
|
||||
disableSwipe: boolean;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* The `scroll-gap` attribute (px) a fixed-layout renderer should use for the
|
||||
* current Webtoon Mode state. `0` = seamless webtoon strip; `4` = the default
|
||||
* inter-page gap. The renderer maps this to the `--scroll-page-gap` CSS var.
|
||||
*/
|
||||
export const getScrollGapAttr = (webtoonMode: boolean): string => (webtoonMode ? '0' : '4');
|
||||
+1
-1
Submodule packages/foliate-js updated: a7b5118c78...1cb26e3fe2
Reference in New Issue
Block a user