@edrlab/thorium-web 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/dist/{ThPreferencesAdapter-D0rzsGRl.d.mts → ThPreferencesAdapter-L1H6gzmu.d.mts} +23 -6
  2. package/dist/{ThSettingsWrapper-BXuRgdqp.d.mts → ThSettingsWrapper-DWEA4hYF.d.mts} +1 -1
  3. package/dist/{actions-BLAr0oaM.d.mts → actions-BjeRjaJU.d.mts} +1 -1
  4. package/dist/{actionsReducer-XWTGGNUd.d.mts → actionsReducer-B32cq2mB.d.mts} +1 -1
  5. package/dist/{chunk-L4XGZAZ5.mjs → chunk-2YRT7RNW.mjs} +20 -3
  6. package/dist/chunk-2YRT7RNW.mjs.map +1 -0
  7. package/dist/{chunk-6BUN7DEA.mjs → chunk-6MONB2DN.mjs} +63 -56
  8. package/dist/chunk-6MONB2DN.mjs.map +1 -0
  9. package/dist/chunk-A575ZW4A.mjs +10 -0
  10. package/dist/chunk-A575ZW4A.mjs.map +1 -0
  11. package/dist/chunk-AE6P4KJB.mjs +13 -0
  12. package/dist/chunk-AE6P4KJB.mjs.map +1 -0
  13. package/dist/{chunk-2ORXUOH3.mjs → chunk-IVXRCKWR.mjs} +4 -4
  14. package/dist/{chunk-2ORXUOH3.mjs.map → chunk-IVXRCKWR.mjs.map} +1 -1
  15. package/dist/{chunk-I4BKU5NN.mjs → chunk-KVUG6BNI.mjs} +90 -26
  16. package/dist/chunk-KVUG6BNI.mjs.map +1 -0
  17. package/dist/{chunk-6EHFW43Y.mjs → chunk-NUXGQWED.mjs} +4 -3
  18. package/dist/chunk-NUXGQWED.mjs.map +1 -0
  19. package/dist/{chunk-DETZMFZ7.mjs → chunk-P6ILEQ5P.mjs} +24 -26
  20. package/dist/chunk-P6ILEQ5P.mjs.map +1 -0
  21. package/dist/{chunk-LP3JFZ4A.mjs → chunk-PXXWEMNL.mjs} +666 -430
  22. package/dist/chunk-PXXWEMNL.mjs.map +1 -0
  23. package/dist/{chunk-SAUOY37Q.mjs → chunk-QUSGPT5M.mjs} +11 -12
  24. package/dist/chunk-QUSGPT5M.mjs.map +1 -0
  25. package/dist/{chunk-A3FZBEUL.mjs → chunk-SZAVAQ6S.mjs} +30 -6
  26. package/dist/chunk-SZAVAQ6S.mjs.map +1 -0
  27. package/dist/{chunk-NKO3K3QS.mjs → chunk-T5ENYSDJ.mjs} +5 -5
  28. package/dist/chunk-T5ENYSDJ.mjs.map +1 -0
  29. package/dist/{chunk-DMZFSOHK.mjs → chunk-TSLTLQ6O.mjs} +125 -40
  30. package/dist/chunk-TSLTLQ6O.mjs.map +1 -0
  31. package/dist/{chunk-ITDBOMY5.mjs → chunk-VENFFPK2.mjs} +3 -3
  32. package/dist/{chunk-ITDBOMY5.mjs.map → chunk-VENFFPK2.mjs.map} +1 -1
  33. package/dist/{chunk-EZG6SBSO.mjs → chunk-WLVE3WNW.mjs} +95 -25
  34. package/dist/chunk-WLVE3WNW.mjs.map +1 -0
  35. package/dist/chunk-XRFLDNAY.mjs +3913 -0
  36. package/dist/chunk-XRFLDNAY.mjs.map +1 -0
  37. package/dist/components/Audio/index.d.mts +12 -12
  38. package/dist/components/Audio/index.mjs +15 -15
  39. package/dist/components/Epub/index.css +3 -3
  40. package/dist/components/Epub/index.css.map +1 -1
  41. package/dist/components/Epub/index.d.mts +12 -12
  42. package/dist/components/Epub/index.mjs +16 -16
  43. package/dist/components/Misc/index.mjs +4 -4
  44. package/dist/components/Reader/index.css +3 -3
  45. package/dist/components/Reader/index.css.map +1 -1
  46. package/dist/components/Reader/index.d.mts +11 -11
  47. package/dist/components/Reader/index.mjs +26 -24
  48. package/dist/components/Reader/index.mjs.map +1 -1
  49. package/dist/components/WebPub/index.css +3 -3
  50. package/dist/components/WebPub/index.css.map +1 -1
  51. package/dist/components/WebPub/index.d.mts +12 -12
  52. package/dist/components/WebPub/index.mjs +16 -16
  53. package/dist/core/Components/index.d.mts +11 -21
  54. package/dist/core/Components/index.mjs +1 -1
  55. package/dist/core/Helpers/index.d.mts +1 -1
  56. package/dist/core/Helpers/index.mjs +2 -3
  57. package/dist/core/Hooks/index.d.mts +12 -8
  58. package/dist/core/Hooks/index.mjs +1 -1
  59. package/dist/i18n/index.mjs +3 -6
  60. package/dist/lib/index.d.mts +49 -20
  61. package/dist/lib/index.mjs +3 -2
  62. package/dist/locales/da/thorium-shared.json +3 -0
  63. package/dist/locales/en/thorium-shared.json +24 -2
  64. package/dist/locales/en/thorium-web.json +2 -2
  65. package/dist/locales/es/thorium-shared.json +364 -0
  66. package/dist/locales/es/thorium-web.json +130 -0
  67. package/dist/locales/et/thorium-shared.json +121 -9
  68. package/dist/locales/et/thorium-web.json +32 -1
  69. package/dist/locales/fi/thorium-shared.json +42 -4
  70. package/dist/locales/fi/thorium-web.json +36 -2
  71. package/dist/locales/fr/thorium-shared.json +108 -1
  72. package/dist/locales/fr/thorium-web.json +121 -86
  73. package/dist/locales/it/thorium-shared.json +108 -1
  74. package/dist/locales/it/thorium-web.json +15 -2
  75. package/dist/locales/lt/thorium-web.json +10 -1
  76. package/dist/locales/pl/thorium-web.json +1 -1
  77. package/dist/locales/pt-BR/thorium-shared.json +6 -0
  78. package/dist/locales/pt-BR/thorium-web.json +88 -88
  79. package/dist/locales/pt-PT/thorium-shared.json +91 -0
  80. package/dist/locales/pt-PT/thorium-web.json +15 -3
  81. package/dist/locales/sv/thorium-shared.json +108 -2
  82. package/dist/locales/sv/thorium-web.json +15 -3
  83. package/dist/locales/tr/thorium-shared.json +42 -0
  84. package/dist/preferences/index.d.mts +59 -13
  85. package/dist/preferences/index.mjs +6 -6
  86. package/dist/{settingsReducer-Bu1zeveu.d.mts → settingsReducer-DLaT2wUB.d.mts} +14 -2
  87. package/dist/{ui-nBv8gfr0.d.mts → ui-DnZZhozX.d.mts} +1 -1
  88. package/dist/{useAudioNavigator-C5aW4-eT.d.mts → useAudioNavigator-CWXyNWq1.d.mts} +3 -1
  89. package/dist/{useContrast-2t429O9O.d.mts → useContrast-Bo7cDw_X.d.mts} +5 -1
  90. package/dist/{usePreferences-VaBf46eP.d.mts → usePreferences-D8NU1yhP.d.mts} +6 -8
  91. package/dist/{useReaderTransitions-IBGdE7qi.d.mts → useReaderTransitions-BQGzKeY2.d.mts} +61 -10
  92. package/package.json +10 -9
  93. package/dist/chunk-6BUN7DEA.mjs.map +0 -1
  94. package/dist/chunk-6EHFW43Y.mjs.map +0 -1
  95. package/dist/chunk-7CGMWOZN.mjs +0 -20
  96. package/dist/chunk-7CGMWOZN.mjs.map +0 -1
  97. package/dist/chunk-A3FZBEUL.mjs.map +0 -1
  98. package/dist/chunk-DETZMFZ7.mjs.map +0 -1
  99. package/dist/chunk-DMZFSOHK.mjs.map +0 -1
  100. package/dist/chunk-DTPO3J2C.mjs +0 -1732
  101. package/dist/chunk-DTPO3J2C.mjs.map +0 -1
  102. package/dist/chunk-EZG6SBSO.mjs.map +0 -1
  103. package/dist/chunk-GPWW5OML.mjs +0 -1955
  104. package/dist/chunk-GPWW5OML.mjs.map +0 -1
  105. package/dist/chunk-I4BKU5NN.mjs.map +0 -1
  106. package/dist/chunk-L4XGZAZ5.mjs.map +0 -1
  107. package/dist/chunk-LP3JFZ4A.mjs.map +0 -1
  108. package/dist/chunk-NKO3K3QS.mjs.map +0 -1
  109. package/dist/chunk-SAUOY37Q.mjs.map +0 -1
@@ -1,21 +1,21 @@
1
- import { ErrorHandler } from './chunk-RRDEPGBK.mjs';
2
- import { useFullscreen, useEpubNavigator, useLocalStorage } from './chunk-A3FZBEUL.mjs';
3
- import { useAppDispatch, setFullscreen, useAppSelector, setActionOpen, dockAction, setHovering, setSettingsContainer, debounce, setColumnCount, setWebPubFontFamily, setFontFamily, setWebPubFontWeight, setFontWeight, setWebPubHyphens, setHyphens, setScroll, setWebPubLetterSpacing, setLetterSpacing, setWebPubLineHeight, setLineHeight, setWebPubParagraphIndent, setParagraphIndent, setWebPubParagraphSpacing, setParagraphSpacing, setWebPubWordSpacing, setWordSpacing, setWebPubSpacingPreset, setWebPubPublisherStyles, setSpacingPreset, setPublisherStyles, setWebPubTextAlign, setTextAlign, setWebPubTextNormalization, setTextNormalization, setTheme, setWebPubZoom, setFontSize, setSkipBackwardInterval, setSkipForwardInterval, setSkipInterval, setAutoPlay, toggleActionOpen, setVolume, setPlaybackRate, setSleepTimerRemainingSeconds, setSleepTimerOnTrackEnd, setSleepTimerOnFragmentEnd, setRemotePlaybackState, ThReduxPreferencesAdapter, setReaderProfile, setPositionsList, setTocTree, setRTL, setFXL, setHasDisplayTransformability, setImmersive, setOverflow, setUserNavigated, setTocEntry, collapseDockPanel, expandDockPanel, activateDockPanel, deactivateDockPanel, setDockPanelWidth } from './chunk-EZG6SBSO.mjs';
1
+ import { makeBreakpointsMap, isKeyboardTriggered, isActiveElement } from './chunk-VENFFPK2.mjs';
2
+ import { useFullscreen, useLocalStorage, useEpubNavigator } from './chunk-SZAVAQ6S.mjs';
3
+ import { setFullscreen, setReaderProfile, setPositionsList, setTocTree, setScriptMode, setRTL, setFXL, setHasDisplayTransformability, setActionOpen, dockAction, setHovering, setSettingsContainer, debounce, setColumnCount, initialWebPubSettingsState, initialSettingsState, setWebPubFontFamily, setFontFamily, setWebPubFontWeight, setFontWeight, setWebPubHyphens, setHyphens, setScroll, setWebPubLetterSpacing, setLetterSpacing, setWebPubLineHeight, setLineHeight, setWebPubParagraphIndent, setParagraphIndent, setWebPubParagraphSpacing, setParagraphSpacing, setWebPubWordSpacing, setWordSpacing, setWebPubSpacingPreset, setWebPubPublisherStyles, setSpacingPreset, setPublisherStyles, setWebPubTextAlign, setTextAlign, setWebPubTextNormalization, setTextNormalization, setWebPubLigatures, setLigatures, setWebPubNoRuby, setNoRuby, setTheme, setWebPubZoom, setFontSize, setSkipBackwardInterval, setSkipForwardInterval, setSkipInterval, setAutoPlay, toggleActionOpen, setVolume, setPlaybackRate, setSleepTimerRemainingSeconds, setSleepTimerOnTrackEnd, setSleepTimerOnFragmentEnd, setRemotePlaybackState, ThReduxPreferencesAdapter, ThReduxGlobalPreferencesAdapter, setImmersive, setOverflow, setUserNavigated, setTocEntry, collapseDockPanel, expandDockPanel, activateDockPanel, deactivateDockPanel, setDockPanelWidth } from './chunk-WLVE3WNW.mjs';
4
4
  import { buildTocTree, findTocItemById } from './chunk-TEZB4ULX.mjs';
5
- import { useActionsPreferences, prefixString, useAudioPreferences, usePreferences, usePreferenceKeys, buildThemeObject, ThPreferencesProvider } from './chunk-GPWW5OML.mjs';
6
- import { useI18n } from './chunk-6EHFW43Y.mjs';
7
- import { useSharedPreferences, ThDockingTypes, ThSheetTypes, defaultSpacingSettingsSubpanel, defaultSpacingSettingsMain, defaultTextSettingsSubpanel, defaultTextSettingsMain, ThTextSettingsKeys, ThSpacingSettingsKeys, defaultAudioSkipBackwardInterval, defaultAudioSkipForwardInterval, defaultAudioSkipInterval, defaultPreferences } from './chunk-DTPO3J2C.mjs';
8
- import { ThMenuItem, ThActionButton, ThMenu, ThCollapsibleActionsBar, ThPopover, ThContainerHeader, ThNavigationButton, ThContainerBody, ThModal, ThBottomSheet, ThCloseButton, ThDockedPanel, useFirstFocusable, ThTypedComponentRenderer, useActions, useActionComponentStatus, ThForm, ThFormNumberField, usePlugins, ThSettingsWrapper, ThRadioGroup, ThDropdown, ThSwitch, ThNumberField, ThSlider, ThSliderWithPresets, ThLink, ThLibrary, ThHome, ThBackArrow, ThFormSearchField } from './chunk-DETZMFZ7.mjs';
9
- import { usePrevious } from './chunk-YZ73DHRU.mjs';
10
- import { makeBreakpointsMap, isKeyboardTriggered, isActiveElement } from './chunk-ITDBOMY5.mjs';
5
+ import { useSharedPreferences, useActionsPreferences, prefixString, ThDockingTypes, ThSheetTypes, useAudioPreferences, usePreferences, useFilteredPreferenceKeys, defaultSpacingSettingsSubpanel, defaultSpacingSettingsMain, defaultTextSettingsSubpanel, defaultTextSettingsMain, ThTextSettingsKeys, ThSpacingSettingsKeys, buildThemeObject, defaultAudioSkipBackwardInterval, defaultAudioSkipForwardInterval, defaultAudioSkipInterval, defaultPreferences, ThPreferencesProvider, ThGlobalPreferencesProvider } from './chunk-XRFLDNAY.mjs';
6
+ import { useAppDispatch, useAppSelector } from './chunk-A575ZW4A.mjs';
11
7
  import { isIOSish, buildShortcut, metaKeys } from './chunk-5LUMM7FW.mjs';
8
+ import { ErrorHandler } from './chunk-RRDEPGBK.mjs';
9
+ import { ThMenuItem, ThActionButton, ThMenu, ThCollapsibleActionsBar, ThPopover, ThContainerHeader, ThNavigationButton, ThContainerBody, ThModal, ThBottomSheet, ThCloseButton, ThDockedPanel, useFirstFocusable, ThTypedComponentRenderer, useActions, useActionComponentStatus, ThForm, ThFormNumberField, usePlugins, ThSettingsWrapper, ThRadioGroup, ThDropdown, ThSwitch, ThNumberField, ThSlider, ThSliderWithPresets, ThLink, ThLibrary, ThHome, ThBackArrow, ThFormSearchField } from './chunk-P6ILEQ5P.mjs';
10
+ import { usePrevious } from './chunk-YZ73DHRU.mjs';
11
+ import { useI18n } from './chunk-NUXGQWED.mjs';
12
12
  import { Text, Popover, Dialog, ListBox, ListBoxItem, Radio, Button, Keyboard, Toolbar, useFilter, Tree, TreeItem, TreeItemContent, Collection } from 'react-aria-components';
13
13
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
14
- import { useObjectRef, useNumberFormatter, FocusScope, useFocusWithin } from 'react-aria';
14
+ import { useObjectRef, useNumberFormatter, FocusScope, useLocale, useFocusWithin } from 'react-aria';
15
15
  import classNames4 from 'classnames';
16
- import React24, { createContext, useCallback, useRef, useContext, useMemo, useEffect, useState } from 'react';
16
+ import React22, { createContext, useCallback, useRef, useContext, useMemo, useState, useEffect } from 'react';
17
17
  import { Link, HttpFetcher, Manifest, Publication, ReadingProgression, Layout, Feature, Profile } from '@readium/shared';
18
- import { TextAlignment } from '@readium/navigator';
18
+ import { getScriptMode, TextAlignment } from '@readium/navigator';
19
19
  import { useStore } from 'react-redux';
20
20
  import { PanelGroup, Panel, PanelResizeHandle } from 'react-resizable-panels';
21
21
 
@@ -357,6 +357,12 @@ var useNavigator = () => {
357
357
  return callback?.(false);
358
358
  },
359
359
  isVisual: () => isVisual,
360
+ getScriptMode: () => {
361
+ if (isVisual && navigator.getScriptMode) {
362
+ return navigator.getScriptMode?.();
363
+ }
364
+ return void 0;
365
+ },
360
366
  getCframes: isVisual ? navigator.getCframes?.bind(navigator) : void 0,
361
367
  underlying: navigator
362
368
  };
@@ -387,6 +393,223 @@ var useNavigator = () => {
387
393
  }), [visualMemo, mediaMemo, unified]);
388
394
  };
389
395
 
396
+ // src/hooks/useIsScroll.ts
397
+ var useIsScroll = () => {
398
+ const profile = useAppSelector((state) => state.reader.profile);
399
+ const scroll = useAppSelector((state) => state.settings.scroll);
400
+ const isFXL = useAppSelector((state) => state.publication.isFXL);
401
+ const scriptMode = useAppSelector((state) => state.publication.scriptMode);
402
+ if (profile === "webPub") return true;
403
+ return (scroll || scriptMode === "cjk-vertical" || scriptMode === "mongolian-vertical") && !isFXL;
404
+ };
405
+
406
+ // src/hooks/useReaderTransitions.ts
407
+ var useReaderTransitions = () => {
408
+ const isImmersive = useAppSelector((state) => state.reader.isImmersive);
409
+ const isFullscreen = useAppSelector((state) => state.reader.isFullscreen);
410
+ const hasUserNavigated = useAppSelector((state) => state.reader.hasUserNavigated);
411
+ const isScroll = useIsScroll();
412
+ const wasImmersive = usePrevious(isImmersive) ?? false;
413
+ const wasFullscreen = usePrevious(isFullscreen) ?? false;
414
+ const wasScroll = usePrevious(isScroll) ?? false;
415
+ const wasUserNavigated = usePrevious(hasUserNavigated) ?? false;
416
+ const fromImmersive = wasImmersive && !isImmersive;
417
+ const toImmersive = !wasImmersive && isImmersive;
418
+ const fromFullscreen = wasFullscreen && !isFullscreen;
419
+ const toFullscreen = !wasFullscreen && isFullscreen;
420
+ const fromScroll = wasScroll && !isScroll;
421
+ const toScroll = !wasScroll && isScroll;
422
+ const fromUserNavigation = wasUserNavigated && !hasUserNavigated;
423
+ const toUserNavigation = !wasUserNavigated && hasUserNavigated;
424
+ return {
425
+ // Current states
426
+ isImmersive,
427
+ isFullscreen,
428
+ isScroll,
429
+ hasUserNavigated,
430
+ // Previous states
431
+ wasImmersive,
432
+ wasFullscreen,
433
+ wasScroll,
434
+ wasUserNavigated,
435
+ // State transitions
436
+ fromImmersive,
437
+ toImmersive,
438
+ fromFullscreen,
439
+ toFullscreen,
440
+ fromScroll,
441
+ toScroll,
442
+ fromUserNavigation,
443
+ toUserNavigation
444
+ };
445
+ };
446
+
447
+ // src/helpers/deserializePositions.ts
448
+ var deserializePositions = (positionsList) => {
449
+ return positionsList?.map((locator) => ({
450
+ href: locator.href,
451
+ type: locator.type,
452
+ locations: {
453
+ position: locator.locations.position,
454
+ progression: locator.locations.progression,
455
+ totalProgression: locator.locations.totalProgression
456
+ }
457
+ }));
458
+ };
459
+
460
+ // src/hooks/usePublication.ts
461
+ var detectProfile = (manifest) => {
462
+ const metadata = manifest.metadata;
463
+ if (!metadata) return "webPub";
464
+ const conformsTo = metadata.conformsTo;
465
+ if (!conformsTo) return "webPub";
466
+ const profiles = Array.isArray(conformsTo) ? conformsTo : [conformsTo];
467
+ if (profiles.some(
468
+ (profile) => profile === Profile.AUDIOBOOK
469
+ )) {
470
+ return "audio";
471
+ }
472
+ if (profiles.some(
473
+ (profile) => profile === Profile.EPUB
474
+ )) {
475
+ return "epub";
476
+ }
477
+ return "webPub";
478
+ };
479
+ var usePublication = ({
480
+ url,
481
+ onError = () => {
482
+ },
483
+ fetcher: customFetcher
484
+ }) => {
485
+ const dispatch = useAppDispatch();
486
+ const [isLoading, setIsLoading] = useState(true);
487
+ const [error, setError] = useState(null);
488
+ const [manifest, setManifest] = useState(null);
489
+ const [selfLink, setSelfLink] = useState(null);
490
+ const [localDataKey, setLocalDataKey] = useState(null);
491
+ const [publication, setPublication] = useState(null);
492
+ const [profile, setProfile] = useState(null);
493
+ const [isRTL, setIsRTL] = useState(false);
494
+ const [isFXL, setIsFXL] = useState(false);
495
+ const [hasDisplayTransformability, setHasDisplayTransformabilityState] = useState(false);
496
+ const handleManifestError = (error2, context) => {
497
+ console.error(`${context}:`, error2);
498
+ const processedError = ErrorHandler.process(error2, context);
499
+ setError(processedError);
500
+ setIsLoading(false);
501
+ };
502
+ useEffect(() => {
503
+ if (!url) {
504
+ const validationError = ErrorHandler.process(new Error("Manifest URL is required"), "Validation");
505
+ setError(validationError);
506
+ setIsLoading(false);
507
+ return;
508
+ }
509
+ setIsLoading(true);
510
+ setError(null);
511
+ const decodedUrl = decodeURIComponent(url);
512
+ const manifestLink = new Link({ href: decodedUrl });
513
+ const fetcher = customFetcher || new HttpFetcher(void 0);
514
+ try {
515
+ const fetched = fetcher.get(manifestLink);
516
+ fetched.link().then(async (link) => {
517
+ try {
518
+ const selfHref = link.toURL(decodedUrl);
519
+ setSelfLink(selfHref || null);
520
+ if (selfHref) {
521
+ setLocalDataKey(`${selfHref}-current-location`);
522
+ const manifestFetcher = customFetcher || new HttpFetcher(void 0, selfHref);
523
+ const manifestFetched = manifestFetcher.get(manifestLink);
524
+ const manifestData = await manifestFetched.readAsJSON();
525
+ setManifest(manifestData);
526
+ const manifestObj = Manifest.deserialize(manifestData);
527
+ manifestObj.setSelfLink(selfHref);
528
+ const detectedProfile = detectProfile(manifestObj);
529
+ setProfile(detectedProfile);
530
+ dispatch(setReaderProfile(detectedProfile));
531
+ const pub = new Publication({
532
+ manifest: manifestObj,
533
+ fetcher: manifestFetcher
534
+ });
535
+ if (detectedProfile === "epub") {
536
+ try {
537
+ const rawPositions = await pub.positionsFromManifest();
538
+ const positionsList = deserializePositions(rawPositions);
539
+ dispatch(setPositionsList(positionsList));
540
+ } catch (error2) {
541
+ console.error("Failed to fetch positions:", error2);
542
+ dispatch(setPositionsList([]));
543
+ }
544
+ }
545
+ if (detectedProfile === "audio") {
546
+ const tocLinks = manifestObj.toc?.items && manifestObj.toc.items.length > 0 ? manifestObj.toc.items : manifestObj.readingOrder?.items || [];
547
+ const publicationTitle = manifestObj.metadata.title.getTranslation("en");
548
+ let idCounter = 0;
549
+ const idGenerator = () => `toc-${++idCounter}`;
550
+ dispatch(setTocTree(buildTocTree(tocLinks, idGenerator, void 0, publicationTitle)));
551
+ }
552
+ setPublication(pub);
553
+ setIsLoading(false);
554
+ }
555
+ } catch (error2) {
556
+ handleManifestError(error2, "Error loading manifest");
557
+ }
558
+ });
559
+ } catch (error2) {
560
+ handleManifestError(error2, "Error loading manifest");
561
+ }
562
+ }, [url, customFetcher, dispatch]);
563
+ useEffect(() => {
564
+ if (!publication) return;
565
+ const mode = getScriptMode(publication.metadata);
566
+ dispatch(setScriptMode(mode));
567
+ const rtl = publication.metadata.effectiveReadingProgression === ReadingProgression.rtl;
568
+ setIsRTL(rtl);
569
+ dispatch(setRTL(rtl));
570
+ if (profile === "epub") {
571
+ const fxl = publication.metadata.effectiveLayout === Layout.fixed;
572
+ setIsFXL(fxl);
573
+ dispatch(setFXL(fxl));
574
+ }
575
+ const displayTransformability = publication.metadata.accessibility?.feature?.some(
576
+ (feature) => feature && feature.value === Feature.DISPLAY_TRANSFORMABILITY.value
577
+ ) || false;
578
+ setHasDisplayTransformabilityState(displayTransformability);
579
+ dispatch(setHasDisplayTransformability(displayTransformability));
580
+ if (profile === "epub" && publication) {
581
+ const fetchPositions = async () => {
582
+ try {
583
+ const positionsList = await publication.positionsFromManifest();
584
+ const deserializedPositionsList = deserializePositions(positionsList);
585
+ dispatch(setPositionsList(deserializedPositionsList));
586
+ } catch (error2) {
587
+ console.error("Failed to fetch positions:", error2);
588
+ dispatch(setPositionsList([]));
589
+ }
590
+ };
591
+ fetchPositions();
592
+ }
593
+ }, [publication, profile, dispatch]);
594
+ useEffect(() => {
595
+ if (error) {
596
+ onError(error);
597
+ }
598
+ }, [error, onError]);
599
+ return {
600
+ isLoading,
601
+ error,
602
+ publication,
603
+ manifest,
604
+ selfLink,
605
+ localDataKey,
606
+ profile,
607
+ isRTL,
608
+ isFXL,
609
+ hasDisplayTransformability
610
+ };
611
+ };
612
+
390
613
  // src/components/Sheets/assets/styles/thorium-web.sheets.module.css
391
614
  var thorium_web_sheets_default = {
392
615
  fullscreen: "thorium_web_sheets_fullscreen",
@@ -624,12 +847,33 @@ var StatefulDocker = ({
624
847
  )
625
848
  ] }) });
626
849
  };
850
+ var usePositionStorage = (key, customStorage) => {
851
+ const localStorageData = useLocalStorage(key);
852
+ const [customData, setCustomData] = useState(
853
+ () => customStorage ? customStorage.get() || null : null
854
+ );
855
+ if (customStorage) {
856
+ const set = (newValue) => {
857
+ if (newValue) {
858
+ customStorage.set(newValue);
859
+ }
860
+ setCustomData(newValue);
861
+ };
862
+ const get = () => customData;
863
+ return {
864
+ setLocalData: set,
865
+ getLocalData: get,
866
+ localData: customData
867
+ };
868
+ }
869
+ return localStorageData;
870
+ };
871
+
872
+ // src/components/Sheets/hooks/useWebkitPatch.ts
627
873
  var useWebkitPatch = (isOpen) => {
628
- const profile = useAppSelector((state) => state.reader.profile);
629
- const isWebPub = profile === "webPub";
630
- const scroll = useAppSelector((state) => state.settings.scroll);
631
- const isFXL = useAppSelector((state) => state.publication.isFXL);
632
- const isScroll = isWebPub || scroll && !isFXL;
874
+ const isScroll = useIsScroll();
875
+ const scriptMode = useAppSelector((state) => state.publication.scriptMode);
876
+ const isHorizontalScroll = scriptMode === "cjk-vertical" || scriptMode === "mongolian-vertical";
633
877
  const prevIsOpen = usePrevious(isOpen);
634
878
  let getCframes;
635
879
  try {
@@ -642,10 +886,19 @@ var useWebkitPatch = (isOpen) => {
642
886
  if (isScroll && prevIsOpen && !isOpen && getCframes) {
643
887
  const container = document.getElementById("thorium-web-container");
644
888
  if (!container) return;
645
- const currentHeight = container.offsetHeight;
646
- container.style.height = `${currentHeight - 1}px`;
889
+ if (isHorizontalScroll) {
890
+ const currentWidth = container.offsetWidth;
891
+ container.style.width = `${currentWidth - 1}px`;
892
+ } else {
893
+ const currentHeight = container.offsetHeight;
894
+ container.style.height = `${currentHeight - 1}px`;
895
+ }
647
896
  setTimeout(() => {
648
- container.style.height = "";
897
+ if (isHorizontalScroll) {
898
+ container.style.width = "";
899
+ } else {
900
+ container.style.height = "";
901
+ }
649
902
  if (!getCframes) return;
650
903
  const frames = getCframes();
651
904
  if (!frames || !Array.isArray(frames) || frames.length === 0) return;
@@ -657,16 +910,23 @@ var useWebkitPatch = (isOpen) => {
657
910
  } catch (e) {
658
911
  return;
659
912
  }
660
- const currentScrollTop = frameWindow.document.scrollingElement.scrollTop;
661
- if (currentScrollTop > 1) {
662
- frameWindow.document.scrollingElement.scrollTop = currentScrollTop - 1;
913
+ if (isHorizontalScroll) {
914
+ const currentScrollLeft = frameWindow.document.scrollingElement.scrollLeft;
915
+ const nudge = currentScrollLeft <= 0 ? -1 : 1;
916
+ frameWindow.document.scrollingElement.scrollLeft = currentScrollLeft + nudge;
917
+ frameWindow.document.scrollingElement.scrollLeft = currentScrollLeft;
663
918
  } else {
664
- frameWindow.document.scrollingElement.scrollTop = currentScrollTop + 1;
919
+ const currentScrollTop = frameWindow.document.scrollingElement.scrollTop;
920
+ if (currentScrollTop > 1) {
921
+ frameWindow.document.scrollingElement.scrollTop = currentScrollTop - 1;
922
+ } else {
923
+ frameWindow.document.scrollingElement.scrollTop = currentScrollTop + 1;
924
+ }
925
+ frameWindow.document.scrollingElement.scrollTop = currentScrollTop;
665
926
  }
666
- frameWindow.document.scrollingElement.scrollTop = currentScrollTop;
667
927
  }, 0);
668
928
  }
669
- }, [isScroll, isOpen, prevIsOpen, getCframes]);
929
+ }, [isScroll, isHorizontalScroll, isOpen, prevIsOpen, getCframes]);
670
930
  };
671
931
  var StatefulPopoverSheet = ({
672
932
  id,
@@ -702,7 +962,7 @@ var StatefulPopoverSheet = ({
702
962
  }
703
963
  }, [isOpen]);
704
964
  useWebkitPatch(!!isOpen);
705
- if (React24.Children.toArray(children).length > 0) {
965
+ if (React22.Children.toArray(children).length > 0) {
706
966
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
707
967
  ThPopover,
708
968
  {
@@ -811,7 +1071,7 @@ var StatefulModalBase = ({
811
1071
  }
812
1072
  }, [isOpen]);
813
1073
  useWebkitPatch(!!isOpen);
814
- if (React24.Children.toArray(children).length > 0) {
1074
+ if (React22.Children.toArray(children).length > 0) {
815
1075
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
816
1076
  ThModal,
817
1077
  {
@@ -1070,7 +1330,7 @@ var StatefulBottomSheet = ({
1070
1330
  return "default";
1071
1331
  }
1072
1332
  };
1073
- if (React24.Children.toArray(children).length > 0) {
1333
+ if (React22.Children.toArray(children).length > 0) {
1074
1334
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
1075
1335
  ThBottomSheet,
1076
1336
  {
@@ -1222,7 +1482,7 @@ var StatefulDockedSheet = ({
1222
1482
  return direction === "ltr" /* ltr */ ? thorium_web_sheets_default.dockedRightBorder : thorium_web_sheets_default.dockedLeftBorder;
1223
1483
  }
1224
1484
  }, [flow, direction]);
1225
- if (React24.Children.toArray(children).length > 0) {
1485
+ if (React22.Children.toArray(children).length > 0) {
1226
1486
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
1227
1487
  ThDockedPanel,
1228
1488
  {
@@ -1322,7 +1582,7 @@ var StatefulCompactPopoverSheet = ({
1322
1582
  updateState: resetFocus
1323
1583
  });
1324
1584
  useWebkitPatch(!!isOpen);
1325
- if (React24.Children.toArray(children).length > 0) {
1585
+ if (React22.Children.toArray(children).length > 0) {
1326
1586
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
1327
1587
  Popover,
1328
1588
  {
@@ -1974,6 +2234,7 @@ var StatefulSpacingGroup = () => {
1974
2234
  const { preferences } = usePreferences();
1975
2235
  const { t } = useI18n();
1976
2236
  const { spacingSettingsComponentsMap } = usePlugins();
2237
+ const { mainSpacingSettingsKeys, subPanelSpacingSettingsKeys } = useFilteredPreferenceKeys();
1977
2238
  const dispatch = useAppDispatch();
1978
2239
  const setSpacingContainer = useCallback(() => {
1979
2240
  dispatch(setSettingsContainer("spacing" /* spacing */));
@@ -1986,7 +2247,11 @@ var StatefulSpacingGroup = () => {
1986
2247
  moreTooltip: t("reader.settings.spacing.advanced.tooltip"),
1987
2248
  onPressMore: setSpacingContainer,
1988
2249
  componentsMap: spacingSettingsComponentsMap,
1989
- prefs: preferences.settings.spacing,
2250
+ prefs: {
2251
+ main: mainSpacingSettingsKeys,
2252
+ subPanel: preferences.settings.spacing?.subPanel === null ? null : subPanelSpacingSettingsKeys,
2253
+ header: preferences.settings.spacing?.header
2254
+ },
1990
2255
  defaultPrefs: {
1991
2256
  main: defaultSpacingSettingsMain,
1992
2257
  subPanel: defaultSpacingSettingsSubpanel
@@ -1995,10 +2260,9 @@ var StatefulSpacingGroup = () => {
1995
2260
  ) });
1996
2261
  };
1997
2262
  var StatefulSpacingGroupContainer = () => {
1998
- const { preferences } = usePreferences();
1999
- const displayOrder = preferences.settings.spacing?.subPanel || defaultSpacingSettingsSubpanel;
2263
+ const { subPanelSpacingSettingsKeys } = useFilteredPreferenceKeys();
2000
2264
  const { spacingSettingsComponentsMap } = usePlugins();
2001
- return /* @__PURE__ */ jsx(Fragment, { children: displayOrder.map((key) => {
2265
+ return /* @__PURE__ */ jsx(Fragment, { children: subPanelSpacingSettingsKeys.map((key) => {
2002
2266
  const match = spacingSettingsComponentsMap[key];
2003
2267
  if (!match) {
2004
2268
  console.warn(`Setting key "${key}" not found in the plugin registry while present in preferences.`);
@@ -2011,6 +2275,7 @@ var StatefulTextGroup = () => {
2011
2275
  const { preferences } = usePreferences();
2012
2276
  const { t } = useI18n();
2013
2277
  const { textSettingsComponentsMap } = usePlugins();
2278
+ const { mainTextSettingsKeys, subPanelTextSettingsKeys } = useFilteredPreferenceKeys();
2014
2279
  const dispatch = useAppDispatch();
2015
2280
  const setTextContainer = useCallback(() => {
2016
2281
  dispatch(setSettingsContainer("text" /* text */));
@@ -2023,7 +2288,11 @@ var StatefulTextGroup = () => {
2023
2288
  moreTooltip: t("reader.settings.text.advanced.tooltip"),
2024
2289
  onPressMore: setTextContainer,
2025
2290
  componentsMap: textSettingsComponentsMap,
2026
- prefs: preferences.settings.text,
2291
+ prefs: {
2292
+ main: mainTextSettingsKeys,
2293
+ subPanel: preferences.settings.text?.subPanel === null ? null : subPanelTextSettingsKeys,
2294
+ header: preferences.settings.text?.header
2295
+ },
2027
2296
  defaultPrefs: {
2028
2297
  main: defaultTextSettingsMain,
2029
2298
  subPanel: defaultTextSettingsSubpanel
@@ -2032,10 +2301,9 @@ var StatefulTextGroup = () => {
2032
2301
  ) });
2033
2302
  };
2034
2303
  var StatefulTextGroupContainer = () => {
2035
- const { preferences } = usePreferences();
2036
- const displayOrder = preferences.settings.text?.subPanel || defaultTextSettingsSubpanel;
2304
+ const { subPanelTextSettingsKeys } = useFilteredPreferenceKeys();
2037
2305
  const { textSettingsComponentsMap } = usePlugins();
2038
- return /* @__PURE__ */ jsx(Fragment, { children: displayOrder.map((key) => {
2306
+ return /* @__PURE__ */ jsx(Fragment, { children: subPanelTextSettingsKeys.map((key) => {
2039
2307
  const match = textSettingsComponentsMap[key];
2040
2308
  if (!match) {
2041
2309
  console.warn(`Action key "${key}" not found in the plugin registry while present in preferences.`);
@@ -2055,7 +2323,7 @@ var StatefulVisualSettingsContainer = ({
2055
2323
  subPanelSpacingSettingsKeys,
2056
2324
  subPanelTextSettingsKeys,
2057
2325
  webPubSettingsKeys
2058
- } = usePreferenceKeys();
2326
+ } = useFilteredPreferenceKeys();
2059
2327
  const { preferences } = usePreferences();
2060
2328
  const { t } = useI18n();
2061
2329
  const { settingsComponentsMap } = usePlugins();
@@ -2344,6 +2612,7 @@ var TocContent = ({
2344
2612
  selectedKeys: tocEntry ? [tocEntry] : [],
2345
2613
  expandedKeys,
2346
2614
  onExpandedChange,
2615
+ dir: isRTL ? "rtl" : "ltr",
2347
2616
  children: function renderItem(item) {
2348
2617
  return /* @__PURE__ */ jsxs(
2349
2618
  TreeItem,
@@ -2382,12 +2651,11 @@ var StatefulTocContainer = ({ triggerRef }) => {
2382
2651
  const tocEntry = unstableTimeline?.toc?.currentEntry ?? void 0;
2383
2652
  const tocEntryId = tocEntry?.id;
2384
2653
  const tocTree = unstableTimeline?.toc?.tree;
2385
- const direction = useAppSelector((state) => state.reader.direction);
2386
- const isRTL = direction === "rtl" /* rtl */;
2654
+ const { goLink, getScriptMode: getScriptMode2 } = useNavigator().unified;
2655
+ const isRTL = getScriptMode2() === "rtl";
2387
2656
  const profile = useAppSelector((state) => state.reader.profile);
2388
2657
  const actionState = useAppSelector((state) => profile ? state.actions.keys[profile]["toc" /* toc */] : void 0);
2389
2658
  const dispatch = useAppDispatch();
2390
- const { goLink } = useNavigator().unified;
2391
2659
  const docking = useDocking("toc" /* toc */);
2392
2660
  const sheetType = docking.sheetType;
2393
2661
  const setOpen = useCallback((value) => {
@@ -2536,7 +2804,7 @@ var useGridNavigation = ({
2536
2804
  onFocus
2537
2805
  }) => {
2538
2806
  const visibleColumns = useGridTemplate(containerRef, "columns");
2539
- const onKeyDown = React24.useCallback((e) => {
2807
+ const onKeyDown = React22.useCallback((e) => {
2540
2808
  const columns = visibleColumns || 1;
2541
2809
  if (columns <= 1 || !items.current?.length) return;
2542
2810
  const currentIdx = items.current.findIndex((val) => {
@@ -2657,7 +2925,26 @@ var StatefulRadioGroup = ({
2657
2925
  }
2658
2926
  ) });
2659
2927
  };
2660
- var SvgDocumentScanner = (props) => /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 -960 960 960", width: "24px", fill: "inherit", ...props, children: /* @__PURE__ */ jsx("path", { d: "M80-720v-200h200v80H160v120H80Zm720 0v-120H680v-80h200v200h-80ZM80-40v-200h80v120h120v80H80Zm600 0v-80h120v-120h80v200H680ZM280-240h400v-480H280v480Zm0 80q-33 0-56.5-23.5T200-240v-480q0-33 23.5-56.5T280-800h400q33 0 56.5 23.5T760-720v480q0 33-23.5 56.5T680-160H280Zm80-400h240v-80H360v80Zm0 120h240v-80H360v80Zm0 120h240v-80H360v80Zm-80 80v-480 480Z" }) });
2928
+
2929
+ // src/components/Settings/helpers/settingsKeyMapping.ts
2930
+ var SETTINGS_KEY_TO_PREFERENCE = {
2931
+ ["columns" /* columns */]: "columnCount",
2932
+ ["fontFamily" /* fontFamily */]: "fontFamily",
2933
+ ["fontWeight" /* fontWeight */]: "fontWeight",
2934
+ ["hyphens" /* hyphens */]: "hyphens",
2935
+ ["layout" /* layout */]: "scroll",
2936
+ ["letterSpacing" /* letterSpacing */]: "letterSpacing",
2937
+ ["ligatures" /* ligatures */]: "ligatures",
2938
+ ["lineHeight" /* lineHeight */]: "lineHeight",
2939
+ ["paragraphIndent" /* paragraphIndent */]: "paragraphIndent",
2940
+ ["paragraphSpacing" /* paragraphSpacing */]: "paragraphSpacing",
2941
+ ["textAlign" /* textAlign */]: "textAlign",
2942
+ ["textNormalize" /* textNormalize */]: "textNormalization",
2943
+ ["noRuby" /* noRuby */]: "noRuby",
2944
+ ["wordSpacing" /* wordSpacing */]: "wordSpacing",
2945
+ ["zoom" /* zoom */]: "zoom"
2946
+ };
2947
+ var SvgDocumentScanner = (props) => /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 -960 960 960", width: "24px", fill: "inherit", ...props, children: /* @__PURE__ */ jsx("path", { d: "M80-720v-200h200v80H160v120H80Zm720 0v-120H680v-80h200v200h-80ZM80-40v-200h80v120h120v80H80Zm600 0v-80h120v-120h80v200H680ZM280-240h400v-480H280v480Zm0 80q-33 0-56.5-23.5T200-240v-480q0-33 23.5-56.5T280-800h400q33 0 56.5 23.5T760-720v480q0 33-23.5 56.5T680-160H280Zm80-400h240v-80H360v80Zm0 120h240v-80H360v80Zm0 120h240v-80H360v80Zm-80 80v-480 480Z" }) });
2661
2948
  var document_scanner_default = SvgDocumentScanner;
2662
2949
  var SvgArticle = (props) => /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 -960 960 960", width: "24px", fill: "inherit", ...props, children: /* @__PURE__ */ jsx("path", { d: "M280-280h280v-80H280v80Zm0-160h400v-80H280v80Zm0-160h400v-80H280v80Zm-80 480q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z" }) });
2663
2950
  var article_default = SvgArticle;
@@ -2665,9 +2952,7 @@ var SvgMenuBook = (props) => /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.
2665
2952
  var menu_book_default = SvgMenuBook;
2666
2953
  var StatefulColumns = () => {
2667
2954
  const { t } = useI18n();
2668
- const scroll = useAppSelector((state) => state.settings.scroll);
2669
- const isFXL = useAppSelector((state) => state.publication.isFXL);
2670
- const isScroll = scroll && !isFXL;
2955
+ const isScroll = useIsScroll();
2671
2956
  const columnCount = useAppSelector((state) => state.settings.columnCount) || "auto";
2672
2957
  const [effectiveValue, setEffectiveValue] = useState(columnCount);
2673
2958
  const fontSize = useAppSelector((state) => state.settings.fontSize);
@@ -2686,6 +2971,7 @@ var StatefulColumns = () => {
2686
2971
  }, [fontSize, fontFamily, wordSpacing, letterSpacing, publisherStyles]);
2687
2972
  const dispatch = useAppDispatch();
2688
2973
  const { submitPreferences, getSetting } = useEpubNavigator();
2974
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["columns" /* columns */];
2689
2975
  const items = useMemo(() => [
2690
2976
  {
2691
2977
  id: "auto",
@@ -2716,12 +3002,12 @@ var StatefulColumns = () => {
2716
3002
  }, []);
2717
3003
  const updatePreference = useCallback(async (value) => {
2718
3004
  const colCount = value === "auto" ? null : Number(value);
2719
- await submitPreferences({ columnCount: colCount });
2720
- updateEffectiveValue(value, getSetting("columnCount"));
3005
+ await submitPreferences({ [prefKey]: colCount });
3006
+ updateEffectiveValue(value, getSetting(prefKey));
2721
3007
  dispatch(setColumnCount(value));
2722
- }, [submitPreferences, getSetting, updateEffectiveValue, dispatch]);
3008
+ }, [prefKey, submitPreferences, getSetting, updateEffectiveValue, dispatch]);
2723
3009
  const debouncedUpdate = useCallback(() => {
2724
- const update = () => updateEffectiveValue(columnCount, getSetting("columnCount"));
3010
+ const update = () => updateEffectiveValue(columnCount, getSetting(prefKey));
2725
3011
  debounce(update, 50)();
2726
3012
  }, [columnCount, layoutSettings, getSetting, updateEffectiveValue]);
2727
3013
  useEffect(() => {
@@ -2766,7 +3052,7 @@ var StatefulDropdown = ({
2766
3052
  className: thorium_web_reader_settings_default.label,
2767
3053
  ...compounds?.label || {}
2768
3054
  },
2769
- ...React24.isValidElement(compounds?.button) ? { button: compounds.button } : {
3055
+ ...React22.isValidElement(compounds?.button) ? { button: compounds.button } : {
2770
3056
  button: {
2771
3057
  className: thorium_web_reader_settings_default.dropdownButton,
2772
3058
  ...compounds?.button || {}
@@ -2777,7 +3063,7 @@ var StatefulDropdown = ({
2777
3063
  placement: "bottom",
2778
3064
  ...compounds?.popover || {}
2779
3065
  },
2780
- ...React24.isValidElement(compounds?.listbox) ? { listbox: compounds.listbox } : {
3066
+ ...React22.isValidElement(compounds?.listbox) ? { listbox: compounds.listbox } : {
2781
3067
  listbox: {
2782
3068
  className: thorium_web_reader_settings_default.dropdownListbox,
2783
3069
  ...compounds?.listbox || {}
@@ -2791,6 +3077,27 @@ var StatefulDropdown = ({
2791
3077
  }
2792
3078
  );
2793
3079
  };
3080
+
3081
+ // src/components/Settings/hooks/useReaderSetting.ts
3082
+ function useReaderSetting(key) {
3083
+ return useAppSelector((state) => {
3084
+ const isWebPub = state.reader.profile === "webPub";
3085
+ if (key === "zoom") {
3086
+ if (isWebPub) {
3087
+ const val3 = state.webPubSettings.zoom;
3088
+ return val3 !== void 0 ? val3 : initialWebPubSettingsState.zoom;
3089
+ }
3090
+ const val2 = state.settings.fontSize;
3091
+ return val2 !== void 0 ? val2 : initialSettingsState.fontSize;
3092
+ }
3093
+ if (isWebPub) {
3094
+ const val2 = state.webPubSettings[key];
3095
+ return val2 !== void 0 ? val2 : initialWebPubSettingsState[key];
3096
+ }
3097
+ const val = state.settings[key];
3098
+ return val !== void 0 ? val : initialSettingsState[key];
3099
+ });
3100
+ }
2794
3101
  var StatefulFontFamily = ({ standalone = true }) => {
2795
3102
  const { getFontMetadata, getFontsList } = usePreferences();
2796
3103
  const { t } = useI18n();
@@ -2810,10 +3117,8 @@ var StatefulFontFamily = ({ standalone = true }) => {
2810
3117
  const isWebPub = profile === "webPub";
2811
3118
  const fontLanguage = useAppSelector((state) => state.publication.fontLanguage) || "default";
2812
3119
  const fontPreferences = getFontsList({ language: fontLanguage });
2813
- const fontFamily = useAppSelector((state) => {
2814
- const fontSettings = isWebPub ? state.webPubSettings.fontFamily : state.settings.fontFamily;
2815
- return fontSettings[fontLanguage] ?? "publisher";
2816
- });
3120
+ const fontFamilySettings = useReaderSetting("fontFamily");
3121
+ const fontFamily = fontFamilySettings[fontLanguage] ?? "publisher";
2817
3122
  const availableFontIds = /* @__PURE__ */ new Set([
2818
3123
  "publisher",
2819
3124
  ...Object.keys(fontPreferences)
@@ -2836,12 +3141,13 @@ var StatefulFontFamily = ({ standalone = true }) => {
2836
3141
  ]);
2837
3142
  const dispatch = useAppDispatch();
2838
3143
  const { getSetting, submitPreferences } = useNavigator().visual;
3144
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["fontFamily" /* fontFamily */];
2839
3145
  const updatePreference = useCallback(async (key) => {
2840
3146
  if (!key || key === fontFamily) return;
2841
3147
  const selectedOption = fontFamilyOptions.current.find((option) => option.id === key);
2842
3148
  if (selectedOption) {
2843
- await submitPreferences({ fontFamily: selectedOption.value });
2844
- const currentSetting = getSetting("fontFamily");
3149
+ await submitPreferences({ [prefKey]: selectedOption.value });
3150
+ const currentSetting = getSetting(prefKey);
2845
3151
  if (currentSetting === null) {
2846
3152
  if (isWebPub) {
2847
3153
  dispatch(setWebPubFontFamily({ key: fontLanguage, value: "publisher" }));
@@ -2863,7 +3169,7 @@ var StatefulFontFamily = ({ standalone = true }) => {
2863
3169
  }
2864
3170
  }
2865
3171
  }
2866
- }, [isWebPub, fontLanguage, fontFamily, submitPreferences, getSetting, fontPreferences, getFontMetadata, dispatch]);
3172
+ }, [prefKey, isWebPub, fontLanguage, fontFamily, submitPreferences, getSetting, fontPreferences, getFontMetadata, dispatch]);
2867
3173
  return /* @__PURE__ */ jsx(
2868
3174
  StatefulDropdown,
2869
3175
  {
@@ -2902,9 +3208,10 @@ var UnstableStatefulFontWeight = ({ standalone = true }) => {
2902
3208
  const { t } = useI18n();
2903
3209
  const profile = useAppSelector((state) => state.reader.profile);
2904
3210
  const isWebPub = profile === "webPub";
2905
- const fontWeight = useAppSelector((state) => isWebPub ? state.webPubSettings.fontWeight : state.settings.fontWeight) ?? 400;
3211
+ const fontWeight = useReaderSetting("fontWeight");
2906
3212
  const dispatch = useAppDispatch();
2907
3213
  const { getSetting, submitPreferences } = useNavigator().visual;
3214
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["fontWeight" /* fontWeight */];
2908
3215
  const items = [
2909
3216
  {
2910
3217
  id: "default",
@@ -2929,14 +3236,14 @@ var UnstableStatefulFontWeight = ({ standalone = true }) => {
2929
3236
  }, [fontWeight]);
2930
3237
  const updatePreference = useCallback(async (value) => {
2931
3238
  const fontWeightValue = value === "default" ? 400 : 700;
2932
- await submitPreferences({ fontWeight: fontWeightValue });
2933
- const effectiveSetting = getSetting("fontWeight");
3239
+ await submitPreferences({ [prefKey]: fontWeightValue });
3240
+ const effectiveSetting = getSetting(prefKey);
2934
3241
  if (isWebPub) {
2935
3242
  dispatch(setWebPubFontWeight(effectiveSetting));
2936
3243
  } else {
2937
3244
  dispatch(setFontWeight(effectiveSetting));
2938
3245
  }
2939
- }, [isWebPub, submitPreferences, getSetting, dispatch]);
3246
+ }, [prefKey, isWebPub, submitPreferences, getSetting, dispatch]);
2940
3247
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
2941
3248
  StatefulRadioGroup,
2942
3249
  {
@@ -2980,19 +3287,20 @@ var StatefulHyphens = ({ standalone = true }) => {
2980
3287
  const { t } = useI18n();
2981
3288
  const profile = useAppSelector((state) => state.reader.profile);
2982
3289
  const isWebPub = profile === "webPub";
2983
- const hyphens = useAppSelector((state) => isWebPub ? state.webPubSettings.hyphens : state.settings.hyphens) ?? false;
2984
- const textAlign = useAppSelector((state) => isWebPub ? state.webPubSettings.textAlign : state.settings.textAlign) ?? "publisher" /* publisher */;
3290
+ const hyphens = useReaderSetting("hyphens");
3291
+ const textAlign = useReaderSetting("textAlign");
2985
3292
  const dispatch = useAppDispatch();
2986
3293
  const { getSetting, submitPreferences } = useNavigator().visual;
3294
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["hyphens" /* hyphens */];
2987
3295
  const updatePreference = useCallback(async (value) => {
2988
- await submitPreferences({ hyphens: value });
2989
- const effectiveSetting = getSetting("hyphens");
3296
+ await submitPreferences({ [prefKey]: value });
3297
+ const effectiveSetting = getSetting(prefKey);
2990
3298
  if (isWebPub) {
2991
3299
  dispatch(setWebPubHyphens(effectiveSetting));
2992
3300
  } else {
2993
3301
  dispatch(setHyphens(effectiveSetting));
2994
3302
  }
2995
- }, [isWebPub, submitPreferences, getSetting, dispatch]);
3303
+ }, [prefKey, isWebPub, submitPreferences, getSetting, dispatch]);
2996
3304
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
2997
3305
  StatefulSwitch,
2998
3306
  {
@@ -3011,9 +3319,7 @@ var SvgDocs = (props) => /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/
3011
3319
  var docs_default = SvgDocs;
3012
3320
  var StatefulLayout = () => {
3013
3321
  const { t } = useI18n();
3014
- const scroll = useAppSelector((state) => state.settings.scroll);
3015
- const isFXL = useAppSelector((state) => state.publication.isFXL);
3016
- const isScroll = scroll && !isFXL;
3322
+ const isScroll = useIsScroll();
3017
3323
  const dispatch = useAppDispatch();
3018
3324
  const { getSetting, submitPreferences } = useEpubNavigator();
3019
3325
  const items = [
@@ -3030,11 +3336,12 @@ var StatefulLayout = () => {
3030
3336
  value: "scroll_option" /* scroll */
3031
3337
  }
3032
3338
  ];
3339
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["layout" /* layout */];
3033
3340
  const updatePreference = useCallback(async (value) => {
3034
3341
  const derivedValue = value === "scroll_option" /* scroll */;
3035
- await submitPreferences({ scroll: derivedValue });
3036
- dispatch(setScroll(getSetting("scroll")));
3037
- }, [submitPreferences, getSetting, dispatch]);
3342
+ await submitPreferences({ [prefKey]: derivedValue });
3343
+ dispatch(setScroll(getSetting(prefKey)));
3344
+ }, [prefKey, submitPreferences, getSetting, dispatch]);
3038
3345
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
3039
3346
  StatefulRadioGroup,
3040
3347
  {
@@ -3177,32 +3484,25 @@ var StatefulSlider = ({
3177
3484
  ) });
3178
3485
  };
3179
3486
  function useSettingsComponentStatus(options) {
3180
- const { settingsKey, publicationType, componentType, additionalCondition = true } = options;
3487
+ const { settingsKey, publicationType, additionalCondition = true } = options;
3181
3488
  const { spacingSettingsComponentsMap, textSettingsComponentsMap, settingsComponentsMap } = usePlugins();
3182
- const { preferences } = usePreferences();
3489
+ const { reflowSettingsKeys, fxlSettingsKeys, webPubSettingsKeys, mainTextSettingsKeys, subPanelTextSettingsKeys, mainSpacingSettingsKeys, subPanelSpacingSettingsKeys } = useFilteredPreferenceKeys();
3183
3490
  return useMemo(() => {
3184
3491
  const isComponentRegistered = !!(settingsComponentsMap?.[settingsKey] || spacingSettingsComponentsMap?.[settingsKey] || textSettingsComponentsMap?.[settingsKey]);
3185
3492
  let isInOrder = false;
3186
3493
  switch (publicationType) {
3187
3494
  case "reflow":
3188
- isInOrder = preferences.settings?.reflowOrder?.includes(settingsKey) || false;
3495
+ isInOrder = reflowSettingsKeys.includes(settingsKey) || false;
3189
3496
  break;
3190
3497
  case "fxl":
3191
- isInOrder = preferences.settings?.fxlOrder?.includes(settingsKey) || false;
3498
+ isInOrder = fxlSettingsKeys.includes(settingsKey) || false;
3192
3499
  break;
3193
3500
  case "webpub":
3194
- isInOrder = preferences.settings?.webPubOrder?.includes(settingsKey) || false;
3501
+ isInOrder = webPubSettingsKeys.includes(settingsKey) || false;
3195
3502
  break;
3196
3503
  }
3197
- let isInMainPanel = false;
3198
- let isInSubPanel = false;
3199
- if (componentType === "text") {
3200
- isInMainPanel = preferences.settings?.text?.main?.includes(settingsKey) || false;
3201
- isInSubPanel = preferences.settings?.text?.subPanel?.includes(settingsKey) || false;
3202
- } else if (componentType === "spacing") {
3203
- isInMainPanel = preferences.settings?.spacing?.main?.includes(settingsKey) || false;
3204
- isInSubPanel = preferences.settings?.spacing?.subPanel?.includes(settingsKey) || false;
3205
- }
3504
+ const isInMainPanel = mainTextSettingsKeys.includes(settingsKey) || mainSpacingSettingsKeys.includes(settingsKey) || false;
3505
+ const isInSubPanel = subPanelTextSettingsKeys.includes(settingsKey) || subPanelSpacingSettingsKeys.includes(settingsKey) || false;
3206
3506
  const isDisplayed = isInOrder || (isInMainPanel || isInSubPanel) && additionalCondition;
3207
3507
  const isComponentUsed = isComponentRegistered && isDisplayed;
3208
3508
  return {
@@ -3215,29 +3515,25 @@ function useSettingsComponentStatus(options) {
3215
3515
  }, [
3216
3516
  settingsKey,
3217
3517
  publicationType,
3218
- componentType,
3219
3518
  additionalCondition,
3220
- preferences,
3519
+ reflowSettingsKeys,
3520
+ fxlSettingsKeys,
3521
+ webPubSettingsKeys,
3522
+ mainTextSettingsKeys,
3523
+ subPanelTextSettingsKeys,
3524
+ mainSpacingSettingsKeys,
3525
+ subPanelSpacingSettingsKeys,
3221
3526
  spacingSettingsComponentsMap,
3222
3527
  textSettingsComponentsMap,
3223
3528
  settingsComponentsMap
3224
3529
  ]);
3225
3530
  }
3226
-
3227
- // src/components/Settings/Spacing/hooks/useSpacingPresets.ts
3228
3531
  var useSpacingPresets = () => {
3229
3532
  const readerProfile = useAppSelector((state) => state.reader.profile);
3230
3533
  const isWebPub = readerProfile === "webPub";
3231
3534
  const isFXL = useAppSelector((state) => state.publication.isFXL);
3232
- const settings = useAppSelector(
3233
- (state) => isWebPub ? state.webPubSettings : state.settings
3234
- );
3235
- const spacing = settings?.spacing || {
3236
- preset: "publisher" /* publisher */,
3237
- custom: {},
3238
- baseline: {}
3239
- };
3240
- const { reflowSpacingPresetKeys, fxlSpacingPresetKeys, webPubSpacingPresetKeys } = usePreferenceKeys();
3535
+ const spacing = useReaderSetting("spacing");
3536
+ const { reflowSpacingPresetKeys, fxlSpacingPresetKeys, webPubSpacingPresetKeys } = useFilteredPreferenceKeys();
3241
3537
  const { preferences } = usePreferences();
3242
3538
  const dispatch = useAppDispatch();
3243
3539
  const spacingKeys = useMemo(() => {
@@ -3246,16 +3542,13 @@ var useSpacingPresets = () => {
3246
3542
  const { isComponentUsed: shouldApplyPresets } = useSettingsComponentStatus({
3247
3543
  settingsKey: "spacingPresets" /* spacingPresets */,
3248
3544
  publicationType: isWebPub ? "webpub" : isFXL ? "fxl" : "reflow",
3249
- componentType: "spacing",
3250
3545
  additionalCondition: spacingKeys.length > 0
3251
3546
  });
3252
- const {
3253
- letterSpacing,
3254
- lineHeight,
3255
- paragraphIndent,
3256
- paragraphSpacing,
3257
- wordSpacing
3258
- } = settings || {};
3547
+ const letterSpacing = useReaderSetting("letterSpacing");
3548
+ const lineHeight = useReaderSetting("lineHeight");
3549
+ const paragraphIndent = useReaderSetting("paragraphIndent");
3550
+ const paragraphSpacing = useReaderSetting("paragraphSpacing");
3551
+ const wordSpacing = useReaderSetting("wordSpacing");
3259
3552
  const getBaseReduxValue = (key) => {
3260
3553
  switch (key) {
3261
3554
  case "letterSpacing" /* letterSpacing */:
@@ -3450,24 +3743,6 @@ var useSpacingPresets = () => {
3450
3743
  setPublisherStyles: setPublisherStylesAction
3451
3744
  };
3452
3745
  };
3453
- var useEffectiveRange = (preferred, supportedRange, presets) => {
3454
- return useMemo(() => {
3455
- let range;
3456
- if (!supportedRange) {
3457
- range = preferred;
3458
- } else {
3459
- const prefMin = Math.min(...preferred);
3460
- const prefMax = Math.max(...preferred);
3461
- const supMin = Math.min(...supportedRange);
3462
- const supMax = Math.max(...supportedRange);
3463
- range = prefMin >= supMin && prefMax <= supMax ? preferred : supportedRange;
3464
- }
3465
- if (!presets) return { range };
3466
- const [min, max] = [Math.min(...range), Math.max(...range)];
3467
- const effectivePresets = presets.filter((p) => p >= min && p <= max);
3468
- return { range, presets: effectivePresets };
3469
- }, [preferred, supportedRange, presets]);
3470
- };
3471
3746
 
3472
3747
  // src/components/Settings/hooks/usePlaceholder.ts
3473
3748
  var usePlaceholder = (placeholder, range, format) => {
@@ -3506,6 +3781,24 @@ var usePlaceholder = (placeholder, range, format) => {
3506
3781
  }
3507
3782
  return void 0;
3508
3783
  };
3784
+ var useEffectiveRange = (preferred, supportedRange, presets) => {
3785
+ return useMemo(() => {
3786
+ let range;
3787
+ if (!supportedRange) {
3788
+ range = preferred;
3789
+ } else {
3790
+ const prefMin = Math.min(...preferred);
3791
+ const prefMax = Math.max(...preferred);
3792
+ const supMin = Math.min(...supportedRange);
3793
+ const supMax = Math.max(...supportedRange);
3794
+ range = prefMin >= supMin && prefMax <= supMax ? preferred : supportedRange;
3795
+ }
3796
+ if (!presets) return { range };
3797
+ const [min, max] = [Math.min(...range), Math.max(...range)];
3798
+ const effectivePresets = presets.filter((p) => p >= min && p <= max);
3799
+ return { range, presets: effectivePresets };
3800
+ }, [preferred, supportedRange, presets]);
3801
+ };
3509
3802
  var StatefulLetterSpacing = ({ standalone = true }) => {
3510
3803
  const { preferences } = usePreferences();
3511
3804
  const { t } = useI18n();
@@ -3521,12 +3814,13 @@ var StatefulLetterSpacing = ({ standalone = true }) => {
3521
3814
  const placeholderText = usePlaceholder(letterSpacingRangeConfig.placeholder, letterSpacingRangeConfig.range, "percent");
3522
3815
  const { getEffectiveSpacingValue, setLetterSpacing: setLetterSpacing2, canBeReset } = useSpacingPresets();
3523
3816
  const letterSpacing = getEffectiveSpacingValue("letterSpacing" /* letterSpacing */);
3817
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["letterSpacing" /* letterSpacing */];
3524
3818
  const updatePreference = useCallback(async (value) => {
3525
3819
  await submitPreferences({
3526
- letterSpacing: Array.isArray(value) ? value[0] : value
3820
+ [prefKey]: Array.isArray(value) ? value[0] : value
3527
3821
  });
3528
- setLetterSpacing2(getSetting("letterSpacing"));
3529
- }, [submitPreferences, getSetting, setLetterSpacing2]);
3822
+ setLetterSpacing2(getSetting(prefKey));
3823
+ }, [prefKey, submitPreferences, getSetting, setLetterSpacing2]);
3530
3824
  return /* @__PURE__ */ jsx(Fragment, { children: letterSpacingRangeConfig.variant === "numberField" /* numberField */ ? /* @__PURE__ */ jsx(
3531
3825
  StatefulNumberField,
3532
3826
  {
@@ -3586,8 +3880,9 @@ var StatefulLineHeight = ({ standalone = true }) => {
3586
3880
  const { preferences } = usePreferences();
3587
3881
  const profile = useAppSelector((state) => state.reader.profile);
3588
3882
  const isWebPub = profile === "webPub";
3589
- const publisherStyles = useAppSelector((state) => isWebPub ? state.webPubSettings.publisherStyles : state.settings.publisherStyles) ?? true;
3883
+ const publisherStyles = useReaderSetting("publisherStyles");
3590
3884
  const { getSetting, submitPreferences, preferencesEditor } = useNavigator().visual;
3885
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["lineHeight" /* lineHeight */];
3591
3886
  const { getEffectiveSpacingValue, setLineHeight: setLineHeight2 } = useSpacingPresets();
3592
3887
  const lineHeight = getEffectiveSpacingValue("lineHeight" /* lineHeight */);
3593
3888
  const lineHeightOptions = useLineHeight();
@@ -3637,12 +3932,12 @@ var StatefulLineHeight = ({ standalone = true }) => {
3637
3932
  const updatePreference = useCallback(async (value) => {
3638
3933
  const computedValue = value === "publisher" /* publisher */ ? null : lineHeightOptions[value];
3639
3934
  await submitPreferences({
3640
- lineHeight: computedValue
3935
+ [prefKey]: computedValue
3641
3936
  });
3642
- const currentLineHeight = getSetting("lineHeight");
3937
+ const currentLineHeight = getSetting(prefKey);
3643
3938
  const currentDisplayLineHeightOption = Object.entries(lineHeightOptions).find(([key, value2]) => value2 === currentLineHeight)?.[0];
3644
3939
  setLineHeight2(currentDisplayLineHeightOption);
3645
- }, [submitPreferences, getSetting, setLineHeight2, lineHeightOptions]);
3940
+ }, [prefKey, submitPreferences, getSetting, setLineHeight2, lineHeightOptions]);
3646
3941
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
3647
3942
  StatefulRadioGroup,
3648
3943
  {
@@ -3670,12 +3965,13 @@ var StatefulParagraphIndent = ({ standalone = true }) => {
3670
3965
  const placeholderText = usePlaceholder(paragraphIndentRangeConfig.placeholder, paragraphIndentRangeConfig.range, "multiplier");
3671
3966
  const { getEffectiveSpacingValue, setParagraphIndent: setParagraphIndent2, canBeReset } = useSpacingPresets();
3672
3967
  const paragraphIndent = getEffectiveSpacingValue("paragraphIndent" /* paragraphIndent */);
3968
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["paragraphIndent" /* paragraphIndent */];
3673
3969
  const updatePreference = useCallback(async (value) => {
3674
3970
  await submitPreferences({
3675
- paragraphIndent: Array.isArray(value) ? value[0] : value
3971
+ [prefKey]: Array.isArray(value) ? value[0] : value
3676
3972
  });
3677
- setParagraphIndent2(getSetting("paragraphIndent"));
3678
- }, [submitPreferences, getSetting, setParagraphIndent2]);
3973
+ setParagraphIndent2(getSetting(prefKey));
3974
+ }, [prefKey, submitPreferences, getSetting, setParagraphIndent2]);
3679
3975
  return /* @__PURE__ */ jsx(Fragment, { children: paragraphIndentRangeConfig.variant === "numberField" /* numberField */ ? /* @__PURE__ */ jsx(
3680
3976
  StatefulNumberField,
3681
3977
  {
@@ -3736,12 +4032,13 @@ var StatefulParagraphSpacing = ({ standalone = true }) => {
3736
4032
  const placeholderText = usePlaceholder(paragraphSpacingRangeConfig.placeholder, paragraphSpacingRangeConfig.range, "multiplier");
3737
4033
  const { getEffectiveSpacingValue, setParagraphSpacing: setParagraphSpacing2, canBeReset } = useSpacingPresets();
3738
4034
  const paragraphSpacing = getEffectiveSpacingValue("paragraphSpacing" /* paragraphSpacing */);
4035
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["paragraphSpacing" /* paragraphSpacing */];
3739
4036
  const updatePreference = useCallback(async (value) => {
3740
4037
  await submitPreferences({
3741
- paragraphSpacing: Array.isArray(value) ? value[0] : value
4038
+ [prefKey]: Array.isArray(value) ? value[0] : value
3742
4039
  });
3743
- setParagraphSpacing2(getSetting("paragraphSpacing"));
3744
- }, [submitPreferences, getSetting, setParagraphSpacing2]);
4040
+ setParagraphSpacing2(getSetting(prefKey));
4041
+ }, [prefKey, submitPreferences, getSetting, setParagraphSpacing2]);
3745
4042
  return /* @__PURE__ */ jsx(Fragment, { children: paragraphSpacingRangeConfig.variant === "numberField" /* numberField */ ? /* @__PURE__ */ jsx(
3746
4043
  StatefulNumberField,
3747
4044
  {
@@ -3789,8 +4086,28 @@ var StatefulParagraphSpacing = ({ standalone = true }) => {
3789
4086
  };
3790
4087
  var StatefulPublisherStyles = ({ standalone = true }) => {
3791
4088
  const { t } = useI18n();
3792
- const publisherStyles = useAppSelector((state) => state.settings.publisherStyles);
4089
+ const publisherStyles = useReaderSetting("publisherStyles");
3793
4090
  const { getEffectiveSpacingValue, setPublisherStyles: setPublisherStyles2 } = useSpacingPresets();
4091
+ const { isComponentUsed: isLineHeightUsed } = useSettingsComponentStatus({
4092
+ settingsKey: "lineHeight" /* lineHeight */,
4093
+ publicationType: "reflow"
4094
+ });
4095
+ const { isComponentUsed: isParagraphIndentUsed } = useSettingsComponentStatus({
4096
+ settingsKey: "paragraphIndent" /* paragraphIndent */,
4097
+ publicationType: "reflow"
4098
+ });
4099
+ const { isComponentUsed: isParagraphSpacingUsed } = useSettingsComponentStatus({
4100
+ settingsKey: "paragraphSpacing" /* paragraphSpacing */,
4101
+ publicationType: "reflow"
4102
+ });
4103
+ const { isComponentUsed: isLetterSpacingUsed } = useSettingsComponentStatus({
4104
+ settingsKey: "letterSpacing" /* letterSpacing */,
4105
+ publicationType: "reflow"
4106
+ });
4107
+ const { isComponentUsed: isWordSpacingUsed } = useSettingsComponentStatus({
4108
+ settingsKey: "wordSpacing" /* wordSpacing */,
4109
+ publicationType: "reflow"
4110
+ });
3794
4111
  const lineHeight = getEffectiveSpacingValue("lineHeight" /* lineHeight */);
3795
4112
  const paragraphIndent = getEffectiveSpacingValue("paragraphIndent" /* paragraphIndent */);
3796
4113
  const paragraphSpacing = getEffectiveSpacingValue("paragraphSpacing" /* paragraphSpacing */);
@@ -3798,23 +4115,49 @@ var StatefulPublisherStyles = ({ standalone = true }) => {
3798
4115
  const wordSpacing = getEffectiveSpacingValue("wordSpacing" /* wordSpacing */);
3799
4116
  const lineHeightOptions = useLineHeight();
3800
4117
  const { submitPreferences } = useNavigator().visual;
4118
+ const lineHeightPrefKey = SETTINGS_KEY_TO_PREFERENCE["lineHeight" /* lineHeight */];
4119
+ const paragraphIndentPrefKey = SETTINGS_KEY_TO_PREFERENCE["paragraphIndent" /* paragraphIndent */];
4120
+ const paragraphSpacingPrefKey = SETTINGS_KEY_TO_PREFERENCE["paragraphSpacing" /* paragraphSpacing */];
4121
+ const letterSpacingPrefKey = SETTINGS_KEY_TO_PREFERENCE["letterSpacing" /* letterSpacing */];
4122
+ const wordSpacingPrefKey = SETTINGS_KEY_TO_PREFERENCE["wordSpacing" /* wordSpacing */];
3801
4123
  const updatePreference = useCallback(async (isSelected) => {
3802
- const values = isSelected ? {
3803
- lineHeight: null,
3804
- paragraphIndent: null,
3805
- paragraphSpacing: null,
3806
- letterSpacing: null,
3807
- wordSpacing: null
3808
- } : {
3809
- lineHeight: lineHeight === "publisher" /* publisher */ ? null : lineHeightOptions[lineHeight],
3810
- paragraphIndent,
3811
- paragraphSpacing,
3812
- letterSpacing,
3813
- wordSpacing
3814
- };
4124
+ const values = {};
4125
+ if (isSelected) {
4126
+ if (isLineHeightUsed) {
4127
+ values[lineHeightPrefKey] = null;
4128
+ }
4129
+ if (isParagraphIndentUsed) {
4130
+ values[paragraphIndentPrefKey] = null;
4131
+ }
4132
+ if (isParagraphSpacingUsed) {
4133
+ values[paragraphSpacingPrefKey] = null;
4134
+ }
4135
+ if (isLetterSpacingUsed) {
4136
+ values[letterSpacingPrefKey] = null;
4137
+ }
4138
+ if (isWordSpacingUsed) {
4139
+ values[wordSpacingPrefKey] = null;
4140
+ }
4141
+ } else {
4142
+ if (isLineHeightUsed) {
4143
+ values[lineHeightPrefKey] = lineHeight === "publisher" /* publisher */ ? null : lineHeightOptions[lineHeight];
4144
+ }
4145
+ if (isParagraphIndentUsed) {
4146
+ values[paragraphIndentPrefKey] = paragraphIndent;
4147
+ }
4148
+ if (isParagraphSpacingUsed) {
4149
+ values[paragraphSpacingPrefKey] = paragraphSpacing;
4150
+ }
4151
+ if (isLetterSpacingUsed) {
4152
+ values[letterSpacingPrefKey] = letterSpacing;
4153
+ }
4154
+ if (isWordSpacingUsed) {
4155
+ values[wordSpacingPrefKey] = wordSpacing;
4156
+ }
4157
+ }
3815
4158
  await submitPreferences(values);
3816
4159
  setPublisherStyles2(isSelected ? true : false);
3817
- }, [submitPreferences, setPublisherStyles2, lineHeight, paragraphIndent, paragraphSpacing, letterSpacing, wordSpacing, lineHeightOptions]);
4160
+ }, [submitPreferences, setPublisherStyles2, lineHeight, paragraphIndent, paragraphSpacing, letterSpacing, wordSpacing, lineHeightOptions, lineHeightPrefKey, paragraphIndentPrefKey, paragraphSpacingPrefKey, letterSpacingPrefKey, wordSpacingPrefKey, isLineHeightUsed, isParagraphIndentUsed, isParagraphSpacingUsed, isLetterSpacingUsed, isWordSpacingUsed]);
3818
4161
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
3819
4162
  StatefulSwitch,
3820
4163
  {
@@ -3851,15 +4194,41 @@ var iconMap = {
3851
4194
  };
3852
4195
  var StatefulSpacingPresets = ({ standalone }) => {
3853
4196
  const { t } = useI18n();
3854
- const { reflowSpacingPresetKeys, fxlSpacingPresetKeys, webPubSpacingPresetKeys, subPanelSpacingSettingsKeys } = usePreferenceKeys();
4197
+ const { reflowSpacingPresetKeys, fxlSpacingPresetKeys, webPubSpacingPresetKeys, subPanelSpacingSettingsKeys } = useFilteredPreferenceKeys();
3855
4198
  const profile = useAppSelector((state) => state.reader.profile);
3856
4199
  const isWebPub = profile === "webPub";
3857
- const spacing = useAppSelector((state) => isWebPub ? state.webPubSettings.spacing : state.settings.spacing);
4200
+ const spacing = useReaderSetting("spacing");
3858
4201
  const isFXL = useAppSelector((state) => state.publication.isFXL);
3859
4202
  const dispatch = useAppDispatch();
3860
4203
  const { submitPreferences } = useNavigator().visual;
4204
+ const letterSpacingPrefKey = SETTINGS_KEY_TO_PREFERENCE["letterSpacing" /* letterSpacing */];
4205
+ const lineHeightPrefKey = SETTINGS_KEY_TO_PREFERENCE["lineHeight" /* lineHeight */];
4206
+ const paragraphIndentPrefKey = SETTINGS_KEY_TO_PREFERENCE["paragraphIndent" /* paragraphIndent */];
4207
+ const paragraphSpacingPrefKey = SETTINGS_KEY_TO_PREFERENCE["paragraphSpacing" /* paragraphSpacing */];
4208
+ const wordSpacingPrefKey = SETTINGS_KEY_TO_PREFERENCE["wordSpacing" /* wordSpacing */];
3861
4209
  const lineHeightOptions = useLineHeight();
3862
4210
  const { getPresetValues } = useSpacingPresets();
4211
+ const publicationType = isWebPub ? "webpub" : isFXL ? "fxl" : "reflow";
4212
+ const { isComponentUsed: isLetterSpacingUsed } = useSettingsComponentStatus({
4213
+ settingsKey: "letterSpacing" /* letterSpacing */,
4214
+ publicationType
4215
+ });
4216
+ const { isComponentUsed: isLineHeightUsed } = useSettingsComponentStatus({
4217
+ settingsKey: "lineHeight" /* lineHeight */,
4218
+ publicationType
4219
+ });
4220
+ const { isComponentUsed: isParagraphIndentUsed } = useSettingsComponentStatus({
4221
+ settingsKey: "paragraphIndent" /* paragraphIndent */,
4222
+ publicationType
4223
+ });
4224
+ const { isComponentUsed: isParagraphSpacingUsed } = useSettingsComponentStatus({
4225
+ settingsKey: "paragraphSpacing" /* paragraphSpacing */,
4226
+ publicationType
4227
+ });
4228
+ const { isComponentUsed: isWordSpacingUsed } = useSettingsComponentStatus({
4229
+ settingsKey: "wordSpacing" /* wordSpacing */,
4230
+ publicationType
4231
+ });
3863
4232
  const updatePreference = useCallback(async (value) => {
3864
4233
  const spacingKey = value;
3865
4234
  const presetValues = getPresetValues(spacingKey);
@@ -3872,13 +4241,22 @@ var StatefulSpacingPresets = ({ standalone }) => {
3872
4241
  };
3873
4242
  const lineHeightValue = reduxValues["lineHeight" /* lineHeight */];
3874
4243
  const lineHeightValueNumber = lineHeightValue && lineHeightValue !== "publisher" /* publisher */ ? lineHeightOptions[lineHeightValue] : null;
3875
- const preferencesToSubmit = {
3876
- ["letterSpacing" /* letterSpacing */]: reduxValues["letterSpacing" /* letterSpacing */],
3877
- ["lineHeight" /* lineHeight */]: lineHeightValueNumber,
3878
- ["paragraphIndent" /* paragraphIndent */]: reduxValues["paragraphIndent" /* paragraphIndent */],
3879
- ["paragraphSpacing" /* paragraphSpacing */]: reduxValues["paragraphSpacing" /* paragraphSpacing */],
3880
- ["wordSpacing" /* wordSpacing */]: reduxValues["wordSpacing" /* wordSpacing */]
3881
- };
4244
+ const preferencesToSubmit = {};
4245
+ if (isLetterSpacingUsed) {
4246
+ preferencesToSubmit[letterSpacingPrefKey] = reduxValues["letterSpacing" /* letterSpacing */];
4247
+ }
4248
+ if (isLineHeightUsed) {
4249
+ preferencesToSubmit[lineHeightPrefKey] = lineHeightValueNumber;
4250
+ }
4251
+ if (isParagraphIndentUsed) {
4252
+ preferencesToSubmit[paragraphIndentPrefKey] = reduxValues["paragraphIndent" /* paragraphIndent */];
4253
+ }
4254
+ if (isParagraphSpacingUsed) {
4255
+ preferencesToSubmit[paragraphSpacingPrefKey] = reduxValues["paragraphSpacing" /* paragraphSpacing */];
4256
+ }
4257
+ if (isWordSpacingUsed) {
4258
+ preferencesToSubmit[wordSpacingPrefKey] = reduxValues["wordSpacing" /* wordSpacing */];
4259
+ }
3882
4260
  await submitPreferences(preferencesToSubmit);
3883
4261
  if (isWebPub) {
3884
4262
  dispatch(setWebPubSpacingPreset({
@@ -3891,7 +4269,7 @@ var StatefulSpacingPresets = ({ standalone }) => {
3891
4269
  values: reduxValues
3892
4270
  }));
3893
4271
  }
3894
- }, [isWebPub, dispatch, submitPreferences, getPresetValues, lineHeightOptions]);
4272
+ }, [isWebPub, dispatch, submitPreferences, getPresetValues, lineHeightOptions, letterSpacingPrefKey, lineHeightPrefKey, paragraphIndentPrefKey, paragraphSpacingPrefKey, wordSpacingPrefKey, isLetterSpacingUsed, isLineHeightUsed, isParagraphIndentUsed, isParagraphSpacingUsed, isWordSpacingUsed]);
3895
4273
  const spacingKeys = useMemo(() => {
3896
4274
  const baseKeys = isWebPub ? webPubSpacingPresetKeys : isFXL ? fxlSpacingPresetKeys : reflowSpacingPresetKeys;
3897
4275
  const subPanelKeys = subPanelSpacingSettingsKeys || [];
@@ -3936,9 +4314,16 @@ var StatefulTextAlign = ({ standalone = true }) => {
3936
4314
  const profile = useAppSelector((state) => state.reader.profile);
3937
4315
  const isWebPub = profile === "webPub";
3938
4316
  const isRTL = useAppSelector((state) => state.publication.isRTL);
3939
- const textAlign = useAppSelector((state) => isWebPub ? state.webPubSettings.textAlign : state.settings.textAlign) ?? "publisher" /* publisher */;
4317
+ const textAlign = useReaderSetting("textAlign");
3940
4318
  const dispatch = useAppDispatch();
3941
4319
  const { getSetting, submitPreferences } = useNavigator().visual;
4320
+ const hyphensPrefKey = SETTINGS_KEY_TO_PREFERENCE["hyphens" /* hyphens */];
4321
+ const textAlignPrefKey = SETTINGS_KEY_TO_PREFERENCE["textAlign" /* textAlign */];
4322
+ const publicationType = isWebPub ? "webpub" : "reflow";
4323
+ const { isComponentUsed: isHyphensUsed } = useSettingsComponentStatus({
4324
+ settingsKey: "hyphens" /* hyphens */,
4325
+ publicationType
4326
+ });
3942
4327
  const items = [
3943
4328
  {
3944
4329
  id: "publisher" /* publisher */,
@@ -3961,15 +4346,18 @@ var StatefulTextAlign = ({ standalone = true }) => {
3961
4346
  ];
3962
4347
  const updatePreference = useCallback(async (value) => {
3963
4348
  const textAlign2 = value === "publisher" /* publisher */ ? null : value === "start" /* start */ ? TextAlignment.start : TextAlignment.justify;
3964
- const currentHyphens = getSetting("hyphens");
4349
+ const currentHyphens = getSetting(hyphensPrefKey);
3965
4350
  const hyphens = textAlign2 === null ? null : currentHyphens ?? textAlign2 === TextAlignment.justify;
3966
- await submitPreferences({
3967
- textAlign: textAlign2,
3968
- hyphens
3969
- });
3970
- const textAlignSetting = getSetting("textAlign");
4351
+ const preferencesToSubmit = {
4352
+ [textAlignPrefKey]: textAlign2
4353
+ };
4354
+ if (isHyphensUsed) {
4355
+ preferencesToSubmit[hyphensPrefKey] = hyphens;
4356
+ }
4357
+ await submitPreferences(preferencesToSubmit);
4358
+ const textAlignSetting = getSetting(textAlignPrefKey);
3971
4359
  const textAlignValue = textAlignSetting === null ? "publisher" /* publisher */ : textAlignSetting;
3972
- const effectiveHyphens = getSetting("hyphens");
4360
+ const effectiveHyphens = getSetting(hyphensPrefKey);
3973
4361
  if (isWebPub) {
3974
4362
  dispatch(setWebPubTextAlign(textAlignValue));
3975
4363
  dispatch(setWebPubHyphens(effectiveHyphens));
@@ -3977,7 +4365,7 @@ var StatefulTextAlign = ({ standalone = true }) => {
3977
4365
  dispatch(setTextAlign(textAlignValue));
3978
4366
  dispatch(setHyphens(effectiveHyphens));
3979
4367
  }
3980
- }, [isWebPub, getSetting, submitPreferences, dispatch]);
4368
+ }, [hyphensPrefKey, textAlignPrefKey, isWebPub, getSetting, submitPreferences, dispatch, isHyphensUsed]);
3981
4369
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
3982
4370
  StatefulRadioGroup,
3983
4371
  {
@@ -3994,18 +4382,19 @@ var StatefulTextNormalize = ({ standalone = true }) => {
3994
4382
  const { t } = useI18n();
3995
4383
  const profile = useAppSelector((state) => state.reader.profile);
3996
4384
  const isWebPub = profile === "webPub";
3997
- const textNormalization = useAppSelector((state) => isWebPub ? state.webPubSettings.textNormalization : state.settings.textNormalization) ?? false;
4385
+ const textNormalization = useReaderSetting("textNormalization");
3998
4386
  const dispatch = useAppDispatch();
3999
4387
  const { getSetting, submitPreferences } = useNavigator().visual;
4388
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["textNormalize" /* textNormalize */];
4000
4389
  const updatePreference = useCallback(async (value) => {
4001
- await submitPreferences({ textNormalization: value });
4002
- const effectiveSetting = getSetting("textNormalization");
4390
+ await submitPreferences({ [prefKey]: value });
4391
+ const effectiveSetting = getSetting(prefKey);
4003
4392
  if (isWebPub) {
4004
4393
  dispatch(setWebPubTextNormalization(effectiveSetting));
4005
4394
  } else {
4006
4395
  dispatch(setTextNormalization(effectiveSetting));
4007
4396
  }
4008
- }, [isWebPub, submitPreferences, getSetting, dispatch]);
4397
+ }, [prefKey, isWebPub, submitPreferences, getSetting, dispatch]);
4009
4398
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
4010
4399
  StatefulSwitch,
4011
4400
  {
@@ -4017,6 +4406,62 @@ var StatefulTextNormalize = ({ standalone = true }) => {
4017
4406
  }
4018
4407
  ) });
4019
4408
  };
4409
+ var StatefulLigatures = ({ standalone = true }) => {
4410
+ const { t } = useI18n();
4411
+ const profile = useAppSelector((state) => state.reader.profile);
4412
+ const isWebPub = profile === "webPub";
4413
+ const ligatures = useReaderSetting("ligatures");
4414
+ const dispatch = useAppDispatch();
4415
+ const { getSetting, submitPreferences } = useNavigator().visual;
4416
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["ligatures" /* ligatures */];
4417
+ const updatePreference = useCallback(async (value) => {
4418
+ await submitPreferences({ [prefKey]: value });
4419
+ const effectiveSetting = getSetting(prefKey);
4420
+ if (isWebPub) {
4421
+ dispatch(setWebPubLigatures(effectiveSetting));
4422
+ } else {
4423
+ dispatch(setLigatures(effectiveSetting));
4424
+ }
4425
+ }, [prefKey, isWebPub, submitPreferences, getSetting, dispatch]);
4426
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
4427
+ StatefulSwitch,
4428
+ {
4429
+ standalone,
4430
+ heading: t("reader.preferences.ligatures.title"),
4431
+ label: t("reader.preferences.ligatures.label"),
4432
+ onChange: async (isSelected) => await updatePreference(isSelected),
4433
+ isSelected: ligatures ?? true
4434
+ }
4435
+ ) });
4436
+ };
4437
+ var StatefulNoRuby = ({ standalone = true }) => {
4438
+ const { t } = useI18n();
4439
+ const profile = useAppSelector((state) => state.reader.profile);
4440
+ const isWebPub = profile === "webPub";
4441
+ const noRuby = useReaderSetting("noRuby");
4442
+ const dispatch = useAppDispatch();
4443
+ const { getSetting, submitPreferences } = useNavigator().visual;
4444
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["noRuby" /* noRuby */];
4445
+ const updatePreference = useCallback(async (value) => {
4446
+ await submitPreferences({ [prefKey]: value });
4447
+ const effectiveSetting = getSetting(prefKey);
4448
+ if (isWebPub) {
4449
+ dispatch(setWebPubNoRuby(effectiveSetting));
4450
+ } else {
4451
+ dispatch(setNoRuby(effectiveSetting));
4452
+ }
4453
+ }, [prefKey, isWebPub, submitPreferences, getSetting, dispatch]);
4454
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
4455
+ StatefulSwitch,
4456
+ {
4457
+ standalone,
4458
+ heading: t("reader.preferences.noRuby.title"),
4459
+ label: t("reader.preferences.noRuby.label"),
4460
+ onChange: async (isSelected) => await updatePreference(isSelected),
4461
+ isSelected: noRuby ?? false
4462
+ }
4463
+ ) });
4464
+ };
4020
4465
  var SvgCheck = (props) => /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 -960 960 960", width: "24px", fill: "inherit", ...props, children: /* @__PURE__ */ jsx("path", { d: "M382-240 154-468l57-57 171 171 367-367 57 57-424 424Z" }) });
4021
4466
  var check_default = SvgCheck;
4022
4467
  var StatefulTheme = () => {
@@ -4163,12 +4608,13 @@ var StatefulWordSpacing = ({ standalone = true }) => {
4163
4608
  const placeholderText = usePlaceholder(wordSpacingRangeConfig.placeholder, wordSpacingRangeConfig.range, "percent");
4164
4609
  const { getEffectiveSpacingValue, setWordSpacing: setWordSpacing2, canBeReset } = useSpacingPresets();
4165
4610
  const wordSpacing = getEffectiveSpacingValue("wordSpacing" /* wordSpacing */);
4611
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["wordSpacing" /* wordSpacing */];
4166
4612
  const updatePreference = useCallback(async (value) => {
4167
4613
  await submitPreferences({
4168
- wordSpacing: Array.isArray(value) ? value[0] : value
4614
+ [prefKey]: Array.isArray(value) ? value[0] : value
4169
4615
  });
4170
- setWordSpacing2(getSetting("wordSpacing"));
4171
- }, [submitPreferences, getSetting, setWordSpacing2]);
4616
+ setWordSpacing2(getSetting(prefKey));
4617
+ }, [prefKey, submitPreferences, getSetting, setWordSpacing2]);
4172
4618
  return /* @__PURE__ */ jsx(Fragment, { children: wordSpacingRangeConfig.variant === "numberField" /* numberField */ ? /* @__PURE__ */ jsx(
4173
4619
  StatefulNumberField,
4174
4620
  {
@@ -4219,9 +4665,7 @@ var StatefulZoom = () => {
4219
4665
  const { t } = useI18n();
4220
4666
  const readerProfile = useAppSelector((state) => state.reader.profile);
4221
4667
  const isFXL = useAppSelector((state) => state.publication.isFXL);
4222
- const fontSize = useAppSelector((state) => state.settings.fontSize) || 1;
4223
- const webPubZoom = useAppSelector((state) => state.webPubSettings.zoom) || 1;
4224
- const derivedState = readerProfile === "webPub" ? webPubZoom : fontSize;
4668
+ const derivedState = useReaderSetting("zoom");
4225
4669
  const dispatch = useAppDispatch();
4226
4670
  const {
4227
4671
  getSetting,
@@ -4229,15 +4673,16 @@ var StatefulZoom = () => {
4229
4673
  preferencesEditor
4230
4674
  } = useNavigator().visual;
4231
4675
  const preferenceEditorProperty = readerProfile === "webPub" ? preferencesEditor?.zoom : isFXL ? preferencesEditor?.zoom : preferencesEditor?.fontSize;
4676
+ const prefKey = readerProfile === "webPub" ? SETTINGS_KEY_TO_PREFERENCE["zoom" /* zoom */] : "fontSize";
4232
4677
  const updatePreference = useCallback(async (value) => {
4678
+ const normalizedValue = Array.isArray(value) ? value[0] : value;
4679
+ await submitPreferences({ [prefKey]: normalizedValue });
4233
4680
  if (readerProfile === "webPub") {
4234
- await submitPreferences({ zoom: Array.isArray(value) ? value[0] : value });
4235
- dispatch(setWebPubZoom(getSetting("zoom")));
4681
+ dispatch(setWebPubZoom(getSetting(prefKey)));
4236
4682
  } else {
4237
- await submitPreferences({ fontSize: Array.isArray(value) ? value[0] : value });
4238
- dispatch(setFontSize(getSetting("fontSize")));
4683
+ dispatch(setFontSize(getSetting(prefKey)));
4239
4684
  }
4240
- }, [readerProfile, submitPreferences, getSetting, dispatch]);
4685
+ }, [readerProfile, prefKey, submitPreferences, getSetting, dispatch]);
4241
4686
  const zoomConfig = preferences.settings.keys["zoom" /* zoom */];
4242
4687
  const { range: effectiveRange } = useEffectiveRange(zoomConfig.range, preferenceEditorProperty?.supportedRange);
4243
4688
  const zoomRangeConfig = {
@@ -4291,7 +4736,7 @@ var createDefaultPlugin = () => {
4291
4736
  id: "core",
4292
4737
  name: "Core Components",
4293
4738
  description: "Default components for Thorium Web Epub StatefulReader",
4294
- version: "1.3.1",
4739
+ version: "1.4.0",
4295
4740
  components: {
4296
4741
  actions: {
4297
4742
  ["fullscreen" /* fullscreen */]: {
@@ -4367,6 +4812,14 @@ var createDefaultPlugin = () => {
4367
4812
  Comp: StatefulTextNormalize,
4368
4813
  type: "text"
4369
4814
  },
4815
+ ["ligatures" /* ligatures */]: {
4816
+ Comp: StatefulLigatures,
4817
+ type: "text"
4818
+ },
4819
+ ["noRuby" /* noRuby */]: {
4820
+ Comp: StatefulNoRuby,
4821
+ type: "text"
4822
+ },
4370
4823
  ["theme" /* theme */]: {
4371
4824
  Comp: StatefulTheme
4372
4825
  },
@@ -4395,7 +4848,8 @@ var StatefulSliderWithPresets = ({
4395
4848
  ...props
4396
4849
  }) => {
4397
4850
  const { t } = useI18n();
4398
- const { theming, direction } = useSharedPreferences();
4851
+ const { theming } = useSharedPreferences();
4852
+ const isRTL = useAppSelector((state) => state.publication.isRTL);
4399
4853
  const numberFormatter = useNumberFormatter(props.formatOptions);
4400
4854
  const resolvedFormatValue = formatValue ?? (props.formatOptions ? (v) => numberFormatter.format(v) : void 0);
4401
4855
  const tooltipDelay = theming.icon.tooltipDelay;
@@ -4408,7 +4862,7 @@ var StatefulSliderWithPresets = ({
4408
4862
  items: presetsRef,
4409
4863
  currentValue: currentScalarValue,
4410
4864
  onChange: (v) => props.onChange?.([v]),
4411
- isRTL: direction === "rtl" /* rtl */,
4865
+ isRTL,
4412
4866
  onEscape,
4413
4867
  onFocus: (v) => {
4414
4868
  const el = presetsListRef.current?.querySelector(`input[value="${v}"]`);
@@ -5124,8 +5578,7 @@ var StatefulAudioTocContainer = ({ triggerRef }) => {
5124
5578
  const tocEntry = unstableTimeline?.toc?.currentEntry ?? void 0;
5125
5579
  const tocEntryId = tocEntry?.id;
5126
5580
  const tocTree = unstableTimeline?.toc?.tree;
5127
- const direction = useAppSelector((state) => state.reader.direction);
5128
- const isRTL = direction === "rtl" /* rtl */;
5581
+ const isRTL = useAppSelector((state) => state.publication.isRTL);
5129
5582
  const docking = useDocking("audio.toc" /* toc */);
5130
5583
  const sheetType = docking.sheetType;
5131
5584
  const setOpen = useCallback((value) => {
@@ -5545,7 +5998,7 @@ var createAudioDefaultPlugin = () => {
5545
5998
  id: "audio-core",
5546
5999
  name: "Audio Core Components",
5547
6000
  description: "Default components for Thorium Web Audio StatefulReader",
5548
- version: "1.3.1",
6001
+ version: "1.4.0",
5549
6002
  components: {
5550
6003
  actions: {
5551
6004
  ["settings" /* settings */]: {
@@ -5599,233 +6052,16 @@ var StatefulPreferencesProvider = ({
5599
6052
  }, [store, initialPreferences]);
5600
6053
  return /* @__PURE__ */ jsx(ThPreferencesProvider, { adapter, children });
5601
6054
  };
5602
-
5603
- // src/hooks/useReaderTransitions.ts
5604
- var useReaderTransitions = () => {
5605
- const isImmersive = useAppSelector((state) => state.reader.isImmersive);
5606
- const isFullscreen = useAppSelector((state) => state.reader.isFullscreen);
5607
- const hasUserNavigated = useAppSelector((state) => state.reader.hasUserNavigated);
5608
- const scroll = useAppSelector((state) => state.settings.scroll);
5609
- const isFXL = useAppSelector((state) => state.publication.isFXL);
5610
- const isScroll = scroll && !isFXL;
5611
- const wasImmersive = usePrevious(isImmersive) ?? false;
5612
- const wasFullscreen = usePrevious(isFullscreen) ?? false;
5613
- const wasScroll = usePrevious(isScroll) ?? false;
5614
- const wasUserNavigated = usePrevious(hasUserNavigated) ?? false;
5615
- const fromImmersive = wasImmersive && !isImmersive;
5616
- const toImmersive = !wasImmersive && isImmersive;
5617
- const fromFullscreen = wasFullscreen && !isFullscreen;
5618
- const toFullscreen = !wasFullscreen && isFullscreen;
5619
- const fromScroll = wasScroll && !isScroll;
5620
- const toScroll = !wasScroll && isScroll;
5621
- const fromUserNavigation = wasUserNavigated && !hasUserNavigated;
5622
- const toUserNavigation = !wasUserNavigated && hasUserNavigated;
5623
- return {
5624
- // Current states
5625
- isImmersive,
5626
- isFullscreen,
5627
- isScroll,
5628
- hasUserNavigated,
5629
- // Previous states
5630
- wasImmersive,
5631
- wasFullscreen,
5632
- wasScroll,
5633
- wasUserNavigated,
5634
- // State transitions
5635
- fromImmersive,
5636
- toImmersive,
5637
- fromFullscreen,
5638
- toFullscreen,
5639
- fromScroll,
5640
- toScroll,
5641
- fromUserNavigation,
5642
- toUserNavigation
5643
- };
5644
- };
5645
-
5646
- // src/helpers/deserializePositions.ts
5647
- var deserializePositions = (positionsList) => {
5648
- return positionsList?.map((locator) => ({
5649
- href: locator.href,
5650
- type: locator.type,
5651
- locations: {
5652
- position: locator.locations.position,
5653
- progression: locator.locations.progression,
5654
- totalProgression: locator.locations.totalProgression
5655
- }
5656
- }));
5657
- };
5658
-
5659
- // src/hooks/usePublication.ts
5660
- var detectProfile = (manifest) => {
5661
- const metadata = manifest.metadata;
5662
- if (!metadata) return "webPub";
5663
- const conformsTo = metadata.conformsTo;
5664
- if (!conformsTo) return "webPub";
5665
- const profiles = Array.isArray(conformsTo) ? conformsTo : [conformsTo];
5666
- if (profiles.some(
5667
- (profile) => profile === Profile.AUDIOBOOK
5668
- )) {
5669
- return "audio";
5670
- }
5671
- if (profiles.some(
5672
- (profile) => profile === Profile.EPUB
5673
- )) {
5674
- return "epub";
5675
- }
5676
- return "webPub";
5677
- };
5678
- var usePublication = ({
5679
- url,
5680
- onError = () => {
5681
- },
5682
- fetcher: customFetcher
6055
+ var StatefulGlobalPreferencesProvider = ({
6056
+ children,
6057
+ initialPreferences = {}
5683
6058
  }) => {
5684
- const dispatch = useAppDispatch();
5685
- const [isLoading, setIsLoading] = useState(true);
5686
- const [error, setError] = useState(null);
5687
- const [manifest, setManifest] = useState(null);
5688
- const [selfLink, setSelfLink] = useState(null);
5689
- const [localDataKey, setLocalDataKey] = useState(null);
5690
- const [publication, setPublication] = useState(null);
5691
- const [profile, setProfile] = useState(null);
5692
- const [isRTL, setIsRTL] = useState(false);
5693
- const [isFXL, setIsFXL] = useState(false);
5694
- const [hasDisplayTransformability, setHasDisplayTransformabilityState] = useState(false);
5695
- const handleManifestError = (error2, context) => {
5696
- console.error(`${context}:`, error2);
5697
- const processedError = ErrorHandler.process(error2, context);
5698
- setError(processedError);
5699
- setIsLoading(false);
5700
- };
5701
- useEffect(() => {
5702
- if (!url) {
5703
- const validationError = ErrorHandler.process(new Error("Manifest URL is required"), "Validation");
5704
- setError(validationError);
5705
- setIsLoading(false);
5706
- return;
5707
- }
5708
- setIsLoading(true);
5709
- setError(null);
5710
- const decodedUrl = decodeURIComponent(url);
5711
- const manifestLink = new Link({ href: decodedUrl });
5712
- const fetcher = customFetcher || new HttpFetcher(void 0);
5713
- try {
5714
- const fetched = fetcher.get(manifestLink);
5715
- fetched.link().then(async (link) => {
5716
- try {
5717
- const selfHref = link.toURL(decodedUrl);
5718
- setSelfLink(selfHref || null);
5719
- if (selfHref) {
5720
- setLocalDataKey(`${selfHref}-current-location`);
5721
- const manifestFetcher = customFetcher || new HttpFetcher(void 0, selfHref);
5722
- const manifestFetched = manifestFetcher.get(manifestLink);
5723
- const manifestData = await manifestFetched.readAsJSON();
5724
- setManifest(manifestData);
5725
- const manifestObj = Manifest.deserialize(manifestData);
5726
- manifestObj.setSelfLink(selfHref);
5727
- const detectedProfile = detectProfile(manifestObj);
5728
- setProfile(detectedProfile);
5729
- dispatch(setReaderProfile(detectedProfile));
5730
- const pub = new Publication({
5731
- manifest: manifestObj,
5732
- fetcher: manifestFetcher
5733
- });
5734
- if (detectedProfile === "epub") {
5735
- try {
5736
- const rawPositions = await pub.positionsFromManifest();
5737
- const positionsList = deserializePositions(rawPositions);
5738
- dispatch(setPositionsList(positionsList));
5739
- } catch (error2) {
5740
- console.error("Failed to fetch positions:", error2);
5741
- dispatch(setPositionsList([]));
5742
- }
5743
- }
5744
- if (detectedProfile === "audio") {
5745
- const tocLinks = manifestObj.toc?.items && manifestObj.toc.items.length > 0 ? manifestObj.toc.items : manifestObj.readingOrder?.items || [];
5746
- const publicationTitle = manifestObj.metadata.title.getTranslation("en");
5747
- let idCounter = 0;
5748
- const idGenerator = () => `toc-${++idCounter}`;
5749
- dispatch(setTocTree(buildTocTree(tocLinks, idGenerator, void 0, publicationTitle)));
5750
- }
5751
- setPublication(pub);
5752
- setIsLoading(false);
5753
- }
5754
- } catch (error2) {
5755
- handleManifestError(error2, "Error loading manifest");
5756
- }
5757
- });
5758
- } catch (error2) {
5759
- handleManifestError(error2, "Error loading manifest");
5760
- }
5761
- }, [url, customFetcher, dispatch]);
5762
- useEffect(() => {
5763
- if (!publication) return;
5764
- const rtl = publication.metadata.effectiveReadingProgression === ReadingProgression.rtl;
5765
- setIsRTL(rtl);
5766
- dispatch(setRTL(rtl));
5767
- if (profile === "epub") {
5768
- const fxl = publication.metadata.effectiveLayout === Layout.fixed;
5769
- setIsFXL(fxl);
5770
- dispatch(setFXL(fxl));
5771
- }
5772
- const displayTransformability = publication.metadata.accessibility?.feature?.some(
5773
- (feature) => feature && feature.value === Feature.DISPLAY_TRANSFORMABILITY.value
5774
- ) || false;
5775
- setHasDisplayTransformabilityState(displayTransformability);
5776
- dispatch(setHasDisplayTransformability(displayTransformability));
5777
- if (profile === "epub" && publication) {
5778
- const fetchPositions = async () => {
5779
- try {
5780
- const positionsList = await publication.positionsFromManifest();
5781
- const deserializedPositionsList = deserializePositions(positionsList);
5782
- dispatch(setPositionsList(deserializedPositionsList));
5783
- } catch (error2) {
5784
- console.error("Failed to fetch positions:", error2);
5785
- dispatch(setPositionsList([]));
5786
- }
5787
- };
5788
- fetchPositions();
5789
- }
5790
- }, [publication, profile, dispatch]);
5791
- useEffect(() => {
5792
- if (error) {
5793
- onError(error);
5794
- }
5795
- }, [error, onError]);
5796
- return {
5797
- isLoading,
5798
- error,
5799
- publication,
5800
- manifest,
5801
- selfLink,
5802
- localDataKey,
5803
- profile,
5804
- isRTL,
5805
- isFXL,
5806
- hasDisplayTransformability
5807
- };
5808
- };
5809
- var usePositionStorage = (key, customStorage) => {
5810
- const localStorageData = useLocalStorage(key);
5811
- const [customData, setCustomData] = useState(
5812
- () => customStorage ? customStorage.get() || null : null
6059
+ const store = useStore();
6060
+ const adapter = useMemo(
6061
+ () => new ThReduxGlobalPreferencesAdapter(store, initialPreferences),
6062
+ [store, initialPreferences]
5813
6063
  );
5814
- if (customStorage) {
5815
- const set = (newValue) => {
5816
- if (newValue) {
5817
- customStorage.set(newValue);
5818
- }
5819
- setCustomData(newValue);
5820
- };
5821
- const get = () => customData;
5822
- return {
5823
- setLocalData: set,
5824
- getLocalData: get,
5825
- localData: customData
5826
- };
5827
- }
5828
- return localStorageData;
6064
+ return /* @__PURE__ */ jsx(ThGlobalPreferencesProvider, { adapter, initialPreferences, children });
5829
6065
  };
5830
6066
 
5831
6067
  // src/components/assets/styles/thorium-web.reader.app.module.css
@@ -6148,10 +6384,11 @@ var StatefulBackLink = ({
6148
6384
  className
6149
6385
  }) => {
6150
6386
  const { t } = useI18n();
6151
- const { direction, theming } = useSharedPreferences();
6387
+ const { theming } = useSharedPreferences();
6388
+ const { direction } = useLocale();
6152
6389
  const backLinkPref = theming.header?.backLink;
6153
6390
  const tooltipDelay = theming.icon.tooltipDelay;
6154
- const isRTL = direction === "rtl" /* rtl */;
6391
+ const isRTL = direction === "rtl";
6155
6392
  const variant = backLinkPref?.variant || "arrow" /* arrow */;
6156
6393
  const href = backLinkPref?.href;
6157
6394
  const content = backLinkPref?.content;
@@ -6215,7 +6452,7 @@ var StatefulBackLink = ({
6215
6452
  for (const { name, value } of Array.from(svgElement.attributes)) {
6216
6453
  attributes[name] = value;
6217
6454
  }
6218
- contentNode = React24.createElement("svg", {
6455
+ contentNode = React22.createElement("svg", {
6219
6456
  ...attributes,
6220
6457
  "aria-hidden": "true",
6221
6458
  focusable: "false",
@@ -6242,9 +6479,8 @@ var useReaderHeaderBase = (actionKeys) => {
6242
6479
  const headerRef = useRef(null);
6243
6480
  const { t } = useI18n();
6244
6481
  const { actionsComponentsMap } = usePlugins();
6245
- useAppSelector((state) => state.actions.keys);
6246
6482
  const overflowMap = useAppSelector((state) => state.actions.overflow);
6247
- const isScroll = useAppSelector((state) => state.settings.scroll);
6483
+ const isScroll = useIsScroll();
6248
6484
  const isImmersive = useAppSelector((state) => state.reader.isImmersive);
6249
6485
  const isHovering = useAppSelector((state) => state.reader.isHovering);
6250
6486
  const hasScrollAffordance = useAppSelector((state) => state.reader.hasScrollAffordance);
@@ -6320,6 +6556,6 @@ var useReaderHeaderBase = (actionKeys) => {
6320
6556
  };
6321
6557
  };
6322
6558
 
6323
- export { NavigatorProvider, StatefulActionIcon, StatefulAudioAutoPlay, StatefulAudioPlaybackRateContainer, StatefulAudioPlaybackRateTrigger, StatefulAudioRemotePlaybackTrigger, StatefulAudioSettingsContainer, StatefulAudioSkipBackwardInterval, StatefulAudioSkipForwardInterval, StatefulAudioSkipInterval, StatefulAudioSleepTimerContainer, StatefulAudioSleepTimerTrigger, StatefulAudioTocContainer, StatefulAudioTocTrigger, StatefulAudioVolumeContainer, StatefulAudioVolumeTrigger, StatefulBackLink, StatefulBottomSheet, StatefulCollapsibleActionsBar, StatefulColumns, StatefulCompactPopoverSheet, StatefulDockedSheet, StatefulDockingWrapper, StatefulDropdown, StatefulFontFamily, StatefulFullScreenSheet, StatefulFullscreenTrigger, StatefulGroupWrapper, StatefulHyphens, StatefulJumpToPositionContainer, StatefulJumpToPositionTrigger, StatefulLayout, StatefulLetterSpacing, StatefulLineHeight, StatefulModalBase, StatefulModalSheet, StatefulNumberField, StatefulOverflowMenu, StatefulOverflowMenuItem, StatefulParagraphIndent, StatefulParagraphSpacing, StatefulPopoverSheet, StatefulPreferencesProvider, StatefulPublisherStyles, StatefulRadioGroup, StatefulSettingsTrigger, StatefulSettingsWrapper, StatefulSheetWrapper, StatefulSlider, StatefulSliderWithPresets, StatefulSpacingGroup, StatefulSpacingGroupContainer, StatefulSpacingPresets, StatefulSwitch, StatefulTextAlign, StatefulTextGroup, StatefulTextGroupContainer, StatefulTextNormalize, StatefulTheme, StatefulTocContainer, StatefulTocTrigger, StatefulVisualSettingsContainer, StatefulWordSpacing, StatefulZoom, UnstableStatefulFontWeight, createAudioDefaultPlugin, createDefaultPlugin, thorium_web_button_default, thorium_web_overflow_default, thorium_web_reader_app_default, thorium_web_reader_header_default, useDocking, useEffectiveRange, useGridNavigation, useGridTemplate, useLineHeight, useNavigator, usePositionStorage, usePublication, useReaderHeaderBase, useReaderTransitions, useSettingsComponentStatus, useSpacingPresets };
6324
- //# sourceMappingURL=chunk-LP3JFZ4A.mjs.map
6325
- //# sourceMappingURL=chunk-LP3JFZ4A.mjs.map
6559
+ export { NavigatorProvider, StatefulActionIcon, StatefulAudioAutoPlay, StatefulAudioPlaybackRateContainer, StatefulAudioPlaybackRateTrigger, StatefulAudioRemotePlaybackTrigger, StatefulAudioSettingsContainer, StatefulAudioSkipBackwardInterval, StatefulAudioSkipForwardInterval, StatefulAudioSkipInterval, StatefulAudioSleepTimerContainer, StatefulAudioSleepTimerTrigger, StatefulAudioTocContainer, StatefulAudioTocTrigger, StatefulAudioVolumeContainer, StatefulAudioVolumeTrigger, StatefulBackLink, StatefulBottomSheet, StatefulCollapsibleActionsBar, StatefulColumns, StatefulCompactPopoverSheet, StatefulDockedSheet, StatefulDockingWrapper, StatefulDropdown, StatefulFontFamily, StatefulFullScreenSheet, StatefulFullscreenTrigger, StatefulGlobalPreferencesProvider, StatefulGroupWrapper, StatefulHyphens, StatefulJumpToPositionContainer, StatefulJumpToPositionTrigger, StatefulLayout, StatefulLetterSpacing, StatefulLigatures, StatefulLineHeight, StatefulModalBase, StatefulModalSheet, StatefulNoRuby, StatefulNumberField, StatefulOverflowMenu, StatefulOverflowMenuItem, StatefulParagraphIndent, StatefulParagraphSpacing, StatefulPopoverSheet, StatefulPreferencesProvider, StatefulPublisherStyles, StatefulRadioGroup, StatefulSettingsTrigger, StatefulSettingsWrapper, StatefulSheetWrapper, StatefulSlider, StatefulSliderWithPresets, StatefulSpacingGroup, StatefulSpacingGroupContainer, StatefulSpacingPresets, StatefulSwitch, StatefulTextAlign, StatefulTextGroup, StatefulTextGroupContainer, StatefulTextNormalize, StatefulTheme, StatefulTocContainer, StatefulTocTrigger, StatefulVisualSettingsContainer, StatefulWordSpacing, StatefulZoom, UnstableStatefulFontWeight, createAudioDefaultPlugin, createDefaultPlugin, thorium_web_button_default, thorium_web_overflow_default, thorium_web_reader_app_default, thorium_web_reader_header_default, useDocking, useEffectiveRange, useGridNavigation, useGridTemplate, useIsScroll, useLineHeight, useNavigator, usePlaceholder, usePositionStorage, usePublication, useReaderHeaderBase, useReaderSetting, useReaderTransitions, useSettingsComponentStatus, useSpacingPresets };
6560
+ //# sourceMappingURL=chunk-PXXWEMNL.mjs.map
6561
+ //# sourceMappingURL=chunk-PXXWEMNL.mjs.map