@edrlab/thorium-web 1.3.0 → 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 (111) 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-BhG1wicI.d.mts → actionsReducer-B32cq2mB.d.mts} +11 -4
  5. package/dist/{chunk-L4XGZAZ5.mjs → chunk-2YRT7RNW.mjs} +20 -3
  6. package/dist/chunk-2YRT7RNW.mjs.map +1 -0
  7. package/dist/{chunk-UCTMVCW7.mjs → chunk-6MONB2DN.mjs} +91 -63
  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-OWHP7ONM.mjs → chunk-IVXRCKWR.mjs} +4 -4
  14. package/dist/{chunk-OWHP7ONM.mjs.map → chunk-IVXRCKWR.mjs.map} +1 -1
  15. package/dist/{chunk-RRVLWDT3.mjs → chunk-KVUG6BNI.mjs} +92 -27
  16. package/dist/chunk-KVUG6BNI.mjs.map +1 -0
  17. package/dist/{chunk-IYAFKTPL.mjs → chunk-NUXGQWED.mjs} +7 -5
  18. package/dist/chunk-NUXGQWED.mjs.map +1 -0
  19. package/dist/{chunk-4ODYHZKD.mjs → chunk-P6ILEQ5P.mjs} +49 -51
  20. package/dist/chunk-P6ILEQ5P.mjs.map +1 -0
  21. package/dist/{chunk-D7MFLHXV.mjs → chunk-PXXWEMNL.mjs} +951 -592
  22. package/dist/chunk-PXXWEMNL.mjs.map +1 -0
  23. package/dist/{chunk-T2E6MRVP.mjs → chunk-QUSGPT5M.mjs} +11 -12
  24. package/dist/chunk-QUSGPT5M.mjs.map +1 -0
  25. package/dist/{chunk-XBZWGRDM.mjs → chunk-SZAVAQ6S.mjs} +30 -6
  26. package/dist/chunk-SZAVAQ6S.mjs.map +1 -0
  27. package/dist/{chunk-3LDWKC5N.mjs → chunk-T5ENYSDJ.mjs} +5 -5
  28. package/dist/chunk-T5ENYSDJ.mjs.map +1 -0
  29. package/dist/{chunk-4TVEDX7L.mjs → chunk-TSLTLQ6O.mjs} +127 -41
  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-YZ3KCMKY.mjs → chunk-WLVE3WNW.mjs} +238 -58
  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 +14 -14
  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 +15 -15
  43. package/dist/components/Misc/index.css +2 -2
  44. package/dist/components/Misc/index.css.map +1 -1
  45. package/dist/components/Misc/index.mjs +4 -4
  46. package/dist/components/Reader/index.css +5 -5
  47. package/dist/components/Reader/index.css.map +1 -1
  48. package/dist/components/Reader/index.d.mts +11 -11
  49. package/dist/components/Reader/index.mjs +26 -24
  50. package/dist/components/Reader/index.mjs.map +1 -1
  51. package/dist/components/WebPub/index.css +3 -3
  52. package/dist/components/WebPub/index.css.map +1 -1
  53. package/dist/components/WebPub/index.d.mts +12 -12
  54. package/dist/components/WebPub/index.mjs +15 -15
  55. package/dist/core/Components/index.d.mts +11 -21
  56. package/dist/core/Components/index.mjs +1 -1
  57. package/dist/core/Helpers/index.d.mts +1 -1
  58. package/dist/core/Helpers/index.mjs +2 -3
  59. package/dist/core/Hooks/index.d.mts +12 -8
  60. package/dist/core/Hooks/index.mjs +1 -1
  61. package/dist/i18n/index.mjs +3 -6
  62. package/dist/lib/index.d.mts +49 -20
  63. package/dist/lib/index.mjs +3 -2
  64. package/dist/locales/da/thorium-shared.json +3 -0
  65. package/dist/locales/en/thorium-shared.json +24 -2
  66. package/dist/locales/en/thorium-web.json +2 -2
  67. package/dist/locales/es/thorium-shared.json +364 -0
  68. package/dist/locales/es/thorium-web.json +130 -0
  69. package/dist/locales/et/thorium-shared.json +121 -9
  70. package/dist/locales/et/thorium-web.json +32 -1
  71. package/dist/locales/fi/thorium-shared.json +42 -4
  72. package/dist/locales/fi/thorium-web.json +36 -2
  73. package/dist/locales/fr/thorium-shared.json +108 -1
  74. package/dist/locales/fr/thorium-web.json +121 -86
  75. package/dist/locales/it/thorium-shared.json +108 -1
  76. package/dist/locales/it/thorium-web.json +15 -2
  77. package/dist/locales/lt/thorium-web.json +10 -1
  78. package/dist/locales/pl/thorium-web.json +1 -1
  79. package/dist/locales/pt-BR/thorium-shared.json +6 -0
  80. package/dist/locales/pt-BR/thorium-web.json +88 -88
  81. package/dist/locales/pt-PT/thorium-shared.json +91 -0
  82. package/dist/locales/pt-PT/thorium-web.json +15 -3
  83. package/dist/locales/sv/thorium-shared.json +108 -2
  84. package/dist/locales/sv/thorium-web.json +15 -3
  85. package/dist/locales/tr/thorium-shared.json +42 -0
  86. package/dist/preferences/index.d.mts +59 -13
  87. package/dist/preferences/index.mjs +6 -6
  88. package/dist/{settingsReducer-Bu1zeveu.d.mts → settingsReducer-DLaT2wUB.d.mts} +14 -2
  89. package/dist/{ui-nBv8gfr0.d.mts → ui-DnZZhozX.d.mts} +1 -1
  90. package/dist/{useAudioNavigator-C5aW4-eT.d.mts → useAudioNavigator-CWXyNWq1.d.mts} +3 -1
  91. package/dist/{useContrast-2t429O9O.d.mts → useContrast-Bo7cDw_X.d.mts} +5 -1
  92. package/dist/{usePreferences-VaBf46eP.d.mts → usePreferences-D8NU1yhP.d.mts} +6 -8
  93. package/dist/{useReaderTransitions-JDzlBFsu.d.mts → useReaderTransitions-BQGzKeY2.d.mts} +61 -10
  94. package/package.json +12 -11
  95. package/dist/chunk-3LDWKC5N.mjs.map +0 -1
  96. package/dist/chunk-4ODYHZKD.mjs.map +0 -1
  97. package/dist/chunk-4TVEDX7L.mjs.map +0 -1
  98. package/dist/chunk-7CGMWOZN.mjs +0 -20
  99. package/dist/chunk-7CGMWOZN.mjs.map +0 -1
  100. package/dist/chunk-C236BQQB.mjs +0 -1725
  101. package/dist/chunk-C236BQQB.mjs.map +0 -1
  102. package/dist/chunk-D7MFLHXV.mjs.map +0 -1
  103. package/dist/chunk-IYAFKTPL.mjs.map +0 -1
  104. package/dist/chunk-L4XGZAZ5.mjs.map +0 -1
  105. package/dist/chunk-RRVLWDT3.mjs.map +0 -1
  106. package/dist/chunk-T2E6MRVP.mjs.map +0 -1
  107. package/dist/chunk-UCTMVCW7.mjs.map +0 -1
  108. package/dist/chunk-WECWPYZB.mjs +0 -1950
  109. package/dist/chunk-WECWPYZB.mjs.map +0 -1
  110. package/dist/chunk-XBZWGRDM.mjs.map +0 -1
  111. package/dist/chunk-YZ3KCMKY.mjs.map +0 -1
@@ -1,21 +1,21 @@
1
- import { makeBreakpointsMap, isKeyboardTriggered, isActiveElement } from './chunk-ITDBOMY5.mjs';
2
- import { useFullscreen, useEpubNavigator, useLocalStorage } from './chunk-XBZWGRDM.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-YZ3KCMKY.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-WECWPYZB.mjs';
6
- import { useSharedPreferences, ThDockingTypes, ThSheetTypes, defaultSpacingSettingsSubpanel, defaultSpacingSettingsMain, defaultTextSettingsSubpanel, defaultTextSettingsMain, ThTextSettingsKeys, ThSpacingSettingsKeys, defaultAudioSkipBackwardInterval, defaultAudioSkipForwardInterval, defaultAudioSkipInterval, defaultPreferences } from './chunk-C236BQQB.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';
7
7
  import { isIOSish, buildShortcut, metaKeys } from './chunk-5LUMM7FW.mjs';
8
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-4ODYHZKD.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
10
  import { usePrevious } from './chunk-YZ73DHRU.mjs';
11
- import { useI18n } from './chunk-IYAFKTPL.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, useEffect, useMemo, 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
 
@@ -320,67 +320,293 @@ var useNavigator = () => {
320
320
  if (!context) {
321
321
  throw new Error("useNavigator must be used within NavigatorProvider");
322
322
  }
323
- return {
323
+ const unified = useMemo(() => {
324
+ const navigator = context.visual || context.media;
325
+ if (!navigator) throw new Error("No navigator available");
326
+ const isVisual = isVisualNavigator(navigator, context.visual);
327
+ return {
328
+ go: (locator, animated, callback) => {
329
+ return navigator.go(locator, animated, callback);
330
+ },
331
+ goLink: (link, animated, callback) => {
332
+ return navigator.goLink(link, animated, callback);
333
+ },
334
+ currentLocator: () => navigator.currentLocator(),
335
+ previousLocator: () => {
336
+ if (isVisual && navigator.previousLocator) {
337
+ return navigator.previousLocator() || null;
338
+ }
339
+ return null;
340
+ },
341
+ nextLocator: () => {
342
+ if (isVisual && navigator.nextLocator) {
343
+ return navigator.nextLocator() || null;
344
+ }
345
+ return null;
346
+ },
347
+ goForward: (animated, callback) => {
348
+ if (navigator.goForward) {
349
+ return navigator.goForward(animated, callback);
350
+ }
351
+ return callback?.(false);
352
+ },
353
+ goBackward: (animated, callback) => {
354
+ if (navigator.goBackward) {
355
+ return navigator.goBackward(animated, callback);
356
+ }
357
+ return callback?.(false);
358
+ },
359
+ isVisual: () => isVisual,
360
+ getScriptMode: () => {
361
+ if (isVisual && navigator.getScriptMode) {
362
+ return navigator.getScriptMode?.();
363
+ }
364
+ return void 0;
365
+ },
366
+ getCframes: isVisual ? navigator.getCframes?.bind(navigator) : void 0,
367
+ underlying: navigator
368
+ };
369
+ }, [context.visual, context.media]);
370
+ const visualMemo = useMemo(() => {
371
+ if (!context.visual) return null;
372
+ const visualNavigator = context.visual;
373
+ return {
374
+ ...visualNavigator,
375
+ getSetting: createUnifiedGetSetting(visualNavigator)
376
+ };
377
+ }, [context.visual]);
378
+ const mediaMemo = useMemo(() => {
379
+ return context.media;
380
+ }, [context.media]);
381
+ return useMemo(() => ({
324
382
  get visual() {
325
- if (!context.visual) throw new Error("Visual navigator not available");
326
- const visualNavigator = context.visual;
327
- return {
328
- ...visualNavigator,
329
- getSetting: createUnifiedGetSetting(visualNavigator)
330
- };
383
+ if (!visualMemo) throw new Error("Visual navigator not available");
384
+ return visualMemo;
331
385
  },
332
386
  get media() {
333
- if (!context.media) throw new Error("Media navigator not available");
334
- return context.media;
387
+ if (!mediaMemo) throw new Error("Media navigator not available");
388
+ return mediaMemo;
335
389
  },
336
- // Unified interface that automatically selects the appropriate navigator
337
390
  get unified() {
338
- const navigator = context.visual || context.media;
339
- if (!navigator) throw new Error("No navigator available");
340
- const isVisual = isVisualNavigator(navigator, context.visual);
341
- return {
342
- // Navigation methods available in both
343
- go: (locator, animated, callback) => {
344
- return navigator.go(locator, animated, callback);
345
- },
346
- goLink: (link, animated, callback) => {
347
- return navigator.goLink(link, animated, callback);
348
- },
349
- currentLocator: () => navigator.currentLocator(),
350
- // Unified previous/next navigation
351
- previousLocator: () => {
352
- if (isVisual && navigator.previousLocator) {
353
- return navigator.previousLocator() || null;
354
- }
355
- return null;
356
- },
357
- nextLocator: () => {
358
- if (isVisual && navigator.nextLocator) {
359
- return navigator.nextLocator() || null;
360
- }
361
- return null;
362
- },
363
- // Unified forward/backward navigation
364
- goForward: (animated, callback) => {
365
- if (navigator.goForward) {
366
- return navigator.goForward(animated, callback);
367
- }
368
- return callback?.(false);
369
- },
370
- goBackward: (animated, callback) => {
371
- if (navigator.goBackward) {
372
- return navigator.goBackward(animated, callback);
391
+ return unified;
392
+ }
393
+ }), [visualMemo, mediaMemo, unified]);
394
+ };
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);
373
554
  }
374
- return callback?.(false);
375
- },
376
- // Check if navigator is visual
377
- isVisual: () => isVisual,
378
- // Visual-specific methods (only available when isVisual() is true)
379
- getCframes: isVisual ? navigator.getCframes?.bind(navigator) : void 0,
380
- // Access to underlying navigator for advanced use cases
381
- underlying: navigator
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
+ }
382
590
  };
591
+ fetchPositions();
383
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
384
610
  };
385
611
  };
386
612
 
@@ -425,15 +651,15 @@ var StatefulDockStart = ({ variant, associatedKey }) => {
425
651
  const preferences = useActionsPreferences();
426
652
  const { t } = useI18n();
427
653
  const direction = useAppSelector((state) => state.reader.direction);
428
- const actionsMap = useAppSelector((state) => state.actions.keys);
429
654
  const profile = useAppSelector((state) => state.reader.profile);
655
+ const actionsMap = useAppSelector((state) => profile ? state.actions.keys[profile] : void 0);
656
+ const actions = useActions(actionsMap || {});
430
657
  const isRTL = direction === "rtl" /* rtl */;
431
658
  const translationKey = isRTL ? "reader.app.docker.dockToRight" : "reader.app.docker.dockToLeft";
432
659
  const localeKey = {
433
660
  trigger: t(`${translationKey}.trigger`),
434
661
  tooltip: t(`${translationKey}.tooltip`)
435
662
  };
436
- const actions = useActions(actionsMap);
437
663
  const isDisabled = actions.whichDocked(associatedKey) === "dockingStart" /* start */;
438
664
  const dispatch = useAppDispatch();
439
665
  const handlePress = useCallback(() => {
@@ -472,15 +698,15 @@ var StatefulDockEnd = ({ variant, associatedKey }) => {
472
698
  const preferences = useActionsPreferences();
473
699
  const { t } = useI18n();
474
700
  const direction = useAppSelector((state) => state.reader.direction);
475
- const actionsMap = useAppSelector((state) => state.actions.keys);
476
701
  const profile = useAppSelector((state) => state.reader.profile);
702
+ const actionsMap = useAppSelector((state) => profile ? state.actions.keys[profile] : void 0);
703
+ const actions = useActions(actionsMap || {});
477
704
  const isRTL = direction === "rtl" /* rtl */;
478
705
  const translationKey = isRTL ? "reader.app.docker.dockToLeft" : "reader.app.docker.dockToRight";
479
706
  const localeKey = {
480
707
  trigger: t(`${translationKey}.trigger`),
481
708
  tooltip: t(`${translationKey}.tooltip`)
482
709
  };
483
- const actions = useActions(actionsMap);
484
710
  const isDisabled = actions.whichDocked(associatedKey) === "dockingEnd" /* end */;
485
711
  const dispatch = useAppDispatch();
486
712
  const handlePress = useCallback(() => {
@@ -520,9 +746,9 @@ var stack_default = SvgStack;
520
746
  var StatefulDockTransientPopover = ({ variant, associatedKey }) => {
521
747
  const preferences = useActionsPreferences();
522
748
  const { t } = useI18n();
523
- const actionsMap = useAppSelector((state) => state.actions.keys);
524
749
  const profile = useAppSelector((state) => state.reader.profile);
525
- const actions = useActions(actionsMap);
750
+ const actionsMap = useAppSelector((state) => profile ? state.actions.keys[profile] : void 0);
751
+ const actions = useActions(actionsMap || {});
526
752
  const isDisabled = !actions.isDocked(associatedKey) || actions.whichDocked(associatedKey) === "dockingTransient" /* transient */;
527
753
  const dispatch = useAppDispatch();
528
754
  const handlePress = useCallback(() => {
@@ -621,38 +847,86 @@ var StatefulDocker = ({
621
847
  )
622
848
  ] }) });
623
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
624
873
  var useWebkitPatch = (isOpen) => {
625
- const profile = useAppSelector((state) => state.reader.profile);
626
- const isWebPub = profile === "webPub";
627
- const scroll = useAppSelector((state) => state.settings.scroll);
628
- const isFXL = useAppSelector((state) => state.publication.isFXL);
629
- const isScroll = isWebPub || scroll && !isFXL;
630
- const {
631
- getCframes
632
- } = useNavigator().unified;
874
+ const isScroll = useIsScroll();
875
+ const scriptMode = useAppSelector((state) => state.publication.scriptMode);
876
+ const isHorizontalScroll = scriptMode === "cjk-vertical" || scriptMode === "mongolian-vertical";
877
+ const prevIsOpen = usePrevious(isOpen);
878
+ let getCframes;
879
+ try {
880
+ const visual = useNavigator().visual;
881
+ getCframes = visual.getCframes;
882
+ } catch (e) {
883
+ getCframes = void 0;
884
+ }
633
885
  useEffect(() => {
634
- if (isScroll && !isOpen) {
886
+ if (isScroll && prevIsOpen && !isOpen && getCframes) {
635
887
  const container = document.getElementById("thorium-web-container");
636
888
  if (!container) return;
637
- const currentHeight = container.offsetHeight;
638
- 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
+ }
639
896
  setTimeout(() => {
640
- container.style.height = "";
897
+ if (isHorizontalScroll) {
898
+ container.style.width = "";
899
+ } else {
900
+ container.style.height = "";
901
+ }
641
902
  if (!getCframes) return;
642
903
  const frames = getCframes();
643
904
  if (!frames || !Array.isArray(frames) || frames.length === 0) return;
644
905
  const frame = frames[0];
645
- if (!frame?.window?.document?.scrollingElement) return;
646
- const currentScrollTop = frame.window.document.scrollingElement.scrollTop;
647
- if (currentScrollTop > 1) {
648
- frame.window.document.scrollingElement.scrollTop = currentScrollTop - 1;
906
+ let frameWindow;
907
+ try {
908
+ frameWindow = frame?.window;
909
+ if (!frameWindow?.document?.scrollingElement) return;
910
+ } catch (e) {
911
+ return;
912
+ }
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;
649
918
  } else {
650
- frame.window.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;
651
926
  }
652
- frame.window.document.scrollingElement.scrollTop = currentScrollTop;
653
927
  }, 0);
654
928
  }
655
- }, [isScroll, isOpen, getCframes]);
929
+ }, [isScroll, isHorizontalScroll, isOpen, prevIsOpen, getCframes]);
656
930
  };
657
931
  var StatefulPopoverSheet = ({
658
932
  id,
@@ -688,7 +962,7 @@ var StatefulPopoverSheet = ({
688
962
  }
689
963
  }, [isOpen]);
690
964
  useWebkitPatch(!!isOpen);
691
- if (React24.Children.toArray(children).length > 0) {
965
+ if (React22.Children.toArray(children).length > 0) {
692
966
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
693
967
  ThPopover,
694
968
  {
@@ -797,7 +1071,7 @@ var StatefulModalBase = ({
797
1071
  }
798
1072
  }, [isOpen]);
799
1073
  useWebkitPatch(!!isOpen);
800
- if (React24.Children.toArray(children).length > 0) {
1074
+ if (React22.Children.toArray(children).length > 0) {
801
1075
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
802
1076
  ThModal,
803
1077
  {
@@ -1056,7 +1330,7 @@ var StatefulBottomSheet = ({
1056
1330
  return "default";
1057
1331
  }
1058
1332
  };
1059
- if (React24.Children.toArray(children).length > 0) {
1333
+ if (React22.Children.toArray(children).length > 0) {
1060
1334
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
1061
1335
  ThBottomSheet,
1062
1336
  {
@@ -1208,7 +1482,7 @@ var StatefulDockedSheet = ({
1208
1482
  return direction === "ltr" /* ltr */ ? thorium_web_sheets_default.dockedRightBorder : thorium_web_sheets_default.dockedLeftBorder;
1209
1483
  }
1210
1484
  }, [flow, direction]);
1211
- if (React24.Children.toArray(children).length > 0) {
1485
+ if (React22.Children.toArray(children).length > 0) {
1212
1486
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
1213
1487
  ThDockedPanel,
1214
1488
  {
@@ -1308,7 +1582,7 @@ var StatefulCompactPopoverSheet = ({
1308
1582
  updateState: resetFocus
1309
1583
  });
1310
1584
  useWebkitPatch(!!isOpen);
1311
- if (React24.Children.toArray(children).length > 0) {
1585
+ if (React22.Children.toArray(children).length > 0) {
1312
1586
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
1313
1587
  Popover,
1314
1588
  {
@@ -1366,12 +1640,12 @@ var dockingMap = null;
1366
1640
  var useDocking = (key) => {
1367
1641
  const preferences = useActionsPreferences();
1368
1642
  const breakpoint = useAppSelector((state) => state.theming.breakpoint);
1369
- const actionsMap = useAppSelector((state) => state.actions.keys);
1370
- const actionState = actionsMap[key];
1371
1643
  const profile = useAppSelector((state) => state.reader.profile);
1644
+ const actionsMap = useAppSelector((state) => profile ? state.actions.keys[profile] : void 0);
1645
+ const actionState = actionsMap?.[key];
1372
1646
  const dock = useAppSelector((state) => profile ? state.actions.dock[profile] : void 0);
1373
1647
  const dispatch = useAppDispatch();
1374
- const actions = useActions(actionsMap);
1648
+ const actions = useActions(actionsMap || {});
1375
1649
  const startActionKey = dock?.["dockingStart" /* start */]?.actionKey;
1376
1650
  const endActionKey = dock?.["dockingEnd" /* end */]?.actionKey;
1377
1651
  const startStatus = useActionComponentStatus({ actionKey: startActionKey || "" });
@@ -1501,13 +1775,16 @@ var useDocking = (key) => {
1501
1775
  if (actionState?.docking === "dockingTransient" /* transient */) return;
1502
1776
  if (sheetType !== "docked start" /* dockedStart */ && sheetType !== "docked end" /* dockedEnd */) {
1503
1777
  if (previousSheetType === "docked start" /* dockedStart */ || previousSheetType === "docked end" /* dockedEnd */) {
1504
- dispatch(setActionOpen({
1505
- key,
1506
- isOpen: false
1507
- }));
1778
+ if (profile) {
1779
+ dispatch(setActionOpen({
1780
+ key,
1781
+ isOpen: false,
1782
+ profile
1783
+ }));
1784
+ }
1508
1785
  }
1509
1786
  }
1510
- }, [dispatch, key, sheetType, previousSheetType, actionState?.docking]);
1787
+ }, [dispatch, key, sheetType, previousSheetType, actionState?.docking, profile]);
1511
1788
  useEffect(() => {
1512
1789
  if (actionState?.isOpen == null && profile) {
1513
1790
  if (sheetType === "docked start" /* dockedStart */) {
@@ -1518,7 +1795,8 @@ var useDocking = (key) => {
1518
1795
  }));
1519
1796
  dispatch(setActionOpen({
1520
1797
  key,
1521
- isOpen: true
1798
+ isOpen: true,
1799
+ profile
1522
1800
  }));
1523
1801
  } else if (sheetType === "docked end" /* dockedEnd */) {
1524
1802
  dispatch(dockAction({
@@ -1528,7 +1806,8 @@ var useDocking = (key) => {
1528
1806
  }));
1529
1807
  dispatch(setActionOpen({
1530
1808
  key,
1531
- isOpen: true
1809
+ isOpen: true,
1810
+ profile
1532
1811
  }));
1533
1812
  }
1534
1813
  }
@@ -1569,7 +1848,8 @@ var useDocking = (key) => {
1569
1848
  if (actionState?.isOpen === false) {
1570
1849
  dispatch(setActionOpen({
1571
1850
  key,
1572
- isOpen: true
1851
+ isOpen: true,
1852
+ profile
1573
1853
  }));
1574
1854
  }
1575
1855
  } else if (isDockedInEnd && actionState?.docking !== "dockingEnd" /* end */) {
@@ -1581,7 +1861,8 @@ var useDocking = (key) => {
1581
1861
  if (actionState?.isOpen === false) {
1582
1862
  dispatch(setActionOpen({
1583
1863
  key,
1584
- isOpen: true
1864
+ isOpen: true,
1865
+ profile
1585
1866
  }));
1586
1867
  }
1587
1868
  }
@@ -1628,7 +1909,8 @@ var StatefulJumpToPositionContainer = ({
1628
1909
  triggerRef
1629
1910
  }) => {
1630
1911
  const { t } = useI18n();
1631
- const actionState = useAppSelector((state) => state.actions.keys["jumpToPosition" /* jumpToPosition */]);
1912
+ const profile = useAppSelector((state) => state.reader.profile);
1913
+ const actionState = useAppSelector((state) => profile ? state.actions.keys[profile]["jumpToPosition" /* jumpToPosition */] : void 0);
1632
1914
  const positionsList = useAppSelector((state) => state.publication.positionsList);
1633
1915
  const positionNumbers = useAppSelector((state) => state.publication.unstableTimeline?.progression?.currentPositions);
1634
1916
  const reducedMotion = useAppSelector((state) => state.theming.prefersReducedMotion);
@@ -1644,11 +1926,14 @@ var StatefulJumpToPositionContainer = ({
1644
1926
  }, [position, positionNumbers]);
1645
1927
  const label = t("reader.jumpToPosition.label", { positionStart: 1, positionEnd: positionsList.length });
1646
1928
  const setOpen = useCallback((value) => {
1647
- dispatch(setActionOpen({
1648
- key: "jumpToPosition" /* jumpToPosition */,
1649
- isOpen: value
1650
- }));
1651
- }, [dispatch]);
1929
+ if (profile) {
1930
+ dispatch(setActionOpen({
1931
+ key: "jumpToPosition" /* jumpToPosition */,
1932
+ isOpen: value,
1933
+ profile
1934
+ }));
1935
+ }
1936
+ }, [dispatch, profile]);
1652
1937
  const handleInput = useCallback((e) => {
1653
1938
  const target = e.target;
1654
1939
  setPosition(parseInt(target.value));
@@ -1737,14 +2022,18 @@ var pin_drop_default = SvgPinDrop;
1737
2022
  var StatefulJumpToPositionTrigger = ({ variant }) => {
1738
2023
  const preferences = useActionsPreferences();
1739
2024
  const { t } = useI18n();
1740
- const actionState = useAppSelector((state) => state.actions.keys["jumpToPosition" /* jumpToPosition */]);
2025
+ const profile = useAppSelector((state) => state.reader.profile);
2026
+ const actionState = useAppSelector((state) => profile ? state.actions.keys[profile]["jumpToPosition" /* jumpToPosition */] : void 0);
1741
2027
  const positionsList = useAppSelector((state) => state.publication.positionsList);
1742
2028
  const dispatch = useAppDispatch();
1743
2029
  const setOpen = (value) => {
1744
- dispatch(setActionOpen({
1745
- key: "jumpToPosition" /* jumpToPosition */,
1746
- isOpen: value
1747
- }));
2030
+ if (profile) {
2031
+ dispatch(setActionOpen({
2032
+ key: "jumpToPosition" /* jumpToPosition */,
2033
+ isOpen: value,
2034
+ profile
2035
+ }));
2036
+ }
1748
2037
  };
1749
2038
  if (!isPositionsListValid(positionsList)) return null;
1750
2039
  return /* @__PURE__ */ jsx(Fragment, { children: variant && variant === "menuItem" /* menu */ ? /* @__PURE__ */ jsx(
@@ -1815,14 +2104,18 @@ var StatefulSettingsWrapper = ({
1815
2104
  onReset,
1816
2105
  children
1817
2106
  }) => {
1818
- const actionState = useAppSelector((state) => state.actions.keys["settings" /* settings */]);
2107
+ const profile = useAppSelector((state) => state.reader.profile);
2108
+ const actionState = useAppSelector((state) => profile ? state.actions.keys[profile]["settings" /* settings */] : void 0);
1819
2109
  const dispatch = useAppDispatch();
1820
2110
  const docking = useDocking("settings" /* settings */);
1821
2111
  const setOpen = (value) => {
1822
- dispatch(setActionOpen({
1823
- key: "settings" /* settings */,
1824
- isOpen: value
1825
- }));
2112
+ if (profile) {
2113
+ dispatch(setActionOpen({
2114
+ key: "settings" /* settings */,
2115
+ isOpen: value,
2116
+ profile
2117
+ }));
2118
+ }
1826
2119
  if (!value) dispatch(setHovering(false));
1827
2120
  };
1828
2121
  useEffect(() => {
@@ -1859,10 +2152,13 @@ var StatefulAudioSettingsContainer = ({
1859
2152
  const { settingsComponentsMap } = usePlugins();
1860
2153
  const { t } = useI18n();
1861
2154
  const dispatch = useAppDispatch();
2155
+ const profile = useAppSelector((state) => state.reader.profile);
1862
2156
  const close = useCallback(() => {
1863
- dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false }));
2157
+ if (profile) {
2158
+ dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false, profile }));
2159
+ }
1864
2160
  dispatch(setHovering(false));
1865
- }, [dispatch]);
2161
+ }, [dispatch, profile]);
1866
2162
  return /* @__PURE__ */ jsx(
1867
2163
  StatefulSettingsWrapper,
1868
2164
  {
@@ -1938,6 +2234,7 @@ var StatefulSpacingGroup = () => {
1938
2234
  const { preferences } = usePreferences();
1939
2235
  const { t } = useI18n();
1940
2236
  const { spacingSettingsComponentsMap } = usePlugins();
2237
+ const { mainSpacingSettingsKeys, subPanelSpacingSettingsKeys } = useFilteredPreferenceKeys();
1941
2238
  const dispatch = useAppDispatch();
1942
2239
  const setSpacingContainer = useCallback(() => {
1943
2240
  dispatch(setSettingsContainer("spacing" /* spacing */));
@@ -1950,7 +2247,11 @@ var StatefulSpacingGroup = () => {
1950
2247
  moreTooltip: t("reader.settings.spacing.advanced.tooltip"),
1951
2248
  onPressMore: setSpacingContainer,
1952
2249
  componentsMap: spacingSettingsComponentsMap,
1953
- 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
+ },
1954
2255
  defaultPrefs: {
1955
2256
  main: defaultSpacingSettingsMain,
1956
2257
  subPanel: defaultSpacingSettingsSubpanel
@@ -1959,10 +2260,9 @@ var StatefulSpacingGroup = () => {
1959
2260
  ) });
1960
2261
  };
1961
2262
  var StatefulSpacingGroupContainer = () => {
1962
- const { preferences } = usePreferences();
1963
- const displayOrder = preferences.settings.spacing?.subPanel || defaultSpacingSettingsSubpanel;
2263
+ const { subPanelSpacingSettingsKeys } = useFilteredPreferenceKeys();
1964
2264
  const { spacingSettingsComponentsMap } = usePlugins();
1965
- return /* @__PURE__ */ jsx(Fragment, { children: displayOrder.map((key) => {
2265
+ return /* @__PURE__ */ jsx(Fragment, { children: subPanelSpacingSettingsKeys.map((key) => {
1966
2266
  const match = spacingSettingsComponentsMap[key];
1967
2267
  if (!match) {
1968
2268
  console.warn(`Setting key "${key}" not found in the plugin registry while present in preferences.`);
@@ -1975,6 +2275,7 @@ var StatefulTextGroup = () => {
1975
2275
  const { preferences } = usePreferences();
1976
2276
  const { t } = useI18n();
1977
2277
  const { textSettingsComponentsMap } = usePlugins();
2278
+ const { mainTextSettingsKeys, subPanelTextSettingsKeys } = useFilteredPreferenceKeys();
1978
2279
  const dispatch = useAppDispatch();
1979
2280
  const setTextContainer = useCallback(() => {
1980
2281
  dispatch(setSettingsContainer("text" /* text */));
@@ -1987,7 +2288,11 @@ var StatefulTextGroup = () => {
1987
2288
  moreTooltip: t("reader.settings.text.advanced.tooltip"),
1988
2289
  onPressMore: setTextContainer,
1989
2290
  componentsMap: textSettingsComponentsMap,
1990
- 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
+ },
1991
2296
  defaultPrefs: {
1992
2297
  main: defaultTextSettingsMain,
1993
2298
  subPanel: defaultTextSettingsSubpanel
@@ -1996,10 +2301,9 @@ var StatefulTextGroup = () => {
1996
2301
  ) });
1997
2302
  };
1998
2303
  var StatefulTextGroupContainer = () => {
1999
- const { preferences } = usePreferences();
2000
- const displayOrder = preferences.settings.text?.subPanel || defaultTextSettingsSubpanel;
2304
+ const { subPanelTextSettingsKeys } = useFilteredPreferenceKeys();
2001
2305
  const { textSettingsComponentsMap } = usePlugins();
2002
- return /* @__PURE__ */ jsx(Fragment, { children: displayOrder.map((key) => {
2306
+ return /* @__PURE__ */ jsx(Fragment, { children: subPanelTextSettingsKeys.map((key) => {
2003
2307
  const match = textSettingsComponentsMap[key];
2004
2308
  if (!match) {
2005
2309
  console.warn(`Action key "${key}" not found in the plugin registry while present in preferences.`);
@@ -2019,7 +2323,7 @@ var StatefulVisualSettingsContainer = ({
2019
2323
  subPanelSpacingSettingsKeys,
2020
2324
  subPanelTextSettingsKeys,
2021
2325
  webPubSettingsKeys
2022
- } = usePreferenceKeys();
2326
+ } = useFilteredPreferenceKeys();
2023
2327
  const { preferences } = usePreferences();
2024
2328
  const { t } = useI18n();
2025
2329
  const { settingsComponentsMap } = usePlugins();
@@ -2035,9 +2339,11 @@ var StatefulVisualSettingsContainer = ({
2035
2339
  dispatch(setSettingsContainer("initial" /* initial */));
2036
2340
  }, [dispatch]);
2037
2341
  const close = useCallback(() => {
2038
- dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false }));
2342
+ if (profile) {
2343
+ dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false, profile }));
2344
+ }
2039
2345
  dispatch(setHovering(false));
2040
- }, [dispatch]);
2346
+ }, [dispatch, profile]);
2041
2347
  const isTextNested = useCallback((key) => {
2042
2348
  const textSettings = [
2043
2349
  mainTextSettingsKeys || defaultTextSettingsMain,
@@ -2144,15 +2450,18 @@ var instant_mix_default = SvgInstantMix;
2144
2450
  var StatefulSettingsTrigger = ({ variant }) => {
2145
2451
  const preferences = useActionsPreferences();
2146
2452
  const { t } = useI18n();
2147
- const actionState = useAppSelector((state) => state.actions.keys["settings" /* settings */]);
2148
2453
  const profile = useAppSelector((state) => state.reader.profile);
2454
+ const actionState = useAppSelector((state) => profile ? state.actions.keys[profile]["settings" /* settings */] : void 0);
2149
2455
  const isAudio = profile === "audio";
2150
2456
  const dispatch = useAppDispatch();
2151
2457
  const setOpen = (value) => {
2152
- dispatch(setActionOpen({
2153
- key: "settings" /* settings */,
2154
- isOpen: value
2155
- }));
2458
+ if (profile) {
2459
+ dispatch(setActionOpen({
2460
+ key: "settings" /* settings */,
2461
+ isOpen: value,
2462
+ profile
2463
+ }));
2464
+ }
2156
2465
  if (!value) dispatch(setHovering(false));
2157
2466
  };
2158
2467
  return /* @__PURE__ */ jsx(Fragment, { children: variant && variant === "menuItem" /* menu */ ? /* @__PURE__ */ jsx(
@@ -2303,6 +2612,7 @@ var TocContent = ({
2303
2612
  selectedKeys: tocEntry ? [tocEntry] : [],
2304
2613
  expandedKeys,
2305
2614
  onExpandedChange,
2615
+ dir: isRTL ? "rtl" : "ltr",
2306
2616
  children: function renderItem(item) {
2307
2617
  return /* @__PURE__ */ jsxs(
2308
2618
  TreeItem,
@@ -2341,16 +2651,18 @@ var StatefulTocContainer = ({ triggerRef }) => {
2341
2651
  const tocEntry = unstableTimeline?.toc?.currentEntry ?? void 0;
2342
2652
  const tocEntryId = tocEntry?.id;
2343
2653
  const tocTree = unstableTimeline?.toc?.tree;
2344
- const direction = useAppSelector((state) => state.reader.direction);
2345
- const isRTL = direction === "rtl" /* rtl */;
2346
- const actionState = useAppSelector((state) => state.actions.keys["toc" /* toc */]);
2654
+ const { goLink, getScriptMode: getScriptMode2 } = useNavigator().unified;
2655
+ const isRTL = getScriptMode2() === "rtl";
2656
+ const profile = useAppSelector((state) => state.reader.profile);
2657
+ const actionState = useAppSelector((state) => profile ? state.actions.keys[profile]["toc" /* toc */] : void 0);
2347
2658
  const dispatch = useAppDispatch();
2348
- const { goLink } = useNavigator().unified;
2349
2659
  const docking = useDocking("toc" /* toc */);
2350
2660
  const sheetType = docking.sheetType;
2351
2661
  const setOpen = useCallback((value) => {
2352
- dispatch(setActionOpen({ key: "toc" /* toc */, isOpen: value }));
2353
- }, [dispatch]);
2662
+ if (profile) {
2663
+ dispatch(setActionOpen({ key: "toc" /* toc */, isOpen: value, profile }));
2664
+ }
2665
+ }, [dispatch, profile]);
2354
2666
  const { expandedKeys, setExpandedKeys, filterValue, setFilterValue, displayedTocTree, treeRef, searchInputRef } = useTocContent({ isOpen: actionState?.isOpen ?? false, tocTree, tocEntry: tocEntryId });
2355
2667
  const handleAction = (keys) => {
2356
2668
  if (keys === "all" || !keys || keys.size === 0) return;
@@ -2426,13 +2738,17 @@ var toc_default = SvgToc;
2426
2738
  var StatefulTocTrigger = ({ variant }) => {
2427
2739
  const preferences = useActionsPreferences();
2428
2740
  const { t } = useI18n();
2429
- const actionState = useAppSelector((state) => state.actions.keys["toc" /* toc */]);
2741
+ const profile = useAppSelector((state) => state.reader.profile);
2742
+ const actionState = useAppSelector((state) => profile ? state.actions.keys[profile]["toc" /* toc */] : void 0);
2430
2743
  const dispatch = useAppDispatch();
2431
2744
  const setOpen = (value) => {
2432
- dispatch(setActionOpen({
2433
- key: "toc" /* toc */,
2434
- isOpen: value
2435
- }));
2745
+ if (profile) {
2746
+ dispatch(setActionOpen({
2747
+ key: "toc" /* toc */,
2748
+ isOpen: value,
2749
+ profile
2750
+ }));
2751
+ }
2436
2752
  };
2437
2753
  return /* @__PURE__ */ jsx(Fragment, { children: variant && variant === "menuItem" /* menu */ ? /* @__PURE__ */ jsx(
2438
2754
  StatefulOverflowMenuItem,
@@ -2488,7 +2804,7 @@ var useGridNavigation = ({
2488
2804
  onFocus
2489
2805
  }) => {
2490
2806
  const visibleColumns = useGridTemplate(containerRef, "columns");
2491
- const onKeyDown = React24.useCallback((e) => {
2807
+ const onKeyDown = React22.useCallback((e) => {
2492
2808
  const columns = visibleColumns || 1;
2493
2809
  if (columns <= 1 || !items.current?.length) return;
2494
2810
  const currentIdx = items.current.findIndex((val) => {
@@ -2550,6 +2866,7 @@ var StatefulRadioGroup = ({
2550
2866
  }) => {
2551
2867
  const itemsRef = useRef(items || []);
2552
2868
  const wrapperRef = useRef(null);
2869
+ const profile = useAppSelector((state) => state.reader.profile);
2553
2870
  const direction = useAppSelector((state) => state.reader.direction);
2554
2871
  const isRTL = direction === "rtl" /* rtl */;
2555
2872
  const settingsContainer = useAppSelector((state) => state.reader.settingsContainer);
@@ -2558,12 +2875,15 @@ var StatefulRadioGroup = ({
2558
2875
  if (settingsContainer !== "initial" /* initial */) {
2559
2876
  dispatch(setSettingsContainer("initial" /* initial */));
2560
2877
  } else {
2561
- dispatch(setActionOpen({
2562
- key: "settings" /* settings */,
2563
- isOpen: false
2564
- }));
2878
+ if (profile) {
2879
+ dispatch(setActionOpen({
2880
+ key: "settings" /* settings */,
2881
+ isOpen: false,
2882
+ profile
2883
+ }));
2884
+ }
2565
2885
  }
2566
- }, [dispatch, settingsContainer]);
2886
+ }, [dispatch, settingsContainer, profile]);
2567
2887
  const onFocusCallback = useCallback((value2) => {
2568
2888
  const element = wrapperRef.current?.querySelector(`[value="${value2}"]`);
2569
2889
  if (element) element.focus();
@@ -2605,6 +2925,25 @@ var StatefulRadioGroup = ({
2605
2925
  }
2606
2926
  ) });
2607
2927
  };
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
+ };
2608
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" }) });
2609
2948
  var document_scanner_default = SvgDocumentScanner;
2610
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" }) });
@@ -2613,9 +2952,7 @@ var SvgMenuBook = (props) => /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.
2613
2952
  var menu_book_default = SvgMenuBook;
2614
2953
  var StatefulColumns = () => {
2615
2954
  const { t } = useI18n();
2616
- const scroll = useAppSelector((state) => state.settings.scroll);
2617
- const isFXL = useAppSelector((state) => state.publication.isFXL);
2618
- const isScroll = scroll && !isFXL;
2955
+ const isScroll = useIsScroll();
2619
2956
  const columnCount = useAppSelector((state) => state.settings.columnCount) || "auto";
2620
2957
  const [effectiveValue, setEffectiveValue] = useState(columnCount);
2621
2958
  const fontSize = useAppSelector((state) => state.settings.fontSize);
@@ -2634,6 +2971,7 @@ var StatefulColumns = () => {
2634
2971
  }, [fontSize, fontFamily, wordSpacing, letterSpacing, publisherStyles]);
2635
2972
  const dispatch = useAppDispatch();
2636
2973
  const { submitPreferences, getSetting } = useEpubNavigator();
2974
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["columns" /* columns */];
2637
2975
  const items = useMemo(() => [
2638
2976
  {
2639
2977
  id: "auto",
@@ -2664,12 +3002,12 @@ var StatefulColumns = () => {
2664
3002
  }, []);
2665
3003
  const updatePreference = useCallback(async (value) => {
2666
3004
  const colCount = value === "auto" ? null : Number(value);
2667
- await submitPreferences({ columnCount: colCount });
2668
- updateEffectiveValue(value, getSetting("columnCount"));
3005
+ await submitPreferences({ [prefKey]: colCount });
3006
+ updateEffectiveValue(value, getSetting(prefKey));
2669
3007
  dispatch(setColumnCount(value));
2670
- }, [submitPreferences, getSetting, updateEffectiveValue, dispatch]);
3008
+ }, [prefKey, submitPreferences, getSetting, updateEffectiveValue, dispatch]);
2671
3009
  const debouncedUpdate = useCallback(() => {
2672
- const update = () => updateEffectiveValue(columnCount, getSetting("columnCount"));
3010
+ const update = () => updateEffectiveValue(columnCount, getSetting(prefKey));
2673
3011
  debounce(update, 50)();
2674
3012
  }, [columnCount, layoutSettings, getSetting, updateEffectiveValue]);
2675
3013
  useEffect(() => {
@@ -2714,7 +3052,7 @@ var StatefulDropdown = ({
2714
3052
  className: thorium_web_reader_settings_default.label,
2715
3053
  ...compounds?.label || {}
2716
3054
  },
2717
- ...React24.isValidElement(compounds?.button) ? { button: compounds.button } : {
3055
+ ...React22.isValidElement(compounds?.button) ? { button: compounds.button } : {
2718
3056
  button: {
2719
3057
  className: thorium_web_reader_settings_default.dropdownButton,
2720
3058
  ...compounds?.button || {}
@@ -2725,7 +3063,7 @@ var StatefulDropdown = ({
2725
3063
  placement: "bottom",
2726
3064
  ...compounds?.popover || {}
2727
3065
  },
2728
- ...React24.isValidElement(compounds?.listbox) ? { listbox: compounds.listbox } : {
3066
+ ...React22.isValidElement(compounds?.listbox) ? { listbox: compounds.listbox } : {
2729
3067
  listbox: {
2730
3068
  className: thorium_web_reader_settings_default.dropdownListbox,
2731
3069
  ...compounds?.listbox || {}
@@ -2739,6 +3077,27 @@ var StatefulDropdown = ({
2739
3077
  }
2740
3078
  );
2741
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
+ }
2742
3101
  var StatefulFontFamily = ({ standalone = true }) => {
2743
3102
  const { getFontMetadata, getFontsList } = usePreferences();
2744
3103
  const { t } = useI18n();
@@ -2758,10 +3117,8 @@ var StatefulFontFamily = ({ standalone = true }) => {
2758
3117
  const isWebPub = profile === "webPub";
2759
3118
  const fontLanguage = useAppSelector((state) => state.publication.fontLanguage) || "default";
2760
3119
  const fontPreferences = getFontsList({ language: fontLanguage });
2761
- const fontFamily = useAppSelector((state) => {
2762
- const fontSettings = isWebPub ? state.webPubSettings.fontFamily : state.settings.fontFamily;
2763
- return fontSettings[fontLanguage] ?? "publisher";
2764
- });
3120
+ const fontFamilySettings = useReaderSetting("fontFamily");
3121
+ const fontFamily = fontFamilySettings[fontLanguage] ?? "publisher";
2765
3122
  const availableFontIds = /* @__PURE__ */ new Set([
2766
3123
  "publisher",
2767
3124
  ...Object.keys(fontPreferences)
@@ -2784,12 +3141,13 @@ var StatefulFontFamily = ({ standalone = true }) => {
2784
3141
  ]);
2785
3142
  const dispatch = useAppDispatch();
2786
3143
  const { getSetting, submitPreferences } = useNavigator().visual;
3144
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["fontFamily" /* fontFamily */];
2787
3145
  const updatePreference = useCallback(async (key) => {
2788
3146
  if (!key || key === fontFamily) return;
2789
3147
  const selectedOption = fontFamilyOptions.current.find((option) => option.id === key);
2790
3148
  if (selectedOption) {
2791
- await submitPreferences({ fontFamily: selectedOption.value });
2792
- const currentSetting = getSetting("fontFamily");
3149
+ await submitPreferences({ [prefKey]: selectedOption.value });
3150
+ const currentSetting = getSetting(prefKey);
2793
3151
  if (currentSetting === null) {
2794
3152
  if (isWebPub) {
2795
3153
  dispatch(setWebPubFontFamily({ key: fontLanguage, value: "publisher" }));
@@ -2811,7 +3169,7 @@ var StatefulFontFamily = ({ standalone = true }) => {
2811
3169
  }
2812
3170
  }
2813
3171
  }
2814
- }, [isWebPub, fontLanguage, fontFamily, submitPreferences, getSetting, fontPreferences, getFontMetadata, dispatch]);
3172
+ }, [prefKey, isWebPub, fontLanguage, fontFamily, submitPreferences, getSetting, fontPreferences, getFontMetadata, dispatch]);
2815
3173
  return /* @__PURE__ */ jsx(
2816
3174
  StatefulDropdown,
2817
3175
  {
@@ -2850,9 +3208,10 @@ var UnstableStatefulFontWeight = ({ standalone = true }) => {
2850
3208
  const { t } = useI18n();
2851
3209
  const profile = useAppSelector((state) => state.reader.profile);
2852
3210
  const isWebPub = profile === "webPub";
2853
- const fontWeight = useAppSelector((state) => isWebPub ? state.webPubSettings.fontWeight : state.settings.fontWeight) ?? 400;
3211
+ const fontWeight = useReaderSetting("fontWeight");
2854
3212
  const dispatch = useAppDispatch();
2855
3213
  const { getSetting, submitPreferences } = useNavigator().visual;
3214
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["fontWeight" /* fontWeight */];
2856
3215
  const items = [
2857
3216
  {
2858
3217
  id: "default",
@@ -2877,14 +3236,14 @@ var UnstableStatefulFontWeight = ({ standalone = true }) => {
2877
3236
  }, [fontWeight]);
2878
3237
  const updatePreference = useCallback(async (value) => {
2879
3238
  const fontWeightValue = value === "default" ? 400 : 700;
2880
- await submitPreferences({ fontWeight: fontWeightValue });
2881
- const effectiveSetting = getSetting("fontWeight");
3239
+ await submitPreferences({ [prefKey]: fontWeightValue });
3240
+ const effectiveSetting = getSetting(prefKey);
2882
3241
  if (isWebPub) {
2883
3242
  dispatch(setWebPubFontWeight(effectiveSetting));
2884
3243
  } else {
2885
3244
  dispatch(setFontWeight(effectiveSetting));
2886
3245
  }
2887
- }, [isWebPub, submitPreferences, getSetting, dispatch]);
3246
+ }, [prefKey, isWebPub, submitPreferences, getSetting, dispatch]);
2888
3247
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
2889
3248
  StatefulRadioGroup,
2890
3249
  {
@@ -2928,19 +3287,20 @@ var StatefulHyphens = ({ standalone = true }) => {
2928
3287
  const { t } = useI18n();
2929
3288
  const profile = useAppSelector((state) => state.reader.profile);
2930
3289
  const isWebPub = profile === "webPub";
2931
- const hyphens = useAppSelector((state) => isWebPub ? state.webPubSettings.hyphens : state.settings.hyphens) ?? false;
2932
- const textAlign = useAppSelector((state) => isWebPub ? state.webPubSettings.textAlign : state.settings.textAlign) ?? "publisher" /* publisher */;
3290
+ const hyphens = useReaderSetting("hyphens");
3291
+ const textAlign = useReaderSetting("textAlign");
2933
3292
  const dispatch = useAppDispatch();
2934
3293
  const { getSetting, submitPreferences } = useNavigator().visual;
3294
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["hyphens" /* hyphens */];
2935
3295
  const updatePreference = useCallback(async (value) => {
2936
- await submitPreferences({ hyphens: value });
2937
- const effectiveSetting = getSetting("hyphens");
3296
+ await submitPreferences({ [prefKey]: value });
3297
+ const effectiveSetting = getSetting(prefKey);
2938
3298
  if (isWebPub) {
2939
3299
  dispatch(setWebPubHyphens(effectiveSetting));
2940
3300
  } else {
2941
3301
  dispatch(setHyphens(effectiveSetting));
2942
3302
  }
2943
- }, [isWebPub, submitPreferences, getSetting, dispatch]);
3303
+ }, [prefKey, isWebPub, submitPreferences, getSetting, dispatch]);
2944
3304
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
2945
3305
  StatefulSwitch,
2946
3306
  {
@@ -2959,9 +3319,7 @@ var SvgDocs = (props) => /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/
2959
3319
  var docs_default = SvgDocs;
2960
3320
  var StatefulLayout = () => {
2961
3321
  const { t } = useI18n();
2962
- const scroll = useAppSelector((state) => state.settings.scroll);
2963
- const isFXL = useAppSelector((state) => state.publication.isFXL);
2964
- const isScroll = scroll && !isFXL;
3322
+ const isScroll = useIsScroll();
2965
3323
  const dispatch = useAppDispatch();
2966
3324
  const { getSetting, submitPreferences } = useEpubNavigator();
2967
3325
  const items = [
@@ -2978,11 +3336,12 @@ var StatefulLayout = () => {
2978
3336
  value: "scroll_option" /* scroll */
2979
3337
  }
2980
3338
  ];
3339
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["layout" /* layout */];
2981
3340
  const updatePreference = useCallback(async (value) => {
2982
3341
  const derivedValue = value === "scroll_option" /* scroll */;
2983
- await submitPreferences({ scroll: derivedValue });
2984
- dispatch(setScroll(getSetting("scroll")));
2985
- }, [submitPreferences, getSetting, dispatch]);
3342
+ await submitPreferences({ [prefKey]: derivedValue });
3343
+ dispatch(setScroll(getSetting(prefKey)));
3344
+ }, [prefKey, submitPreferences, getSetting, dispatch]);
2986
3345
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
2987
3346
  StatefulRadioGroup,
2988
3347
  {
@@ -3125,32 +3484,25 @@ var StatefulSlider = ({
3125
3484
  ) });
3126
3485
  };
3127
3486
  function useSettingsComponentStatus(options) {
3128
- const { settingsKey, publicationType, componentType, additionalCondition = true } = options;
3487
+ const { settingsKey, publicationType, additionalCondition = true } = options;
3129
3488
  const { spacingSettingsComponentsMap, textSettingsComponentsMap, settingsComponentsMap } = usePlugins();
3130
- const { preferences } = usePreferences();
3489
+ const { reflowSettingsKeys, fxlSettingsKeys, webPubSettingsKeys, mainTextSettingsKeys, subPanelTextSettingsKeys, mainSpacingSettingsKeys, subPanelSpacingSettingsKeys } = useFilteredPreferenceKeys();
3131
3490
  return useMemo(() => {
3132
3491
  const isComponentRegistered = !!(settingsComponentsMap?.[settingsKey] || spacingSettingsComponentsMap?.[settingsKey] || textSettingsComponentsMap?.[settingsKey]);
3133
3492
  let isInOrder = false;
3134
3493
  switch (publicationType) {
3135
3494
  case "reflow":
3136
- isInOrder = preferences.settings?.reflowOrder?.includes(settingsKey) || false;
3495
+ isInOrder = reflowSettingsKeys.includes(settingsKey) || false;
3137
3496
  break;
3138
3497
  case "fxl":
3139
- isInOrder = preferences.settings?.fxlOrder?.includes(settingsKey) || false;
3498
+ isInOrder = fxlSettingsKeys.includes(settingsKey) || false;
3140
3499
  break;
3141
3500
  case "webpub":
3142
- isInOrder = preferences.settings?.webPubOrder?.includes(settingsKey) || false;
3501
+ isInOrder = webPubSettingsKeys.includes(settingsKey) || false;
3143
3502
  break;
3144
3503
  }
3145
- let isInMainPanel = false;
3146
- let isInSubPanel = false;
3147
- if (componentType === "text") {
3148
- isInMainPanel = preferences.settings?.text?.main?.includes(settingsKey) || false;
3149
- isInSubPanel = preferences.settings?.text?.subPanel?.includes(settingsKey) || false;
3150
- } else if (componentType === "spacing") {
3151
- isInMainPanel = preferences.settings?.spacing?.main?.includes(settingsKey) || false;
3152
- isInSubPanel = preferences.settings?.spacing?.subPanel?.includes(settingsKey) || false;
3153
- }
3504
+ const isInMainPanel = mainTextSettingsKeys.includes(settingsKey) || mainSpacingSettingsKeys.includes(settingsKey) || false;
3505
+ const isInSubPanel = subPanelTextSettingsKeys.includes(settingsKey) || subPanelSpacingSettingsKeys.includes(settingsKey) || false;
3154
3506
  const isDisplayed = isInOrder || (isInMainPanel || isInSubPanel) && additionalCondition;
3155
3507
  const isComponentUsed = isComponentRegistered && isDisplayed;
3156
3508
  return {
@@ -3163,29 +3515,25 @@ function useSettingsComponentStatus(options) {
3163
3515
  }, [
3164
3516
  settingsKey,
3165
3517
  publicationType,
3166
- componentType,
3167
3518
  additionalCondition,
3168
- preferences,
3519
+ reflowSettingsKeys,
3520
+ fxlSettingsKeys,
3521
+ webPubSettingsKeys,
3522
+ mainTextSettingsKeys,
3523
+ subPanelTextSettingsKeys,
3524
+ mainSpacingSettingsKeys,
3525
+ subPanelSpacingSettingsKeys,
3169
3526
  spacingSettingsComponentsMap,
3170
3527
  textSettingsComponentsMap,
3171
3528
  settingsComponentsMap
3172
3529
  ]);
3173
3530
  }
3174
-
3175
- // src/components/Settings/Spacing/hooks/useSpacingPresets.ts
3176
3531
  var useSpacingPresets = () => {
3177
3532
  const readerProfile = useAppSelector((state) => state.reader.profile);
3178
3533
  const isWebPub = readerProfile === "webPub";
3179
3534
  const isFXL = useAppSelector((state) => state.publication.isFXL);
3180
- const settings = useAppSelector(
3181
- (state) => isWebPub ? state.webPubSettings : state.settings
3182
- );
3183
- const spacing = settings?.spacing || {
3184
- preset: "publisher" /* publisher */,
3185
- custom: {},
3186
- baseline: {}
3187
- };
3188
- const { reflowSpacingPresetKeys, fxlSpacingPresetKeys, webPubSpacingPresetKeys } = usePreferenceKeys();
3535
+ const spacing = useReaderSetting("spacing");
3536
+ const { reflowSpacingPresetKeys, fxlSpacingPresetKeys, webPubSpacingPresetKeys } = useFilteredPreferenceKeys();
3189
3537
  const { preferences } = usePreferences();
3190
3538
  const dispatch = useAppDispatch();
3191
3539
  const spacingKeys = useMemo(() => {
@@ -3194,16 +3542,13 @@ var useSpacingPresets = () => {
3194
3542
  const { isComponentUsed: shouldApplyPresets } = useSettingsComponentStatus({
3195
3543
  settingsKey: "spacingPresets" /* spacingPresets */,
3196
3544
  publicationType: isWebPub ? "webpub" : isFXL ? "fxl" : "reflow",
3197
- componentType: "spacing",
3198
3545
  additionalCondition: spacingKeys.length > 0
3199
3546
  });
3200
- const {
3201
- letterSpacing,
3202
- lineHeight,
3203
- paragraphIndent,
3204
- paragraphSpacing,
3205
- wordSpacing
3206
- } = 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");
3207
3552
  const getBaseReduxValue = (key) => {
3208
3553
  switch (key) {
3209
3554
  case "letterSpacing" /* letterSpacing */:
@@ -3398,24 +3743,6 @@ var useSpacingPresets = () => {
3398
3743
  setPublisherStyles: setPublisherStylesAction
3399
3744
  };
3400
3745
  };
3401
- var useEffectiveRange = (preferred, supportedRange, presets) => {
3402
- return useMemo(() => {
3403
- let range;
3404
- if (!supportedRange) {
3405
- range = preferred;
3406
- } else {
3407
- const prefMin = Math.min(...preferred);
3408
- const prefMax = Math.max(...preferred);
3409
- const supMin = Math.min(...supportedRange);
3410
- const supMax = Math.max(...supportedRange);
3411
- range = prefMin >= supMin && prefMax <= supMax ? preferred : supportedRange;
3412
- }
3413
- if (!presets) return { range };
3414
- const [min, max] = [Math.min(...range), Math.max(...range)];
3415
- const effectivePresets = presets.filter((p) => p >= min && p <= max);
3416
- return { range, presets: effectivePresets };
3417
- }, [preferred, supportedRange, presets]);
3418
- };
3419
3746
 
3420
3747
  // src/components/Settings/hooks/usePlaceholder.ts
3421
3748
  var usePlaceholder = (placeholder, range, format) => {
@@ -3454,6 +3781,24 @@ var usePlaceholder = (placeholder, range, format) => {
3454
3781
  }
3455
3782
  return void 0;
3456
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
+ };
3457
3802
  var StatefulLetterSpacing = ({ standalone = true }) => {
3458
3803
  const { preferences } = usePreferences();
3459
3804
  const { t } = useI18n();
@@ -3469,12 +3814,13 @@ var StatefulLetterSpacing = ({ standalone = true }) => {
3469
3814
  const placeholderText = usePlaceholder(letterSpacingRangeConfig.placeholder, letterSpacingRangeConfig.range, "percent");
3470
3815
  const { getEffectiveSpacingValue, setLetterSpacing: setLetterSpacing2, canBeReset } = useSpacingPresets();
3471
3816
  const letterSpacing = getEffectiveSpacingValue("letterSpacing" /* letterSpacing */);
3817
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["letterSpacing" /* letterSpacing */];
3472
3818
  const updatePreference = useCallback(async (value) => {
3473
3819
  await submitPreferences({
3474
- letterSpacing: Array.isArray(value) ? value[0] : value
3820
+ [prefKey]: Array.isArray(value) ? value[0] : value
3475
3821
  });
3476
- setLetterSpacing2(getSetting("letterSpacing"));
3477
- }, [submitPreferences, getSetting, setLetterSpacing2]);
3822
+ setLetterSpacing2(getSetting(prefKey));
3823
+ }, [prefKey, submitPreferences, getSetting, setLetterSpacing2]);
3478
3824
  return /* @__PURE__ */ jsx(Fragment, { children: letterSpacingRangeConfig.variant === "numberField" /* numberField */ ? /* @__PURE__ */ jsx(
3479
3825
  StatefulNumberField,
3480
3826
  {
@@ -3534,8 +3880,9 @@ var StatefulLineHeight = ({ standalone = true }) => {
3534
3880
  const { preferences } = usePreferences();
3535
3881
  const profile = useAppSelector((state) => state.reader.profile);
3536
3882
  const isWebPub = profile === "webPub";
3537
- const publisherStyles = useAppSelector((state) => isWebPub ? state.webPubSettings.publisherStyles : state.settings.publisherStyles) ?? true;
3883
+ const publisherStyles = useReaderSetting("publisherStyles");
3538
3884
  const { getSetting, submitPreferences, preferencesEditor } = useNavigator().visual;
3885
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["lineHeight" /* lineHeight */];
3539
3886
  const { getEffectiveSpacingValue, setLineHeight: setLineHeight2 } = useSpacingPresets();
3540
3887
  const lineHeight = getEffectiveSpacingValue("lineHeight" /* lineHeight */);
3541
3888
  const lineHeightOptions = useLineHeight();
@@ -3585,12 +3932,12 @@ var StatefulLineHeight = ({ standalone = true }) => {
3585
3932
  const updatePreference = useCallback(async (value) => {
3586
3933
  const computedValue = value === "publisher" /* publisher */ ? null : lineHeightOptions[value];
3587
3934
  await submitPreferences({
3588
- lineHeight: computedValue
3935
+ [prefKey]: computedValue
3589
3936
  });
3590
- const currentLineHeight = getSetting("lineHeight");
3937
+ const currentLineHeight = getSetting(prefKey);
3591
3938
  const currentDisplayLineHeightOption = Object.entries(lineHeightOptions).find(([key, value2]) => value2 === currentLineHeight)?.[0];
3592
3939
  setLineHeight2(currentDisplayLineHeightOption);
3593
- }, [submitPreferences, getSetting, setLineHeight2, lineHeightOptions]);
3940
+ }, [prefKey, submitPreferences, getSetting, setLineHeight2, lineHeightOptions]);
3594
3941
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
3595
3942
  StatefulRadioGroup,
3596
3943
  {
@@ -3618,12 +3965,13 @@ var StatefulParagraphIndent = ({ standalone = true }) => {
3618
3965
  const placeholderText = usePlaceholder(paragraphIndentRangeConfig.placeholder, paragraphIndentRangeConfig.range, "multiplier");
3619
3966
  const { getEffectiveSpacingValue, setParagraphIndent: setParagraphIndent2, canBeReset } = useSpacingPresets();
3620
3967
  const paragraphIndent = getEffectiveSpacingValue("paragraphIndent" /* paragraphIndent */);
3968
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["paragraphIndent" /* paragraphIndent */];
3621
3969
  const updatePreference = useCallback(async (value) => {
3622
3970
  await submitPreferences({
3623
- paragraphIndent: Array.isArray(value) ? value[0] : value
3971
+ [prefKey]: Array.isArray(value) ? value[0] : value
3624
3972
  });
3625
- setParagraphIndent2(getSetting("paragraphIndent"));
3626
- }, [submitPreferences, getSetting, setParagraphIndent2]);
3973
+ setParagraphIndent2(getSetting(prefKey));
3974
+ }, [prefKey, submitPreferences, getSetting, setParagraphIndent2]);
3627
3975
  return /* @__PURE__ */ jsx(Fragment, { children: paragraphIndentRangeConfig.variant === "numberField" /* numberField */ ? /* @__PURE__ */ jsx(
3628
3976
  StatefulNumberField,
3629
3977
  {
@@ -3684,12 +4032,13 @@ var StatefulParagraphSpacing = ({ standalone = true }) => {
3684
4032
  const placeholderText = usePlaceholder(paragraphSpacingRangeConfig.placeholder, paragraphSpacingRangeConfig.range, "multiplier");
3685
4033
  const { getEffectiveSpacingValue, setParagraphSpacing: setParagraphSpacing2, canBeReset } = useSpacingPresets();
3686
4034
  const paragraphSpacing = getEffectiveSpacingValue("paragraphSpacing" /* paragraphSpacing */);
4035
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["paragraphSpacing" /* paragraphSpacing */];
3687
4036
  const updatePreference = useCallback(async (value) => {
3688
4037
  await submitPreferences({
3689
- paragraphSpacing: Array.isArray(value) ? value[0] : value
4038
+ [prefKey]: Array.isArray(value) ? value[0] : value
3690
4039
  });
3691
- setParagraphSpacing2(getSetting("paragraphSpacing"));
3692
- }, [submitPreferences, getSetting, setParagraphSpacing2]);
4040
+ setParagraphSpacing2(getSetting(prefKey));
4041
+ }, [prefKey, submitPreferences, getSetting, setParagraphSpacing2]);
3693
4042
  return /* @__PURE__ */ jsx(Fragment, { children: paragraphSpacingRangeConfig.variant === "numberField" /* numberField */ ? /* @__PURE__ */ jsx(
3694
4043
  StatefulNumberField,
3695
4044
  {
@@ -3737,8 +4086,28 @@ var StatefulParagraphSpacing = ({ standalone = true }) => {
3737
4086
  };
3738
4087
  var StatefulPublisherStyles = ({ standalone = true }) => {
3739
4088
  const { t } = useI18n();
3740
- const publisherStyles = useAppSelector((state) => state.settings.publisherStyles);
4089
+ const publisherStyles = useReaderSetting("publisherStyles");
3741
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
+ });
3742
4111
  const lineHeight = getEffectiveSpacingValue("lineHeight" /* lineHeight */);
3743
4112
  const paragraphIndent = getEffectiveSpacingValue("paragraphIndent" /* paragraphIndent */);
3744
4113
  const paragraphSpacing = getEffectiveSpacingValue("paragraphSpacing" /* paragraphSpacing */);
@@ -3746,23 +4115,49 @@ var StatefulPublisherStyles = ({ standalone = true }) => {
3746
4115
  const wordSpacing = getEffectiveSpacingValue("wordSpacing" /* wordSpacing */);
3747
4116
  const lineHeightOptions = useLineHeight();
3748
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 */];
3749
4123
  const updatePreference = useCallback(async (isSelected) => {
3750
- const values = isSelected ? {
3751
- lineHeight: null,
3752
- paragraphIndent: null,
3753
- paragraphSpacing: null,
3754
- letterSpacing: null,
3755
- wordSpacing: null
3756
- } : {
3757
- lineHeight: lineHeight === "publisher" /* publisher */ ? null : lineHeightOptions[lineHeight],
3758
- paragraphIndent,
3759
- paragraphSpacing,
3760
- letterSpacing,
3761
- wordSpacing
3762
- };
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
+ }
3763
4158
  await submitPreferences(values);
3764
4159
  setPublisherStyles2(isSelected ? true : false);
3765
- }, [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]);
3766
4161
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
3767
4162
  StatefulSwitch,
3768
4163
  {
@@ -3799,15 +4194,41 @@ var iconMap = {
3799
4194
  };
3800
4195
  var StatefulSpacingPresets = ({ standalone }) => {
3801
4196
  const { t } = useI18n();
3802
- const { reflowSpacingPresetKeys, fxlSpacingPresetKeys, webPubSpacingPresetKeys, subPanelSpacingSettingsKeys } = usePreferenceKeys();
4197
+ const { reflowSpacingPresetKeys, fxlSpacingPresetKeys, webPubSpacingPresetKeys, subPanelSpacingSettingsKeys } = useFilteredPreferenceKeys();
3803
4198
  const profile = useAppSelector((state) => state.reader.profile);
3804
4199
  const isWebPub = profile === "webPub";
3805
- const spacing = useAppSelector((state) => isWebPub ? state.webPubSettings.spacing : state.settings.spacing);
4200
+ const spacing = useReaderSetting("spacing");
3806
4201
  const isFXL = useAppSelector((state) => state.publication.isFXL);
3807
4202
  const dispatch = useAppDispatch();
3808
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 */];
3809
4209
  const lineHeightOptions = useLineHeight();
3810
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
+ });
3811
4232
  const updatePreference = useCallback(async (value) => {
3812
4233
  const spacingKey = value;
3813
4234
  const presetValues = getPresetValues(spacingKey);
@@ -3820,13 +4241,22 @@ var StatefulSpacingPresets = ({ standalone }) => {
3820
4241
  };
3821
4242
  const lineHeightValue = reduxValues["lineHeight" /* lineHeight */];
3822
4243
  const lineHeightValueNumber = lineHeightValue && lineHeightValue !== "publisher" /* publisher */ ? lineHeightOptions[lineHeightValue] : null;
3823
- const preferencesToSubmit = {
3824
- ["letterSpacing" /* letterSpacing */]: reduxValues["letterSpacing" /* letterSpacing */],
3825
- ["lineHeight" /* lineHeight */]: lineHeightValueNumber,
3826
- ["paragraphIndent" /* paragraphIndent */]: reduxValues["paragraphIndent" /* paragraphIndent */],
3827
- ["paragraphSpacing" /* paragraphSpacing */]: reduxValues["paragraphSpacing" /* paragraphSpacing */],
3828
- ["wordSpacing" /* wordSpacing */]: reduxValues["wordSpacing" /* wordSpacing */]
3829
- };
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
+ }
3830
4260
  await submitPreferences(preferencesToSubmit);
3831
4261
  if (isWebPub) {
3832
4262
  dispatch(setWebPubSpacingPreset({
@@ -3839,7 +4269,7 @@ var StatefulSpacingPresets = ({ standalone }) => {
3839
4269
  values: reduxValues
3840
4270
  }));
3841
4271
  }
3842
- }, [isWebPub, dispatch, submitPreferences, getPresetValues, lineHeightOptions]);
4272
+ }, [isWebPub, dispatch, submitPreferences, getPresetValues, lineHeightOptions, letterSpacingPrefKey, lineHeightPrefKey, paragraphIndentPrefKey, paragraphSpacingPrefKey, wordSpacingPrefKey, isLetterSpacingUsed, isLineHeightUsed, isParagraphIndentUsed, isParagraphSpacingUsed, isWordSpacingUsed]);
3843
4273
  const spacingKeys = useMemo(() => {
3844
4274
  const baseKeys = isWebPub ? webPubSpacingPresetKeys : isFXL ? fxlSpacingPresetKeys : reflowSpacingPresetKeys;
3845
4275
  const subPanelKeys = subPanelSpacingSettingsKeys || [];
@@ -3884,9 +4314,16 @@ var StatefulTextAlign = ({ standalone = true }) => {
3884
4314
  const profile = useAppSelector((state) => state.reader.profile);
3885
4315
  const isWebPub = profile === "webPub";
3886
4316
  const isRTL = useAppSelector((state) => state.publication.isRTL);
3887
- const textAlign = useAppSelector((state) => isWebPub ? state.webPubSettings.textAlign : state.settings.textAlign) ?? "publisher" /* publisher */;
4317
+ const textAlign = useReaderSetting("textAlign");
3888
4318
  const dispatch = useAppDispatch();
3889
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
+ });
3890
4327
  const items = [
3891
4328
  {
3892
4329
  id: "publisher" /* publisher */,
@@ -3909,15 +4346,18 @@ var StatefulTextAlign = ({ standalone = true }) => {
3909
4346
  ];
3910
4347
  const updatePreference = useCallback(async (value) => {
3911
4348
  const textAlign2 = value === "publisher" /* publisher */ ? null : value === "start" /* start */ ? TextAlignment.start : TextAlignment.justify;
3912
- const currentHyphens = getSetting("hyphens");
4349
+ const currentHyphens = getSetting(hyphensPrefKey);
3913
4350
  const hyphens = textAlign2 === null ? null : currentHyphens ?? textAlign2 === TextAlignment.justify;
3914
- await submitPreferences({
3915
- textAlign: textAlign2,
3916
- hyphens
3917
- });
3918
- 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);
3919
4359
  const textAlignValue = textAlignSetting === null ? "publisher" /* publisher */ : textAlignSetting;
3920
- const effectiveHyphens = getSetting("hyphens");
4360
+ const effectiveHyphens = getSetting(hyphensPrefKey);
3921
4361
  if (isWebPub) {
3922
4362
  dispatch(setWebPubTextAlign(textAlignValue));
3923
4363
  dispatch(setWebPubHyphens(effectiveHyphens));
@@ -3925,7 +4365,7 @@ var StatefulTextAlign = ({ standalone = true }) => {
3925
4365
  dispatch(setTextAlign(textAlignValue));
3926
4366
  dispatch(setHyphens(effectiveHyphens));
3927
4367
  }
3928
- }, [isWebPub, getSetting, submitPreferences, dispatch]);
4368
+ }, [hyphensPrefKey, textAlignPrefKey, isWebPub, getSetting, submitPreferences, dispatch, isHyphensUsed]);
3929
4369
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
3930
4370
  StatefulRadioGroup,
3931
4371
  {
@@ -3942,18 +4382,19 @@ var StatefulTextNormalize = ({ standalone = true }) => {
3942
4382
  const { t } = useI18n();
3943
4383
  const profile = useAppSelector((state) => state.reader.profile);
3944
4384
  const isWebPub = profile === "webPub";
3945
- const textNormalization = useAppSelector((state) => isWebPub ? state.webPubSettings.textNormalization : state.settings.textNormalization) ?? false;
4385
+ const textNormalization = useReaderSetting("textNormalization");
3946
4386
  const dispatch = useAppDispatch();
3947
4387
  const { getSetting, submitPreferences } = useNavigator().visual;
4388
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["textNormalize" /* textNormalize */];
3948
4389
  const updatePreference = useCallback(async (value) => {
3949
- await submitPreferences({ textNormalization: value });
3950
- const effectiveSetting = getSetting("textNormalization");
4390
+ await submitPreferences({ [prefKey]: value });
4391
+ const effectiveSetting = getSetting(prefKey);
3951
4392
  if (isWebPub) {
3952
4393
  dispatch(setWebPubTextNormalization(effectiveSetting));
3953
4394
  } else {
3954
4395
  dispatch(setTextNormalization(effectiveSetting));
3955
4396
  }
3956
- }, [isWebPub, submitPreferences, getSetting, dispatch]);
4397
+ }, [prefKey, isWebPub, submitPreferences, getSetting, dispatch]);
3957
4398
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
3958
4399
  StatefulSwitch,
3959
4400
  {
@@ -3965,6 +4406,62 @@ var StatefulTextNormalize = ({ standalone = true }) => {
3965
4406
  }
3966
4407
  ) });
3967
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
+ };
3968
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" }) });
3969
4466
  var check_default = SvgCheck;
3970
4467
  var StatefulTheme = () => {
@@ -3979,7 +4476,7 @@ var StatefulTheme = () => {
3979
4476
  const isRTL = direction === "rtl" /* rtl */;
3980
4477
  const themeArray = profile === "audio" ? audioThemeOrder ?? [] : isFXL ? fxlThemeOrder ?? [] : reflowThemeOrder ?? [];
3981
4478
  const themeObject = useAppSelector((state) => state.theming.theme);
3982
- const theme = profile === "audio" ? themeObject.audio : isFXL ? themeObject.fxl : themeObject.reflow;
4479
+ const theme = profile === "audio" ? themeObject.audio ?? "auto" : isFXL ? themeObject.fxl ?? "auto" : themeObject.reflow ?? "auto";
3983
4480
  const colorScheme = useAppSelector((state) => state.theming.colorScheme);
3984
4481
  const coverTheme = useAppSelector((state) => state.theming.coverTheme);
3985
4482
  const themeItems = useRef(
@@ -3997,10 +4494,15 @@ var StatefulTheme = () => {
3997
4494
  currentValue: theme,
3998
4495
  onChange: async (val) => await updatePreference(val),
3999
4496
  isRTL,
4000
- onEscape: () => dispatch(setActionOpen({
4001
- key: "settings" /* settings */,
4002
- isOpen: false
4003
- })),
4497
+ onEscape: () => {
4498
+ if (profile) {
4499
+ dispatch(setActionOpen({
4500
+ key: "settings" /* settings */,
4501
+ isOpen: false,
4502
+ profile
4503
+ }));
4504
+ }
4505
+ },
4004
4506
  onFocus: (id) => {
4005
4507
  const element = radioGroupWrapperRef.current?.querySelector(`[id="${id}"]`);
4006
4508
  if (element) element.focus();
@@ -4106,12 +4608,13 @@ var StatefulWordSpacing = ({ standalone = true }) => {
4106
4608
  const placeholderText = usePlaceholder(wordSpacingRangeConfig.placeholder, wordSpacingRangeConfig.range, "percent");
4107
4609
  const { getEffectiveSpacingValue, setWordSpacing: setWordSpacing2, canBeReset } = useSpacingPresets();
4108
4610
  const wordSpacing = getEffectiveSpacingValue("wordSpacing" /* wordSpacing */);
4611
+ const prefKey = SETTINGS_KEY_TO_PREFERENCE["wordSpacing" /* wordSpacing */];
4109
4612
  const updatePreference = useCallback(async (value) => {
4110
4613
  await submitPreferences({
4111
- wordSpacing: Array.isArray(value) ? value[0] : value
4614
+ [prefKey]: Array.isArray(value) ? value[0] : value
4112
4615
  });
4113
- setWordSpacing2(getSetting("wordSpacing"));
4114
- }, [submitPreferences, getSetting, setWordSpacing2]);
4616
+ setWordSpacing2(getSetting(prefKey));
4617
+ }, [prefKey, submitPreferences, getSetting, setWordSpacing2]);
4115
4618
  return /* @__PURE__ */ jsx(Fragment, { children: wordSpacingRangeConfig.variant === "numberField" /* numberField */ ? /* @__PURE__ */ jsx(
4116
4619
  StatefulNumberField,
4117
4620
  {
@@ -4162,9 +4665,7 @@ var StatefulZoom = () => {
4162
4665
  const { t } = useI18n();
4163
4666
  const readerProfile = useAppSelector((state) => state.reader.profile);
4164
4667
  const isFXL = useAppSelector((state) => state.publication.isFXL);
4165
- const fontSize = useAppSelector((state) => state.settings.fontSize) || 1;
4166
- const webPubZoom = useAppSelector((state) => state.webPubSettings.zoom) || 1;
4167
- const derivedState = readerProfile === "webPub" ? webPubZoom : fontSize;
4668
+ const derivedState = useReaderSetting("zoom");
4168
4669
  const dispatch = useAppDispatch();
4169
4670
  const {
4170
4671
  getSetting,
@@ -4172,15 +4673,16 @@ var StatefulZoom = () => {
4172
4673
  preferencesEditor
4173
4674
  } = useNavigator().visual;
4174
4675
  const preferenceEditorProperty = readerProfile === "webPub" ? preferencesEditor?.zoom : isFXL ? preferencesEditor?.zoom : preferencesEditor?.fontSize;
4676
+ const prefKey = readerProfile === "webPub" ? SETTINGS_KEY_TO_PREFERENCE["zoom" /* zoom */] : "fontSize";
4175
4677
  const updatePreference = useCallback(async (value) => {
4678
+ const normalizedValue = Array.isArray(value) ? value[0] : value;
4679
+ await submitPreferences({ [prefKey]: normalizedValue });
4176
4680
  if (readerProfile === "webPub") {
4177
- await submitPreferences({ zoom: Array.isArray(value) ? value[0] : value });
4178
- dispatch(setWebPubZoom(getSetting("zoom")));
4681
+ dispatch(setWebPubZoom(getSetting(prefKey)));
4179
4682
  } else {
4180
- await submitPreferences({ fontSize: Array.isArray(value) ? value[0] : value });
4181
- dispatch(setFontSize(getSetting("fontSize")));
4683
+ dispatch(setFontSize(getSetting(prefKey)));
4182
4684
  }
4183
- }, [readerProfile, submitPreferences, getSetting, dispatch]);
4685
+ }, [readerProfile, prefKey, submitPreferences, getSetting, dispatch]);
4184
4686
  const zoomConfig = preferences.settings.keys["zoom" /* zoom */];
4185
4687
  const { range: effectiveRange } = useEffectiveRange(zoomConfig.range, preferenceEditorProperty?.supportedRange);
4186
4688
  const zoomRangeConfig = {
@@ -4234,7 +4736,7 @@ var createDefaultPlugin = () => {
4234
4736
  id: "core",
4235
4737
  name: "Core Components",
4236
4738
  description: "Default components for Thorium Web Epub StatefulReader",
4237
- version: "1.3.0",
4739
+ version: "1.4.0",
4238
4740
  components: {
4239
4741
  actions: {
4240
4742
  ["fullscreen" /* fullscreen */]: {
@@ -4310,6 +4812,14 @@ var createDefaultPlugin = () => {
4310
4812
  Comp: StatefulTextNormalize,
4311
4813
  type: "text"
4312
4814
  },
4815
+ ["ligatures" /* ligatures */]: {
4816
+ Comp: StatefulLigatures,
4817
+ type: "text"
4818
+ },
4819
+ ["noRuby" /* noRuby */]: {
4820
+ Comp: StatefulNoRuby,
4821
+ type: "text"
4822
+ },
4313
4823
  ["theme" /* theme */]: {
4314
4824
  Comp: StatefulTheme
4315
4825
  },
@@ -4338,7 +4848,8 @@ var StatefulSliderWithPresets = ({
4338
4848
  ...props
4339
4849
  }) => {
4340
4850
  const { t } = useI18n();
4341
- const { theming, direction } = useSharedPreferences();
4851
+ const { theming } = useSharedPreferences();
4852
+ const isRTL = useAppSelector((state) => state.publication.isRTL);
4342
4853
  const numberFormatter = useNumberFormatter(props.formatOptions);
4343
4854
  const resolvedFormatValue = formatValue ?? (props.formatOptions ? (v) => numberFormatter.format(v) : void 0);
4344
4855
  const tooltipDelay = theming.icon.tooltipDelay;
@@ -4351,7 +4862,7 @@ var StatefulSliderWithPresets = ({
4351
4862
  items: presetsRef,
4352
4863
  currentValue: currentScalarValue,
4353
4864
  onChange: (v) => props.onChange?.([v]),
4354
- isRTL: direction === "rtl" /* rtl */,
4865
+ isRTL,
4355
4866
  onEscape,
4356
4867
  onFocus: (v) => {
4357
4868
  const el = presetsListRef.current?.querySelector(`input[value="${v}"]`);
@@ -4474,6 +4985,7 @@ var StatefulAudioSkipBackwardInterval = ({
4474
4985
  }) => {
4475
4986
  const { t } = useI18n();
4476
4987
  const { preferences } = useAudioPreferences();
4988
+ const profile = useAppSelector((state) => state.reader.profile);
4477
4989
  const skipBackwardInterval = useAppSelector((state) => state.audioSettings.skipBackwardInterval);
4478
4990
  const dispatch = useAppDispatch();
4479
4991
  const { submitPreferences, getSetting, preferencesEditor } = useNavigator().media;
@@ -4523,7 +5035,11 @@ var StatefulAudioSkipBackwardInterval = ({
4523
5035
  label: t("reader.playback.preferences.audio.skipBackwardInterval"),
4524
5036
  presets: presets || [],
4525
5037
  formatOptions: { style: "unit", unit: "second" },
4526
- onEscape: () => dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false })),
5038
+ onEscape: () => {
5039
+ if (profile) {
5040
+ dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false, profile }));
5041
+ }
5042
+ },
4527
5043
  value: skipBackwardInterval ?? void 0,
4528
5044
  onChange: (v) => updatePreference(v)
4529
5045
  }
@@ -4538,7 +5054,11 @@ var StatefulAudioSkipBackwardInterval = ({
4538
5054
  placeholder: placeholderText,
4539
5055
  presets: presets || [],
4540
5056
  formatOptions: { style: "unit", unit: "second" },
4541
- onEscape: () => dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false })),
5057
+ onEscape: () => {
5058
+ if (profile) {
5059
+ dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false, profile }));
5060
+ }
5061
+ },
4542
5062
  value: skipBackwardInterval ?? void 0,
4543
5063
  onChange: updatePreference,
4544
5064
  range: skipBackwardIntervalRangeConfig.range,
@@ -4567,6 +5087,7 @@ var StatefulAudioSkipForwardInterval = ({
4567
5087
  }) => {
4568
5088
  const { t } = useI18n();
4569
5089
  const { preferences } = useAudioPreferences();
5090
+ const profile = useAppSelector((state) => state.reader.profile);
4570
5091
  const skipForwardInterval = useAppSelector((state) => state.audioSettings.skipForwardInterval);
4571
5092
  const dispatch = useAppDispatch();
4572
5093
  const { submitPreferences, getSetting, preferencesEditor } = useNavigator().media;
@@ -4616,7 +5137,11 @@ var StatefulAudioSkipForwardInterval = ({
4616
5137
  label: t("reader.playback.preferences.audio.skipForwardInterval"),
4617
5138
  presets: presets || [],
4618
5139
  formatOptions: { style: "unit", unit: "second" },
4619
- onEscape: () => dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false })),
5140
+ onEscape: () => {
5141
+ if (profile) {
5142
+ dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false, profile }));
5143
+ }
5144
+ },
4620
5145
  value: skipForwardInterval,
4621
5146
  onChange: (v) => updatePreference(v)
4622
5147
  }
@@ -4631,7 +5156,11 @@ var StatefulAudioSkipForwardInterval = ({
4631
5156
  placeholder: placeholderText,
4632
5157
  presets: presets || [],
4633
5158
  formatOptions: { style: "unit", unit: "second" },
4634
- onEscape: () => dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false })),
5159
+ onEscape: () => {
5160
+ if (profile) {
5161
+ dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false, profile }));
5162
+ }
5163
+ },
4635
5164
  value: skipForwardInterval,
4636
5165
  onChange: updatePreference,
4637
5166
  range: skipForwardIntervalRangeConfig.range,
@@ -4659,6 +5188,7 @@ var StatefulAudioSkipInterval = ({
4659
5188
  }) => {
4660
5189
  const { t } = useI18n();
4661
5190
  const { preferences } = useAudioPreferences();
5191
+ const profile = useAppSelector((state) => state.reader.profile);
4662
5192
  const skipInterval = useAppSelector((state) => state.audioSettings.skipInterval);
4663
5193
  const dispatch = useAppDispatch();
4664
5194
  const { submitPreferences, getSetting, preferencesEditor } = useNavigator().media;
@@ -4710,7 +5240,11 @@ var StatefulAudioSkipInterval = ({
4710
5240
  label: t("reader.playback.preferences.audio.skipInterval"),
4711
5241
  presets: presets || [],
4712
5242
  formatOptions: { style: "unit", unit: "second" },
4713
- onEscape: () => dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false })),
5243
+ onEscape: () => {
5244
+ if (profile) {
5245
+ dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false, profile }));
5246
+ }
5247
+ },
4714
5248
  value: skipInterval,
4715
5249
  onChange: (v) => updatePreference(v)
4716
5250
  }
@@ -4725,7 +5259,11 @@ var StatefulAudioSkipInterval = ({
4725
5259
  placeholder: placeholderText,
4726
5260
  presets: presets || [],
4727
5261
  formatOptions: { style: "unit", unit: "second" },
4728
- onEscape: () => dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false })),
5262
+ onEscape: () => {
5263
+ if (profile) {
5264
+ dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false, profile }));
5265
+ }
5266
+ },
4729
5267
  value: skipInterval,
4730
5268
  onChange: updatePreference,
4731
5269
  range: skipIntervalRangeConfig.range,
@@ -4790,6 +5328,7 @@ var thorium_web_volume_default = {
4790
5328
  };
4791
5329
  var StatefulAudioVolumeTrigger = ({ ref }) => {
4792
5330
  const { t } = useI18n();
5331
+ const profile = useAppSelector((state) => state.reader.profile);
4793
5332
  const { preferences } = useAudioPreferences();
4794
5333
  const { preferencesEditor } = useNavigator().media;
4795
5334
  const volume = useAppSelector((state) => state.audioSettings.volume);
@@ -4812,7 +5351,11 @@ var StatefulAudioVolumeTrigger = ({ ref }) => {
4812
5351
  ref,
4813
5352
  tooltipLabel: t("reader.playback.preferences.audio.volume"),
4814
5353
  placement: "top",
4815
- onPress: () => dispatch(toggleActionOpen({ key: "audio.volume" /* volume */ })),
5354
+ onPress: () => {
5355
+ if (profile) {
5356
+ dispatch(toggleActionOpen({ key: "audio.volume" /* volume */, profile }));
5357
+ }
5358
+ },
4816
5359
  isDisabled,
4817
5360
  className: thorium_web_volume_default.button,
4818
5361
  children: /* @__PURE__ */ jsx(VolumeIcon, { "aria-hidden": "true", focusable: "false" })
@@ -4821,7 +5364,8 @@ var StatefulAudioVolumeTrigger = ({ ref }) => {
4821
5364
  };
4822
5365
  var StatefulAudioVolumeContainer = ({ triggerRef, placement = "top" }) => {
4823
5366
  const volume = useAppSelector((state) => state.audioSettings.volume);
4824
- const isOpen = useAppSelector((state) => state.actions.keys["audio.volume" /* volume */]?.isOpen ?? false);
5367
+ const profile = useAppSelector((state) => state.reader.profile);
5368
+ const isOpen = useAppSelector((state) => profile ? state.actions.keys[profile]["audio.volume" /* volume */]?.isOpen ?? false : false);
4825
5369
  const { t } = useI18n();
4826
5370
  const { preferences } = useAudioPreferences();
4827
5371
  const dispatch = useAppDispatch();
@@ -4837,8 +5381,10 @@ var StatefulAudioVolumeContainer = ({ triggerRef, placement = "top" }) => {
4837
5381
  const docking = useDocking("audio.volume" /* volume */);
4838
5382
  const sliderOrientation = docking.sheetType === "popover" /* popover */ || docking.sheetType === "compactPopover" /* compactPopover */ ? "vertical" : "horizontal";
4839
5383
  const setOpen = useCallback((open) => {
4840
- dispatch(setActionOpen({ key: "audio.volume" /* volume */, isOpen: open }));
4841
- }, [dispatch]);
5384
+ if (profile) {
5385
+ dispatch(setActionOpen({ key: "audio.volume" /* volume */, isOpen: open, profile }));
5386
+ }
5387
+ }, [dispatch, profile]);
4842
5388
  return /* @__PURE__ */ jsx(
4843
5389
  StatefulSheetWrapper,
4844
5390
  {
@@ -4885,6 +5431,7 @@ var thorium_web_playbackRate_default = {
4885
5431
  slider: "thorium_web_playbackRate_slider"};
4886
5432
  var StatefulAudioPlaybackRateTrigger = ({ ref }) => {
4887
5433
  const { t } = useI18n();
5434
+ const profile = useAppSelector((state) => state.reader.profile);
4888
5435
  const playbackRate = useAppSelector((state) => state.audioSettings.playbackRate);
4889
5436
  const isTrackReady = useAppSelector((state) => state.player.isTrackReady);
4890
5437
  const isStalled = useAppSelector((state) => state.player.isStalled);
@@ -4896,7 +5443,11 @@ var StatefulAudioPlaybackRateTrigger = ({ ref }) => {
4896
5443
  ref,
4897
5444
  tooltipLabel: t("reader.playback.preferences.playbackRate.descriptive"),
4898
5445
  placement: "top",
4899
- onPress: () => dispatch(toggleActionOpen({ key: "audio.playbackRate" /* playbackRate */ })),
5446
+ onPress: () => {
5447
+ if (profile) {
5448
+ dispatch(toggleActionOpen({ key: "audio.playbackRate" /* playbackRate */, profile }));
5449
+ }
5450
+ },
4900
5451
  isDisabled,
4901
5452
  className: thorium_web_playbackRate_default.button,
4902
5453
  children: [
@@ -4910,7 +5461,8 @@ var StatefulAudioPlaybackRateTrigger = ({ ref }) => {
4910
5461
  );
4911
5462
  };
4912
5463
  var StatefulAudioPlaybackRateContainer = ({ triggerRef, placement = "top" }) => {
4913
- const isOpen = useAppSelector((state) => state.actions.keys["audio.playbackRate" /* playbackRate */]?.isOpen ?? false);
5464
+ const profile = useAppSelector((state) => state.reader.profile);
5465
+ const isOpen = useAppSelector((state) => profile ? state.actions.keys[profile]["audio.playbackRate" /* playbackRate */]?.isOpen ?? false : false);
4914
5466
  const { t } = useI18n();
4915
5467
  const { preferences } = useAudioPreferences();
4916
5468
  const playbackRate = useAppSelector((state) => state.audioSettings.playbackRate);
@@ -4924,8 +5476,10 @@ var StatefulAudioPlaybackRateContainer = ({ triggerRef, placement = "top" }) =>
4924
5476
  }, [submitPreferences, getSetting, dispatch]);
4925
5477
  const docking = useDocking("audio.playbackRate" /* playbackRate */);
4926
5478
  const setOpen = useCallback((open) => {
4927
- dispatch(setActionOpen({ key: "audio.playbackRate" /* playbackRate */, isOpen: open }));
4928
- }, [dispatch]);
5479
+ if (profile) {
5480
+ dispatch(setActionOpen({ key: "audio.playbackRate" /* playbackRate */, isOpen: open, profile }));
5481
+ }
5482
+ }, [dispatch, profile]);
4929
5483
  const renderContent = () => {
4930
5484
  if (config.variant === "slider" /* slider */) {
4931
5485
  return /* @__PURE__ */ jsx("div", { className: thorium_web_playbackRate_default.slider, children: /* @__PURE__ */ jsx(
@@ -4992,6 +5546,7 @@ var thorium_web_audioToc_default = {
4992
5546
  };
4993
5547
  var StatefulAudioTocTrigger = ({ ref }) => {
4994
5548
  const { t } = useI18n();
5549
+ const profile = useAppSelector((state) => state.reader.profile);
4995
5550
  const isTrackReady = useAppSelector((state) => state.player.isTrackReady);
4996
5551
  const isStalled = useAppSelector((state) => state.player.isStalled);
4997
5552
  const isDisabled = !isTrackReady || isStalled;
@@ -5002,7 +5557,11 @@ var StatefulAudioTocTrigger = ({ ref }) => {
5002
5557
  ref,
5003
5558
  tooltipLabel: t("reader.tableOfContents.title"),
5004
5559
  placement: "top",
5005
- onPress: () => dispatch(toggleActionOpen({ key: "audio.toc" /* toc */ })),
5560
+ onPress: () => {
5561
+ if (profile) {
5562
+ dispatch(toggleActionOpen({ key: "audio.toc" /* toc */, profile }));
5563
+ }
5564
+ },
5006
5565
  isDisabled,
5007
5566
  className: thorium_web_audioToc_default.button,
5008
5567
  children: /* @__PURE__ */ jsx(toc_default, { "aria-hidden": "true", focusable: "false" })
@@ -5011,20 +5570,22 @@ var StatefulAudioTocTrigger = ({ ref }) => {
5011
5570
  };
5012
5571
  var StatefulAudioTocContainer = ({ triggerRef }) => {
5013
5572
  const { t } = useI18n();
5573
+ const profile = useAppSelector((state) => state.reader.profile);
5014
5574
  const { goLink } = useNavigator().unified;
5015
5575
  const dispatch = useAppDispatch();
5016
- const isOpen = useAppSelector((state) => state.actions.keys["audio.toc" /* toc */]?.isOpen ?? false);
5576
+ const isOpen = useAppSelector((state) => profile ? state.actions.keys[profile]["audio.toc" /* toc */]?.isOpen ?? false : false);
5017
5577
  const unstableTimeline = useAppSelector((state) => state.publication.unstableTimeline);
5018
5578
  const tocEntry = unstableTimeline?.toc?.currentEntry ?? void 0;
5019
5579
  const tocEntryId = tocEntry?.id;
5020
5580
  const tocTree = unstableTimeline?.toc?.tree;
5021
- const direction = useAppSelector((state) => state.reader.direction);
5022
- const isRTL = direction === "rtl" /* rtl */;
5581
+ const isRTL = useAppSelector((state) => state.publication.isRTL);
5023
5582
  const docking = useDocking("audio.toc" /* toc */);
5024
5583
  const sheetType = docking.sheetType;
5025
5584
  const setOpen = useCallback((value) => {
5026
- dispatch(setActionOpen({ key: "audio.toc" /* toc */, isOpen: value }));
5027
- }, [dispatch]);
5585
+ if (profile) {
5586
+ dispatch(setActionOpen({ key: "audio.toc" /* toc */, isOpen: value, profile }));
5587
+ }
5588
+ }, [dispatch, profile]);
5028
5589
  const { expandedKeys, setExpandedKeys, filterValue, setFilterValue, displayedTocTree, treeRef, searchInputRef } = useTocContent({ isOpen, tocTree, tocEntry: tocEntryId });
5029
5590
  useEffect(() => {
5030
5591
  if (isOpen) {
@@ -5113,6 +5674,7 @@ var thorium_web_sleepTimer_default = {
5113
5674
  };
5114
5675
  var StatefulAudioSleepTimerTrigger = ({ ref }) => {
5115
5676
  const { t } = useI18n();
5677
+ const profile = useAppSelector((state) => state.reader.profile);
5116
5678
  const remainingSeconds = useAppSelector((state) => state.player.sleepTimer.remainingSeconds);
5117
5679
  const onTrackEnd = useAppSelector((state) => state.player.sleepTimer.onTrackEnd);
5118
5680
  const onFragmentEnd = useAppSelector((state) => state.player.sleepTimer.onFragmentEnd);
@@ -5136,7 +5698,11 @@ var StatefulAudioSleepTimerTrigger = ({ ref }) => {
5136
5698
  ref,
5137
5699
  tooltipLabel: t("reader.playback.preferences.sleepTimer.descriptive"),
5138
5700
  placement: "top",
5139
- onPress: () => dispatch(toggleActionOpen({ key: "audio.sleepTimer" /* sleepTimer */ })),
5701
+ onPress: () => {
5702
+ if (profile) {
5703
+ dispatch(toggleActionOpen({ key: "audio.sleepTimer" /* sleepTimer */, profile }));
5704
+ }
5705
+ },
5140
5706
  isDisabled,
5141
5707
  className: thorium_web_sleepTimer_default.button,
5142
5708
  children: [
@@ -5149,7 +5715,11 @@ var StatefulAudioSleepTimerTrigger = ({ ref }) => {
5149
5715
  var StatefulAudioSleepTimerContainer = ({ triggerRef, placement = "top" }) => {
5150
5716
  const [hours, setHours] = useState(0);
5151
5717
  const [minutes, setMinutes] = useState(0);
5152
- const isOpen = useAppSelector((state) => state.actions.keys["audio.sleepTimer" /* sleepTimer */]?.isOpen ?? false);
5718
+ const profile = useAppSelector((state) => state.reader.profile);
5719
+ const isOpen = useAppSelector((state) => {
5720
+ if (!profile || !state.actions.keys[profile]) return false;
5721
+ return state.actions.keys[profile]["audio.sleepTimer" /* sleepTimer */]?.isOpen ?? false;
5722
+ });
5153
5723
  const remainingSeconds = useAppSelector((state) => state.player.sleepTimer.remainingSeconds);
5154
5724
  const onTrackEnd = useAppSelector((state) => state.player.sleepTimer.onTrackEnd);
5155
5725
  const onFragmentEnd = useAppSelector((state) => state.player.sleepTimer.onFragmentEnd);
@@ -5188,14 +5758,18 @@ var StatefulAudioSleepTimerContainer = ({ triggerRef, placement = "top" }) => {
5188
5758
  dispatch(setSleepTimerRemainingSeconds(null));
5189
5759
  dispatch(setSleepTimerOnTrackEnd(false));
5190
5760
  dispatch(setSleepTimerOnFragmentEnd(false));
5191
- dispatch(setActionOpen({ key: "audio.sleepTimer" /* sleepTimer */, isOpen: false }));
5192
- }, [dispatch]);
5761
+ if (profile) {
5762
+ dispatch(setActionOpen({ key: "audio.sleepTimer" /* sleepTimer */, isOpen: false, profile }));
5763
+ }
5764
+ }, [dispatch, profile]);
5193
5765
  const handleStart = useCallback(() => {
5194
5766
  const totalSeconds = hours * 3600 + minutes * 60;
5195
5767
  if (totalSeconds <= 0) return;
5196
5768
  dispatch(setSleepTimerRemainingSeconds(totalSeconds));
5197
- dispatch(setActionOpen({ key: "audio.sleepTimer" /* sleepTimer */, isOpen: false }));
5198
- }, [hours, minutes, dispatch]);
5769
+ if (profile) {
5770
+ dispatch(setActionOpen({ key: "audio.sleepTimer" /* sleepTimer */, isOpen: false, profile }));
5771
+ }
5772
+ }, [hours, minutes, dispatch, profile]);
5199
5773
  const handlePresetSelect = useCallback((value) => {
5200
5774
  if (value === "endOfResource") {
5201
5775
  dispatch(setSleepTimerOnTrackEnd(true));
@@ -5210,12 +5784,16 @@ var StatefulAudioSleepTimerContainer = ({ triggerRef, placement = "top" }) => {
5210
5784
  dispatch(setSleepTimerOnFragmentEnd(false));
5211
5785
  dispatch(setSleepTimerRemainingSeconds(Number(value) * 60));
5212
5786
  }
5213
- dispatch(setActionOpen({ key: "audio.sleepTimer" /* sleepTimer */, isOpen: false }));
5214
- }, [dispatch]);
5787
+ if (profile) {
5788
+ dispatch(setActionOpen({ key: "audio.sleepTimer" /* sleepTimer */, isOpen: false, profile }));
5789
+ }
5790
+ }, [dispatch, profile]);
5215
5791
  const docking = useDocking("audio.sleepTimer" /* sleepTimer */);
5216
5792
  const setOpen = useCallback((open) => {
5217
- dispatch(setActionOpen({ key: "audio.sleepTimer" /* sleepTimer */, isOpen: open }));
5218
- }, [dispatch]);
5793
+ if (profile) {
5794
+ dispatch(setActionOpen({ key: "audio.sleepTimer" /* sleepTimer */, isOpen: open, profile }));
5795
+ }
5796
+ }, [dispatch, profile]);
5219
5797
  const isActive = remainingSeconds !== null || onTrackEnd || onFragmentEnd;
5220
5798
  const maxHours = (config.variant === "durationField" /* durationField */ ? config.maxHours : void 0) ?? 23;
5221
5799
  const renderContent = () => {
@@ -5420,7 +5998,7 @@ var createAudioDefaultPlugin = () => {
5420
5998
  id: "audio-core",
5421
5999
  name: "Audio Core Components",
5422
6000
  description: "Default components for Thorium Web Audio StatefulReader",
5423
- version: "1.3.0",
6001
+ version: "1.4.0",
5424
6002
  components: {
5425
6003
  actions: {
5426
6004
  ["settings" /* settings */]: {
@@ -5474,233 +6052,16 @@ var StatefulPreferencesProvider = ({
5474
6052
  }, [store, initialPreferences]);
5475
6053
  return /* @__PURE__ */ jsx(ThPreferencesProvider, { adapter, children });
5476
6054
  };
5477
-
5478
- // src/hooks/useReaderTransitions.ts
5479
- var useReaderTransitions = () => {
5480
- const isImmersive = useAppSelector((state) => state.reader.isImmersive);
5481
- const isFullscreen = useAppSelector((state) => state.reader.isFullscreen);
5482
- const hasUserNavigated = useAppSelector((state) => state.reader.hasUserNavigated);
5483
- const scroll = useAppSelector((state) => state.settings.scroll);
5484
- const isFXL = useAppSelector((state) => state.publication.isFXL);
5485
- const isScroll = scroll && !isFXL;
5486
- const wasImmersive = usePrevious(isImmersive) ?? false;
5487
- const wasFullscreen = usePrevious(isFullscreen) ?? false;
5488
- const wasScroll = usePrevious(isScroll) ?? false;
5489
- const wasUserNavigated = usePrevious(hasUserNavigated) ?? false;
5490
- const fromImmersive = wasImmersive && !isImmersive;
5491
- const toImmersive = !wasImmersive && isImmersive;
5492
- const fromFullscreen = wasFullscreen && !isFullscreen;
5493
- const toFullscreen = !wasFullscreen && isFullscreen;
5494
- const fromScroll = wasScroll && !isScroll;
5495
- const toScroll = !wasScroll && isScroll;
5496
- const fromUserNavigation = wasUserNavigated && !hasUserNavigated;
5497
- const toUserNavigation = !wasUserNavigated && hasUserNavigated;
5498
- return {
5499
- // Current states
5500
- isImmersive,
5501
- isFullscreen,
5502
- isScroll,
5503
- hasUserNavigated,
5504
- // Previous states
5505
- wasImmersive,
5506
- wasFullscreen,
5507
- wasScroll,
5508
- wasUserNavigated,
5509
- // State transitions
5510
- fromImmersive,
5511
- toImmersive,
5512
- fromFullscreen,
5513
- toFullscreen,
5514
- fromScroll,
5515
- toScroll,
5516
- fromUserNavigation,
5517
- toUserNavigation
5518
- };
5519
- };
5520
-
5521
- // src/helpers/deserializePositions.ts
5522
- var deserializePositions = (positionsList) => {
5523
- return positionsList?.map((locator) => ({
5524
- href: locator.href,
5525
- type: locator.type,
5526
- locations: {
5527
- position: locator.locations.position,
5528
- progression: locator.locations.progression,
5529
- totalProgression: locator.locations.totalProgression
5530
- }
5531
- }));
5532
- };
5533
-
5534
- // src/hooks/usePublication.ts
5535
- var detectProfile = (manifest) => {
5536
- const metadata = manifest.metadata;
5537
- if (!metadata) return "webPub";
5538
- const conformsTo = metadata.conformsTo;
5539
- if (!conformsTo) return "webPub";
5540
- const profiles = Array.isArray(conformsTo) ? conformsTo : [conformsTo];
5541
- if (profiles.some(
5542
- (profile) => profile === Profile.AUDIOBOOK
5543
- )) {
5544
- return "audio";
5545
- }
5546
- if (profiles.some(
5547
- (profile) => profile === Profile.EPUB
5548
- )) {
5549
- return "epub";
5550
- }
5551
- return "webPub";
5552
- };
5553
- var usePublication = ({
5554
- url,
5555
- onError = () => {
5556
- },
5557
- fetcher: customFetcher
6055
+ var StatefulGlobalPreferencesProvider = ({
6056
+ children,
6057
+ initialPreferences = {}
5558
6058
  }) => {
5559
- const dispatch = useAppDispatch();
5560
- const [isLoading, setIsLoading] = useState(true);
5561
- const [error, setError] = useState(null);
5562
- const [manifest, setManifest] = useState(null);
5563
- const [selfLink, setSelfLink] = useState(null);
5564
- const [localDataKey, setLocalDataKey] = useState(null);
5565
- const [publication, setPublication] = useState(null);
5566
- const [profile, setProfile] = useState(null);
5567
- const [isRTL, setIsRTL] = useState(false);
5568
- const [isFXL, setIsFXL] = useState(false);
5569
- const [hasDisplayTransformability, setHasDisplayTransformabilityState] = useState(false);
5570
- const handleManifestError = (error2, context) => {
5571
- console.error(`${context}:`, error2);
5572
- const processedError = ErrorHandler.process(error2, context);
5573
- setError(processedError);
5574
- setIsLoading(false);
5575
- };
5576
- useEffect(() => {
5577
- if (!url) {
5578
- const validationError = ErrorHandler.process(new Error("Manifest URL is required"), "Validation");
5579
- setError(validationError);
5580
- setIsLoading(false);
5581
- return;
5582
- }
5583
- setIsLoading(true);
5584
- setError(null);
5585
- const decodedUrl = decodeURIComponent(url);
5586
- const manifestLink = new Link({ href: decodedUrl });
5587
- const fetcher = customFetcher || new HttpFetcher(void 0);
5588
- try {
5589
- const fetched = fetcher.get(manifestLink);
5590
- fetched.link().then(async (link) => {
5591
- try {
5592
- const selfHref = link.toURL(decodedUrl);
5593
- setSelfLink(selfHref || null);
5594
- if (selfHref) {
5595
- setLocalDataKey(`${selfHref}-current-location`);
5596
- const manifestFetcher = customFetcher || new HttpFetcher(void 0, selfHref);
5597
- const manifestFetched = manifestFetcher.get(manifestLink);
5598
- const manifestData = await manifestFetched.readAsJSON();
5599
- setManifest(manifestData);
5600
- const manifestObj = Manifest.deserialize(manifestData);
5601
- manifestObj.setSelfLink(selfHref);
5602
- const detectedProfile = detectProfile(manifestObj);
5603
- setProfile(detectedProfile);
5604
- dispatch(setReaderProfile(detectedProfile));
5605
- const pub = new Publication({
5606
- manifest: manifestObj,
5607
- fetcher: manifestFetcher
5608
- });
5609
- if (detectedProfile === "epub") {
5610
- try {
5611
- const rawPositions = await pub.positionsFromManifest();
5612
- const positionsList = deserializePositions(rawPositions);
5613
- dispatch(setPositionsList(positionsList));
5614
- } catch (error2) {
5615
- console.error("Failed to fetch positions:", error2);
5616
- dispatch(setPositionsList([]));
5617
- }
5618
- }
5619
- if (detectedProfile === "audio") {
5620
- const tocLinks = manifestObj.toc?.items && manifestObj.toc.items.length > 0 ? manifestObj.toc.items : manifestObj.readingOrder?.items || [];
5621
- const publicationTitle = manifestObj.metadata.title.getTranslation("en");
5622
- let idCounter = 0;
5623
- const idGenerator = () => `toc-${++idCounter}`;
5624
- dispatch(setTocTree(buildTocTree(tocLinks, idGenerator, void 0, publicationTitle)));
5625
- }
5626
- setPublication(pub);
5627
- setIsLoading(false);
5628
- }
5629
- } catch (error2) {
5630
- handleManifestError(error2, "Error loading manifest");
5631
- }
5632
- });
5633
- } catch (error2) {
5634
- handleManifestError(error2, "Error loading manifest");
5635
- }
5636
- }, [url, customFetcher, dispatch]);
5637
- useEffect(() => {
5638
- if (!publication) return;
5639
- const rtl = publication.metadata.effectiveReadingProgression === ReadingProgression.rtl;
5640
- setIsRTL(rtl);
5641
- dispatch(setRTL(rtl));
5642
- if (profile === "epub") {
5643
- const fxl = publication.metadata.effectiveLayout === Layout.fixed;
5644
- setIsFXL(fxl);
5645
- dispatch(setFXL(fxl));
5646
- }
5647
- const displayTransformability = publication.metadata.accessibility?.feature?.some(
5648
- (feature) => feature && feature.value === Feature.DISPLAY_TRANSFORMABILITY.value
5649
- ) || false;
5650
- setHasDisplayTransformabilityState(displayTransformability);
5651
- dispatch(setHasDisplayTransformability(displayTransformability));
5652
- if (profile === "epub" && publication) {
5653
- const fetchPositions = async () => {
5654
- try {
5655
- const positionsList = await publication.positionsFromManifest();
5656
- const deserializedPositionsList = deserializePositions(positionsList);
5657
- dispatch(setPositionsList(deserializedPositionsList));
5658
- } catch (error2) {
5659
- console.error("Failed to fetch positions:", error2);
5660
- dispatch(setPositionsList([]));
5661
- }
5662
- };
5663
- fetchPositions();
5664
- }
5665
- }, [publication, profile, dispatch]);
5666
- useEffect(() => {
5667
- if (error) {
5668
- onError(error);
5669
- }
5670
- }, [error, onError]);
5671
- return {
5672
- isLoading,
5673
- error,
5674
- publication,
5675
- manifest,
5676
- selfLink,
5677
- localDataKey,
5678
- profile,
5679
- isRTL,
5680
- isFXL,
5681
- hasDisplayTransformability
5682
- };
5683
- };
5684
- var usePositionStorage = (key, customStorage) => {
5685
- const localStorageData = useLocalStorage(key);
5686
- const [customData, setCustomData] = useState(
5687
- () => customStorage ? customStorage.get() || null : null
6059
+ const store = useStore();
6060
+ const adapter = useMemo(
6061
+ () => new ThReduxGlobalPreferencesAdapter(store, initialPreferences),
6062
+ [store, initialPreferences]
5688
6063
  );
5689
- if (customStorage) {
5690
- const set = (newValue) => {
5691
- if (newValue) {
5692
- customStorage.set(newValue);
5693
- }
5694
- setCustomData(newValue);
5695
- };
5696
- const get = () => customData;
5697
- return {
5698
- setLocalData: set,
5699
- getLocalData: get,
5700
- localData: customData
5701
- };
5702
- }
5703
- return localStorageData;
6064
+ return /* @__PURE__ */ jsx(ThGlobalPreferencesProvider, { adapter, initialPreferences, children });
5704
6065
  };
5705
6066
 
5706
6067
  // src/components/assets/styles/thorium-web.reader.app.module.css
@@ -5720,36 +6081,31 @@ var thorium_web_reader_app_default = {
5720
6081
  srOnly: "thorium_web_reader_app_srOnly"
5721
6082
  };
5722
6083
  var useResizablePanel = (panel) => {
5723
- const defaultPanel = {
5724
- actionKey: null,
5725
- active: false,
5726
- collapsed: false
5727
- };
5728
- const safePanel = panel || defaultPanel;
5729
6084
  const preferences = useActionsPreferences();
5730
6085
  const { theming } = useSharedPreferences();
5731
6086
  const defaultWidth = theming.layout.defaults.dockingWidth;
5732
6087
  const [pref, setPref] = useState(
5733
- safePanel.actionKey ? preferences.actionsKeys[safePanel.actionKey]?.docked || null : null
6088
+ panel?.actionKey ? preferences.actionsKeys[panel.actionKey]?.docked || null : null
5734
6089
  );
5735
- const actionsMap = useAppSelector((state) => state.actions.keys);
5736
- const actions = useActions(actionsMap);
5737
- const previouslyCollapsed = usePrevious(safePanel.collapsed);
5738
- const previousWidth = actions.getDockedWidth(safePanel.actionKey) || null;
6090
+ const profile = useAppSelector((state) => state.reader.profile);
6091
+ const actionsMap = useAppSelector((state) => profile ? state.actions.keys[profile] : void 0);
6092
+ const actions = useActions(actionsMap || {});
6093
+ const previouslyCollapsed = usePrevious(panel?.collapsed);
6094
+ const previousWidth = actions.getDockedWidth(panel?.actionKey) || null;
5739
6095
  const width = pref?.width || defaultWidth;
5740
6096
  const minWidth = pref?.minWidth && pref.minWidth < width ? pref.minWidth : defaultWidth < width ? defaultWidth : width;
5741
6097
  const maxWidth = pref?.maxWidth && pref.maxWidth > width ? pref.maxWidth : defaultWidth > width ? defaultWidth : width;
5742
6098
  const isPopulated = () => {
5743
- return safePanel.active && actions.isOpen(safePanel.actionKey);
6099
+ return !!(panel?.active && actions.isOpen(panel?.actionKey));
5744
6100
  };
5745
6101
  const isCollapsed = () => {
5746
- return safePanel.collapsed;
6102
+ return !!panel?.collapsed;
5747
6103
  };
5748
6104
  const forceExpand = () => {
5749
- return !!(isPopulated() && previouslyCollapsed && !safePanel.collapsed);
6105
+ return !!(isPopulated() && previouslyCollapsed && !panel?.collapsed);
5750
6106
  };
5751
6107
  const currentKey = () => {
5752
- return safePanel.actionKey;
6108
+ return panel?.actionKey ?? null;
5753
6109
  };
5754
6110
  const isResizable = () => {
5755
6111
  return isPopulated() ? Math.round(width) > Math.round(minWidth) && Math.round(width) < Math.round(maxWidth) : false;
@@ -5777,8 +6133,8 @@ var useResizablePanel = (panel) => {
5777
6133
  return current;
5778
6134
  }, [minWidth, maxWidth]);
5779
6135
  useEffect(() => {
5780
- setPref(safePanel.actionKey ? preferences.actionsKeys[safePanel.actionKey]?.docked || null : null);
5781
- }, [safePanel.actionKey, preferences]);
6136
+ setPref(panel?.actionKey ? preferences.actionsKeys[panel.actionKey]?.docked || null : null);
6137
+ }, [panel?.actionKey, preferences]);
5782
6138
  return {
5783
6139
  currentKey,
5784
6140
  isPopulated,
@@ -6028,10 +6384,11 @@ var StatefulBackLink = ({
6028
6384
  className
6029
6385
  }) => {
6030
6386
  const { t } = useI18n();
6031
- const { direction, theming } = useSharedPreferences();
6387
+ const { theming } = useSharedPreferences();
6388
+ const { direction } = useLocale();
6032
6389
  const backLinkPref = theming.header?.backLink;
6033
6390
  const tooltipDelay = theming.icon.tooltipDelay;
6034
- const isRTL = direction === "rtl" /* rtl */;
6391
+ const isRTL = direction === "rtl";
6035
6392
  const variant = backLinkPref?.variant || "arrow" /* arrow */;
6036
6393
  const href = backLinkPref?.href;
6037
6394
  const content = backLinkPref?.content;
@@ -6095,7 +6452,7 @@ var StatefulBackLink = ({
6095
6452
  for (const { name, value } of Array.from(svgElement.attributes)) {
6096
6453
  attributes[name] = value;
6097
6454
  }
6098
- contentNode = React24.createElement("svg", {
6455
+ contentNode = React22.createElement("svg", {
6099
6456
  ...attributes,
6100
6457
  "aria-hidden": "true",
6101
6458
  focusable: "false",
@@ -6122,14 +6479,16 @@ var useReaderHeaderBase = (actionKeys) => {
6122
6479
  const headerRef = useRef(null);
6123
6480
  const { t } = useI18n();
6124
6481
  const { actionsComponentsMap } = usePlugins();
6125
- const actionsMap = useAppSelector((state) => state.actions.keys);
6126
6482
  const overflowMap = useAppSelector((state) => state.actions.overflow);
6127
- const isScroll = useAppSelector((state) => state.settings.scroll);
6483
+ const isScroll = useIsScroll();
6128
6484
  const isImmersive = useAppSelector((state) => state.reader.isImmersive);
6129
6485
  const isHovering = useAppSelector((state) => state.reader.isHovering);
6130
6486
  const hasScrollAffordance = useAppSelector((state) => state.reader.hasScrollAffordance);
6131
6487
  const positionsList = useAppSelector((state) => state.publication.positionsList);
6132
- const actions = useActions({ ...actionsMap, ...overflowMap });
6488
+ const profile = useAppSelector((state) => state.reader.profile);
6489
+ const profileActionsMap = useAppSelector((state) => profile ? state.actions.keys[profile] : void 0);
6490
+ const mergedActionsMap = useMemo(() => ({ ...profileActionsMap, ...overflowMap }), [profileActionsMap, overflowMap]);
6491
+ const actions = useActions(mergedActionsMap);
6133
6492
  const dispatch = useAppDispatch();
6134
6493
  const { focusWithinProps } = useFocusWithin({
6135
6494
  onFocusWithin() {
@@ -6197,6 +6556,6 @@ var useReaderHeaderBase = (actionKeys) => {
6197
6556
  };
6198
6557
  };
6199
6558
 
6200
- 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 };
6201
- //# sourceMappingURL=chunk-D7MFLHXV.mjs.map
6202
- //# sourceMappingURL=chunk-D7MFLHXV.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