@edrlab/thorium-web 1.2.1 → 1.3.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 (97) hide show
  1. package/dist/{ThPreferencesAdapter-DrZ5_6Dv.d.mts → ThPreferencesAdapter-D0rzsGRl.d.mts} +50 -13
  2. package/dist/{ThSettingsWrapper-8Kx0SnH4.d.mts → ThSettingsWrapper-BXuRgdqp.d.mts} +42 -4
  3. package/dist/{actions-D2CHvCHu.d.mts → actions-BLAr0oaM.d.mts} +16 -4
  4. package/dist/{actionsReducer-kc-S130w.d.mts → actionsReducer-BhG1wicI.d.mts} +37 -12
  5. package/dist/chunk-3GDQP6AS.mjs +14 -0
  6. package/dist/chunk-3GDQP6AS.mjs.map +1 -0
  7. package/dist/{chunk-72XCX5TD.mjs → chunk-3LDWKC5N.mjs} +13 -8
  8. package/dist/chunk-3LDWKC5N.mjs.map +1 -0
  9. package/dist/{chunk-NYZBHYW2.mjs → chunk-4ODYHZKD.mjs} +343 -38
  10. package/dist/chunk-4ODYHZKD.mjs.map +1 -0
  11. package/dist/{chunk-QPE574OW.mjs → chunk-4TVEDX7L.mjs} +23 -32
  12. package/dist/chunk-4TVEDX7L.mjs.map +1 -0
  13. package/dist/chunk-7CGMWOZN.mjs +20 -0
  14. package/dist/chunk-7CGMWOZN.mjs.map +1 -0
  15. package/dist/{chunk-7NEQAW7J.mjs → chunk-C236BQQB.mjs} +656 -917
  16. package/dist/chunk-C236BQQB.mjs.map +1 -0
  17. package/dist/{chunk-PXAUQJEU.mjs → chunk-D7MFLHXV.mjs} +2267 -1599
  18. package/dist/chunk-D7MFLHXV.mjs.map +1 -0
  19. package/dist/{chunk-47AIIJFO.mjs → chunk-ITDBOMY5.mjs} +3 -3
  20. package/dist/{chunk-47AIIJFO.mjs.map → chunk-ITDBOMY5.mjs.map} +1 -1
  21. package/dist/{chunk-XVSFXHYB.mjs → chunk-L4XGZAZ5.mjs} +23 -20
  22. package/dist/chunk-L4XGZAZ5.mjs.map +1 -0
  23. package/dist/chunk-OWHP7ONM.mjs +134 -0
  24. package/dist/chunk-OWHP7ONM.mjs.map +1 -0
  25. package/dist/{chunk-P4V3LA5R.mjs → chunk-RRVLWDT3.mjs} +10 -7
  26. package/dist/chunk-RRVLWDT3.mjs.map +1 -0
  27. package/dist/chunk-T2E6MRVP.mjs +862 -0
  28. package/dist/chunk-T2E6MRVP.mjs.map +1 -0
  29. package/dist/chunk-TEZB4ULX.mjs +57 -0
  30. package/dist/chunk-TEZB4ULX.mjs.map +1 -0
  31. package/dist/chunk-UCTMVCW7.mjs +833 -0
  32. package/dist/chunk-UCTMVCW7.mjs.map +1 -0
  33. package/dist/chunk-WECWPYZB.mjs +1950 -0
  34. package/dist/chunk-WECWPYZB.mjs.map +1 -0
  35. package/dist/{chunk-4VHEHMJN.mjs → chunk-XBZWGRDM.mjs} +228 -94
  36. package/dist/chunk-XBZWGRDM.mjs.map +1 -0
  37. package/dist/{chunk-K3K7TUWM.mjs → chunk-YZ3KCMKY.mjs} +237 -83
  38. package/dist/chunk-YZ3KCMKY.mjs.map +1 -0
  39. package/dist/components/Audio/index.css +1858 -0
  40. package/dist/components/Audio/index.css.map +1 -0
  41. package/dist/components/Audio/index.d.mts +103 -0
  42. package/dist/components/Audio/index.mjs +23 -0
  43. package/dist/components/Audio/index.mjs.map +1 -0
  44. package/dist/components/Epub/index.css +365 -9
  45. package/dist/components/Epub/index.css.map +1 -1
  46. package/dist/components/Epub/index.d.mts +17 -19
  47. package/dist/components/Epub/index.mjs +15 -10
  48. package/dist/components/Misc/index.css +5 -2
  49. package/dist/components/Misc/index.css.map +1 -1
  50. package/dist/components/Misc/index.mjs +4 -132
  51. package/dist/components/Misc/index.mjs.map +1 -1
  52. package/dist/components/Reader/index.css +1022 -183
  53. package/dist/components/Reader/index.css.map +1 -1
  54. package/dist/components/Reader/index.d.mts +16 -16
  55. package/dist/components/Reader/index.mjs +121 -22
  56. package/dist/components/Reader/index.mjs.map +1 -1
  57. package/dist/components/WebPub/index.css +365 -9
  58. package/dist/components/WebPub/index.css.map +1 -1
  59. package/dist/components/WebPub/index.d.mts +16 -16
  60. package/dist/components/WebPub/index.mjs +15 -10
  61. package/dist/core/Components/index.d.mts +64 -15
  62. package/dist/core/Components/index.mjs +2 -1
  63. package/dist/core/Helpers/index.d.mts +2 -2
  64. package/dist/core/Helpers/index.mjs +4 -2
  65. package/dist/core/Hooks/index.d.mts +7 -8
  66. package/dist/core/Hooks/index.mjs +3 -1
  67. package/dist/i18n/index.mjs +4 -5
  68. package/dist/lib/index.d.mts +159 -15
  69. package/dist/lib/index.mjs +4 -2
  70. package/dist/lib-M3PPQDJJ.mjs +6548 -0
  71. package/dist/lib-M3PPQDJJ.mjs.map +1 -0
  72. package/dist/locales/en/thorium-web.json +22 -0
  73. package/dist/next-lib/index.mjs +2 -0
  74. package/dist/next-lib/index.mjs.map +1 -1
  75. package/dist/preferences/index.d.mts +111 -13
  76. package/dist/preferences/index.mjs +6 -3
  77. package/dist/{settingsReducer-C1wwCAMv.d.mts → settingsReducer-Bu1zeveu.d.mts} +1 -1
  78. package/dist/{ui-CamWuqOo.d.mts → ui-nBv8gfr0.d.mts} +20 -1
  79. package/dist/useAudioNavigator-C5aW4-eT.d.mts +133 -0
  80. package/dist/{useContrast-D6sjPjxy.d.mts → useContrast-2t429O9O.d.mts} +16 -8
  81. package/dist/usePreferences-VaBf46eP.d.mts +230 -0
  82. package/dist/useReaderTransitions-JDzlBFsu.d.mts +530 -0
  83. package/dist/{useTimeline-DyMx_aWY.d.mts → useTimeline-DCZ1qoCO.d.mts} +4 -2
  84. package/package.json +15 -11
  85. package/dist/chunk-4VHEHMJN.mjs.map +0 -1
  86. package/dist/chunk-72XCX5TD.mjs.map +0 -1
  87. package/dist/chunk-7NEQAW7J.mjs.map +0 -1
  88. package/dist/chunk-K3K7TUWM.mjs.map +0 -1
  89. package/dist/chunk-NYZBHYW2.mjs.map +0 -1
  90. package/dist/chunk-P4V3LA5R.mjs.map +0 -1
  91. package/dist/chunk-PXAUQJEU.mjs.map +0 -1
  92. package/dist/chunk-QPE574OW.mjs.map +0 -1
  93. package/dist/chunk-XVSFXHYB.mjs.map +0 -1
  94. package/dist/useEpubNavigator-CwHJfoiV.d.mts +0 -42
  95. package/dist/usePreferences-BXFJbval.d.mts +0 -43
  96. package/dist/useReaderTransitions-guT-eA-Q.d.mts +0 -365
  97. package/dist/useWebPubNavigator-CuSNQKMw.d.mts +0 -39
@@ -1,22 +1,23 @@
1
- import { makeBreakpointsMap, isInteractiveElement, isKeyboardTriggered, isActiveElement, getBestMatchingProgressionFormat } from './chunk-47AIIJFO.mjs';
2
- import { useFullscreen, useEpubNavigator, useLocalStorage } from './chunk-4VHEHMJN.mjs';
3
- import { useAppDispatch, setFullscreen, useAppSelector, setActionOpen, dockAction, 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, ThReduxPreferencesAdapter, setReaderProfile, setPositionsList, setRTL, setFXL, setFontLanguage, setHasDisplayTransformability, setHovering, setImmersive, setOverflow, setUserNavigated, setTocEntry, collapseDockPanel, expandDockPanel, activateDockPanel, deactivateDockPanel, setDockPanelWidth } from './chunk-K3K7TUWM.mjs';
4
- import { usePreferences, prefixString, ThDockingTypes, ThSheetTypes, defaultSpacingSettingsSubpanel, defaultSpacingSettingsMain, defaultTextSettingsSubpanel, defaultTextSettingsMain, usePreferenceKeys, ThTextSettingsKeys, ThSpacingSettingsKeys, buildThemeObject, defaultPreferences, ThPreferencesProvider } from './chunk-7NEQAW7J.mjs';
5
- import { ThRunningHeadFormat, ThProgressionFormat } from './chunk-XVSFXHYB.mjs';
6
- import { isIOSish, getPlatform, buildShortcut, metaKeys } from './chunk-5LUMM7FW.mjs';
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';
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';
7
+ import { isIOSish, buildShortcut, metaKeys } from './chunk-5LUMM7FW.mjs';
7
8
  import { ErrorHandler } from './chunk-RRDEPGBK.mjs';
8
- import { ThMenuItem, ThActionButton, ThMenu, ThCollapsibleActionsBar, ThPopover, ThContainerHeader, ThNavigationButton, ThContainerBody, ThBottomSheet, ThCloseButton, ThModal, ThDockedPanel, ThTypedComponentRenderer, useActions, ThForm, ThFormNumberField, ThSettingsWrapper, ThFormSearchField, ThRadioGroup, ThDropdown, ThSwitch, ThNumberField, ThSlider, ThInteractiveOverlay, ThHeader, ThFooter, ThLink, ThLibrary, ThHome, ThBackArrow, ThRunningHead, ThPagination, ThProgression } from './chunk-NYZBHYW2.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
10
  import { usePrevious } from './chunk-YZ73DHRU.mjs';
10
11
  import { useI18n } from './chunk-IYAFKTPL.mjs';
11
- import { Text, useFilter, Tree, TreeItem, TreeItemContent, Button, Collection, ListBox, ListBoxItem, Radio, Keyboard, Toolbar } from 'react-aria-components';
12
+ import { Text, Popover, Dialog, ListBox, ListBoxItem, Radio, Button, Keyboard, Toolbar, useFilter, Tree, TreeItem, TreeItemContent, Collection } from 'react-aria-components';
12
13
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
13
- import React18, { createContext, useRef, useCallback, useEffect, useMemo, useState, useContext } from 'react';
14
- import classNames3 from 'classnames';
14
+ import { useObjectRef, useNumberFormatter, FocusScope, useFocusWithin } from 'react-aria';
15
+ import classNames4 from 'classnames';
16
+ import React24, { createContext, useCallback, useRef, useContext, useEffect, useMemo, useState } from 'react';
15
17
  import { Link, HttpFetcher, Manifest, Publication, ReadingProgression, Layout, Feature, Profile } from '@readium/shared';
16
18
  import { TextAlignment } from '@readium/navigator';
17
19
  import { useStore } from 'react-redux';
18
20
  import { PanelGroup, Panel, PanelResizeHandle } from 'react-resizable-panels';
19
- import { useFocusWithin } from 'react-aria';
20
21
 
21
22
  // src/components/Actions/assets/styles/thorium-web.overflow.module.css
22
23
  var thorium_web_overflow_default = {
@@ -33,10 +34,10 @@ var UnstableStatefulShortcut = ({
33
34
  representation,
34
35
  joiner
35
36
  }) => {
36
- const { preferences } = usePreferences();
37
+ const { shortcuts } = useSharedPreferences();
37
38
  const platformModifier = useAppSelector((state) => state.reader.platformModifier);
38
- representation = representation ? representation : preferences.shortcuts.representation || "symbol" /* symbol */;
39
- joiner = joiner ? joiner : preferences.shortcuts.joiner || " + ";
39
+ representation = representation ? representation : shortcuts.representation || "symbol" /* symbol */;
40
+ joiner = joiner ? joiner : shortcuts.joiner || " + ";
40
41
  const shortcutObj = buildShortcut(rawForm);
41
42
  if (shortcutObj) {
42
43
  let shortcutRepresentation = [];
@@ -115,14 +116,15 @@ var thorium_web_button_default = {
115
116
  iconApplyStroke: "thorium_web_button_iconApplyStroke"
116
117
  };
117
118
  var StatefulActionIcon = ({
119
+ ref: externalRef,
118
120
  visibility,
119
121
  placement,
120
122
  tooltipLabel,
121
123
  children,
122
124
  ...props
123
125
  }) => {
124
- const { preferences } = usePreferences();
125
- const triggerRef = useRef(null);
126
+ const { theming } = useSharedPreferences();
127
+ const triggerRef = useObjectRef(externalRef ?? null);
126
128
  const dispatch = useAppDispatch();
127
129
  const handleClassNameFromState = () => {
128
130
  let className = "";
@@ -153,19 +155,19 @@ var StatefulActionIcon = ({
153
155
  ThActionButton,
154
156
  {
155
157
  ref: triggerRef,
156
- className: classNames3(thorium_web_button_default.icon, handleClassNameFromState(), props.className),
158
+ className: classNames4(thorium_web_button_default.icon, handleClassNameFromState(), props.className),
157
159
  onPress: props.onPress || defaultOnPressFunc,
158
160
  onKeyDown: blurOnEsc,
159
161
  onFocus: handleImmersive,
160
162
  compounds: tooltipLabel ? {
161
163
  tooltipTrigger: {
162
- delay: preferences.theming.icon.tooltipDelay,
163
- closeDelay: preferences.theming.icon.tooltipDelay
164
+ delay: theming.icon.tooltipDelay,
165
+ closeDelay: theming.icon.tooltipDelay
164
166
  },
165
167
  tooltip: {
166
168
  className: thorium_web_button_default.tooltip,
167
169
  placement,
168
- offset: preferences.theming.icon.tooltipOffset || 0
170
+ offset: theming.icon.tooltipOffset || 0
169
171
  },
170
172
  label: tooltipLabel
171
173
  } : void 0,
@@ -179,7 +181,7 @@ var fullscreen_default = SvgFullscreen;
179
181
  var SvgFullscreenExit = (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: "M240-120v-120H120v-80h200v200h-80Zm400 0v-200h200v80H720v120h-80ZM120-640v-80h120v-120h80v200H120Zm520 0v-200h80v120h120v80H640Z" }) });
180
182
  var fullscreen_exit_default = SvgFullscreenExit;
181
183
  var StatefulFullscreenTrigger = ({ variant }) => {
182
- const { preferences } = usePreferences();
184
+ const preferences = useActionsPreferences();
183
185
  const { t } = useI18n();
184
186
  const dispatch = useAppDispatch();
185
187
  const onChange = useCallback((isFullscreen) => {
@@ -198,7 +200,7 @@ var StatefulFullscreenTrigger = ({ variant }) => {
198
200
  {
199
201
  label,
200
202
  SVGIcon: Icon,
201
- shortcut: preferences.actions.keys["fullscreen" /* fullscreen */].shortcut,
203
+ shortcut: preferences.actionsKeys["fullscreen" /* fullscreen */].shortcut,
202
204
  onAction: fs.handleFullscreen,
203
205
  id: "fullscreen" /* fullscreen */
204
206
  }
@@ -206,7 +208,7 @@ var StatefulFullscreenTrigger = ({ variant }) => {
206
208
  StatefulActionIcon,
207
209
  {
208
210
  className: thorium_web_button_default.iconCompSm,
209
- visibility: preferences.actions.keys["fullscreen" /* fullscreen */].visibility,
211
+ visibility: preferences.actionsKeys["fullscreen" /* fullscreen */].visibility,
210
212
  "aria-label": label,
211
213
  placement: "bottom",
212
214
  tooltipLabel: t("reader.fullscreen.tooltip"),
@@ -296,26 +298,99 @@ var StatefulCollapsibleActionsBar = ({
296
298
  ) });
297
299
  };
298
300
  var NavigatorContext = createContext(null);
299
- var NavigatorProvider = ({ navigator, children }) => {
300
- return /* @__PURE__ */ jsx(NavigatorContext.Provider, { value: { navigator }, children });
301
- };
302
- var useNavigatorContext = () => {
303
- const context = useContext(NavigatorContext);
304
- if (!context) {
305
- throw new Error("Navigator hooks must be used within NavigatorProvider");
306
- }
307
- return context.navigator;
301
+ var NavigatorProvider = ({
302
+ mediaNavigator,
303
+ visualNavigator,
304
+ children
305
+ }) => {
306
+ return /* @__PURE__ */ jsx(NavigatorContext.Provider, { value: { media: mediaNavigator, visual: visualNavigator }, children });
308
307
  };
309
308
 
310
309
  // src/core/Navigator/hooks/useNavigatorContext.ts
310
+ var createUnifiedGetSetting = (navigator) => {
311
+ return (settingKey) => {
312
+ return navigator.getSetting(settingKey);
313
+ };
314
+ };
315
+ var isVisualNavigator = (navigator, contextVisual) => {
316
+ return navigator === contextVisual;
317
+ };
311
318
  var useNavigator = () => {
312
- return useNavigatorContext();
319
+ const context = useContext(NavigatorContext);
320
+ if (!context) {
321
+ throw new Error("useNavigator must be used within NavigatorProvider");
322
+ }
323
+ return {
324
+ 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
+ };
331
+ },
332
+ get media() {
333
+ if (!context.media) throw new Error("Media navigator not available");
334
+ return context.media;
335
+ },
336
+ // Unified interface that automatically selects the appropriate navigator
337
+ 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);
373
+ }
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
382
+ };
383
+ }
384
+ };
313
385
  };
314
386
 
315
387
  // src/components/Sheets/assets/styles/thorium-web.sheets.module.css
316
388
  var thorium_web_sheets_default = {
317
389
  fullscreen: "thorium_web_sheets_fullscreen",
390
+ modal: "thorium_web_sheets_modal",
391
+ modalDialog: "thorium_web_sheets_modalDialog",
318
392
  popover: "thorium_web_sheets_popover",
393
+ compactPopover: "thorium_web_sheets_compactPopover",
319
394
  draggable: "thorium_web_sheets_draggable",
320
395
  draggableFullHeightDetent: "thorium_web_sheets_draggableFullHeightDetent",
321
396
  draggableContentHeightDetent: "thorium_web_sheets_draggableContentHeightDetent",
@@ -347,10 +422,11 @@ var dock_to_right_default = SvgDockToRight;
347
422
  var SvgDockToLeft = (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: "M200-120q-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-120H200Zm440-80h120v-560H640v560Zm-80 0v-560H200v560h360Zm80 0h120-120Z" }) });
348
423
  var dock_to_left_default = SvgDockToLeft;
349
424
  var StatefulDockStart = ({ variant, associatedKey }) => {
350
- const { preferences } = usePreferences();
425
+ const preferences = useActionsPreferences();
351
426
  const { t } = useI18n();
352
427
  const direction = useAppSelector((state) => state.reader.direction);
353
428
  const actionsMap = useAppSelector((state) => state.actions.keys);
429
+ const profile = useAppSelector((state) => state.reader.profile);
354
430
  const isRTL = direction === "rtl" /* rtl */;
355
431
  const translationKey = isRTL ? "reader.app.docker.dockToRight" : "reader.app.docker.dockToLeft";
356
432
  const localeKey = {
@@ -361,13 +437,14 @@ var StatefulDockStart = ({ variant, associatedKey }) => {
361
437
  const isDisabled = actions.whichDocked(associatedKey) === "dockingStart" /* start */;
362
438
  const dispatch = useAppDispatch();
363
439
  const handlePress = useCallback(() => {
364
- if (associatedKey) {
440
+ if (associatedKey && profile) {
365
441
  dispatch(dockAction({
366
442
  key: associatedKey,
367
- dockingKey: "dockingStart" /* start */
443
+ dockingKey: "dockingStart" /* start */,
444
+ profile
368
445
  }));
369
446
  }
370
- }, [dispatch, associatedKey]);
447
+ }, [dispatch, associatedKey, profile]);
371
448
  return /* @__PURE__ */ jsx(Fragment, { children: variant && variant === "menuItem" /* menu */ ? /* @__PURE__ */ jsx(
372
449
  StatefulOverflowMenuItem,
373
450
  {
@@ -392,10 +469,11 @@ var StatefulDockStart = ({ variant, associatedKey }) => {
392
469
  ) });
393
470
  };
394
471
  var StatefulDockEnd = ({ variant, associatedKey }) => {
395
- const { preferences } = usePreferences();
472
+ const preferences = useActionsPreferences();
396
473
  const { t } = useI18n();
397
474
  const direction = useAppSelector((state) => state.reader.direction);
398
475
  const actionsMap = useAppSelector((state) => state.actions.keys);
476
+ const profile = useAppSelector((state) => state.reader.profile);
399
477
  const isRTL = direction === "rtl" /* rtl */;
400
478
  const translationKey = isRTL ? "reader.app.docker.dockToLeft" : "reader.app.docker.dockToRight";
401
479
  const localeKey = {
@@ -406,13 +484,14 @@ var StatefulDockEnd = ({ variant, associatedKey }) => {
406
484
  const isDisabled = actions.whichDocked(associatedKey) === "dockingEnd" /* end */;
407
485
  const dispatch = useAppDispatch();
408
486
  const handlePress = useCallback(() => {
409
- if (associatedKey) {
487
+ if (associatedKey && profile) {
410
488
  dispatch(dockAction({
411
489
  key: associatedKey,
412
- dockingKey: "dockingEnd" /* end */
490
+ dockingKey: "dockingEnd" /* end */,
491
+ profile
413
492
  }));
414
493
  }
415
- }, [dispatch, associatedKey]);
494
+ }, [dispatch, associatedKey, profile]);
416
495
  return /* @__PURE__ */ jsx(Fragment, { children: variant && variant === "menuItem" /* menu */ ? /* @__PURE__ */ jsx(
417
496
  StatefulOverflowMenuItem,
418
497
  {
@@ -439,20 +518,22 @@ var StatefulDockEnd = ({ variant, associatedKey }) => {
439
518
  var SvgStack = (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: "M240-400v80h-80q-33 0-56.5-23.5T80-400v-400q0-33 23.5-56.5T160-880h400q33 0 56.5 23.5T640-800v80h-80v-80H160v400h80ZM400-80q-33 0-56.5-23.5T320-160v-400q0-33 23.5-56.5T400-640h400q33 0 56.5 23.5T880-560v400q0 33-23.5 56.5T800-80H400Zm0-80h400v-400H400v400Zm200-200Z" }) });
440
519
  var stack_default = SvgStack;
441
520
  var StatefulDockTransientPopover = ({ variant, associatedKey }) => {
442
- const { preferences } = usePreferences();
521
+ const preferences = useActionsPreferences();
443
522
  const { t } = useI18n();
444
523
  const actionsMap = useAppSelector((state) => state.actions.keys);
524
+ const profile = useAppSelector((state) => state.reader.profile);
445
525
  const actions = useActions(actionsMap);
446
526
  const isDisabled = !actions.isDocked(associatedKey) || actions.whichDocked(associatedKey) === "dockingTransient" /* transient */;
447
527
  const dispatch = useAppDispatch();
448
528
  const handlePress = useCallback(() => {
449
- if (associatedKey) {
529
+ if (associatedKey && profile) {
450
530
  dispatch(dockAction({
451
531
  key: associatedKey,
452
- dockingKey: "dockingTransient" /* transient */
532
+ dockingKey: "dockingTransient" /* transient */,
533
+ profile
453
534
  }));
454
535
  }
455
- }, [dispatch, associatedKey]);
536
+ }, [dispatch, associatedKey, profile]);
456
537
  return /* @__PURE__ */ jsx(Fragment, { children: variant && variant === "menuItem" /* menu */ ? /* @__PURE__ */ jsx(
457
538
  StatefulOverflowMenuItem,
458
539
  {
@@ -493,7 +574,8 @@ var StatefulDocker = ({
493
574
  ref,
494
575
  onClose
495
576
  }) => {
496
- const { preferences } = usePreferences();
577
+ const preferences = useActionsPreferences();
578
+ const { theming } = useSharedPreferences();
497
579
  const { t } = useI18n();
498
580
  const listActionItems = useCallback(() => {
499
581
  const actionsItems = [];
@@ -527,8 +609,8 @@ var StatefulDocker = ({
527
609
  onPress: onClose,
528
610
  compounds: {
529
611
  tooltipTrigger: {
530
- delay: preferences.theming.icon.tooltipDelay,
531
- closeDelay: preferences.theming.icon.tooltipDelay
612
+ delay: theming.icon.tooltipDelay,
613
+ closeDelay: theming.icon.tooltipDelay
532
614
  },
533
615
  tooltip: {
534
616
  className: thorium_web_button_default.tooltip
@@ -547,7 +629,7 @@ var useWebkitPatch = (isOpen) => {
547
629
  const isScroll = isWebPub || scroll && !isFXL;
548
630
  const {
549
631
  getCframes
550
- } = useNavigator();
632
+ } = useNavigator().unified;
551
633
  useEffect(() => {
552
634
  if (isScroll && !isOpen) {
553
635
  const container = document.getElementById("thorium-web-container");
@@ -556,7 +638,10 @@ var useWebkitPatch = (isOpen) => {
556
638
  container.style.height = `${currentHeight - 1}px`;
557
639
  setTimeout(() => {
558
640
  container.style.height = "";
559
- const frame = getCframes()?.[0];
641
+ if (!getCframes) return;
642
+ const frames = getCframes();
643
+ if (!frames || !Array.isArray(frames) || frames.length === 0) return;
644
+ const frame = frames[0];
560
645
  if (!frame?.window?.document?.scrollingElement) return;
561
646
  const currentScrollTop = frame.window.document.scrollingElement.scrollTop;
562
647
  if (currentScrollTop > 1) {
@@ -575,6 +660,7 @@ var StatefulPopoverSheet = ({
575
660
  heading,
576
661
  headerVariant,
577
662
  className,
663
+ headerClassName,
578
664
  isOpen,
579
665
  onOpenChange,
580
666
  onClosePress,
@@ -602,7 +688,7 @@ var StatefulPopoverSheet = ({
602
688
  }
603
689
  }, [isOpen]);
604
690
  useWebkitPatch(!!isOpen);
605
- if (React18.Children.toArray(children).length > 0) {
691
+ if (React24.Children.toArray(children).length > 0) {
606
692
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
607
693
  ThPopover,
608
694
  {
@@ -623,7 +709,7 @@ var StatefulPopoverSheet = ({
623
709
  updateState: resetFocus
624
710
  },
625
711
  placement: placement || "bottom",
626
- className: classNames3(thorium_web_sheets_default.popover, className),
712
+ className: classNames4(thorium_web_sheets_default.popover, className),
627
713
  isOpen,
628
714
  onOpenChange,
629
715
  isKeyboardDismissDisabled: dismissEscapeKeyClose,
@@ -637,7 +723,7 @@ var StatefulPopoverSheet = ({
637
723
  ThContainerHeader,
638
724
  {
639
725
  ref: popoverHeaderRef,
640
- className: thorium_web_sheets_default.header,
726
+ className: classNames4(thorium_web_sheets_default.header, headerClassName),
641
727
  label: heading,
642
728
  compounds: {
643
729
  heading: {
@@ -650,7 +736,7 @@ var StatefulPopoverSheet = ({
650
736
  direction: direction === "ltr" ? "left" : "right",
651
737
  label: t("reader.app.back.trigger"),
652
738
  ref: popoverCloseRef,
653
- className: classNames3(className, thorium_web_button_default.backButton),
739
+ className: classNames4(className, thorium_web_button_default.backButton),
654
740
  "aria-label": t("reader.app.back.trigger"),
655
741
  onPress: onClosePress
656
742
  }
@@ -678,6 +764,124 @@ var StatefulPopoverSheet = ({
678
764
  ) });
679
765
  }
680
766
  };
767
+ var StatefulModalBase = ({
768
+ id,
769
+ heading,
770
+ headerVariant,
771
+ className,
772
+ sheetClassName,
773
+ dialogClassName,
774
+ isOpen,
775
+ onOpenChange,
776
+ onClosePress,
777
+ docker,
778
+ children,
779
+ resetFocus,
780
+ focusWithinRef,
781
+ focusSelector,
782
+ scrollTopOnFocus,
783
+ dismissEscapeKeyClose
784
+ }) => {
785
+ const { t } = useI18n();
786
+ const direction = useAppSelector((state) => state.reader.direction);
787
+ const sheetRef = useRef(null);
788
+ const sheetHeaderRef = useRef(null);
789
+ const sheetBodyRef = useRef(null);
790
+ const sheetCloseRef = useRef(null);
791
+ useEffect(() => {
792
+ if (isOpen && sheetRef.current && sheetHeaderRef.current) {
793
+ sheetRef.current.style.setProperty(
794
+ `--${prefixString("sheet-sticky-header")}`,
795
+ `${sheetHeaderRef.current.clientHeight}px`
796
+ );
797
+ }
798
+ }, [isOpen]);
799
+ useWebkitPatch(!!isOpen);
800
+ if (React24.Children.toArray(children).length > 0) {
801
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
802
+ ThModal,
803
+ {
804
+ ref: sheetRef,
805
+ focusOptions: {
806
+ withinRef: focusWithinRef ?? sheetBodyRef,
807
+ trackedState: isOpen,
808
+ fallbackRef: sheetCloseRef,
809
+ withSelector: focusSelector,
810
+ action: {
811
+ type: "focus",
812
+ options: {
813
+ preventScroll: scrollTopOnFocus ? true : false,
814
+ scrollContainerToTop: scrollTopOnFocus
815
+ }
816
+ },
817
+ updateState: resetFocus
818
+ },
819
+ compounds: {
820
+ dialog: {
821
+ className: classNames4(thorium_web_sheets_default.dialog, dialogClassName, className)
822
+ }
823
+ },
824
+ isOpen,
825
+ onOpenChange,
826
+ isDismissable: true,
827
+ className: sheetClassName,
828
+ isKeyboardDismissDisabled: dismissEscapeKeyClose,
829
+ children: [
830
+ /* @__PURE__ */ jsx(
831
+ ThContainerHeader,
832
+ {
833
+ ref: sheetHeaderRef,
834
+ className: thorium_web_sheets_default.header,
835
+ label: heading,
836
+ compounds: {
837
+ heading: {
838
+ className: thorium_web_sheets_default.heading
839
+ }
840
+ },
841
+ children: headerVariant === "previous" /* previous */ ? /* @__PURE__ */ jsx(
842
+ ThNavigationButton,
843
+ {
844
+ direction: direction === "ltr" ? "left" : "right",
845
+ label: t("reader.app.back.trigger"),
846
+ ref: sheetCloseRef,
847
+ className: classNames4(className, thorium_web_button_default.backButton),
848
+ "aria-label": t("reader.app.back.trigger"),
849
+ onPress: onClosePress
850
+ }
851
+ ) : /* @__PURE__ */ jsx(
852
+ StatefulDocker,
853
+ {
854
+ id,
855
+ keys: docker || [],
856
+ ref: sheetCloseRef,
857
+ onClose: onClosePress
858
+ }
859
+ )
860
+ }
861
+ ),
862
+ /* @__PURE__ */ jsx(
863
+ ThContainerBody,
864
+ {
865
+ ref: sheetBodyRef,
866
+ className: thorium_web_sheets_default.body,
867
+ children
868
+ }
869
+ )
870
+ ]
871
+ }
872
+ ) });
873
+ }
874
+ };
875
+ var StatefulModalSheet = (props) => {
876
+ return /* @__PURE__ */ jsx(
877
+ StatefulModalBase,
878
+ {
879
+ ...props,
880
+ sheetClassName: thorium_web_sheets_default.modal,
881
+ dialogClassName: thorium_web_sheets_default.modalDialog
882
+ }
883
+ );
884
+ };
681
885
  var DEFAULT_SNAPPOINTS = {
682
886
  min: 0.3,
683
887
  peek: 0.5,
@@ -698,7 +902,8 @@ var StatefulBottomSheet = ({
698
902
  scrollTopOnFocus,
699
903
  dismissEscapeKeyClose
700
904
  }) => {
701
- const { preferences } = usePreferences();
905
+ const preferences = useActionsPreferences();
906
+ const sharedPreferences = useSharedPreferences();
702
907
  const { t } = useI18n();
703
908
  const direction = useAppSelector((state) => state.reader.direction);
704
909
  const prefersReducedMotion = useAppSelector((state) => state.theming.prefersReducedMotion);
@@ -717,7 +922,7 @@ var StatefulBottomSheet = ({
717
922
  }
718
923
  };
719
924
  let snapArray2 = [0];
720
- const snapPref = preferences.actions.keys[id].snapped;
925
+ const snapPref = preferences.actionsKeys[id].snapped;
721
926
  if (snapPref) {
722
927
  if (snapPref.minHeight) {
723
928
  switch (snapPref.minHeight) {
@@ -806,7 +1011,7 @@ var StatefulBottomSheet = ({
806
1011
  }
807
1012
  }, [snapArray, onClosePress]);
808
1013
  const maxWidthPref = useMemo(() => {
809
- const maxWidth = preferences.actions.keys[id].snapped?.maxWidth;
1014
+ const maxWidth = preferences.actionsKeys[id].snapped?.maxWidth;
810
1015
  if (typeof maxWidth === "undefined") {
811
1016
  return void 0;
812
1017
  } else if (maxWidth === null) {
@@ -820,7 +1025,7 @@ var StatefulBottomSheet = ({
820
1025
  active: false,
821
1026
  override: void 0
822
1027
  };
823
- const scrim = preferences.actions.keys[id].snapped?.scrim;
1028
+ const scrim = preferences.actionsKeys[id].snapped?.scrim ?? sharedPreferences.theming.layout.defaults.scrim;
824
1029
  if (scrim) {
825
1030
  scrimPref2.active = true;
826
1031
  if (typeof scrim === "string") {
@@ -828,7 +1033,7 @@ var StatefulBottomSheet = ({
828
1033
  }
829
1034
  }
830
1035
  return scrimPref2;
831
- }, [id, preferences]);
1036
+ }, [id, preferences, sharedPreferences]);
832
1037
  const detentClassName = useMemo(() => {
833
1038
  let className2 = "";
834
1039
  if (detent.current === "content-height") {
@@ -851,7 +1056,7 @@ var StatefulBottomSheet = ({
851
1056
  return "default";
852
1057
  }
853
1058
  };
854
- if (React18.Children.toArray(children).length > 0) {
1059
+ if (React24.Children.toArray(children).length > 0) {
855
1060
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
856
1061
  ThBottomSheet,
857
1062
  {
@@ -889,7 +1094,7 @@ var StatefulBottomSheet = ({
889
1094
  prefersReducedMotion,
890
1095
  compounds: {
891
1096
  container: {
892
- className: classNames3(thorium_web_sheets_default.draggable, detentClassName),
1097
+ className: classNames4(thorium_web_sheets_default.draggable, detentClassName),
893
1098
  ref: sheetContainerRef,
894
1099
  style: {
895
1100
  maxWidth: maxWidthPref
@@ -901,14 +1106,14 @@ var StatefulBottomSheet = ({
901
1106
  onKeyDown: onDragKeyCallback
902
1107
  },
903
1108
  content: {
904
- className: classNames3(thorium_web_sheets_default.draggableContent, className),
1109
+ className: classNames4(thorium_web_sheets_default.draggableContent, className),
905
1110
  disableDrag: true
906
1111
  },
907
1112
  scroller: {
908
1113
  className: thorium_web_sheets_default.draggableScroller
909
1114
  },
910
1115
  backdrop: {
911
- className: classNames3(thorium_web_sheets_default.draggableBackdrop, scrimClassName),
1116
+ className: classNames4(thorium_web_sheets_default.draggableBackdrop, scrimClassName),
912
1117
  style: { [`--${prefixString("layout-defaults-scrim")}`]: scrimPref.override }
913
1118
  }
914
1119
  },
@@ -929,7 +1134,7 @@ var StatefulBottomSheet = ({
929
1134
  direction: direction === "ltr" ? "left" : "right",
930
1135
  label: t("reader.app.back.trigger"),
931
1136
  ref: bottomSheetCloseRef,
932
- className: classNames3(className, thorium_web_button_default.backButton),
1137
+ className: classNames4(className, thorium_web_button_default.backButton),
933
1138
  "aria-label": t("reader.app.back.trigger"),
934
1139
  onPress: onClosePress
935
1140
  }
@@ -957,109 +1162,14 @@ var StatefulBottomSheet = ({
957
1162
  ) });
958
1163
  }
959
1164
  };
960
- var StatefulFullScreenSheet = ({
961
- heading,
962
- headerVariant,
963
- className,
964
- isOpen,
965
- onOpenChange,
966
- onClosePress,
967
- children,
968
- resetFocus,
969
- focusWithinRef,
970
- focusSelector,
971
- scrollTopOnFocus,
972
- dismissEscapeKeyClose
973
- }) => {
974
- const { t } = useI18n();
975
- const direction = useAppSelector((state) => state.reader.direction);
976
- const fullScreenRef = useRef(null);
977
- const fullScreenHeaderRef = useRef(null);
978
- const fullScreenBodyRef = useRef(null);
979
- const fullScreenCloseRef = useRef(null);
980
- useEffect(() => {
981
- if (isOpen && fullScreenRef.current && fullScreenHeaderRef.current) {
982
- fullScreenRef.current.style.setProperty(
983
- `--${prefixString("sheet-sticky-header")}`,
984
- `${fullScreenHeaderRef.current.clientHeight}px`
985
- );
1165
+ var StatefulFullScreenSheet = (props) => {
1166
+ return /* @__PURE__ */ jsx(
1167
+ StatefulModalBase,
1168
+ {
1169
+ ...props,
1170
+ sheetClassName: thorium_web_sheets_default.fullscreen
986
1171
  }
987
- }, [isOpen]);
988
- useWebkitPatch(!!isOpen);
989
- if (React18.Children.toArray(children).length > 0) {
990
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
991
- ThModal,
992
- {
993
- ref: fullScreenRef,
994
- focusOptions: {
995
- withinRef: focusWithinRef ?? fullScreenBodyRef,
996
- trackedState: isOpen,
997
- fallbackRef: fullScreenCloseRef,
998
- withSelector: focusSelector,
999
- action: {
1000
- type: "focus",
1001
- options: {
1002
- preventScroll: scrollTopOnFocus ? true : false,
1003
- scrollContainerToTop: scrollTopOnFocus
1004
- }
1005
- },
1006
- updateState: resetFocus
1007
- },
1008
- compounds: {
1009
- dialog: {
1010
- className: thorium_web_sheets_default.dialog
1011
- }
1012
- },
1013
- isOpen,
1014
- onOpenChange,
1015
- isDismissable: true,
1016
- className: classNames3(thorium_web_sheets_default.fullscreen, className),
1017
- isKeyboardDismissDisabled: dismissEscapeKeyClose,
1018
- children: [
1019
- /* @__PURE__ */ jsx(
1020
- ThContainerHeader,
1021
- {
1022
- ref: fullScreenHeaderRef,
1023
- className: thorium_web_sheets_default.header,
1024
- label: heading,
1025
- compounds: {
1026
- heading: {
1027
- className: thorium_web_sheets_default.heading
1028
- }
1029
- },
1030
- children: headerVariant === "previous" /* previous */ ? /* @__PURE__ */ jsx(
1031
- ThNavigationButton,
1032
- {
1033
- direction: direction === "ltr" ? "left" : "right",
1034
- label: t("reader.app.back.trigger"),
1035
- ref: fullScreenCloseRef,
1036
- className: classNames3(className, thorium_web_button_default.backButton),
1037
- "aria-label": t("reader.app.back.trigger"),
1038
- onPress: onClosePress
1039
- }
1040
- ) : /* @__PURE__ */ jsx(
1041
- ThCloseButton,
1042
- {
1043
- ref: fullScreenCloseRef,
1044
- className: thorium_web_button_default.closeButton,
1045
- "aria-label": t("common.actions.close"),
1046
- onPress: onClosePress
1047
- }
1048
- )
1049
- }
1050
- ),
1051
- /* @__PURE__ */ jsx(
1052
- ThContainerBody,
1053
- {
1054
- ref: fullScreenBodyRef,
1055
- className: thorium_web_sheets_default.body,
1056
- children
1057
- }
1058
- )
1059
- ]
1060
- }
1061
- ) });
1062
- }
1172
+ );
1063
1173
  };
1064
1174
  var StatefulDockedSheet = ({
1065
1175
  id,
@@ -1098,7 +1208,7 @@ var StatefulDockedSheet = ({
1098
1208
  return direction === "ltr" /* ltr */ ? thorium_web_sheets_default.dockedRightBorder : thorium_web_sheets_default.dockedLeftBorder;
1099
1209
  }
1100
1210
  }, [flow, direction]);
1101
- if (React18.Children.toArray(children).length > 0) {
1211
+ if (React24.Children.toArray(children).length > 0) {
1102
1212
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
1103
1213
  ThDockedPanel,
1104
1214
  {
@@ -1120,7 +1230,7 @@ var StatefulDockedSheet = ({
1120
1230
  },
1121
1231
  updateState: resetFocus
1122
1232
  },
1123
- className: classNames3(thorium_web_sheets_default.docked, className, classFromFlow()),
1233
+ className: classNames4(thorium_web_sheets_default.docked, className, classFromFlow()),
1124
1234
  children: [
1125
1235
  /* @__PURE__ */ jsx(
1126
1236
  ThContainerHeader,
@@ -1139,7 +1249,7 @@ var StatefulDockedSheet = ({
1139
1249
  direction: direction === "ltr" ? "left" : "right",
1140
1250
  label: t("reader.app.back.trigger"),
1141
1251
  ref: dockedSheetCloseRef,
1142
- className: classNames3(className, thorium_web_button_default.backButton),
1252
+ className: classNames4(className, thorium_web_button_default.backButton),
1143
1253
  "aria-label": t("reader.app.back.trigger"),
1144
1254
  onPress: onClosePress
1145
1255
  }
@@ -1167,23 +1277,86 @@ var StatefulDockedSheet = ({
1167
1277
  ) });
1168
1278
  }
1169
1279
  };
1170
- var componentMap = {
1171
- ["popover" /* popover */]: StatefulPopoverSheet,
1172
- ["bottomSheet" /* bottomSheet */]: StatefulBottomSheet,
1173
- ["fullscreen" /* fullscreen */]: StatefulFullScreenSheet,
1174
- ["docked start" /* dockedStart */]: (props) => /* @__PURE__ */ jsx(StatefulDockedSheet, { ...props, flow: "dockingStart" /* start */ }),
1175
- ["docked end" /* dockedEnd */]: (props) => /* @__PURE__ */ jsx(StatefulDockedSheet, { ...props, flow: "dockingEnd" /* end */ })
1176
- };
1177
- var StatefulSheetWrapper = ({
1178
- sheetType,
1179
- sheetProps,
1180
- children
1280
+ var StatefulCompactPopoverSheet = ({
1281
+ id,
1282
+ triggerRef,
1283
+ heading,
1284
+ className,
1285
+ isOpen,
1286
+ onOpenChange,
1287
+ placement,
1288
+ children,
1289
+ resetFocus,
1290
+ focusWithinRef,
1291
+ focusSelector,
1292
+ scrollTopOnFocus,
1293
+ dismissEscapeKeyClose
1181
1294
  }) => {
1182
- return /* @__PURE__ */ jsx(
1183
- ThTypedComponentRenderer,
1184
- {
1185
- type: sheetType,
1186
- componentMap,
1295
+ const popoverRef = useRef(null);
1296
+ const popoverBodyRef = useRef(null);
1297
+ useFirstFocusable({
1298
+ withinRef: focusWithinRef ?? popoverBodyRef,
1299
+ trackedState: isOpen,
1300
+ withSelector: focusSelector,
1301
+ action: {
1302
+ type: "focus",
1303
+ options: {
1304
+ preventScroll: scrollTopOnFocus ? true : false,
1305
+ scrollContainerToTop: scrollTopOnFocus
1306
+ }
1307
+ },
1308
+ updateState: resetFocus
1309
+ });
1310
+ useWebkitPatch(!!isOpen);
1311
+ if (React24.Children.toArray(children).length > 0) {
1312
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
1313
+ Popover,
1314
+ {
1315
+ ref: popoverRef,
1316
+ triggerRef,
1317
+ placement: placement || "bottom",
1318
+ isOpen,
1319
+ onOpenChange,
1320
+ isKeyboardDismissDisabled: dismissEscapeKeyClose,
1321
+ className: classNames4(thorium_web_sheets_default.compactPopover, className),
1322
+ children: /* @__PURE__ */ jsx(
1323
+ Dialog,
1324
+ {
1325
+ "aria-label": heading,
1326
+ className: thorium_web_sheets_default.dialog,
1327
+ children: /* @__PURE__ */ jsx(
1328
+ "div",
1329
+ {
1330
+ ref: popoverBodyRef,
1331
+ className: thorium_web_sheets_default.body,
1332
+ children
1333
+ }
1334
+ )
1335
+ }
1336
+ )
1337
+ }
1338
+ ) });
1339
+ }
1340
+ };
1341
+ var componentMap = {
1342
+ ["compactPopover" /* compactPopover */]: StatefulCompactPopoverSheet,
1343
+ ["popover" /* popover */]: StatefulPopoverSheet,
1344
+ ["modal" /* modal */]: StatefulModalSheet,
1345
+ ["bottomSheet" /* bottomSheet */]: StatefulBottomSheet,
1346
+ ["fullscreen" /* fullscreen */]: StatefulFullScreenSheet,
1347
+ ["docked start" /* dockedStart */]: (props) => /* @__PURE__ */ jsx(StatefulDockedSheet, { ...props, flow: "dockingStart" /* start */ }),
1348
+ ["docked end" /* dockedEnd */]: (props) => /* @__PURE__ */ jsx(StatefulDockedSheet, { ...props, flow: "dockingEnd" /* end */ })
1349
+ };
1350
+ var StatefulSheetWrapper = ({
1351
+ sheetType,
1352
+ sheetProps,
1353
+ children
1354
+ }) => {
1355
+ return /* @__PURE__ */ jsx(
1356
+ ThTypedComponentRenderer,
1357
+ {
1358
+ type: sheetType,
1359
+ componentMap,
1187
1360
  props: sheetProps,
1188
1361
  children
1189
1362
  }
@@ -1191,12 +1364,18 @@ var StatefulSheetWrapper = ({
1191
1364
  };
1192
1365
  var dockingMap = null;
1193
1366
  var useDocking = (key) => {
1194
- const { preferences } = usePreferences();
1367
+ const preferences = useActionsPreferences();
1195
1368
  const breakpoint = useAppSelector((state) => state.theming.breakpoint);
1196
1369
  const actionsMap = useAppSelector((state) => state.actions.keys);
1197
1370
  const actionState = actionsMap[key];
1371
+ const profile = useAppSelector((state) => state.reader.profile);
1372
+ const dock = useAppSelector((state) => profile ? state.actions.dock[profile] : void 0);
1198
1373
  const dispatch = useAppDispatch();
1199
1374
  const actions = useActions(actionsMap);
1375
+ const startActionKey = dock?.["dockingStart" /* start */]?.actionKey;
1376
+ const endActionKey = dock?.["dockingEnd" /* end */]?.actionKey;
1377
+ const startStatus = useActionComponentStatus({ actionKey: startActionKey || "" });
1378
+ const endStatus = useActionComponentStatus({ actionKey: endActionKey || "" });
1200
1379
  if (!dockingMap) {
1201
1380
  dockingMap = makeBreakpointsMap({
1202
1381
  defaultValue: "both" /* both */,
@@ -1206,12 +1385,12 @@ var useDocking = (key) => {
1206
1385
  });
1207
1386
  }
1208
1387
  const currentDockConfig = breakpoint && dockingMap[breakpoint];
1209
- const dockablePref = preferences.actions.keys[key]?.docked?.dockable || "none" /* none */;
1210
- const defaultSheet = preferences.actions.keys[key]?.sheet?.defaultSheet || "popover" /* popover */;
1388
+ const dockablePref = preferences.actionsKeys[key]?.docked?.dockable || "none" /* none */;
1389
+ const defaultSheet = preferences.actionsKeys[key]?.sheet?.defaultSheet || "popover" /* popover */;
1211
1390
  const sheetMap = makeBreakpointsMap({
1212
- defaultValue: preferences.actions.keys[key]?.sheet?.defaultSheet || "popover" /* popover */,
1391
+ defaultValue: preferences.actionsKeys[key]?.sheet?.defaultSheet || "popover" /* popover */,
1213
1392
  fromEnum: ThSheetTypes,
1214
- pref: preferences.actions.keys[key]?.sheet?.breakpoints
1393
+ pref: preferences.actionsKeys[key]?.sheet?.breakpoints
1215
1394
  });
1216
1395
  const sheetPref = breakpoint && sheetMap[breakpoint] || defaultSheet;
1217
1396
  const [sheetType, setSheetType] = useState(defaultSheet);
@@ -1330,11 +1509,12 @@ var useDocking = (key) => {
1330
1509
  }
1331
1510
  }, [dispatch, key, sheetType, previousSheetType, actionState?.docking]);
1332
1511
  useEffect(() => {
1333
- if (actionState?.isOpen == null) {
1512
+ if (actionState?.isOpen == null && profile) {
1334
1513
  if (sheetType === "docked start" /* dockedStart */) {
1335
1514
  dispatch(dockAction({
1336
1515
  key,
1337
- dockingKey: "dockingStart" /* start */
1516
+ dockingKey: "dockingStart" /* start */,
1517
+ profile
1338
1518
  }));
1339
1519
  dispatch(setActionOpen({
1340
1520
  key,
@@ -1343,7 +1523,8 @@ var useDocking = (key) => {
1343
1523
  } else if (sheetType === "docked end" /* dockedEnd */) {
1344
1524
  dispatch(dockAction({
1345
1525
  key,
1346
- dockingKey: "dockingEnd" /* end */
1526
+ dockingKey: "dockingEnd" /* end */,
1527
+ profile
1347
1528
  }));
1348
1529
  dispatch(setActionOpen({
1349
1530
  key,
@@ -1351,15 +1532,16 @@ var useDocking = (key) => {
1351
1532
  }));
1352
1533
  }
1353
1534
  }
1354
- });
1535
+ }, [actionState?.isOpen, sheetType, key, dispatch, profile]);
1355
1536
  useEffect(() => {
1356
- if (actionState?.isOpen != null && actionState?.docking == null) {
1537
+ if (actionState?.isOpen != null && actionState?.docking == null && profile) {
1357
1538
  if (sheetType === "docked start" /* dockedStart */) {
1358
1539
  const dockingKey = actions.whichDocked(key);
1359
1540
  if (dockingKey !== "dockingStart" /* start */) {
1360
1541
  dispatch(dockAction({
1361
1542
  key,
1362
- dockingKey: "dockingStart" /* start */
1543
+ dockingKey: "dockingStart" /* start */,
1544
+ profile
1363
1545
  }));
1364
1546
  }
1365
1547
  } else if (sheetType === "docked end" /* dockedEnd */) {
@@ -1367,12 +1549,61 @@ var useDocking = (key) => {
1367
1549
  if (dockingKey !== "dockingEnd" /* end */) {
1368
1550
  dispatch(dockAction({
1369
1551
  key,
1370
- dockingKey: "dockingEnd" /* end */
1552
+ dockingKey: "dockingEnd" /* end */,
1553
+ profile
1554
+ }));
1555
+ }
1556
+ }
1557
+ }
1558
+ }, [dispatch, key, sheetType, actionState?.isOpen, actionState?.docking, actions, profile]);
1559
+ useEffect(() => {
1560
+ if (profile && dock) {
1561
+ const isDockedInStart = dock["dockingStart" /* start */]?.actionKey === key;
1562
+ const isDockedInEnd = dock["dockingEnd" /* end */]?.actionKey === key;
1563
+ if (isDockedInStart && actionState?.docking !== "dockingStart" /* start */) {
1564
+ dispatch(dockAction({
1565
+ key,
1566
+ dockingKey: "dockingStart" /* start */,
1567
+ profile
1568
+ }));
1569
+ if (actionState?.isOpen === false) {
1570
+ dispatch(setActionOpen({
1571
+ key,
1572
+ isOpen: true
1573
+ }));
1574
+ }
1575
+ } else if (isDockedInEnd && actionState?.docking !== "dockingEnd" /* end */) {
1576
+ dispatch(dockAction({
1577
+ key,
1578
+ dockingKey: "dockingEnd" /* end */,
1579
+ profile
1580
+ }));
1581
+ if (actionState?.isOpen === false) {
1582
+ dispatch(setActionOpen({
1583
+ key,
1584
+ isOpen: true
1371
1585
  }));
1372
1586
  }
1373
1587
  }
1374
1588
  }
1375
- }, [dispatch, key, sheetType, actionState?.isOpen, actionState?.docking, actions]);
1589
+ }, [profile, dock, actionState?.docking, actionState?.isOpen, key, dispatch]);
1590
+ useEffect(() => {
1591
+ if (!profile || !dock) return;
1592
+ if (startActionKey && !startStatus.isComponentRegistered) {
1593
+ dispatch(dockAction({
1594
+ key: startActionKey,
1595
+ dockingKey: "dockingTransient" /* transient */,
1596
+ profile
1597
+ }));
1598
+ }
1599
+ if (endActionKey && !endStatus.isComponentRegistered) {
1600
+ dispatch(dockAction({
1601
+ key: endActionKey,
1602
+ dockingKey: "dockingTransient" /* transient */,
1603
+ profile
1604
+ }));
1605
+ }
1606
+ }, [profile, dock, startActionKey, endActionKey, startStatus.isComponentRegistered, endStatus.isComponentRegistered, dispatch]);
1376
1607
  return {
1377
1608
  getDocker,
1378
1609
  sheetType
@@ -1504,7 +1735,7 @@ var StatefulJumpToPositionContainer = ({
1504
1735
  var SvgPinDrop = (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: "M480-301q99-80 149.5-154T680-594q0-90-56-148t-144-58q-88 0-144 58t-56 148q0 65 50.5 139T480-301Zm0 101Q339-304 269.5-402T200-594q0-125 78-205.5T480-880q124 0 202 80.5T760-594q0 94-69.5 192T480-200Zm0-320q33 0 56.5-23.5T560-600q0-33-23.5-56.5T480-680q-33 0-56.5 23.5T400-600q0 33 23.5 56.5T480-520ZM200-80v-80h560v80H200Zm280-520Z" }) });
1505
1736
  var pin_drop_default = SvgPinDrop;
1506
1737
  var StatefulJumpToPositionTrigger = ({ variant }) => {
1507
- const { preferences } = usePreferences();
1738
+ const preferences = useActionsPreferences();
1508
1739
  const { t } = useI18n();
1509
1740
  const actionState = useAppSelector((state) => state.actions.keys["jumpToPosition" /* jumpToPosition */]);
1510
1741
  const positionsList = useAppSelector((state) => state.publication.positionsList);
@@ -1521,14 +1752,14 @@ var StatefulJumpToPositionTrigger = ({ variant }) => {
1521
1752
  {
1522
1753
  label: t("reader.actions.goToPosition.descriptive"),
1523
1754
  SVGIcon: pin_drop_default,
1524
- shortcut: preferences.actions.keys["jumpToPosition" /* jumpToPosition */].shortcut,
1755
+ shortcut: preferences.actionsKeys["jumpToPosition" /* jumpToPosition */].shortcut,
1525
1756
  id: "jumpToPosition" /* jumpToPosition */,
1526
1757
  onAction: () => setOpen(!actionState?.isOpen)
1527
1758
  }
1528
1759
  ) : /* @__PURE__ */ jsx(
1529
1760
  StatefulActionIcon,
1530
1761
  {
1531
- visibility: preferences.actions.keys["jumpToPosition" /* jumpToPosition */].visibility,
1762
+ visibility: preferences.actionsKeys["jumpToPosition" /* jumpToPosition */].visibility,
1532
1763
  "aria-label": t("reader.actions.goToPosition.descriptive"),
1533
1764
  placement: "bottom",
1534
1765
  tooltipLabel: t("reader.actions.goToPosition.compact"),
@@ -1562,6 +1793,9 @@ var thorium_web_reader_settings_default = {
1562
1793
  sliderTrack: "thorium_web_reader_settings_sliderTrack",
1563
1794
  sliderThumb: "thorium_web_reader_settings_sliderThumb",
1564
1795
  sliderPlaceholder: "thorium_web_reader_settings_sliderPlaceholder",
1796
+ sliderWithPresetsWrapper: "thorium_web_reader_settings_sliderWithPresetsWrapper",
1797
+ sliderWithPresetsRadioGroup: "thorium_web_reader_settings_sliderWithPresetsRadioGroup",
1798
+ sliderWithPresetsRadio: "thorium_web_reader_settings_sliderWithPresetsRadio",
1565
1799
  sliderWithTicks: "thorium_web_reader_settings_sliderWithTicks",
1566
1800
  dropdown: "thorium_web_reader_settings_dropdown",
1567
1801
  dropdownButton: "thorium_web_reader_settings_dropdownButton",
@@ -1571,6 +1805,82 @@ var thorium_web_reader_settings_default = {
1571
1805
  switch: "thorium_web_reader_settings_switch",
1572
1806
  switchIndicator: "thorium_web_reader_settings_switchIndicator"
1573
1807
  };
1808
+ var StatefulSettingsWrapper = ({
1809
+ triggerRef,
1810
+ heading,
1811
+ headerVariant,
1812
+ onClosePress,
1813
+ dismissEscapeKeyClose,
1814
+ resetFocus,
1815
+ onReset,
1816
+ children
1817
+ }) => {
1818
+ const actionState = useAppSelector((state) => state.actions.keys["settings" /* settings */]);
1819
+ const dispatch = useAppDispatch();
1820
+ const docking = useDocking("settings" /* settings */);
1821
+ const setOpen = (value) => {
1822
+ dispatch(setActionOpen({
1823
+ key: "settings" /* settings */,
1824
+ isOpen: value
1825
+ }));
1826
+ if (!value) dispatch(setHovering(false));
1827
+ };
1828
+ useEffect(() => {
1829
+ if (!actionState?.isOpen) onReset?.();
1830
+ }, [actionState?.isOpen, onReset]);
1831
+ return /* @__PURE__ */ jsx(
1832
+ StatefulSheetWrapper,
1833
+ {
1834
+ sheetType: docking.sheetType,
1835
+ sheetProps: {
1836
+ id: "settings" /* settings */,
1837
+ triggerRef,
1838
+ heading,
1839
+ headerVariant,
1840
+ className: thorium_web_reader_settings_default.wrapper,
1841
+ placement: "bottom",
1842
+ isOpen: actionState?.isOpen || false,
1843
+ onOpenChange: setOpen,
1844
+ onClosePress,
1845
+ docker: docking.getDocker(),
1846
+ resetFocus,
1847
+ scrollTopOnFocus: true,
1848
+ dismissEscapeKeyClose
1849
+ },
1850
+ children
1851
+ }
1852
+ );
1853
+ };
1854
+ var StatefulAudioSettingsContainer = ({
1855
+ triggerRef
1856
+ }) => {
1857
+ const { preferences } = useAudioPreferences();
1858
+ const audioSettingsKeys = preferences.settings.order;
1859
+ const { settingsComponentsMap } = usePlugins();
1860
+ const { t } = useI18n();
1861
+ const dispatch = useAppDispatch();
1862
+ const close = useCallback(() => {
1863
+ dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false }));
1864
+ dispatch(setHovering(false));
1865
+ }, [dispatch]);
1866
+ return /* @__PURE__ */ jsx(
1867
+ StatefulSettingsWrapper,
1868
+ {
1869
+ triggerRef,
1870
+ heading: t("reader.playback.preferences.audio.title"),
1871
+ headerVariant: "close" /* close */,
1872
+ onClosePress: close,
1873
+ children: audioSettingsKeys.length > 0 && settingsComponentsMap ? audioSettingsKeys.map((key) => {
1874
+ const match = settingsComponentsMap[key];
1875
+ if (!match) {
1876
+ console.warn(`Action key "${key}" not found in the plugin registry while present in preferences.`);
1877
+ return null;
1878
+ }
1879
+ return /* @__PURE__ */ jsx(match.Comp, { ...match.props }, key);
1880
+ }) : null
1881
+ }
1882
+ );
1883
+ };
1574
1884
  var StatefulGroupWrapper = ({
1575
1885
  label,
1576
1886
  moreLabel,
@@ -1592,18 +1902,18 @@ var StatefulGroupWrapper = ({
1592
1902
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
1593
1903
  ThSettingsWrapper,
1594
1904
  {
1595
- className: classNames3(thorium_web_reader_settings_default.group, thorium_web_reader_settings_default.advancedGroup),
1905
+ className: classNames4(thorium_web_reader_settings_default.group, thorium_web_reader_settings_default.advancedGroup),
1596
1906
  label,
1597
1907
  items: componentsMap,
1598
1908
  prefs: resolvedPrefs,
1599
1909
  compounds: {
1600
1910
  ...compounds?.heading ? { heading: compounds.heading } : {
1601
1911
  heading: {
1602
- className: classNames3(thorium_web_reader_settings_default.label, thorium_web_reader_settings_default.groupLabel)
1912
+ className: classNames4(thorium_web_reader_settings_default.label, thorium_web_reader_settings_default.groupLabel)
1603
1913
  }
1604
1914
  },
1605
1915
  button: {
1606
- className: classNames3(thorium_web_button_default.icon, thorium_web_reader_settings_default.advancedIcon),
1916
+ className: classNames4(thorium_web_button_default.icon, thorium_web_reader_settings_default.advancedIcon),
1607
1917
  "aria-label": moreLabel,
1608
1918
  isDisabled,
1609
1919
  compounds: {
@@ -1624,96 +1934,6 @@ var StatefulGroupWrapper = ({
1624
1934
  }
1625
1935
  ) });
1626
1936
  };
1627
-
1628
- // src/components/Plugins/PluginRegistry.ts
1629
- var pluginsStore = [];
1630
- var PluginRegistryClass = class {
1631
- register(plugin) {
1632
- const existingPluginIndex = pluginsStore.findIndex((p) => p.id === plugin.id);
1633
- if (existingPluginIndex >= 0) {
1634
- pluginsStore[existingPluginIndex] = plugin;
1635
- } else {
1636
- pluginsStore.push(plugin);
1637
- }
1638
- }
1639
- unregister(pluginId) {
1640
- const filteredPlugins = pluginsStore.filter((plugin) => plugin.id !== pluginId);
1641
- pluginsStore.length = 0;
1642
- pluginsStore.push(...filteredPlugins);
1643
- }
1644
- getPlugins() {
1645
- return [...pluginsStore];
1646
- }
1647
- getComponentMaps() {
1648
- const actionsComponentsMap = {};
1649
- const settingsComponentsMap = {};
1650
- [...pluginsStore].reverse().forEach((plugin) => {
1651
- if (plugin.components.actions) {
1652
- Object.entries(plugin.components.actions).forEach(([key, component]) => {
1653
- actionsComponentsMap[key] = component;
1654
- });
1655
- }
1656
- if (plugin.components.settings) {
1657
- Object.entries(plugin.components.settings).forEach(([key, component]) => {
1658
- settingsComponentsMap[key] = component;
1659
- });
1660
- }
1661
- });
1662
- return {
1663
- actionsComponentsMap,
1664
- settingsComponentsMap
1665
- };
1666
- }
1667
- };
1668
- var ThPluginRegistry = new PluginRegistryClass();
1669
- var ThPluginContext = createContext({
1670
- actionsComponentsMap: {},
1671
- settingsComponentsMap: {},
1672
- textSettingsComponentsMap: {},
1673
- spacingSettingsComponentsMap: {},
1674
- registerPlugin: ThPluginRegistry.register.bind(ThPluginRegistry),
1675
- unregisterPlugin: ThPluginRegistry.unregister.bind(ThPluginRegistry)
1676
- });
1677
- var usePlugins = () => useContext(ThPluginContext);
1678
- var ThPluginProvider = ({ children }) => {
1679
- const [componentMaps, setComponentMaps] = useState(() => {
1680
- const maps = ThPluginRegistry.getComponentMaps();
1681
- return {
1682
- ...maps,
1683
- textSettingsComponentsMap: getTypedSettingsComponents(maps.settingsComponentsMap, "text"),
1684
- spacingSettingsComponentsMap: getTypedSettingsComponents(maps.settingsComponentsMap, "spacing")
1685
- };
1686
- });
1687
- function getTypedSettingsComponents(componentsMap, type) {
1688
- return Object.entries(componentsMap).filter(([_, component]) => component.type === type).reduce((acc, [key, component]) => {
1689
- acc[key] = component;
1690
- return acc;
1691
- }, {});
1692
- }
1693
- useEffect(() => {
1694
- const updateComponentMaps = () => {
1695
- const maps = ThPluginRegistry.getComponentMaps();
1696
- setComponentMaps({
1697
- ...maps,
1698
- textSettingsComponentsMap: getTypedSettingsComponents(maps.settingsComponentsMap, "text"),
1699
- spacingSettingsComponentsMap: getTypedSettingsComponents(maps.settingsComponentsMap, "spacing")
1700
- });
1701
- };
1702
- updateComponentMaps();
1703
- }, []);
1704
- const registerPlugin = (plugin) => {
1705
- ThPluginRegistry.register(plugin);
1706
- };
1707
- const unregisterPlugin = (pluginId) => {
1708
- ThPluginRegistry.unregister(pluginId);
1709
- };
1710
- const value = {
1711
- ...componentMaps,
1712
- registerPlugin,
1713
- unregisterPlugin
1714
- };
1715
- return /* @__PURE__ */ jsx(ThPluginContext.Provider, { value, children });
1716
- };
1717
1937
  var StatefulSpacingGroup = () => {
1718
1938
  const { preferences } = usePreferences();
1719
1939
  const { t } = useI18n();
@@ -1788,7 +2008,7 @@ var StatefulTextGroupContainer = () => {
1788
2008
  return /* @__PURE__ */ jsx(match.Comp, { standalone: true }, key);
1789
2009
  }) });
1790
2010
  };
1791
- var StatefulSettingsContainer = ({
2011
+ var StatefulVisualSettingsContainer = ({
1792
2012
  triggerRef
1793
2013
  }) => {
1794
2014
  const {
@@ -1807,23 +2027,17 @@ var StatefulSettingsContainer = ({
1807
2027
  const isFXL = useAppSelector((state) => state.publication.isFXL);
1808
2028
  const hasDisplayTransformability = useAppSelector((state) => state.publication.hasDisplayTransformability);
1809
2029
  const contains = useAppSelector((state) => state.reader.settingsContainer);
1810
- const actionState = useAppSelector((state) => state.actions.keys["settings" /* settings */]);
1811
2030
  const dispatch = useAppDispatch();
1812
2031
  const settingItems = useMemo(() => {
1813
2032
  return profile === "webPub" ? webPubSettingsKeys : isFXL ? fxlSettingsKeys : reflowSettingsKeys;
1814
2033
  }, [profile, isFXL, fxlSettingsKeys, reflowSettingsKeys, webPubSettingsKeys]);
1815
- const docking = useDocking("settings" /* settings */);
1816
- const sheetType = docking.sheetType;
1817
- const setOpen = (value) => {
1818
- dispatch(setActionOpen({
1819
- key: "settings" /* settings */,
1820
- isOpen: value
1821
- }));
1822
- if (!value) dispatch(setHovering(false));
1823
- };
1824
2034
  const setInitial = useCallback(() => {
1825
2035
  dispatch(setSettingsContainer("initial" /* initial */));
1826
2036
  }, [dispatch]);
2037
+ const close = useCallback(() => {
2038
+ dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false }));
2039
+ dispatch(setHovering(false));
2040
+ }, [dispatch]);
1827
2041
  const isTextNested = useCallback((key) => {
1828
2042
  const textSettings = [
1829
2043
  mainTextSettingsKeys || defaultTextSettingsMain,
@@ -1909,40 +2123,30 @@ var StatefulSettingsContainer = ({
1909
2123
  document.removeEventListener("keydown", handleEscape, true);
1910
2124
  };
1911
2125
  }, [contains, dispatch]);
1912
- useEffect(() => {
1913
- if (!actionState?.isOpen) setInitial();
1914
- }, [actionState?.isOpen, setInitial]);
1915
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
1916
- StatefulSheetWrapper,
2126
+ return /* @__PURE__ */ jsx(
2127
+ StatefulSettingsWrapper,
1917
2128
  {
1918
- sheetType,
1919
- sheetProps: {
1920
- id: "settings" /* settings */,
1921
- triggerRef,
1922
- heading: getHeading(),
1923
- headerVariant: getHeaderVariant(),
1924
- className: thorium_web_reader_settings_default.wrapper,
1925
- placement: "bottom",
1926
- isOpen: actionState?.isOpen || false,
1927
- onOpenChange: setOpen,
1928
- onClosePress: () => {
1929
- contains === "initial" /* initial */ ? setOpen(false) : setInitial();
1930
- },
1931
- docker: docking.getDocker(),
1932
- resetFocus: contains,
1933
- scrollTopOnFocus: true,
1934
- dismissEscapeKeyClose: contains !== "initial" /* initial */
1935
- },
2129
+ triggerRef,
2130
+ heading: getHeading(),
2131
+ headerVariant: getHeaderVariant(),
2132
+ onClosePress: contains === "initial" /* initial */ ? close : setInitial,
2133
+ onReset: setInitial,
2134
+ dismissEscapeKeyClose: contains !== "initial" /* initial */,
2135
+ resetFocus: contains,
1936
2136
  children: renderSettings()
1937
2137
  }
1938
- ) });
2138
+ );
1939
2139
  };
1940
2140
  var SvgMatchCase = (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: "m131-252 165-440h79l165 440h-76l-39-112H247l-40 112h-76Zm139-176h131l-64-182h-4l-63 182Zm395 186q-51 0-81-27.5T554-342q0-44 34.5-72.5T677-443q23 0 45 4t38 11v-12q0-29-20.5-47T685-505q-23 0-42 9.5T610-468l-47-35q24-29 54.5-43t68.5-14q69 0 103 32.5t34 97.5v178h-63v-37h-4q-14 23-38 35t-53 12Zm12-54q35 0 59.5-24t24.5-56q-14-8-33.5-12.5T689-393q-32 0-50 14t-18 37q0 20 16 33t40 13Z" }) });
1941
2141
  var match_case_default = SvgMatchCase;
2142
+ var SvgInstantMix = (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: "M200-160v-280h-80v-80h240v80h-80v280h-80Zm0-440v-200h80v200h-80Zm160 0v-80h80v-120h80v120h80v80H360Zm80 440v-360h80v360h-80Zm240 0v-120h-80v-80h240v80h-80v120h-80Zm0-280v-360h80v360h-80Z" }) });
2143
+ var instant_mix_default = SvgInstantMix;
1942
2144
  var StatefulSettingsTrigger = ({ variant }) => {
1943
- const { preferences } = usePreferences();
2145
+ const preferences = useActionsPreferences();
1944
2146
  const { t } = useI18n();
1945
2147
  const actionState = useAppSelector((state) => state.actions.keys["settings" /* settings */]);
2148
+ const profile = useAppSelector((state) => state.reader.profile);
2149
+ const isAudio = profile === "audio";
1946
2150
  const dispatch = useAppDispatch();
1947
2151
  const setOpen = (value) => {
1948
2152
  dispatch(setActionOpen({
@@ -1954,21 +2158,21 @@ var StatefulSettingsTrigger = ({ variant }) => {
1954
2158
  return /* @__PURE__ */ jsx(Fragment, { children: variant && variant === "menuItem" /* menu */ ? /* @__PURE__ */ jsx(
1955
2159
  StatefulOverflowMenuItem,
1956
2160
  {
1957
- label: t("reader.preferences.title"),
1958
- SVGIcon: match_case_default,
1959
- shortcut: preferences.actions.keys["settings" /* settings */].shortcut,
2161
+ label: isAudio ? t("reader.playback.preferences.audio.title") : t("reader.preferences.title"),
2162
+ SVGIcon: isAudio ? instant_mix_default : match_case_default,
2163
+ shortcut: preferences.actionsKeys["settings" /* settings */].shortcut,
1960
2164
  id: "settings" /* settings */,
1961
2165
  onAction: () => setOpen(!actionState?.isOpen)
1962
2166
  }
1963
2167
  ) : /* @__PURE__ */ jsx(
1964
2168
  StatefulActionIcon,
1965
2169
  {
1966
- visibility: preferences.actions.keys["settings" /* settings */].visibility,
1967
- "aria-label": t("reader.preferences.title"),
2170
+ visibility: preferences.actionsKeys["settings" /* settings */].visibility,
2171
+ "aria-label": isAudio ? t("reader.playback.preferences.audio.title") : t("reader.preferences.title"),
1968
2172
  placement: "bottom",
1969
- tooltipLabel: t("reader.preferences.title"),
2173
+ tooltipLabel: isAudio ? t("reader.playback.preferences.audio.title") : t("reader.preferences.title"),
1970
2174
  onPress: () => setOpen(!actionState?.isOpen),
1971
- children: /* @__PURE__ */ jsx(match_case_default, { "aria-hidden": "true", focusable: "false" })
2175
+ children: isAudio ? /* @__PURE__ */ jsx(instant_mix_default, { "aria-hidden": "true", focusable: "false" }) : /* @__PURE__ */ jsx(match_case_default, { "aria-hidden": "true", focusable: "false" })
1972
2176
  }
1973
2177
  ) });
1974
2178
  };
@@ -1989,51 +2193,165 @@ var thorium_web_toc_default = {
1989
2193
  treeItemTextPosition: "thorium_web_toc_treeItemTextPosition",
1990
2194
  empty: "thorium_web_toc_empty"
1991
2195
  };
2196
+ function filterTocTree(items, query, contains) {
2197
+ if (!query) return items;
2198
+ const recursiveFilter = (items2) => items2.reduce((acc, item) => {
2199
+ if (item.title && contains(item.title, query)) acc.push({ ...item, children: void 0 });
2200
+ if (item.children) acc.push(...recursiveFilter(item.children));
2201
+ return acc;
2202
+ }, []);
2203
+ return recursiveFilter(items).map((item, i) => ({ ...item, key: `${item.id}-${i}` }));
2204
+ }
2205
+ function useTocContent({ isOpen, tocTree, tocEntry }) {
2206
+ const [expandedKeys, setExpandedKeys] = useState(/* @__PURE__ */ new Set());
2207
+ const [filterValue, setFilterValue] = useState("");
2208
+ const treeRef = useRef(null);
2209
+ const searchInputRef = useRef(null);
2210
+ const { contains } = useFilter({ sensitivity: "base" });
2211
+ useEffect(() => {
2212
+ if (!isOpen) setFilterValue("");
2213
+ }, [isOpen]);
2214
+ useEffect(() => {
2215
+ if (!isOpen || !filterValue) return;
2216
+ const handleEscape = (event) => {
2217
+ if (event.key === "Escape") {
2218
+ event.stopPropagation();
2219
+ setFilterValue("");
2220
+ }
2221
+ };
2222
+ document.addEventListener("keydown", handleEscape, true);
2223
+ return () => document.removeEventListener("keydown", handleEscape, true);
2224
+ }, [isOpen, filterValue]);
2225
+ useEffect(() => {
2226
+ if (!tocEntry || !tocTree) return;
2227
+ setExpandedKeys((prev) => {
2228
+ const next = new Set(prev);
2229
+ let changed = false;
2230
+ const expand = (items) => items.some((item) => {
2231
+ if (item.id === tocEntry) return true;
2232
+ if (item.children) {
2233
+ const found = expand(item.children);
2234
+ if (found && !next.has(item.id)) {
2235
+ next.add(item.id);
2236
+ changed = true;
2237
+ }
2238
+ return found;
2239
+ }
2240
+ return false;
2241
+ });
2242
+ expand(tocTree);
2243
+ return changed ? next : prev;
2244
+ });
2245
+ }, [tocEntry, tocTree]);
2246
+ const displayedTocTree = filterTocTree(tocTree || [], filterValue, contains);
2247
+ return { expandedKeys, setExpandedKeys, filterValue, setFilterValue, displayedTocTree, treeRef, searchInputRef };
2248
+ }
1992
2249
  var SvgChevronRight = (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: "M504-480 320-664l56-56 240 240-240 240-56-56 184-184Z" }) });
1993
2250
  var chevron_right_default = SvgChevronRight;
2251
+ var TocContent = ({
2252
+ filterValue,
2253
+ onFilterChange,
2254
+ displayedTocTree,
2255
+ tocTree,
2256
+ tocEntry,
2257
+ expandedKeys,
2258
+ onExpandedChange,
2259
+ onSelectionChange,
2260
+ isRTL = false,
2261
+ treeRef,
2262
+ searchInputRef,
2263
+ compounds
2264
+ }) => {
2265
+ const { t } = useI18n();
2266
+ if (!tocTree || tocTree.length === 0) {
2267
+ return /* @__PURE__ */ jsx("div", { className: thorium_web_toc_default.empty, children: t("reader.tableOfContents.emptyState.description") });
2268
+ }
2269
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2270
+ /* @__PURE__ */ jsx(
2271
+ ThFormSearchField,
2272
+ {
2273
+ "aria-label": t("common.actions.search"),
2274
+ value: filterValue,
2275
+ onChange: onFilterChange,
2276
+ onClear: () => onFilterChange(""),
2277
+ className: thorium_web_toc_default.search,
2278
+ compounds: {
2279
+ label: { className: thorium_web_toc_default.searchLabel },
2280
+ input: {
2281
+ ref: searchInputRef,
2282
+ className: thorium_web_toc_default.searchInput,
2283
+ placeholder: t("common.actions.search")
2284
+ },
2285
+ searchIcon: { className: thorium_web_toc_default.searchIcon, hidden: !!filterValue },
2286
+ clearButton: {
2287
+ className: thorium_web_toc_default.clearButton,
2288
+ isDisabled: !filterValue,
2289
+ "aria-label": t("common.actions.clear")
2290
+ }
2291
+ }
2292
+ }
2293
+ ),
2294
+ /* @__PURE__ */ jsx("div", { ...compounds?.treeWrapper, children: /* @__PURE__ */ jsx(
2295
+ Tree,
2296
+ {
2297
+ ref: treeRef,
2298
+ "aria-label": t("reader.toc.entries"),
2299
+ selectionMode: "single",
2300
+ items: displayedTocTree,
2301
+ className: [thorium_web_toc_default.tree, compounds?.tree?.className].filter(Boolean).join(" "),
2302
+ onSelectionChange,
2303
+ selectedKeys: tocEntry ? [tocEntry] : [],
2304
+ expandedKeys,
2305
+ onExpandedChange,
2306
+ children: function renderItem(item) {
2307
+ return /* @__PURE__ */ jsxs(
2308
+ TreeItem,
2309
+ {
2310
+ "data-href": item.href,
2311
+ className: thorium_web_toc_default.treeItem,
2312
+ textValue: item.title || "",
2313
+ children: [
2314
+ /* @__PURE__ */ jsxs(TreeItemContent, { children: [
2315
+ item.children && /* @__PURE__ */ jsx(
2316
+ Button,
2317
+ {
2318
+ slot: "chevron",
2319
+ className: thorium_web_toc_default.treeItemButton,
2320
+ style: { transform: isRTL ? "scaleX(-1)" : "none" },
2321
+ children: /* @__PURE__ */ jsx(chevron_right_default, { "aria-hidden": "true", focusable: "false" })
2322
+ }
2323
+ ),
2324
+ /* @__PURE__ */ jsxs("div", { className: thorium_web_toc_default.treeItemText, children: [
2325
+ /* @__PURE__ */ jsx("div", { className: thorium_web_toc_default.treeItemTextTitle, children: item.title }),
2326
+ item.position && /* @__PURE__ */ jsx("div", { className: thorium_web_toc_default.treeItemTextPosition, children: item.position })
2327
+ ] })
2328
+ ] }),
2329
+ /* @__PURE__ */ jsx(Collection, { items: item.children, children: renderItem })
2330
+ ]
2331
+ }
2332
+ );
2333
+ }
2334
+ }
2335
+ ) })
2336
+ ] });
2337
+ };
1994
2338
  var StatefulTocContainer = ({ triggerRef }) => {
1995
2339
  const { t } = useI18n();
1996
- const treeRef = useRef(null);
1997
- const [expandedKeys, setExpandedKeys] = useState(/* @__PURE__ */ new Set());
1998
2340
  const unstableTimeline = useAppSelector((state) => state.publication.unstableTimeline);
1999
- const tocEntry = unstableTimeline?.toc?.currentEntry;
2341
+ const tocEntry = unstableTimeline?.toc?.currentEntry ?? void 0;
2342
+ const tocEntryId = tocEntry?.id;
2000
2343
  const tocTree = unstableTimeline?.toc?.tree;
2001
2344
  const direction = useAppSelector((state) => state.reader.direction);
2002
2345
  const isRTL = direction === "rtl" /* rtl */;
2003
2346
  const actionState = useAppSelector((state) => state.actions.keys["toc" /* toc */]);
2004
2347
  const dispatch = useAppDispatch();
2005
- const { goLink } = useNavigator();
2348
+ const { goLink } = useNavigator().unified;
2006
2349
  const docking = useDocking("toc" /* toc */);
2007
2350
  const sheetType = docking.sheetType;
2008
- const { contains } = useFilter({ sensitivity: "base" });
2009
- const [filterValue, setFilterValue] = React18.useState("");
2010
- const searchInputRef = React18.useRef(null);
2011
- const filterTocTree = (items, filterValue2) => {
2012
- if (!filterValue2) {
2013
- return items;
2014
- }
2015
- const recursiveFilter = (items2) => {
2016
- return items2.reduce((acc, item) => {
2017
- if (item.title && contains(item.title, filterValue2)) {
2018
- acc.push({ ...item, children: void 0 });
2019
- }
2020
- if (item.children) {
2021
- acc.push(...recursiveFilter(item.children));
2022
- }
2023
- return acc;
2024
- }, []);
2025
- };
2026
- const result = recursiveFilter(items);
2027
- return result.map((item, index) => ({ ...item, key: `${item.id}-${index}` }));
2028
- };
2029
- const displayedTocTree = filterTocTree(tocTree || [], filterValue);
2030
2351
  const setOpen = useCallback((value) => {
2031
- if (!value) setFilterValue("");
2032
- dispatch(setActionOpen({
2033
- key: "toc" /* toc */,
2034
- isOpen: value
2035
- }));
2036
- }, [dispatch, setFilterValue]);
2352
+ dispatch(setActionOpen({ key: "toc" /* toc */, isOpen: value }));
2353
+ }, [dispatch]);
2354
+ const { expandedKeys, setExpandedKeys, filterValue, setFilterValue, displayedTocTree, treeRef, searchInputRef } = useTocContent({ isOpen: actionState?.isOpen ?? false, tocTree, tocEntry: tocEntryId });
2037
2355
  const handleAction = (keys) => {
2038
2356
  if (keys === "all" || !keys || keys.size === 0) return;
2039
2357
  const key = [...keys][0];
@@ -2041,12 +2359,13 @@ var StatefulTocContainer = ({ triggerRef }) => {
2041
2359
  const href = el?.getAttribute("data-href");
2042
2360
  if (!href) return;
2043
2361
  const link = new Link({ href });
2362
+ const matched = findTocItemById(tocTree || [], key);
2044
2363
  const cb = actionState?.isOpen && (sheetType === "docked start" /* dockedStart */ || sheetType === "docked end" /* dockedEnd */) ? () => {
2045
- dispatch(setTocEntry(key));
2364
+ dispatch(setTocEntry(matched || null));
2046
2365
  dispatch(setImmersive(true));
2047
2366
  dispatch(setUserNavigated(true));
2048
2367
  } : () => {
2049
- dispatch(setTocEntry(key));
2368
+ dispatch(setTocEntry(matched || null));
2050
2369
  dispatch(setImmersive(true));
2051
2370
  dispatch(setUserNavigated(true));
2052
2371
  setOpen(false);
@@ -2065,32 +2384,8 @@ var StatefulTocContainer = ({ triggerRef }) => {
2065
2384
  document.removeEventListener("keydown", handleEscape, true);
2066
2385
  };
2067
2386
  }
2068
- }, [actionState, setOpen, filterValue]);
2069
- useEffect(() => {
2070
- if (tocEntry && tocTree) {
2071
- setExpandedKeys((prevExpandedKeys) => {
2072
- const newExpandedKeys = new Set(prevExpandedKeys);
2073
- let hasUpdates = false;
2074
- const updateExpanded = (items) => {
2075
- return items.some((item) => {
2076
- if (item.id === tocEntry) return true;
2077
- if (item.children) {
2078
- const hasChild = updateExpanded(item.children);
2079
- if (hasChild && !newExpandedKeys.has(item.id)) {
2080
- newExpandedKeys.add(item.id);
2081
- hasUpdates = true;
2082
- }
2083
- return hasChild;
2084
- }
2085
- return false;
2086
- });
2087
- };
2088
- updateExpanded(tocTree);
2089
- return hasUpdates ? newExpandedKeys : prevExpandedKeys;
2090
- });
2091
- }
2092
- }, [tocEntry, tocTree]);
2093
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
2387
+ }, [actionState, setOpen, filterValue, searchInputRef]);
2388
+ return /* @__PURE__ */ jsx(
2094
2389
  StatefulSheetWrapper,
2095
2390
  {
2096
2391
  sheetType,
@@ -2104,89 +2399,32 @@ var StatefulTocContainer = ({ triggerRef }) => {
2104
2399
  onOpenChange: setOpen,
2105
2400
  onClosePress: () => setOpen(false),
2106
2401
  docker: docking.getDocker(),
2107
- resetFocus: tocEntry,
2402
+ resetFocus: tocEntryId,
2108
2403
  focusWithinRef: treeRef
2109
2404
  },
2110
- children: tocTree && tocTree.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
2111
- /* @__PURE__ */ jsx(
2112
- ThFormSearchField,
2113
- {
2114
- "aria-label": t("common.actions.search"),
2115
- value: filterValue,
2116
- onChange: setFilterValue,
2117
- onClear: () => setFilterValue(""),
2118
- className: thorium_web_toc_default.search,
2119
- compounds: {
2120
- label: {
2121
- className: thorium_web_toc_default.searchLabel
2122
- },
2123
- input: {
2124
- ref: searchInputRef,
2125
- className: thorium_web_toc_default.searchInput,
2126
- placeholder: t("common.actions.search")
2127
- },
2128
- searchIcon: {
2129
- className: thorium_web_toc_default.searchIcon,
2130
- hidden: !!filterValue
2131
- },
2132
- clearButton: {
2133
- className: thorium_web_toc_default.clearButton,
2134
- isDisabled: !filterValue,
2135
- "aria-label": t("common.actions.clear")
2136
- }
2137
- }
2138
- }
2139
- ),
2140
- /* @__PURE__ */ jsx(
2141
- Tree,
2142
- {
2143
- ref: treeRef,
2144
- "aria-label": t("reader.toc.entries"),
2145
- selectionMode: "single",
2146
- items: displayedTocTree,
2147
- className: thorium_web_toc_default.tree,
2148
- onSelectionChange: handleAction,
2149
- selectedKeys: tocEntry ? [tocEntry] : [],
2150
- expandedKeys,
2151
- onExpandedChange: setExpandedKeys,
2152
- children: function renderItem(item) {
2153
- return /* @__PURE__ */ jsxs(
2154
- TreeItem,
2155
- {
2156
- "data-href": item.href,
2157
- className: thorium_web_toc_default.treeItem,
2158
- textValue: item.title || "",
2159
- children: [
2160
- /* @__PURE__ */ jsxs(TreeItemContent, { children: [
2161
- item.children && /* @__PURE__ */ jsx(
2162
- Button,
2163
- {
2164
- slot: "chevron",
2165
- className: thorium_web_toc_default.treeItemButton,
2166
- style: { transform: isRTL ? "scaleX(-1)" : "none" },
2167
- children: /* @__PURE__ */ jsx(chevron_right_default, { "aria-hidden": "true", focusable: "false" })
2168
- }
2169
- ),
2170
- /* @__PURE__ */ jsxs("div", { className: thorium_web_toc_default.treeItemText, children: [
2171
- /* @__PURE__ */ jsx("div", { className: thorium_web_toc_default.treeItemTextTitle, children: item.title }),
2172
- item.position && /* @__PURE__ */ jsx("div", { className: thorium_web_toc_default.treeItemTextPosition, children: item.position })
2173
- ] })
2174
- ] }),
2175
- /* @__PURE__ */ jsx(Collection, { items: item.children, children: renderItem })
2176
- ]
2177
- }
2178
- );
2179
- }
2180
- }
2181
- )
2182
- ] }) : /* @__PURE__ */ jsx("div", { className: thorium_web_toc_default.empty, children: t("reader.tableOfContents.emptyState.description") })
2405
+ children: /* @__PURE__ */ jsx(
2406
+ TocContent,
2407
+ {
2408
+ filterValue,
2409
+ onFilterChange: setFilterValue,
2410
+ displayedTocTree,
2411
+ tocTree,
2412
+ tocEntry: tocEntryId,
2413
+ expandedKeys,
2414
+ onExpandedChange: setExpandedKeys,
2415
+ onSelectionChange: handleAction,
2416
+ isRTL,
2417
+ treeRef,
2418
+ searchInputRef
2419
+ }
2420
+ )
2183
2421
  }
2184
- ) });
2422
+ );
2185
2423
  };
2186
2424
  var SvgToc = (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: "M120-280v-80h560v80H120Zm0-160v-80h560v80H120Zm0-160v-80h560v80H120Zm680 320q-17 0-28.5-11.5T760-320q0-17 11.5-28.5T800-360q17 0 28.5 11.5T840-320q0 17-11.5 28.5T800-280Zm0-160q-17 0-28.5-11.5T760-480q0-17 11.5-28.5T800-520q17 0 28.5 11.5T840-480q0 17-11.5 28.5T800-440Zm0-160q-17 0-28.5-11.5T760-640q0-17 11.5-28.5T800-680q17 0 28.5 11.5T840-640q0 17-11.5 28.5T800-600Z" }) });
2187
2425
  var toc_default = SvgToc;
2188
2426
  var StatefulTocTrigger = ({ variant }) => {
2189
- const { preferences } = usePreferences();
2427
+ const preferences = useActionsPreferences();
2190
2428
  const { t } = useI18n();
2191
2429
  const actionState = useAppSelector((state) => state.actions.keys["toc" /* toc */]);
2192
2430
  const dispatch = useAppDispatch();
@@ -2201,14 +2439,14 @@ var StatefulTocTrigger = ({ variant }) => {
2201
2439
  {
2202
2440
  label: t("reader.tableOfContents.title"),
2203
2441
  SVGIcon: toc_default,
2204
- shortcut: preferences.actions.keys["toc" /* toc */].shortcut,
2442
+ shortcut: preferences.actionsKeys["toc" /* toc */].shortcut,
2205
2443
  id: "toc" /* toc */,
2206
2444
  onAction: () => setOpen(!actionState?.isOpen)
2207
2445
  }
2208
2446
  ) : /* @__PURE__ */ jsx(
2209
2447
  StatefulActionIcon,
2210
2448
  {
2211
- visibility: preferences.actions.keys["toc" /* toc */].visibility,
2449
+ visibility: preferences.actionsKeys["toc" /* toc */].visibility,
2212
2450
  "aria-label": t("reader.tableOfContents.title"),
2213
2451
  placement: "bottom",
2214
2452
  tooltipLabel: t("reader.tableOfContents.title"),
@@ -2250,7 +2488,7 @@ var useGridNavigation = ({
2250
2488
  onFocus
2251
2489
  }) => {
2252
2490
  const visibleColumns = useGridTemplate(containerRef, "columns");
2253
- const onKeyDown = React18.useCallback((e) => {
2491
+ const onKeyDown = React24.useCallback((e) => {
2254
2492
  const columns = visibleColumns || 1;
2255
2493
  if (columns <= 1 || !items.current?.length) return;
2256
2494
  const currentIdx = items.current.findIndex((val) => {
@@ -2466,7 +2704,7 @@ var StatefulDropdown = ({
2466
2704
  {
2467
2705
  ...props,
2468
2706
  ...standalone ? { label } : { "aria-label": label },
2469
- className: classNames3(
2707
+ className: classNames4(
2470
2708
  thorium_web_reader_settings_default.dropdown,
2471
2709
  standalone && thorium_web_reader_settings_default.group,
2472
2710
  className
@@ -2476,7 +2714,7 @@ var StatefulDropdown = ({
2476
2714
  className: thorium_web_reader_settings_default.label,
2477
2715
  ...compounds?.label || {}
2478
2716
  },
2479
- ...React18.isValidElement(compounds?.button) ? { button: compounds.button } : {
2717
+ ...React24.isValidElement(compounds?.button) ? { button: compounds.button } : {
2480
2718
  button: {
2481
2719
  className: thorium_web_reader_settings_default.dropdownButton,
2482
2720
  ...compounds?.button || {}
@@ -2487,7 +2725,7 @@ var StatefulDropdown = ({
2487
2725
  placement: "bottom",
2488
2726
  ...compounds?.popover || {}
2489
2727
  },
2490
- ...React18.isValidElement(compounds?.listbox) ? { listbox: compounds.listbox } : {
2728
+ ...React24.isValidElement(compounds?.listbox) ? { listbox: compounds.listbox } : {
2491
2729
  listbox: {
2492
2730
  className: thorium_web_reader_settings_default.dropdownListbox,
2493
2731
  ...compounds?.listbox || {}
@@ -2545,7 +2783,7 @@ var StatefulFontFamily = ({ standalone = true }) => {
2545
2783
  })
2546
2784
  ]);
2547
2785
  const dispatch = useAppDispatch();
2548
- const { getSetting, submitPreferences } = useNavigator();
2786
+ const { getSetting, submitPreferences } = useNavigator().visual;
2549
2787
  const updatePreference = useCallback(async (key) => {
2550
2788
  if (!key || key === fontFamily) return;
2551
2789
  const selectedOption = fontFamilyOptions.current.find((option) => option.id === key);
@@ -2614,7 +2852,7 @@ var UnstableStatefulFontWeight = ({ standalone = true }) => {
2614
2852
  const isWebPub = profile === "webPub";
2615
2853
  const fontWeight = useAppSelector((state) => isWebPub ? state.webPubSettings.fontWeight : state.settings.fontWeight) ?? 400;
2616
2854
  const dispatch = useAppDispatch();
2617
- const { getSetting, submitPreferences } = useNavigator();
2855
+ const { getSetting, submitPreferences } = useNavigator().visual;
2618
2856
  const items = [
2619
2857
  {
2620
2858
  id: "default",
@@ -2693,7 +2931,7 @@ var StatefulHyphens = ({ standalone = true }) => {
2693
2931
  const hyphens = useAppSelector((state) => isWebPub ? state.webPubSettings.hyphens : state.settings.hyphens) ?? false;
2694
2932
  const textAlign = useAppSelector((state) => isWebPub ? state.webPubSettings.textAlign : state.settings.textAlign) ?? "publisher" /* publisher */;
2695
2933
  const dispatch = useAppDispatch();
2696
- const { getSetting, submitPreferences } = useNavigator();
2934
+ const { getSetting, submitPreferences } = useNavigator().visual;
2697
2935
  const updatePreference = useCallback(async (value) => {
2698
2936
  await submitPreferences({ hyphens: value });
2699
2937
  const effectiveSetting = getSetting("hyphens");
@@ -2777,7 +3015,7 @@ var StatefulNumberField = ({
2777
3015
  className: thorium_web_reader_settings_default.numberField,
2778
3016
  compounds: {
2779
3017
  wrapper: {
2780
- className: classNames3(
3018
+ className: classNames4(
2781
3019
  thorium_web_reader_settings_default.numberFieldWrapper,
2782
3020
  standalone && thorium_web_reader_settings_default.group
2783
3021
  )
@@ -2795,7 +3033,7 @@ var StatefulNumberField = ({
2795
3033
  className: thorium_web_reader_settings_default.input
2796
3034
  },
2797
3035
  reset: {
2798
- className: classNames3(thorium_web_button_default.icon, thorium_web_reader_settings_default.resetButton),
3036
+ className: classNames4(thorium_web_button_default.icon, thorium_web_reader_settings_default.resetButton),
2799
3037
  compounds: {
2800
3038
  tooltipTrigger: {
2801
3039
  delay: preferences.theming.arrow.tooltipDelay,
@@ -2821,7 +3059,8 @@ var StatefulSlider = ({
2821
3059
  ...props
2822
3060
  }) => {
2823
3061
  const { t } = useI18n();
2824
- const { preferences } = usePreferences();
3062
+ const { theming } = useSharedPreferences();
3063
+ const tooltipDelay = theming.icon.tooltipDelay;
2825
3064
  const style = {
2826
3065
  ...displayTicks && props.range && props.step ? {
2827
3066
  "--th-slider-ticks": (() => {
@@ -2841,20 +3080,20 @@ var StatefulSlider = ({
2841
3080
  ...props,
2842
3081
  ...standalone ? { label } : { "aria-label": label },
2843
3082
  placeholder,
2844
- className: classNames3(
3083
+ className: classNames4(
2845
3084
  thorium_web_reader_settings_default.slider,
2846
3085
  displayTicks && thorium_web_reader_settings_default.sliderWithTicks
2847
3086
  ),
2848
3087
  style,
2849
3088
  compounds: {
2850
3089
  wrapper: {
2851
- className: classNames3(
3090
+ className: classNames4(
2852
3091
  thorium_web_reader_settings_default.sliderWrapper,
2853
3092
  standalone && thorium_web_reader_settings_default.group
2854
3093
  )
2855
3094
  },
2856
3095
  label: {
2857
- className: classNames3(thorium_web_reader_settings_default.label, thorium_web_reader_settings_default.sliderLabel)
3096
+ className: classNames4(thorium_web_reader_settings_default.label, thorium_web_reader_settings_default.sliderLabel)
2858
3097
  },
2859
3098
  output: {
2860
3099
  className: thorium_web_reader_settings_default.sliderOutput
@@ -2869,11 +3108,11 @@ var StatefulSlider = ({
2869
3108
  className: thorium_web_reader_settings_default.sliderThumb
2870
3109
  },
2871
3110
  reset: {
2872
- className: classNames3(thorium_web_button_default.icon, thorium_web_reader_settings_default.resetButton),
3111
+ className: classNames4(thorium_web_button_default.icon, thorium_web_reader_settings_default.resetButton),
2873
3112
  compounds: {
2874
3113
  tooltipTrigger: {
2875
- delay: preferences.theming.arrow.tooltipDelay,
2876
- closeDelay: preferences.theming.arrow.tooltipDelay
3114
+ delay: tooltipDelay,
3115
+ closeDelay: tooltipDelay
2877
3116
  },
2878
3117
  tooltip: {
2879
3118
  className: thorium_web_button_default.tooltip
@@ -3159,6 +3398,24 @@ var useSpacingPresets = () => {
3159
3398
  setPublisherStyles: setPublisherStylesAction
3160
3399
  };
3161
3400
  };
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
+ };
3162
3419
 
3163
3420
  // src/components/Settings/hooks/usePlaceholder.ts
3164
3421
  var usePlaceholder = (placeholder, range, format) => {
@@ -3200,14 +3457,16 @@ var usePlaceholder = (placeholder, range, format) => {
3200
3457
  var StatefulLetterSpacing = ({ standalone = true }) => {
3201
3458
  const { preferences } = usePreferences();
3202
3459
  const { t } = useI18n();
3460
+ const config = preferences.settings.keys["letterSpacing" /* letterSpacing */];
3461
+ const { getSetting, submitPreferences, preferencesEditor } = useNavigator().visual;
3462
+ const { range } = useEffectiveRange(config.range, preferencesEditor?.letterSpacing?.supportedRange);
3203
3463
  const letterSpacingRangeConfig = {
3204
- variant: preferences.settings.keys["letterSpacing" /* letterSpacing */].variant,
3205
- placeholder: preferences.settings.keys["letterSpacing" /* letterSpacing */].placeholder,
3206
- range: preferences.settings.keys["letterSpacing" /* letterSpacing */].range,
3207
- step: preferences.settings.keys["letterSpacing" /* letterSpacing */].step
3464
+ variant: config.variant,
3465
+ placeholder: config.placeholder,
3466
+ range,
3467
+ step: config.step
3208
3468
  };
3209
3469
  const placeholderText = usePlaceholder(letterSpacingRangeConfig.placeholder, letterSpacingRangeConfig.range, "percent");
3210
- const { getSetting, submitPreferences } = useNavigator();
3211
3470
  const { getEffectiveSpacingValue, setLetterSpacing: setLetterSpacing2, canBeReset } = useSpacingPresets();
3212
3471
  const letterSpacing = getEffectiveSpacingValue("letterSpacing" /* letterSpacing */);
3213
3472
  const updatePreference = useCallback(async (value) => {
@@ -3276,10 +3535,19 @@ var StatefulLineHeight = ({ standalone = true }) => {
3276
3535
  const profile = useAppSelector((state) => state.reader.profile);
3277
3536
  const isWebPub = profile === "webPub";
3278
3537
  const publisherStyles = useAppSelector((state) => isWebPub ? state.webPubSettings.publisherStyles : state.settings.publisherStyles) ?? true;
3279
- const { getSetting, submitPreferences } = useNavigator();
3538
+ const { getSetting, submitPreferences, preferencesEditor } = useNavigator().visual;
3280
3539
  const { getEffectiveSpacingValue, setLineHeight: setLineHeight2 } = useSpacingPresets();
3281
3540
  const lineHeight = getEffectiveSpacingValue("lineHeight" /* lineHeight */);
3282
3541
  const lineHeightOptions = useLineHeight();
3542
+ const lineHeightNumericValues = useMemo(
3543
+ () => Object.values(lineHeightOptions).filter((v) => v !== null),
3544
+ [lineHeightOptions]
3545
+ );
3546
+ const { presets: effectivePresets } = useEffectiveRange(
3547
+ [Math.min(...lineHeightNumericValues), Math.max(...lineHeightNumericValues)],
3548
+ preferencesEditor?.lineHeight?.supportedRange,
3549
+ lineHeightNumericValues
3550
+ );
3283
3551
  const items = useMemo(() => {
3284
3552
  const baseItems = [
3285
3553
  {
@@ -3300,7 +3568,10 @@ var StatefulLineHeight = ({ standalone = true }) => {
3300
3568
  label: t("reader.preferences.lineHeight.large"),
3301
3569
  value: "large" /* large */
3302
3570
  }
3303
- ];
3571
+ ].filter((item) => {
3572
+ const v = lineHeightOptions[item.id];
3573
+ return effectivePresets === void 0 || effectivePresets.includes(v);
3574
+ });
3304
3575
  if (preferences.settings.keys["lineHeight" /* lineHeight */].allowUnset !== false) {
3305
3576
  baseItems.unshift({
3306
3577
  id: "publisher" /* publisher */,
@@ -3310,7 +3581,7 @@ var StatefulLineHeight = ({ standalone = true }) => {
3310
3581
  });
3311
3582
  }
3312
3583
  return baseItems;
3313
- }, [preferences.settings.keys, t]);
3584
+ }, [preferences.settings.keys, lineHeightOptions, effectivePresets, t]);
3314
3585
  const updatePreference = useCallback(async (value) => {
3315
3586
  const computedValue = value === "publisher" /* publisher */ ? null : lineHeightOptions[value];
3316
3587
  await submitPreferences({
@@ -3335,14 +3606,16 @@ var StatefulLineHeight = ({ standalone = true }) => {
3335
3606
  var StatefulParagraphIndent = ({ standalone = true }) => {
3336
3607
  const { preferences } = usePreferences();
3337
3608
  const { t } = useI18n();
3609
+ const config = preferences.settings.keys["paragraphIndent" /* paragraphIndent */];
3610
+ const { getSetting, submitPreferences, preferencesEditor } = useNavigator().visual;
3611
+ const { range } = useEffectiveRange(config.range, preferencesEditor?.paragraphIndent?.supportedRange);
3338
3612
  const paragraphIndentRangeConfig = {
3339
- variant: preferences.settings.keys["paragraphIndent" /* paragraphIndent */].variant,
3340
- placeholder: preferences.settings.keys["paragraphIndent" /* paragraphIndent */].placeholder,
3341
- range: preferences.settings.keys["paragraphIndent" /* paragraphIndent */].range,
3342
- step: preferences.settings.keys["paragraphIndent" /* paragraphIndent */].step
3613
+ variant: config.variant,
3614
+ placeholder: config.placeholder,
3615
+ range,
3616
+ step: config.step
3343
3617
  };
3344
3618
  const placeholderText = usePlaceholder(paragraphIndentRangeConfig.placeholder, paragraphIndentRangeConfig.range, "multiplier");
3345
- const { getSetting, submitPreferences } = useNavigator();
3346
3619
  const { getEffectiveSpacingValue, setParagraphIndent: setParagraphIndent2, canBeReset } = useSpacingPresets();
3347
3620
  const paragraphIndent = getEffectiveSpacingValue("paragraphIndent" /* paragraphIndent */);
3348
3621
  const updatePreference = useCallback(async (value) => {
@@ -3399,14 +3672,16 @@ var StatefulParagraphIndent = ({ standalone = true }) => {
3399
3672
  var StatefulParagraphSpacing = ({ standalone = true }) => {
3400
3673
  const { preferences } = usePreferences();
3401
3674
  const { t } = useI18n();
3675
+ const config = preferences.settings.keys["paragraphSpacing" /* paragraphSpacing */];
3676
+ const { getSetting, submitPreferences, preferencesEditor } = useNavigator().visual;
3677
+ const { range } = useEffectiveRange(config.range, preferencesEditor?.paragraphSpacing?.supportedRange);
3402
3678
  const paragraphSpacingRangeConfig = {
3403
- variant: preferences.settings.keys["paragraphSpacing" /* paragraphSpacing */].variant,
3404
- placeholder: preferences.settings.keys["paragraphSpacing" /* paragraphSpacing */].placeholder,
3405
- range: preferences.settings.keys["paragraphSpacing" /* paragraphSpacing */].range,
3406
- step: preferences.settings.keys["paragraphSpacing" /* paragraphSpacing */].step
3679
+ variant: config.variant,
3680
+ placeholder: config.placeholder,
3681
+ range,
3682
+ step: config.step
3407
3683
  };
3408
3684
  const placeholderText = usePlaceholder(paragraphSpacingRangeConfig.placeholder, paragraphSpacingRangeConfig.range, "multiplier");
3409
- const { getSetting, submitPreferences } = useNavigator();
3410
3685
  const { getEffectiveSpacingValue, setParagraphSpacing: setParagraphSpacing2, canBeReset } = useSpacingPresets();
3411
3686
  const paragraphSpacing = getEffectiveSpacingValue("paragraphSpacing" /* paragraphSpacing */);
3412
3687
  const updatePreference = useCallback(async (value) => {
@@ -3470,7 +3745,7 @@ var StatefulPublisherStyles = ({ standalone = true }) => {
3470
3745
  const letterSpacing = getEffectiveSpacingValue("letterSpacing" /* letterSpacing */);
3471
3746
  const wordSpacing = getEffectiveSpacingValue("wordSpacing" /* wordSpacing */);
3472
3747
  const lineHeightOptions = useLineHeight();
3473
- const { submitPreferences } = useNavigator();
3748
+ const { submitPreferences } = useNavigator().visual;
3474
3749
  const updatePreference = useCallback(async (isSelected) => {
3475
3750
  const values = isSelected ? {
3476
3751
  lineHeight: null,
@@ -3530,7 +3805,7 @@ var StatefulSpacingPresets = ({ standalone }) => {
3530
3805
  const spacing = useAppSelector((state) => isWebPub ? state.webPubSettings.spacing : state.settings.spacing);
3531
3806
  const isFXL = useAppSelector((state) => state.publication.isFXL);
3532
3807
  const dispatch = useAppDispatch();
3533
- const { submitPreferences } = useNavigator();
3808
+ const { submitPreferences } = useNavigator().visual;
3534
3809
  const lineHeightOptions = useLineHeight();
3535
3810
  const { getPresetValues } = useSpacingPresets();
3536
3811
  const updatePreference = useCallback(async (value) => {
@@ -3611,7 +3886,7 @@ var StatefulTextAlign = ({ standalone = true }) => {
3611
3886
  const isRTL = useAppSelector((state) => state.publication.isRTL);
3612
3887
  const textAlign = useAppSelector((state) => isWebPub ? state.webPubSettings.textAlign : state.settings.textAlign) ?? "publisher" /* publisher */;
3613
3888
  const dispatch = useAppDispatch();
3614
- const { getSetting, submitPreferences } = useNavigator();
3889
+ const { getSetting, submitPreferences } = useNavigator().visual;
3615
3890
  const items = [
3616
3891
  {
3617
3892
  id: "publisher" /* publisher */,
@@ -3669,7 +3944,7 @@ var StatefulTextNormalize = ({ standalone = true }) => {
3669
3944
  const isWebPub = profile === "webPub";
3670
3945
  const textNormalization = useAppSelector((state) => isWebPub ? state.webPubSettings.textNormalization : state.settings.textNormalization) ?? false;
3671
3946
  const dispatch = useAppDispatch();
3672
- const { getSetting, submitPreferences } = useNavigator();
3947
+ const { getSetting, submitPreferences } = useNavigator().visual;
3673
3948
  const updatePreference = useCallback(async (value) => {
3674
3949
  await submitPreferences({ textNormalization: value });
3675
3950
  const effectiveSetting = getSetting("textNormalization");
@@ -3693,24 +3968,24 @@ var StatefulTextNormalize = ({ standalone = true }) => {
3693
3968
  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" }) });
3694
3969
  var check_default = SvgCheck;
3695
3970
  var StatefulTheme = () => {
3696
- const { fxlThemeKeys, reflowThemeKeys } = usePreferenceKeys();
3697
- const { preferences } = usePreferences();
3971
+ const profile = useAppSelector((state) => state.reader.profile);
3972
+ const { theming } = useSharedPreferences();
3973
+ const { systemThemes, keys: themeKeys, audioOrder: audioThemeOrder, reflowOrder: reflowThemeOrder, fxlOrder: fxlThemeOrder } = theming.themes;
3698
3974
  const { t } = useI18n();
3699
3975
  const radioGroupRef = useRef(null);
3700
3976
  const radioGroupWrapperRef = useRef(null);
3701
3977
  const isFXL = useAppSelector((state) => state.publication.isFXL);
3702
3978
  const direction = useAppSelector((state) => state.reader.direction);
3703
3979
  const isRTL = direction === "rtl" /* rtl */;
3704
- const themeArray = isFXL ? fxlThemeKeys : reflowThemeKeys;
3980
+ const themeArray = profile === "audio" ? audioThemeOrder ?? [] : isFXL ? fxlThemeOrder ?? [] : reflowThemeOrder ?? [];
3705
3981
  const themeObject = useAppSelector((state) => state.theming.theme);
3706
- const theme = isFXL ? themeObject.fxl : themeObject.reflow;
3982
+ const theme = profile === "audio" ? themeObject.audio : isFXL ? themeObject.fxl : themeObject.reflow;
3707
3983
  const colorScheme = useAppSelector((state) => state.theming.colorScheme);
3984
+ const coverTheme = useAppSelector((state) => state.theming.coverTheme);
3708
3985
  const themeItems = useRef(
3709
3986
  themeArray.filter((theme2) => {
3710
3987
  if (theme2 === "auto") {
3711
- return preferences.theming.themes.systemThemes !== void 0 && Object.values(preferences.theming.themes.systemThemes).every(
3712
- (t2) => themeArray.includes(t2)
3713
- );
3988
+ return systemThemes !== void 0 && Object.values(systemThemes).every((t2) => themeArray.includes(t2));
3714
3989
  }
3715
3990
  return true;
3716
3991
  })
@@ -3735,38 +4010,43 @@ var StatefulTheme = () => {
3735
4010
  const updatePreference = useCallback(async (value) => {
3736
4011
  const themeProps = buildThemeObject({
3737
4012
  theme: value,
3738
- themeKeys: preferences.theming.themes.keys,
3739
- systemThemes: preferences.theming.themes.systemThemes,
4013
+ themeKeys: themeKeys ?? {},
4014
+ systemThemes,
3740
4015
  colorScheme
3741
4016
  });
3742
4017
  await submitPreferences(themeProps);
3743
4018
  dispatch(setTheme({
3744
- key: isFXL ? "fxl" : "reflow",
4019
+ key: profile === "audio" ? "audio" : isFXL ? "fxl" : "reflow",
3745
4020
  value
3746
4021
  }));
3747
- }, [isFXL, preferences.theming.themes.keys, preferences.theming.themes.systemThemes, submitPreferences, dispatch, colorScheme]);
4022
+ }, [isFXL, themeKeys, systemThemes, submitPreferences, dispatch, colorScheme, profile]);
3748
4023
  const doStyles = useCallback((t2) => {
3749
4024
  let cssProps = {
3750
4025
  boxSizing: "border-box",
3751
4026
  color: "#999999"
3752
4027
  };
3753
4028
  if (t2 === "auto") {
3754
- if (preferences.theming.themes.systemThemes !== void 0) {
3755
- cssProps.background = isRTL ? `linear-gradient(148deg, ${preferences.theming.themes.keys[preferences.theming.themes.systemThemes.dark].background} 48%, ${preferences.theming.themes.keys[preferences.theming.themes.systemThemes.light].background} 100%)` : `linear-gradient(148deg, ${preferences.theming.themes.keys[preferences.theming.themes.systemThemes.light].background} 0%, ${preferences.theming.themes.keys[preferences.theming.themes.systemThemes.dark].background} 48%)`;
4029
+ if (profile === "audio" && coverTheme) {
4030
+ cssProps.background = coverTheme.background;
4031
+ cssProps.color = coverTheme.text;
4032
+ cssProps.border = `1px solid ${coverTheme.subdue}`;
4033
+ } else if (systemThemes !== void 0) {
4034
+ cssProps.background = isRTL ? `linear-gradient(148deg, ${themeKeys[systemThemes.dark].background} 48%, ${themeKeys[systemThemes.light].background} 100%)` : `linear-gradient(148deg, ${themeKeys[systemThemes.light].background} 0%, ${themeKeys[systemThemes.dark].background} 48%)`;
3756
4035
  cssProps.color = "#ffffff";
3757
- cssProps.border = `1px solid ${preferences.theming.themes.keys[preferences.theming.themes.systemThemes.light].subdue}`;
4036
+ cssProps.border = `1px solid ${themeKeys[systemThemes.light].subdue}`;
3758
4037
  } else {
3759
4038
  cssProps.display = "none";
3760
4039
  }
3761
4040
  } else {
3762
- const themeKey = t2;
3763
- const theme2 = preferences.theming.themes.keys[themeKey];
3764
- cssProps.background = theme2.background;
3765
- cssProps.color = theme2.text;
3766
- cssProps.border = `1px solid ${theme2.subdue}`;
4041
+ const theme2 = themeKeys[t2];
4042
+ if (theme2) {
4043
+ cssProps.background = theme2.background;
4044
+ cssProps.color = theme2.text;
4045
+ cssProps.border = `1px solid ${theme2.subdue}`;
4046
+ }
3767
4047
  }
3768
4048
  return cssProps;
3769
- }, [preferences, isRTL]);
4049
+ }, [themeKeys, systemThemes, isRTL, profile, coverTheme]);
3770
4050
  useEffect(() => {
3771
4051
  if (theme === "auto" && !themeItems.current.includes(theme)) {
3772
4052
  updatePreference(themeItems.current[0]);
@@ -3785,12 +4065,12 @@ var StatefulTheme = () => {
3785
4065
  "div",
3786
4066
  {
3787
4067
  ref: radioGroupWrapperRef,
3788
- className: classNames3(thorium_web_reader_settings_default.radioWrapper, thorium_web_reader_settings_default.themesWrapper),
4068
+ className: classNames4(thorium_web_reader_settings_default.radioWrapper, thorium_web_reader_settings_default.themesWrapper),
3789
4069
  children: themeItems.current.map(
3790
4070
  (themeItem) => /* @__PURE__ */ jsx(
3791
4071
  Radio,
3792
4072
  {
3793
- className: classNames3(
4073
+ className: classNames4(
3794
4074
  thorium_web_reader_settings_default.radio,
3795
4075
  thorium_web_reader_settings_default.themeRadio
3796
4076
  ),
@@ -3814,14 +4094,16 @@ var StatefulTheme = () => {
3814
4094
  var StatefulWordSpacing = ({ standalone = true }) => {
3815
4095
  const { preferences } = usePreferences();
3816
4096
  const { t } = useI18n();
4097
+ const config = preferences.settings.keys["wordSpacing" /* wordSpacing */];
4098
+ const { getSetting, submitPreferences, preferencesEditor } = useNavigator().visual;
4099
+ const { range } = useEffectiveRange(config.range, preferencesEditor?.wordSpacing?.supportedRange);
3817
4100
  const wordSpacingRangeConfig = {
3818
- variant: preferences.settings.keys["wordSpacing" /* wordSpacing */].variant,
3819
- placeholder: preferences.settings.keys["wordSpacing" /* wordSpacing */].placeholder,
3820
- range: preferences.settings.keys["wordSpacing" /* wordSpacing */].range,
3821
- step: preferences.settings.keys["wordSpacing" /* wordSpacing */].step
4101
+ variant: config.variant,
4102
+ placeholder: config.placeholder,
4103
+ range,
4104
+ step: config.step
3822
4105
  };
3823
4106
  const placeholderText = usePlaceholder(wordSpacingRangeConfig.placeholder, wordSpacingRangeConfig.range, "percent");
3824
- const { getSetting, submitPreferences } = useNavigator();
3825
4107
  const { getEffectiveSpacingValue, setWordSpacing: setWordSpacing2, canBeReset } = useSpacingPresets();
3826
4108
  const wordSpacing = getEffectiveSpacingValue("wordSpacing" /* wordSpacing */);
3827
4109
  const updatePreference = useCallback(async (value) => {
@@ -3888,7 +4170,7 @@ var StatefulZoom = () => {
3888
4170
  getSetting,
3889
4171
  submitPreferences,
3890
4172
  preferencesEditor
3891
- } = useNavigator();
4173
+ } = useNavigator().visual;
3892
4174
  const preferenceEditorProperty = readerProfile === "webPub" ? preferencesEditor?.zoom : isFXL ? preferencesEditor?.zoom : preferencesEditor?.fontSize;
3893
4175
  const updatePreference = useCallback(async (value) => {
3894
4176
  if (readerProfile === "webPub") {
@@ -3899,23 +4181,13 @@ var StatefulZoom = () => {
3899
4181
  dispatch(setFontSize(getSetting("fontSize")));
3900
4182
  }
3901
4183
  }, [readerProfile, submitPreferences, getSetting, dispatch]);
3902
- const getEffectiveRange = (preferred, supportedRange) => {
3903
- if (!supportedRange) {
3904
- return preferred;
3905
- }
3906
- if (preferred && isRangeWithinSupportedRange(preferred, supportedRange)) {
3907
- return preferred;
3908
- }
3909
- return supportedRange;
3910
- };
3911
- const isRangeWithinSupportedRange = (range, supportedRange) => {
3912
- return Math.min(range[0], range[1]) >= Math.min(supportedRange[0], supportedRange[1]) && Math.max(range[0], range[1]) <= Math.max(supportedRange[0], supportedRange[1]);
3913
- };
4184
+ const zoomConfig = preferences.settings.keys["zoom" /* zoom */];
4185
+ const { range: effectiveRange } = useEffectiveRange(zoomConfig.range, preferenceEditorProperty?.supportedRange);
3914
4186
  const zoomRangeConfig = {
3915
- variant: preferences.settings.keys["zoom" /* zoom */].variant,
3916
- placeholder: preferences.settings.keys["zoom" /* zoom */].placeholder,
3917
- range: preferenceEditorProperty?.supportedRange ? getEffectiveRange(preferences.settings.keys["zoom" /* zoom */].range, preferenceEditorProperty.supportedRange) : preferences.settings.keys["zoom" /* zoom */].range,
3918
- step: preferences.settings.keys["zoom" /* zoom */].step
4187
+ variant: zoomConfig.variant,
4188
+ placeholder: zoomConfig.placeholder,
4189
+ range: effectiveRange,
4190
+ step: zoomConfig.step
3919
4191
  };
3920
4192
  const placeholderText = usePlaceholder(zoomRangeConfig.placeholder, zoomRangeConfig.range);
3921
4193
  return /* @__PURE__ */ jsx(Fragment, { children: zoomRangeConfig.variant === "numberField" /* numberField */ ? /* @__PURE__ */ jsx(
@@ -3962,7 +4234,7 @@ var createDefaultPlugin = () => {
3962
4234
  id: "core",
3963
4235
  name: "Core Components",
3964
4236
  description: "Default components for Thorium Web Epub StatefulReader",
3965
- version: "1.2.0",
4237
+ version: "1.3.0",
3966
4238
  components: {
3967
4239
  actions: {
3968
4240
  ["fullscreen" /* fullscreen */]: {
@@ -3974,7 +4246,7 @@ var createDefaultPlugin = () => {
3974
4246
  },
3975
4247
  ["settings" /* settings */]: {
3976
4248
  Trigger: StatefulSettingsTrigger,
3977
- Target: StatefulSettingsContainer
4249
+ Target: StatefulVisualSettingsContainer
3978
4250
  },
3979
4251
  ["toc" /* toc */]: {
3980
4252
  Trigger: StatefulTocTrigger,
@@ -4052,136 +4324,1274 @@ var createDefaultPlugin = () => {
4052
4324
  }
4053
4325
  };
4054
4326
  };
4055
- var StatefulPreferencesProvider = ({
4056
- children,
4057
- initialPreferences = defaultPreferences
4327
+ var StatefulSliderWithPresets = ({
4328
+ standalone,
4329
+ label,
4330
+ placeholder,
4331
+ displayTicks = false,
4332
+ hideOutput = false,
4333
+ value,
4334
+ resetLabel,
4335
+ presets,
4336
+ formatValue,
4337
+ onEscape,
4338
+ ...props
4058
4339
  }) => {
4059
- const store = useStore();
4060
- const adapter = useMemo(() => {
4061
- return new ThReduxPreferencesAdapter(store, initialPreferences);
4062
- }, [store, initialPreferences]);
4063
- return /* @__PURE__ */ jsx(ThPreferencesProvider, { adapter, children });
4064
- };
4065
-
4066
- // src/hooks/useReaderTransitions.ts
4067
- var useReaderTransitions = () => {
4068
- const isImmersive = useAppSelector((state) => state.reader.isImmersive);
4069
- const isFullscreen = useAppSelector((state) => state.reader.isFullscreen);
4070
- const hasUserNavigated = useAppSelector((state) => state.reader.hasUserNavigated);
4071
- const scroll = useAppSelector((state) => state.settings.scroll);
4072
- const isFXL = useAppSelector((state) => state.publication.isFXL);
4073
- const isScroll = scroll && !isFXL;
4074
- const wasImmersive = usePrevious(isImmersive) ?? false;
4075
- const wasFullscreen = usePrevious(isFullscreen) ?? false;
4076
- const wasScroll = usePrevious(isScroll) ?? false;
4077
- const wasUserNavigated = usePrevious(hasUserNavigated) ?? false;
4078
- const fromImmersive = wasImmersive && !isImmersive;
4079
- const toImmersive = !wasImmersive && isImmersive;
4080
- const fromFullscreen = wasFullscreen && !isFullscreen;
4081
- const toFullscreen = !wasFullscreen && isFullscreen;
4082
- const fromScroll = wasScroll && !isScroll;
4083
- const toScroll = !wasScroll && isScroll;
4084
- const fromUserNavigation = wasUserNavigated && !hasUserNavigated;
4085
- const toUserNavigation = !wasUserNavigated && hasUserNavigated;
4086
- return {
4087
- // Current states
4088
- isImmersive,
4089
- isFullscreen,
4090
- isScroll,
4091
- hasUserNavigated,
4092
- // Previous states
4093
- wasImmersive,
4094
- wasFullscreen,
4095
- wasScroll,
4096
- wasUserNavigated,
4097
- // State transitions
4098
- fromImmersive,
4099
- toImmersive,
4100
- fromFullscreen,
4101
- toFullscreen,
4102
- fromScroll,
4103
- toScroll,
4104
- fromUserNavigation,
4105
- toUserNavigation
4340
+ const { t } = useI18n();
4341
+ const { theming, direction } = useSharedPreferences();
4342
+ const numberFormatter = useNumberFormatter(props.formatOptions);
4343
+ const resolvedFormatValue = formatValue ?? (props.formatOptions ? (v) => numberFormatter.format(v) : void 0);
4344
+ const tooltipDelay = theming.icon.tooltipDelay;
4345
+ const presetsListRef = useRef(null);
4346
+ const presetsRef = useRef(presets);
4347
+ presetsRef.current = presets;
4348
+ const currentScalarValue = Array.isArray(value) ? value[0] : value;
4349
+ const { onKeyDown } = useGridNavigation({
4350
+ containerRef: presetsListRef,
4351
+ items: presetsRef,
4352
+ currentValue: currentScalarValue,
4353
+ onChange: (v) => props.onChange?.([v]),
4354
+ isRTL: direction === "rtl" /* rtl */,
4355
+ onEscape,
4356
+ onFocus: (v) => {
4357
+ const el = presetsListRef.current?.querySelector(`input[value="${v}"]`);
4358
+ el?.focus();
4359
+ }
4360
+ });
4361
+ const style = {
4362
+ ...displayTicks && props.range && props.step ? {
4363
+ "--th-slider-ticks": (() => {
4364
+ const [min, max] = [Math.min(...props.range), Math.max(...props.range)];
4365
+ const step = props.step || 1;
4366
+ const range = max - min;
4367
+ const totalIntervals = range / step;
4368
+ return Math.ceil(totalIntervals);
4369
+ })()
4370
+ } : {},
4371
+ ...props.style
4106
4372
  };
4107
- };
4108
-
4109
- // src/helpers/deserializePositions.ts
4110
- var deserializePositions = (positionsList) => {
4111
- return positionsList?.map((locator) => ({
4112
- href: locator.href,
4113
- type: locator.type,
4114
- locations: {
4115
- position: locator.locations.position,
4116
- progression: locator.locations.progression,
4117
- totalProgression: locator.locations.totalProgression
4373
+ return /* @__PURE__ */ jsx(
4374
+ ThSliderWithPresets,
4375
+ {
4376
+ presets,
4377
+ formatValue: resolvedFormatValue,
4378
+ value,
4379
+ ...props,
4380
+ ...standalone ? { label } : { "aria-label": label },
4381
+ placeholder,
4382
+ className: classNames4(
4383
+ thorium_web_reader_settings_default.slider,
4384
+ displayTicks && thorium_web_reader_settings_default.sliderWithTicks
4385
+ ),
4386
+ style,
4387
+ compounds: {
4388
+ wrapper: {
4389
+ className: classNames4(
4390
+ thorium_web_reader_settings_default.sliderWithPresetsWrapper,
4391
+ standalone && thorium_web_reader_settings_default.group
4392
+ )
4393
+ },
4394
+ slider: {
4395
+ wrapper: {
4396
+ className: thorium_web_reader_settings_default.sliderWrapper
4397
+ },
4398
+ label: {
4399
+ className: classNames4(thorium_web_reader_settings_default.label, thorium_web_reader_settings_default.sliderLabel)
4400
+ },
4401
+ output: {
4402
+ className: thorium_web_reader_settings_default.sliderOutput,
4403
+ ...hideOutput && { style: () => ({ display: "none" }) }
4404
+ },
4405
+ placeholder: {
4406
+ className: thorium_web_reader_settings_default.sliderPlaceholder
4407
+ },
4408
+ track: {
4409
+ className: thorium_web_reader_settings_default.sliderTrack
4410
+ },
4411
+ thumb: {
4412
+ className: thorium_web_reader_settings_default.sliderThumb
4413
+ },
4414
+ reset: {
4415
+ className: classNames4(thorium_web_button_default.icon, thorium_web_reader_settings_default.resetButton),
4416
+ compounds: {
4417
+ tooltipTrigger: {
4418
+ delay: tooltipDelay,
4419
+ closeDelay: tooltipDelay
4420
+ },
4421
+ tooltip: {
4422
+ className: thorium_web_button_default.tooltip
4423
+ },
4424
+ label: resetLabel ?? t("common.actions.reset")
4425
+ }
4426
+ }
4427
+ },
4428
+ presetsList: {
4429
+ "aria-label": label
4430
+ },
4431
+ presetsWrapper: {
4432
+ ref: presetsListRef,
4433
+ className: thorium_web_reader_settings_default.sliderWithPresetsRadioGroup
4434
+ },
4435
+ preset: {
4436
+ className: thorium_web_reader_settings_default.sliderWithPresetsRadio,
4437
+ onKeyDown
4438
+ }
4439
+ }
4118
4440
  }
4441
+ );
4442
+ };
4443
+ var StatefulPresetsGroup = ({
4444
+ presets,
4445
+ value,
4446
+ formatOptions,
4447
+ formatValue,
4448
+ onChange,
4449
+ ...props
4450
+ }) => {
4451
+ const numberFormatter = useNumberFormatter(formatOptions);
4452
+ const resolvedFormatValue = formatValue ?? (formatOptions ? (v) => numberFormatter.format(v) : String);
4453
+ const items = presets.map((p) => ({
4454
+ id: String(p),
4455
+ value: String(p),
4456
+ label: resolvedFormatValue(p)
4119
4457
  }));
4458
+ const radioValue = value !== void 0 && presets.includes(value) ? String(value) : "";
4459
+ const handleChange = (v) => {
4460
+ onChange?.(parseFloat(v));
4461
+ };
4462
+ return /* @__PURE__ */ jsx(
4463
+ StatefulRadioGroup,
4464
+ {
4465
+ ...props,
4466
+ items,
4467
+ value: radioValue,
4468
+ onChange: handleChange
4469
+ }
4470
+ );
4120
4471
  };
4121
-
4122
- // src/hooks/usePublication.ts
4123
- var detectProfile = (manifest) => {
4124
- const metadata = manifest.metadata;
4125
- if (!metadata) return "webPub";
4126
- const conformsTo = metadata.conformsTo;
4127
- if (!conformsTo) return "webPub";
4128
- const profiles = Array.isArray(conformsTo) ? conformsTo : [conformsTo];
4129
- if (profiles.some(
4130
- (profile) => profile === Profile.AUDIOBOOK
4131
- )) {
4132
- return "audio";
4472
+ var StatefulAudioSkipBackwardInterval = ({
4473
+ standalone = true
4474
+ }) => {
4475
+ const { t } = useI18n();
4476
+ const { preferences } = useAudioPreferences();
4477
+ const skipBackwardInterval = useAppSelector((state) => state.audioSettings.skipBackwardInterval);
4478
+ const dispatch = useAppDispatch();
4479
+ const { submitPreferences, getSetting, preferencesEditor } = useNavigator().media;
4480
+ const config = preferences.settings.keys["skipBackwardInterval" /* skipBackwardInterval */] ?? defaultAudioSkipBackwardInterval;
4481
+ const { range, presets } = useEffectiveRange(config.range, preferencesEditor?.skipBackwardInterval?.supportedRange, config.presets);
4482
+ const skipBackwardIntervalRangeConfig = {
4483
+ variant: config.variant,
4484
+ placeholder: config.placeholder,
4485
+ range,
4486
+ step: config.step
4487
+ };
4488
+ const placeholderText = usePlaceholder(skipBackwardIntervalRangeConfig.placeholder, skipBackwardIntervalRangeConfig.range);
4489
+ const updatePreference = useCallback(async (value) => {
4490
+ const val = Array.isArray(value) ? value[0] : value;
4491
+ await submitPreferences({ skipBackwardInterval: val });
4492
+ const effectiveSkipBackwardInterval = getSetting("skipBackwardInterval");
4493
+ dispatch(setSkipBackwardInterval(effectiveSkipBackwardInterval));
4494
+ }, [submitPreferences, getSetting, dispatch]);
4495
+ if (skipBackwardIntervalRangeConfig.variant === "numberField" /* numberField */) {
4496
+ return /* @__PURE__ */ jsx(
4497
+ StatefulNumberField,
4498
+ {
4499
+ standalone,
4500
+ label: t("reader.playback.preferences.audio.skipBackwardInterval"),
4501
+ placeholder: placeholderText,
4502
+ defaultValue: void 0,
4503
+ value: skipBackwardInterval ?? void 0,
4504
+ onChange: updatePreference,
4505
+ onReset: void 0,
4506
+ range: skipBackwardIntervalRangeConfig.range || [1, 60],
4507
+ step: skipBackwardIntervalRangeConfig.step,
4508
+ steppers: {
4509
+ decrementLabel: t("common.actions.decrease"),
4510
+ incrementLabel: t("common.actions.increase")
4511
+ },
4512
+ formatOptions: { style: "unit", unit: "second" },
4513
+ isWheelDisabled: true,
4514
+ isVirtualKeyboardDisabled: true
4515
+ }
4516
+ );
4133
4517
  }
4134
- if (profiles.some(
4135
- (profile) => profile === Profile.EPUB
4136
- )) {
4137
- return "epub";
4518
+ if (skipBackwardIntervalRangeConfig.variant === "presetsGroup" /* presetsGroup */) {
4519
+ return /* @__PURE__ */ jsx(
4520
+ StatefulPresetsGroup,
4521
+ {
4522
+ standalone,
4523
+ label: t("reader.playback.preferences.audio.skipBackwardInterval"),
4524
+ presets: presets || [],
4525
+ formatOptions: { style: "unit", unit: "second" },
4526
+ onEscape: () => dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false })),
4527
+ value: skipBackwardInterval ?? void 0,
4528
+ onChange: (v) => updatePreference(v)
4529
+ }
4530
+ );
4138
4531
  }
4139
- return "webPub";
4532
+ if (skipBackwardIntervalRangeConfig.variant === "sliderWithPresets" /* sliderWithPresets */) {
4533
+ return /* @__PURE__ */ jsx(
4534
+ StatefulSliderWithPresets,
4535
+ {
4536
+ standalone,
4537
+ label: t("reader.playback.preferences.audio.skipBackwardInterval"),
4538
+ placeholder: placeholderText,
4539
+ presets: presets || [],
4540
+ formatOptions: { style: "unit", unit: "second" },
4541
+ onEscape: () => dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false })),
4542
+ value: skipBackwardInterval ?? void 0,
4543
+ onChange: updatePreference,
4544
+ range: skipBackwardIntervalRangeConfig.range,
4545
+ step: skipBackwardIntervalRangeConfig.step
4546
+ }
4547
+ );
4548
+ }
4549
+ return /* @__PURE__ */ jsx(
4550
+ StatefulSlider,
4551
+ {
4552
+ standalone,
4553
+ displayTicks: skipBackwardIntervalRangeConfig.variant === "incrementedSlider" /* incrementedSlider */,
4554
+ label: t("reader.playback.preferences.audio.skipBackwardInterval"),
4555
+ placeholder: placeholderText,
4556
+ defaultValue: void 0,
4557
+ value: skipBackwardInterval ?? void 0,
4558
+ onChange: updatePreference,
4559
+ range: skipBackwardIntervalRangeConfig.range,
4560
+ step: skipBackwardIntervalRangeConfig.step,
4561
+ formatOptions: { style: "unit", unit: "second" }
4562
+ }
4563
+ );
4140
4564
  };
4141
- var usePublication = ({
4142
- url,
4143
- onError = () => {
4144
- },
4145
- fetcher: customFetcher
4565
+ var StatefulAudioSkipForwardInterval = ({
4566
+ standalone = true
4146
4567
  }) => {
4568
+ const { t } = useI18n();
4569
+ const { preferences } = useAudioPreferences();
4570
+ const skipForwardInterval = useAppSelector((state) => state.audioSettings.skipForwardInterval);
4147
4571
  const dispatch = useAppDispatch();
4148
- const { resolveFontLanguage } = usePreferences();
4149
- const [isLoading, setIsLoading] = useState(true);
4150
- const [error, setError] = useState(null);
4151
- const [manifest, setManifest] = useState(null);
4152
- const [selfLink, setSelfLink] = useState(null);
4153
- const [localDataKey, setLocalDataKey] = useState(null);
4154
- const [publication, setPublication] = useState(null);
4155
- const [profile, setProfile] = useState(null);
4156
- const [isRTL, setIsRTL] = useState(false);
4157
- const [isFXL, setIsFXL] = useState(false);
4158
- const [fontLanguage, setFontLanguageState] = useState("");
4159
- const [hasDisplayTransformability, setHasDisplayTransformabilityState] = useState(false);
4160
- const handleManifestError = (error2, context) => {
4161
- console.error(`${context}:`, error2);
4162
- const processedError = ErrorHandler.process(error2, context);
4163
- setError(processedError);
4164
- setIsLoading(false);
4572
+ const { submitPreferences, getSetting, preferencesEditor } = useNavigator().media;
4573
+ const config = preferences.settings.keys["skipForwardInterval" /* skipForwardInterval */] ?? defaultAudioSkipForwardInterval;
4574
+ const { range, presets } = useEffectiveRange(config.range, preferencesEditor?.skipForwardInterval?.supportedRange, config.presets);
4575
+ const skipForwardIntervalRangeConfig = {
4576
+ variant: config.variant,
4577
+ placeholder: config.placeholder,
4578
+ range,
4579
+ step: config.step
4165
4580
  };
4166
- useEffect(() => {
4167
- if (!url) {
4168
- const validationError = ErrorHandler.process(new Error("Manifest URL is required"), "Validation");
4169
- setError(validationError);
4170
- setIsLoading(false);
4171
- return;
4581
+ const placeholderText = usePlaceholder(skipForwardIntervalRangeConfig.placeholder, skipForwardIntervalRangeConfig.range);
4582
+ const updatePreference = useCallback(async (value) => {
4583
+ const val = Array.isArray(value) ? value[0] : value;
4584
+ await submitPreferences({ skipForwardInterval: val });
4585
+ const effectiveSkipForwardInterval = getSetting("skipForwardInterval");
4586
+ dispatch(setSkipForwardInterval(effectiveSkipForwardInterval));
4587
+ }, [submitPreferences, getSetting, dispatch]);
4588
+ if (skipForwardIntervalRangeConfig.variant === "numberField" /* numberField */) {
4589
+ return /* @__PURE__ */ jsx(
4590
+ StatefulNumberField,
4591
+ {
4592
+ standalone,
4593
+ label: t("reader.playback.preferences.audio.skipForwardInterval"),
4594
+ placeholder: placeholderText,
4595
+ defaultValue: void 0,
4596
+ value: skipForwardInterval,
4597
+ onChange: updatePreference,
4598
+ onReset: void 0,
4599
+ range: skipForwardIntervalRangeConfig.range || [1, 60],
4600
+ step: skipForwardIntervalRangeConfig.step,
4601
+ steppers: {
4602
+ decrementLabel: t("common.actions.decrease"),
4603
+ incrementLabel: t("common.actions.increase")
4604
+ },
4605
+ formatOptions: { style: "unit", unit: "second" },
4606
+ isWheelDisabled: true,
4607
+ isVirtualKeyboardDisabled: true
4608
+ }
4609
+ );
4610
+ }
4611
+ if (skipForwardIntervalRangeConfig.variant === "presetsGroup" /* presetsGroup */) {
4612
+ return /* @__PURE__ */ jsx(
4613
+ StatefulPresetsGroup,
4614
+ {
4615
+ standalone,
4616
+ label: t("reader.playback.preferences.audio.skipForwardInterval"),
4617
+ presets: presets || [],
4618
+ formatOptions: { style: "unit", unit: "second" },
4619
+ onEscape: () => dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false })),
4620
+ value: skipForwardInterval,
4621
+ onChange: (v) => updatePreference(v)
4622
+ }
4623
+ );
4624
+ }
4625
+ if (skipForwardIntervalRangeConfig.variant === "sliderWithPresets" /* sliderWithPresets */) {
4626
+ return /* @__PURE__ */ jsx(
4627
+ StatefulSliderWithPresets,
4628
+ {
4629
+ standalone,
4630
+ label: t("reader.playback.preferences.audio.skipForwardInterval"),
4631
+ placeholder: placeholderText,
4632
+ presets: presets || [],
4633
+ formatOptions: { style: "unit", unit: "second" },
4634
+ onEscape: () => dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false })),
4635
+ value: skipForwardInterval,
4636
+ onChange: updatePreference,
4637
+ range: skipForwardIntervalRangeConfig.range,
4638
+ step: skipForwardIntervalRangeConfig.step
4639
+ }
4640
+ );
4641
+ }
4642
+ return /* @__PURE__ */ jsx(
4643
+ StatefulSlider,
4644
+ {
4645
+ standalone,
4646
+ displayTicks: skipForwardIntervalRangeConfig.variant === "incrementedSlider" /* incrementedSlider */,
4647
+ label: t("reader.playback.preferences.audio.skipForwardInterval"),
4648
+ placeholder: placeholderText,
4649
+ value: skipForwardInterval,
4650
+ onChange: updatePreference,
4651
+ range: skipForwardIntervalRangeConfig.range,
4652
+ step: skipForwardIntervalRangeConfig.step,
4653
+ formatOptions: { style: "unit", unit: "second" }
4172
4654
  }
4173
- setIsLoading(true);
4174
- setError(null);
4175
- const decodedUrl = decodeURIComponent(url);
4176
- const manifestLink = new Link({ href: decodedUrl });
4177
- const fetcher = customFetcher || new HttpFetcher(void 0);
4178
- try {
4179
- const fetched = fetcher.get(manifestLink);
4180
- fetched.link().then(async (link) => {
4181
- try {
4182
- const selfHref = link.toURL(decodedUrl);
4183
- setSelfLink(selfHref || null);
4184
- if (selfHref) {
4655
+ );
4656
+ };
4657
+ var StatefulAudioSkipInterval = ({
4658
+ standalone = true
4659
+ }) => {
4660
+ const { t } = useI18n();
4661
+ const { preferences } = useAudioPreferences();
4662
+ const skipInterval = useAppSelector((state) => state.audioSettings.skipInterval);
4663
+ const dispatch = useAppDispatch();
4664
+ const { submitPreferences, getSetting, preferencesEditor } = useNavigator().media;
4665
+ const config = preferences.settings.keys["skipInterval" /* skipInterval */] ?? defaultAudioSkipInterval;
4666
+ const { range, presets } = useEffectiveRange(config.range, preferencesEditor?.skipForwardInterval?.supportedRange, config.presets);
4667
+ const skipIntervalRangeConfig = {
4668
+ variant: config.variant,
4669
+ placeholder: config.placeholder,
4670
+ range,
4671
+ step: config.step
4672
+ };
4673
+ const placeholderText = usePlaceholder(skipIntervalRangeConfig.placeholder, skipIntervalRangeConfig.range);
4674
+ const updatePreference = useCallback(async (value) => {
4675
+ const val = Array.isArray(value) ? value[0] : value;
4676
+ await submitPreferences({
4677
+ skipForwardInterval: val,
4678
+ skipBackwardInterval: val
4679
+ });
4680
+ dispatch(setSkipInterval(getSetting("skipForwardInterval")));
4681
+ }, [submitPreferences, getSetting, dispatch]);
4682
+ if (skipIntervalRangeConfig.variant === "numberField" /* numberField */) {
4683
+ return /* @__PURE__ */ jsx(
4684
+ StatefulNumberField,
4685
+ {
4686
+ standalone,
4687
+ label: t("reader.playback.preferences.audio.skipInterval"),
4688
+ placeholder: placeholderText,
4689
+ defaultValue: void 0,
4690
+ value: skipInterval,
4691
+ onChange: updatePreference,
4692
+ onReset: void 0,
4693
+ range: skipIntervalRangeConfig.range || [1, 60],
4694
+ step: skipIntervalRangeConfig.step,
4695
+ steppers: {
4696
+ decrementLabel: t("common.actions.decrease"),
4697
+ incrementLabel: t("common.actions.increase")
4698
+ },
4699
+ formatOptions: { style: "unit", unit: "second" },
4700
+ isWheelDisabled: true,
4701
+ isVirtualKeyboardDisabled: true
4702
+ }
4703
+ );
4704
+ }
4705
+ if (skipIntervalRangeConfig.variant === "presetsGroup" /* presetsGroup */) {
4706
+ return /* @__PURE__ */ jsx(
4707
+ StatefulPresetsGroup,
4708
+ {
4709
+ standalone,
4710
+ label: t("reader.playback.preferences.audio.skipInterval"),
4711
+ presets: presets || [],
4712
+ formatOptions: { style: "unit", unit: "second" },
4713
+ onEscape: () => dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false })),
4714
+ value: skipInterval,
4715
+ onChange: (v) => updatePreference(v)
4716
+ }
4717
+ );
4718
+ }
4719
+ if (skipIntervalRangeConfig.variant === "sliderWithPresets" /* sliderWithPresets */) {
4720
+ return /* @__PURE__ */ jsx(
4721
+ StatefulSliderWithPresets,
4722
+ {
4723
+ standalone,
4724
+ label: t("reader.playback.preferences.audio.skipInterval"),
4725
+ placeholder: placeholderText,
4726
+ presets: presets || [],
4727
+ formatOptions: { style: "unit", unit: "second" },
4728
+ onEscape: () => dispatch(setActionOpen({ key: "settings" /* settings */, isOpen: false })),
4729
+ value: skipInterval,
4730
+ onChange: updatePreference,
4731
+ range: skipIntervalRangeConfig.range,
4732
+ step: skipIntervalRangeConfig.step
4733
+ }
4734
+ );
4735
+ }
4736
+ return /* @__PURE__ */ jsx(
4737
+ StatefulSlider,
4738
+ {
4739
+ standalone,
4740
+ displayTicks: skipIntervalRangeConfig.variant === "incrementedSlider" /* incrementedSlider */,
4741
+ label: t("reader.playback.preferences.audio.skipInterval"),
4742
+ placeholder: placeholderText,
4743
+ value: skipInterval,
4744
+ onChange: updatePreference,
4745
+ range: skipIntervalRangeConfig.range,
4746
+ step: skipIntervalRangeConfig.step,
4747
+ formatOptions: { style: "unit", unit: "second" }
4748
+ }
4749
+ );
4750
+ };
4751
+ var StatefulAudioAutoPlay = ({
4752
+ standalone = true
4753
+ }) => {
4754
+ const { t } = useI18n();
4755
+ const autoPlay = useAppSelector((state) => state.audioSettings.autoPlay);
4756
+ const dispatch = useAppDispatch();
4757
+ const { submitPreferences, getSetting } = useNavigator().media;
4758
+ const updatePreference = useCallback(async (isSelected) => {
4759
+ await submitPreferences({ autoPlay: isSelected });
4760
+ const effectiveAutoPlay = getSetting("autoPlay");
4761
+ dispatch(setAutoPlay(effectiveAutoPlay));
4762
+ }, [submitPreferences, getSetting, dispatch]);
4763
+ return /* @__PURE__ */ jsx(
4764
+ StatefulSwitch,
4765
+ {
4766
+ standalone,
4767
+ heading: t("reader.playback.preferences.autoPlay.title"),
4768
+ label: t("reader.playback.preferences.autoPlay.label"),
4769
+ isSelected: autoPlay,
4770
+ onChange: updatePreference
4771
+ }
4772
+ );
4773
+ };
4774
+ var SvgVolumeUp = (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: "M560-131v-82q90-26 145-100t55-168q0-94-55-168T560-749v-82q124 28 202 125.5T840-481q0 127-78 224.5T560-131ZM120-360v-240h160l200-200v640L280-360H120Zm440 40v-322q47 22 73.5 66t26.5 96q0 51-26.5 94.5T560-320ZM400-606l-86 86H200v80h114l86 86v-252ZM300-480Z" }) });
4775
+ var volume_up_default = SvgVolumeUp;
4776
+ var SvgVolumeDown = (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: "M200-360v-240h160l200-200v640L360-360H200Zm440 40v-322q45 21 72.5 65t27.5 97q0 53-27.5 96T640-320ZM480-606l-86 86H280v80h114l86 86v-252ZM380-480Z" }) });
4777
+ var volume_down_default = SvgVolumeDown;
4778
+ var SvgVolumeMute = (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-360v-240h160l200-200v640L440-360H280Zm80-80h114l86 86v-252l-86 86H360v80Zm100-40Z" }) });
4779
+ var volume_mute_default = SvgVolumeMute;
4780
+ var SvgVolumeOff = (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: "M792-56 671-177q-25 16-53 27.5T560-131v-82q14-5 27.5-10t25.5-12L480-368v208L280-360H120v-240h128L56-792l56-56 736 736-56 56Zm-8-232-58-58q17-31 25.5-65t8.5-70q0-94-55-168T560-749v-82q124 28 202 125.5T840-481q0 53-14.5 102T784-288ZM650-422l-90-90v-130q47 22 73.5 66t26.5 96q0 15-2.5 29.5T650-422ZM480-592 376-696l104-104v208Zm-80 238v-94l-72-72H200v80h114l86 86Zm-36-130Z" }) });
4781
+ var volume_off_default = SvgVolumeOff;
4782
+
4783
+ // src/components/Audio/actions/Volume/assets/styles/thorium-web.volume.module.css
4784
+ var thorium_web_volume_default = {
4785
+ wrapper: "thorium_web_volume_wrapper",
4786
+ button: "thorium_web_volume_button",
4787
+ sliderTrack: "thorium_web_volume_sliderTrack",
4788
+ slider: "thorium_web_volume_slider",
4789
+ sliderThumb: "thorium_web_volume_sliderThumb"
4790
+ };
4791
+ var StatefulAudioVolumeTrigger = ({ ref }) => {
4792
+ const { t } = useI18n();
4793
+ const { preferences } = useAudioPreferences();
4794
+ const { preferencesEditor } = useNavigator().media;
4795
+ const volume = useAppSelector((state) => state.audioSettings.volume);
4796
+ const isTrackReady = useAppSelector((state) => state.player.isTrackReady);
4797
+ const isStalled = useAppSelector((state) => state.player.isStalled);
4798
+ const isDisabled = !isTrackReady || isStalled;
4799
+ const dispatch = useAppDispatch();
4800
+ const config = preferences.settings.keys["volume" /* volume */];
4801
+ const { range } = useEffectiveRange(config.range, preferencesEditor?.volume?.supportedRange);
4802
+ const VolumeIcon = useMemo(() => {
4803
+ if (volume === 0) return volume_off_default;
4804
+ const max = Math.max(...range);
4805
+ if (volume <= max / 3) return volume_mute_default;
4806
+ if (volume <= max / 3 * 2) return volume_down_default;
4807
+ return volume_up_default;
4808
+ }, [volume, range]);
4809
+ return /* @__PURE__ */ jsx(
4810
+ StatefulActionIcon,
4811
+ {
4812
+ ref,
4813
+ tooltipLabel: t("reader.playback.preferences.audio.volume"),
4814
+ placement: "top",
4815
+ onPress: () => dispatch(toggleActionOpen({ key: "audio.volume" /* volume */ })),
4816
+ isDisabled,
4817
+ className: thorium_web_volume_default.button,
4818
+ children: /* @__PURE__ */ jsx(VolumeIcon, { "aria-hidden": "true", focusable: "false" })
4819
+ }
4820
+ );
4821
+ };
4822
+ var StatefulAudioVolumeContainer = ({ triggerRef, placement = "top" }) => {
4823
+ const volume = useAppSelector((state) => state.audioSettings.volume);
4824
+ const isOpen = useAppSelector((state) => state.actions.keys["audio.volume" /* volume */]?.isOpen ?? false);
4825
+ const { t } = useI18n();
4826
+ const { preferences } = useAudioPreferences();
4827
+ const dispatch = useAppDispatch();
4828
+ const { submitPreferences, getSetting, preferencesEditor } = useNavigator().media;
4829
+ const config = preferences.settings.keys["volume" /* volume */];
4830
+ const { range } = useEffectiveRange(config.range, preferencesEditor?.volume?.supportedRange);
4831
+ const updatePreference = useCallback(async (value) => {
4832
+ const val = Array.isArray(value) ? value[0] : value;
4833
+ await submitPreferences({ volume: val });
4834
+ const effectiveVolume = getSetting("volume");
4835
+ dispatch(setVolume(effectiveVolume));
4836
+ }, [submitPreferences, getSetting, dispatch]);
4837
+ const docking = useDocking("audio.volume" /* volume */);
4838
+ const sliderOrientation = docking.sheetType === "popover" /* popover */ || docking.sheetType === "compactPopover" /* compactPopover */ ? "vertical" : "horizontal";
4839
+ const setOpen = useCallback((open) => {
4840
+ dispatch(setActionOpen({ key: "audio.volume" /* volume */, isOpen: open }));
4841
+ }, [dispatch]);
4842
+ return /* @__PURE__ */ jsx(
4843
+ StatefulSheetWrapper,
4844
+ {
4845
+ sheetType: docking.sheetType,
4846
+ sheetProps: {
4847
+ id: "audio.volume" /* volume */,
4848
+ triggerRef,
4849
+ heading: t("reader.playback.preferences.audio.volume"),
4850
+ className: thorium_web_volume_default.wrapper,
4851
+ placement,
4852
+ isOpen,
4853
+ onOpenChange: setOpen,
4854
+ onClosePress: () => setOpen(false),
4855
+ docker: docking.getDocker()
4856
+ },
4857
+ children: /* @__PURE__ */ jsx(
4858
+ ThSlider,
4859
+ {
4860
+ "aria-label": t("reader.playback.preferences.audio.volume"),
4861
+ className: thorium_web_volume_default.slider,
4862
+ orientation: sliderOrientation,
4863
+ range,
4864
+ step: config.step,
4865
+ value: volume,
4866
+ onChange: updatePreference,
4867
+ compounds: {
4868
+ track: { className: thorium_web_volume_default.sliderTrack },
4869
+ thumb: { className: thorium_web_volume_default.sliderThumb },
4870
+ output: { style: () => ({ display: "none" }) }
4871
+ }
4872
+ }
4873
+ )
4874
+ }
4875
+ );
4876
+ };
4877
+ var SvgSpeed = (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: "M480-316.5q38-.5 56-27.5l224-336-336 224q-27 18-28.5 55t22.5 61q24 24 62 23.5Zm0-483.5q59 0 113.5 16.5T696-734l-76 48q-33-17-68.5-25.5T480-720q-133 0-226.5 93.5T160-400q0 42 11.5 83t32.5 77h552q23-38 33.5-79t10.5-85q0-36-8.5-70T766-540l48-76q30 47 47.5 100T880-406q1 57-13 109t-41 99q-11 18-30 28t-40 10H204q-21 0-40-10t-30-28q-26-45-40-95.5T80-400q0-83 31.5-155.5t86-127Q252-737 325-768.5T480-800Zm7 313Z" }) });
4878
+ var speed_default = SvgSpeed;
4879
+
4880
+ // src/components/Audio/actions/PlaybackRate/assets/styles/thorium-web.playbackRate.module.css
4881
+ var thorium_web_playbackRate_default = {
4882
+ wrapper: "thorium_web_playbackRate_wrapper",
4883
+ button: "thorium_web_playbackRate_button",
4884
+ label: "thorium_web_playbackRate_label",
4885
+ slider: "thorium_web_playbackRate_slider"};
4886
+ var StatefulAudioPlaybackRateTrigger = ({ ref }) => {
4887
+ const { t } = useI18n();
4888
+ const playbackRate = useAppSelector((state) => state.audioSettings.playbackRate);
4889
+ const isTrackReady = useAppSelector((state) => state.player.isTrackReady);
4890
+ const isStalled = useAppSelector((state) => state.player.isStalled);
4891
+ const isDisabled = !isTrackReady || isStalled;
4892
+ const dispatch = useAppDispatch();
4893
+ return /* @__PURE__ */ jsxs(
4894
+ StatefulActionIcon,
4895
+ {
4896
+ ref,
4897
+ tooltipLabel: t("reader.playback.preferences.playbackRate.descriptive"),
4898
+ placement: "top",
4899
+ onPress: () => dispatch(toggleActionOpen({ key: "audio.playbackRate" /* playbackRate */ })),
4900
+ isDisabled,
4901
+ className: thorium_web_playbackRate_default.button,
4902
+ children: [
4903
+ /* @__PURE__ */ jsx(speed_default, { "aria-hidden": "true", focusable: "false" }),
4904
+ /* @__PURE__ */ jsxs("span", { className: thorium_web_playbackRate_default.label, "aria-hidden": "true", children: [
4905
+ playbackRate,
4906
+ "\xD7"
4907
+ ] })
4908
+ ]
4909
+ }
4910
+ );
4911
+ };
4912
+ var StatefulAudioPlaybackRateContainer = ({ triggerRef, placement = "top" }) => {
4913
+ const isOpen = useAppSelector((state) => state.actions.keys["audio.playbackRate" /* playbackRate */]?.isOpen ?? false);
4914
+ const { t } = useI18n();
4915
+ const { preferences } = useAudioPreferences();
4916
+ const playbackRate = useAppSelector((state) => state.audioSettings.playbackRate);
4917
+ const dispatch = useAppDispatch();
4918
+ const { submitPreferences, getSetting, preferencesEditor } = useNavigator().media;
4919
+ const config = preferences.settings.keys["playbackRate" /* playbackRate */];
4920
+ const { range, presets } = useEffectiveRange(config.range, preferencesEditor?.playbackRate?.supportedRange, config.presets);
4921
+ const updatePreference = useCallback(async (value) => {
4922
+ await submitPreferences({ playbackRate: value });
4923
+ dispatch(setPlaybackRate(getSetting("playbackRate")));
4924
+ }, [submitPreferences, getSetting, dispatch]);
4925
+ const docking = useDocking("audio.playbackRate" /* playbackRate */);
4926
+ const setOpen = useCallback((open) => {
4927
+ dispatch(setActionOpen({ key: "audio.playbackRate" /* playbackRate */, isOpen: open }));
4928
+ }, [dispatch]);
4929
+ const renderContent = () => {
4930
+ if (config.variant === "slider" /* slider */) {
4931
+ return /* @__PURE__ */ jsx("div", { className: thorium_web_playbackRate_default.slider, children: /* @__PURE__ */ jsx(
4932
+ ThSlider,
4933
+ {
4934
+ "aria-label": t("reader.playback.preferences.playbackRate.descriptive"),
4935
+ range,
4936
+ step: config.step,
4937
+ value: playbackRate,
4938
+ onChange: (v) => updatePreference(Array.isArray(v) ? v[0] : v)
4939
+ }
4940
+ ) });
4941
+ }
4942
+ if (config.variant === "numberField" /* numberField */) {
4943
+ return /* @__PURE__ */ jsx("div", { className: thorium_web_playbackRate_default.numberField, children: /* @__PURE__ */ jsx(
4944
+ ThNumberField,
4945
+ {
4946
+ "aria-label": t("reader.playback.preferences.playbackRate.descriptive"),
4947
+ range,
4948
+ step: config.step,
4949
+ value: playbackRate,
4950
+ onChange: updatePreference
4951
+ }
4952
+ ) });
4953
+ }
4954
+ return /* @__PURE__ */ jsx("div", { className: thorium_web_playbackRate_default.slider, children: /* @__PURE__ */ jsx(
4955
+ StatefulSliderWithPresets,
4956
+ {
4957
+ standalone: true,
4958
+ label: t("reader.playback.preferences.playbackRate.descriptive"),
4959
+ presets: presets || [],
4960
+ formatValue: (v) => `${v}\xD7`,
4961
+ value: playbackRate,
4962
+ onChange: (v) => updatePreference(Array.isArray(v) ? v[0] : v),
4963
+ range,
4964
+ step: config.step,
4965
+ onEscape: () => setOpen(false)
4966
+ }
4967
+ ) });
4968
+ };
4969
+ return /* @__PURE__ */ jsx(
4970
+ StatefulSheetWrapper,
4971
+ {
4972
+ sheetType: docking.sheetType,
4973
+ sheetProps: {
4974
+ id: "audio.playbackRate" /* playbackRate */,
4975
+ triggerRef,
4976
+ heading: t("reader.playback.preferences.playbackRate.descriptive"),
4977
+ className: thorium_web_playbackRate_default.wrapper,
4978
+ placement,
4979
+ isOpen,
4980
+ onOpenChange: setOpen,
4981
+ onClosePress: () => setOpen(false),
4982
+ docker: docking.getDocker()
4983
+ },
4984
+ children: renderContent()
4985
+ }
4986
+ );
4987
+ };
4988
+
4989
+ // src/components/Audio/actions/Toc/assets/styles/thorium-web.audioToc.module.css
4990
+ var thorium_web_audioToc_default = {
4991
+ button: "thorium_web_audioToc_button"
4992
+ };
4993
+ var StatefulAudioTocTrigger = ({ ref }) => {
4994
+ const { t } = useI18n();
4995
+ const isTrackReady = useAppSelector((state) => state.player.isTrackReady);
4996
+ const isStalled = useAppSelector((state) => state.player.isStalled);
4997
+ const isDisabled = !isTrackReady || isStalled;
4998
+ const dispatch = useAppDispatch();
4999
+ return /* @__PURE__ */ jsx(
5000
+ StatefulActionIcon,
5001
+ {
5002
+ ref,
5003
+ tooltipLabel: t("reader.tableOfContents.title"),
5004
+ placement: "top",
5005
+ onPress: () => dispatch(toggleActionOpen({ key: "audio.toc" /* toc */ })),
5006
+ isDisabled,
5007
+ className: thorium_web_audioToc_default.button,
5008
+ children: /* @__PURE__ */ jsx(toc_default, { "aria-hidden": "true", focusable: "false" })
5009
+ }
5010
+ );
5011
+ };
5012
+ var StatefulAudioTocContainer = ({ triggerRef }) => {
5013
+ const { t } = useI18n();
5014
+ const { goLink } = useNavigator().unified;
5015
+ const dispatch = useAppDispatch();
5016
+ const isOpen = useAppSelector((state) => state.actions.keys["audio.toc" /* toc */]?.isOpen ?? false);
5017
+ const unstableTimeline = useAppSelector((state) => state.publication.unstableTimeline);
5018
+ const tocEntry = unstableTimeline?.toc?.currentEntry ?? void 0;
5019
+ const tocEntryId = tocEntry?.id;
5020
+ const tocTree = unstableTimeline?.toc?.tree;
5021
+ const direction = useAppSelector((state) => state.reader.direction);
5022
+ const isRTL = direction === "rtl" /* rtl */;
5023
+ const docking = useDocking("audio.toc" /* toc */);
5024
+ const sheetType = docking.sheetType;
5025
+ const setOpen = useCallback((value) => {
5026
+ dispatch(setActionOpen({ key: "audio.toc" /* toc */, isOpen: value }));
5027
+ }, [dispatch]);
5028
+ const { expandedKeys, setExpandedKeys, filterValue, setFilterValue, displayedTocTree, treeRef, searchInputRef } = useTocContent({ isOpen, tocTree, tocEntry: tocEntryId });
5029
+ useEffect(() => {
5030
+ if (isOpen) {
5031
+ const handleEscape = (event) => {
5032
+ if (!isActiveElement(searchInputRef.current) && !filterValue && event.key === "Escape") {
5033
+ setOpen(false);
5034
+ }
5035
+ };
5036
+ document.addEventListener("keydown", handleEscape, true);
5037
+ return () => document.removeEventListener("keydown", handleEscape, true);
5038
+ }
5039
+ }, [isOpen, filterValue, searchInputRef, setOpen]);
5040
+ const handleAction = (keys) => {
5041
+ if (keys === "all" || !keys || keys.size === 0) return;
5042
+ const key = [...keys][0];
5043
+ const el = document.querySelector(`[data-key=${key}]`);
5044
+ const href = el?.getAttribute("data-href");
5045
+ if (!href) return;
5046
+ const matched = findTocItemById(tocTree || [], key);
5047
+ const cb = isOpen && (sheetType === "docked start" /* dockedStart */ || sheetType === "docked end" /* dockedEnd */) ? () => {
5048
+ dispatch(setTocEntry(matched || null));
5049
+ dispatch(setImmersive(true));
5050
+ dispatch(setUserNavigated(true));
5051
+ } : () => {
5052
+ dispatch(setTocEntry(matched || null));
5053
+ dispatch(setImmersive(true));
5054
+ dispatch(setUserNavigated(true));
5055
+ setOpen(false);
5056
+ };
5057
+ goLink(new Link({ href }), true, cb);
5058
+ };
5059
+ return /* @__PURE__ */ jsx(
5060
+ StatefulSheetWrapper,
5061
+ {
5062
+ sheetType,
5063
+ sheetProps: {
5064
+ id: "audio.toc" /* toc */,
5065
+ triggerRef,
5066
+ heading: t("reader.tableOfContents.title"),
5067
+ className: "",
5068
+ isOpen,
5069
+ onOpenChange: setOpen,
5070
+ onClosePress: () => setOpen(false),
5071
+ docker: docking.getDocker(),
5072
+ resetFocus: tocEntryId,
5073
+ focusWithinRef: treeRef
5074
+ },
5075
+ children: /* @__PURE__ */ jsx(
5076
+ TocContent,
5077
+ {
5078
+ filterValue,
5079
+ onFilterChange: setFilterValue,
5080
+ displayedTocTree,
5081
+ tocTree,
5082
+ tocEntry: tocEntryId,
5083
+ expandedKeys,
5084
+ onExpandedChange: setExpandedKeys,
5085
+ onSelectionChange: handleAction,
5086
+ isRTL,
5087
+ treeRef,
5088
+ searchInputRef
5089
+ }
5090
+ )
5091
+ }
5092
+ );
5093
+ };
5094
+ var SvgSnooze = (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: "M380-334h200v-60H468l112-126v-54H380v60h114L380-386v52Zm-40.5 225.5q-65.5-28.5-114-77t-77-114Q120-365 120-440t28.5-140.5q28.5-65.5 77-114t114-77Q405-800 480-800t140.5 28.5q65.5 28.5 114 77t77 114Q840-515 840-440t-28.5 140.5q-28.5 65.5-77 114t-114 77Q555-80 480-80t-140.5-28.5ZM480-440ZM224-866l56 56-170 170-56-56 170-170Zm512 0 170 170-56 56-170-170 56-56ZM480-160q117 0 198.5-81.5T760-440q0-117-81.5-198.5T480-720q-117 0-198.5 81.5T200-440q0 117 81.5 198.5T480-160Z" }) });
5095
+ var snooze_default = SvgSnooze;
5096
+
5097
+ // src/components/Audio/actions/SleepTimer/assets/styles/thorium-web.sleepTimer.module.css
5098
+ var thorium_web_sleepTimer_default = {
5099
+ wrapper: "thorium_web_sleepTimer_wrapper",
5100
+ button: "thorium_web_sleepTimer_button",
5101
+ label: "thorium_web_sleepTimer_label",
5102
+ listbox: "thorium_web_sleepTimer_listbox",
5103
+ listboxItem: "thorium_web_sleepTimer_listboxItem",
5104
+ durationField: "thorium_web_sleepTimer_durationField",
5105
+ instruction: "thorium_web_sleepTimer_instruction",
5106
+ inputs: "thorium_web_sleepTimer_inputs",
5107
+ fieldGroup: "thorium_web_sleepTimer_fieldGroup",
5108
+ fieldInput: "thorium_web_sleepTimer_fieldInput",
5109
+ unitLabel: "thorium_web_sleepTimer_unitLabel",
5110
+ remaining: "thorium_web_sleepTimer_remaining",
5111
+ cancelButton: "thorium_web_sleepTimer_cancelButton",
5112
+ startButton: "thorium_web_sleepTimer_startButton"
5113
+ };
5114
+ var StatefulAudioSleepTimerTrigger = ({ ref }) => {
5115
+ const { t } = useI18n();
5116
+ const remainingSeconds = useAppSelector((state) => state.player.sleepTimer.remainingSeconds);
5117
+ const onTrackEnd = useAppSelector((state) => state.player.sleepTimer.onTrackEnd);
5118
+ const onFragmentEnd = useAppSelector((state) => state.player.sleepTimer.onFragmentEnd);
5119
+ const isTrackReady = useAppSelector((state) => state.player.isTrackReady);
5120
+ const isStalled = useAppSelector((state) => state.player.isStalled);
5121
+ const isDisabled = !isTrackReady || isStalled;
5122
+ const dispatch = useAppDispatch();
5123
+ const isActive = remainingSeconds !== null || onTrackEnd || onFragmentEnd;
5124
+ const formatBadge = (seconds) => {
5125
+ if (seconds < 60) return `${seconds}${t("audio.settings.sleepTimer.seconds")}`;
5126
+ return `${Math.ceil(seconds / 60)}${t("audio.settings.sleepTimer.minutes")}`;
5127
+ };
5128
+ const sleepTimerLabel = (() => {
5129
+ if (onTrackEnd) return t("reader.playback.preferences.sleepTimer.presets.endOfResource");
5130
+ if (onFragmentEnd) return t("reader.playback.preferences.sleepTimer.presets.endOfFragment");
5131
+ return formatBadge(remainingSeconds);
5132
+ })();
5133
+ return /* @__PURE__ */ jsxs(
5134
+ StatefulActionIcon,
5135
+ {
5136
+ ref,
5137
+ tooltipLabel: t("reader.playback.preferences.sleepTimer.descriptive"),
5138
+ placement: "top",
5139
+ onPress: () => dispatch(toggleActionOpen({ key: "audio.sleepTimer" /* sleepTimer */ })),
5140
+ isDisabled,
5141
+ className: thorium_web_sleepTimer_default.button,
5142
+ children: [
5143
+ /* @__PURE__ */ jsx(snooze_default, { "aria-hidden": "true", focusable: "false" }),
5144
+ isActive && /* @__PURE__ */ jsx("span", { className: thorium_web_sleepTimer_default.label, "aria-hidden": "true", children: sleepTimerLabel })
5145
+ ]
5146
+ }
5147
+ );
5148
+ };
5149
+ var StatefulAudioSleepTimerContainer = ({ triggerRef, placement = "top" }) => {
5150
+ const [hours, setHours] = useState(0);
5151
+ const [minutes, setMinutes] = useState(0);
5152
+ const isOpen = useAppSelector((state) => state.actions.keys["audio.sleepTimer" /* sleepTimer */]?.isOpen ?? false);
5153
+ const remainingSeconds = useAppSelector((state) => state.player.sleepTimer.remainingSeconds);
5154
+ const onTrackEnd = useAppSelector((state) => state.player.sleepTimer.onTrackEnd);
5155
+ const onFragmentEnd = useAppSelector((state) => state.player.sleepTimer.onFragmentEnd);
5156
+ const playerStatus = useAppSelector((state) => state.player.status);
5157
+ const dispatch = useAppDispatch();
5158
+ const { t } = useI18n();
5159
+ const formatRemaining = (seconds) => {
5160
+ const h = Math.floor(seconds / 3600);
5161
+ const m = Math.floor(seconds % 3600 / 60);
5162
+ const s = seconds % 60;
5163
+ const mm = m.toString().padStart(2, "0");
5164
+ const ss = s.toString().padStart(2, "0");
5165
+ const min = t("audio.settings.sleepTimer.minutes");
5166
+ const sec = t("audio.settings.sleepTimer.seconds");
5167
+ if (h > 0) return `${h}${t("audio.settings.sleepTimer.hours")} ${mm}${min} ${ss}${sec}`;
5168
+ return `${mm}${min} ${ss}${sec}`;
5169
+ };
5170
+ const { preferences } = useAudioPreferences();
5171
+ const { pause } = useNavigator().media;
5172
+ const config = preferences.settings.keys["sleepTimer" /* sleepTimer */];
5173
+ const variant = config.variant;
5174
+ useEffect(() => {
5175
+ if (remainingSeconds === null) return;
5176
+ if (remainingSeconds <= 0) {
5177
+ pause();
5178
+ dispatch(setSleepTimerRemainingSeconds(null));
5179
+ return;
5180
+ }
5181
+ if (playerStatus !== "playing") return;
5182
+ const id = setTimeout(() => {
5183
+ dispatch(setSleepTimerRemainingSeconds(remainingSeconds - 1));
5184
+ }, 1e3);
5185
+ return () => clearTimeout(id);
5186
+ }, [remainingSeconds, playerStatus, pause, dispatch]);
5187
+ const handleCancel = useCallback(() => {
5188
+ dispatch(setSleepTimerRemainingSeconds(null));
5189
+ dispatch(setSleepTimerOnTrackEnd(false));
5190
+ dispatch(setSleepTimerOnFragmentEnd(false));
5191
+ dispatch(setActionOpen({ key: "audio.sleepTimer" /* sleepTimer */, isOpen: false }));
5192
+ }, [dispatch]);
5193
+ const handleStart = useCallback(() => {
5194
+ const totalSeconds = hours * 3600 + minutes * 60;
5195
+ if (totalSeconds <= 0) return;
5196
+ dispatch(setSleepTimerRemainingSeconds(totalSeconds));
5197
+ dispatch(setActionOpen({ key: "audio.sleepTimer" /* sleepTimer */, isOpen: false }));
5198
+ }, [hours, minutes, dispatch]);
5199
+ const handlePresetSelect = useCallback((value) => {
5200
+ if (value === "endOfResource") {
5201
+ dispatch(setSleepTimerOnTrackEnd(true));
5202
+ dispatch(setSleepTimerOnFragmentEnd(false));
5203
+ dispatch(setSleepTimerRemainingSeconds(null));
5204
+ } else if (value === "endOfFragment") {
5205
+ dispatch(setSleepTimerOnTrackEnd(false));
5206
+ dispatch(setSleepTimerOnFragmentEnd(true));
5207
+ dispatch(setSleepTimerRemainingSeconds(null));
5208
+ } else {
5209
+ dispatch(setSleepTimerOnTrackEnd(false));
5210
+ dispatch(setSleepTimerOnFragmentEnd(false));
5211
+ dispatch(setSleepTimerRemainingSeconds(Number(value) * 60));
5212
+ }
5213
+ dispatch(setActionOpen({ key: "audio.sleepTimer" /* sleepTimer */, isOpen: false }));
5214
+ }, [dispatch]);
5215
+ const docking = useDocking("audio.sleepTimer" /* sleepTimer */);
5216
+ const setOpen = useCallback((open) => {
5217
+ dispatch(setActionOpen({ key: "audio.sleepTimer" /* sleepTimer */, isOpen: open }));
5218
+ }, [dispatch]);
5219
+ const isActive = remainingSeconds !== null || onTrackEnd || onFragmentEnd;
5220
+ const maxHours = (config.variant === "durationField" /* durationField */ ? config.maxHours : void 0) ?? 23;
5221
+ const renderContent = () => {
5222
+ if (variant === "presetList" /* presetList */ && config?.variant === "presetList" /* presetList */) {
5223
+ const items = config.presets.map((preset) => {
5224
+ if (preset === "endOfResource") {
5225
+ return {
5226
+ id: "endOfResource",
5227
+ value: "endOfResource",
5228
+ label: t("reader.playback.preferences.sleepTimer.presets.endOfResource")
5229
+ };
5230
+ } else if (preset === "endOfFragment") {
5231
+ return {
5232
+ id: "endOfFragment",
5233
+ value: "endOfFragment",
5234
+ label: t("reader.playback.preferences.sleepTimer.presets.endOfFragment")
5235
+ };
5236
+ } else {
5237
+ return {
5238
+ id: String(preset),
5239
+ value: String(preset),
5240
+ label: `${preset} ${t("audio.settings.sleepTimer.minutes")}`
5241
+ };
5242
+ }
5243
+ });
5244
+ const activeValue = onTrackEnd ? "endOfResource" : onFragmentEnd ? "endOfFragment" : remainingSeconds !== null ? String(remainingSeconds / 60) : "";
5245
+ return /* @__PURE__ */ jsxs("div", { className: thorium_web_sleepTimer_default.durationField, children: [
5246
+ /* @__PURE__ */ jsx(
5247
+ ThRadioGroup,
5248
+ {
5249
+ "aria-label": t("reader.playback.preferences.sleepTimer.descriptive"),
5250
+ value: activeValue,
5251
+ onChange: handlePresetSelect,
5252
+ items,
5253
+ compounds: {
5254
+ wrapper: { className: thorium_web_sleepTimer_default.listbox },
5255
+ radio: { className: thorium_web_sleepTimer_default.listboxItem }
5256
+ }
5257
+ }
5258
+ ),
5259
+ isActive && /* @__PURE__ */ jsx(
5260
+ Button,
5261
+ {
5262
+ className: `${thorium_web_sleepTimer_default.startButton} ${thorium_web_sleepTimer_default.cancelButton}`,
5263
+ onPress: handleCancel,
5264
+ children: t("common.actions.cancel")
5265
+ }
5266
+ )
5267
+ ] });
5268
+ }
5269
+ if (isActive && remainingSeconds !== null) {
5270
+ return /* @__PURE__ */ jsxs("div", { className: thorium_web_sleepTimer_default.durationField, children: [
5271
+ /* @__PURE__ */ jsx("p", { className: thorium_web_sleepTimer_default.remaining, children: t("audio.settings.sleepTimer.remaining", { remaining: formatRemaining(remainingSeconds) }) }),
5272
+ /* @__PURE__ */ jsx(
5273
+ Button,
5274
+ {
5275
+ className: thorium_web_sleepTimer_default.startButton,
5276
+ onPress: handleCancel,
5277
+ children: t("common.actions.cancel")
5278
+ }
5279
+ )
5280
+ ] });
5281
+ }
5282
+ return /* @__PURE__ */ jsxs("div", { className: thorium_web_sleepTimer_default.durationField, children: [
5283
+ /* @__PURE__ */ jsx("p", { className: thorium_web_sleepTimer_default.instruction, children: t("audio.settings.sleepTimer.instruction") }),
5284
+ /* @__PURE__ */ jsxs("div", { className: thorium_web_sleepTimer_default.inputs, children: [
5285
+ /* @__PURE__ */ jsx(
5286
+ ThNumberField,
5287
+ {
5288
+ "aria-label": t("audio.settings.sleepTimer.hours"),
5289
+ range: [0, maxHours],
5290
+ step: 1,
5291
+ value: hours,
5292
+ onChange: setHours,
5293
+ onInputChange: (raw) => setHours(parseInt(raw) || 0),
5294
+ compounds: {
5295
+ group: { className: thorium_web_sleepTimer_default.fieldGroup },
5296
+ input: { className: thorium_web_sleepTimer_default.fieldInput }
5297
+ }
5298
+ }
5299
+ ),
5300
+ /* @__PURE__ */ jsx("span", { className: thorium_web_sleepTimer_default.unitLabel, "aria-hidden": "true", children: t("audio.settings.sleepTimer.hours") }),
5301
+ /* @__PURE__ */ jsx(
5302
+ ThNumberField,
5303
+ {
5304
+ "aria-label": t("audio.settings.sleepTimer.minutes"),
5305
+ range: [0, 59],
5306
+ step: 1,
5307
+ value: minutes,
5308
+ onChange: setMinutes,
5309
+ onInputChange: (raw) => setMinutes(parseInt(raw) || 0),
5310
+ compounds: {
5311
+ group: { className: thorium_web_sleepTimer_default.fieldGroup },
5312
+ input: { className: thorium_web_sleepTimer_default.fieldInput }
5313
+ }
5314
+ }
5315
+ ),
5316
+ /* @__PURE__ */ jsx("span", { className: thorium_web_sleepTimer_default.unitLabel, "aria-hidden": "true", children: t("audio.settings.sleepTimer.minutes") })
5317
+ ] }),
5318
+ /* @__PURE__ */ jsx(
5319
+ Button,
5320
+ {
5321
+ className: thorium_web_sleepTimer_default.startButton,
5322
+ isDisabled: hours === 0 && minutes === 0,
5323
+ onPress: handleStart,
5324
+ children: t("audio.settings.sleepTimer.start")
5325
+ }
5326
+ )
5327
+ ] });
5328
+ };
5329
+ return /* @__PURE__ */ jsx(
5330
+ StatefulSheetWrapper,
5331
+ {
5332
+ sheetType: docking.sheetType,
5333
+ sheetProps: {
5334
+ id: "audio.sleepTimer" /* sleepTimer */,
5335
+ triggerRef,
5336
+ heading: t("reader.playback.preferences.sleepTimer.descriptive"),
5337
+ className: thorium_web_sleepTimer_default.wrapper,
5338
+ placement,
5339
+ isOpen,
5340
+ onOpenChange: setOpen,
5341
+ onClosePress: () => setOpen(false),
5342
+ docker: docking.getDocker()
5343
+ },
5344
+ children: /* @__PURE__ */ jsx(FocusScope, { contain: true, children: renderContent() })
5345
+ }
5346
+ );
5347
+ };
5348
+ var SvgCast = (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: "M480-480Zm320 320H600q0-20-1.5-40t-4.5-40h206v-480H160v46q-20-3-40-4.5T80-680v-40q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160Zm-720 0v-120q50 0 85 35t35 85H80Zm200 0q0-83-58.5-141.5T80-360v-80q117 0 198.5 81.5T360-160h-80Zm160 0q0-75-28.5-140.5t-77-114q-48.5-48.5-114-77T80-520v-80q91 0 171 34.5T391-471q60 60 94.5 140T520-160h-80Z" }) });
5349
+ var cast_default = SvgCast;
5350
+ var SvgCastConnected = (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: "M720-320H575q-7-21-15.5-41.5T542-400h98v-160H413q-29-25-62.5-45T281-640h439v320ZM480-480ZM80-160v-120q50 0 85 35t35 85H80Zm200 0q0-83-58.5-141.5T80-360v-80q117 0 198.5 81.5T360-160h-80Zm160 0q0-75-28.5-140.5t-77-114q-48.5-48.5-114-77T80-520v-80q91 0 171 34.5T391-471q60 60 94.5 140T520-160h-80Zm360 0H600q0-20-1.5-40t-4.5-40h206v-480H160v46q-20-3-40-4.5T80-680v-40q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160Z" }) });
5351
+ var cast_connected_default = SvgCastConnected;
5352
+ var SvgCastWarning = (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: "M480-480ZM80-280q50 0 85 35t35 85H80v-120Zm0-160q117 0 198.5 81.5T360-160h-80q0-83-58.5-141.5T80-360v-80Zm0-160q91 0 171 34.5T391-471q60 60 94.5 140T520-160h-80q0-75-28.5-140.5t-77-114q-48.5-48.5-114-77T80-520v-80Zm720 440H600q0-20-1.5-40t-4.5-40h206v-212q22-7 42-16.5t38-22.5v251q0 33-23.5 56.5T800-160ZM80-680v-40q0-33 23.5-56.5T160-800h292q-6 19-9 39t-3 41H160v46q-20-3-40-4.5T80-680Zm498.5 101.5Q520-637 520-720t58.5-141.5Q637-920 720-920t141.5 58.5Q920-803 920-720t-58.5 141.5Q803-520 720-520t-141.5-58.5ZM720-600q8 0 14-6t6-14q0-8-6-14t-14-6q-8 0-14 6t-6 14q0 8 6 14t14 6Zm-20-80h40v-160h-40v160Z" }) });
5353
+ var cast_warning_default = SvgCastWarning;
5354
+ var StatefulAudioRemotePlaybackTrigger = ({ variant }) => {
5355
+ const { t } = useI18n();
5356
+ const { preferences } = useAudioPreferences();
5357
+ const dispatch = useAppDispatch();
5358
+ const remotePlaybackState = useAppSelector((state) => state.player.remotePlaybackState);
5359
+ const [isAvailable, setIsAvailable] = useState(null);
5360
+ const watchIdRef = useRef(void 0);
5361
+ const { remotePlayback } = useNavigator().media;
5362
+ useEffect(() => {
5363
+ if (!remotePlayback) return;
5364
+ remotePlayback.watchAvailability((available) => {
5365
+ setIsAvailable(available);
5366
+ }).then((id) => {
5367
+ watchIdRef.current = id;
5368
+ }).catch(() => {
5369
+ setIsAvailable(true);
5370
+ });
5371
+ return () => {
5372
+ if (watchIdRef.current !== void 0) {
5373
+ remotePlayback.cancelWatchAvailability(watchIdRef.current);
5374
+ }
5375
+ };
5376
+ }, [remotePlayback]);
5377
+ const handlePress = useCallback(async () => {
5378
+ if (!remotePlayback) return;
5379
+ try {
5380
+ await remotePlayback.prompt();
5381
+ } catch (err) {
5382
+ if (err instanceof DOMException && (err.name === "AbortError" || err.name === "NotAllowedError")) return;
5383
+ dispatch(setRemotePlaybackState("error"));
5384
+ }
5385
+ }, [remotePlayback, dispatch]);
5386
+ if (preferences.contentProtection?.disableRemotePlayback) return null;
5387
+ if (!remotePlayback || isAvailable === false) return null;
5388
+ const isConnected = remotePlaybackState === "connected" || remotePlaybackState === "connecting";
5389
+ const isError = remotePlaybackState === "error";
5390
+ const Icon = isError ? cast_warning_default : isConnected ? cast_connected_default : cast_default;
5391
+ const token = preferences.actions.secondary.keys["audio.remotePlayback" /* remotePlayback */];
5392
+ const label = isConnected ? t("audio.remotePlayback.connected") : t("audio.remotePlayback.trigger");
5393
+ return /* @__PURE__ */ jsx(Fragment, { children: variant && variant === "menuItem" /* menu */ ? /* @__PURE__ */ jsx(
5394
+ StatefulOverflowMenuItem,
5395
+ {
5396
+ label,
5397
+ SVGIcon: Icon,
5398
+ shortcut: token?.shortcut ?? null,
5399
+ id: "audio.remotePlayback" /* remotePlayback */,
5400
+ isDisabled: isError,
5401
+ onAction: handlePress
5402
+ }
5403
+ ) : /* @__PURE__ */ jsx(
5404
+ StatefulActionIcon,
5405
+ {
5406
+ visibility: token?.visibility,
5407
+ tooltipLabel: label,
5408
+ placement: "bottom",
5409
+ "aria-label": label,
5410
+ isDisabled: isError,
5411
+ onPress: handlePress,
5412
+ children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true", focusable: "false" })
5413
+ }
5414
+ ) });
5415
+ };
5416
+
5417
+ // src/components/Plugins/helpers/createAudioDefaultPlugin.ts
5418
+ var createAudioDefaultPlugin = () => {
5419
+ return {
5420
+ id: "audio-core",
5421
+ name: "Audio Core Components",
5422
+ description: "Default components for Thorium Web Audio StatefulReader",
5423
+ version: "1.3.0",
5424
+ components: {
5425
+ actions: {
5426
+ ["settings" /* settings */]: {
5427
+ Trigger: StatefulSettingsTrigger,
5428
+ Target: StatefulAudioSettingsContainer
5429
+ },
5430
+ ["toc" /* toc */]: {
5431
+ Trigger: StatefulTocTrigger,
5432
+ Target: StatefulTocContainer
5433
+ },
5434
+ ["fullscreen" /* fullscreen */]: {
5435
+ Trigger: StatefulFullscreenTrigger
5436
+ },
5437
+ ["audio.remotePlayback" /* remotePlayback */]: {
5438
+ Trigger: StatefulAudioRemotePlaybackTrigger
5439
+ }
5440
+ },
5441
+ primaryAudioActions: {
5442
+ ["audio.volume" /* volume */]: { Trigger: StatefulAudioVolumeTrigger, Target: StatefulAudioVolumeContainer },
5443
+ ["audio.playbackRate" /* playbackRate */]: { Trigger: StatefulAudioPlaybackRateTrigger, Target: StatefulAudioPlaybackRateContainer },
5444
+ ["audio.toc" /* toc */]: { Trigger: StatefulAudioTocTrigger, Target: StatefulAudioTocContainer },
5445
+ ["audio.sleepTimer" /* sleepTimer */]: { Trigger: StatefulAudioSleepTimerTrigger, Target: StatefulAudioSleepTimerContainer }
5446
+ },
5447
+ settings: {
5448
+ ["theme" /* theme */]: {
5449
+ Comp: StatefulTheme
5450
+ },
5451
+ ["skipInterval" /* skipInterval */]: {
5452
+ Comp: StatefulAudioSkipInterval
5453
+ },
5454
+ ["skipBackwardInterval" /* skipBackwardInterval */]: {
5455
+ Comp: StatefulAudioSkipBackwardInterval
5456
+ },
5457
+ ["skipForwardInterval" /* skipForwardInterval */]: {
5458
+ Comp: StatefulAudioSkipForwardInterval
5459
+ },
5460
+ ["autoPlay" /* autoPlay */]: {
5461
+ Comp: StatefulAudioAutoPlay
5462
+ }
5463
+ }
5464
+ }
5465
+ };
5466
+ };
5467
+ var StatefulPreferencesProvider = ({
5468
+ children,
5469
+ initialPreferences = defaultPreferences
5470
+ }) => {
5471
+ const store = useStore();
5472
+ const adapter = useMemo(() => {
5473
+ return new ThReduxPreferencesAdapter(store, initialPreferences);
5474
+ }, [store, initialPreferences]);
5475
+ return /* @__PURE__ */ jsx(ThPreferencesProvider, { adapter, children });
5476
+ };
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
5558
+ }) => {
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) {
4185
5595
  setLocalDataKey(`${selfHref}-current-location`);
4186
5596
  const manifestFetcher = customFetcher || new HttpFetcher(void 0, selfHref);
4187
5597
  const manifestFetched = manifestFetcher.get(manifestLink);
@@ -4206,6 +5616,13 @@ var usePublication = ({
4206
5616
  dispatch(setPositionsList([]));
4207
5617
  }
4208
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
+ }
4209
5626
  setPublication(pub);
4210
5627
  setIsLoading(false);
4211
5628
  }
@@ -4227,12 +5644,6 @@ var usePublication = ({
4227
5644
  setIsFXL(fxl);
4228
5645
  dispatch(setFXL(fxl));
4229
5646
  }
4230
- const resolvedLang = resolveFontLanguage(
4231
- publication.metadata.languages?.[0],
4232
- publication.metadata.effectiveReadingProgression
4233
- );
4234
- setFontLanguageState(resolvedLang);
4235
- dispatch(setFontLanguage(resolvedLang));
4236
5647
  const displayTransformability = publication.metadata.accessibility?.feature?.some(
4237
5648
  (feature) => feature && feature.value === Feature.DISPLAY_TRANSFORMABILITY.value
4238
5649
  ) || false;
@@ -4251,7 +5662,7 @@ var usePublication = ({
4251
5662
  };
4252
5663
  fetchPositions();
4253
5664
  }
4254
- }, [publication, profile, resolveFontLanguage, dispatch]);
5665
+ }, [publication, profile, dispatch]);
4255
5666
  useEffect(() => {
4256
5667
  if (error) {
4257
5668
  onError(error);
@@ -4267,7 +5678,6 @@ var usePublication = ({
4267
5678
  profile,
4268
5679
  isRTL,
4269
5680
  isFXL,
4270
- fontLanguage,
4271
5681
  hasDisplayTransformability
4272
5682
  };
4273
5683
  };
@@ -4310,29 +5720,36 @@ var thorium_web_reader_app_default = {
4310
5720
  srOnly: "thorium_web_reader_app_srOnly"
4311
5721
  };
4312
5722
  var useResizablePanel = (panel) => {
4313
- const { preferences } = usePreferences();
4314
- const defaultWidth = preferences.theming.layout.defaults.dockingWidth;
5723
+ const defaultPanel = {
5724
+ actionKey: null,
5725
+ active: false,
5726
+ collapsed: false
5727
+ };
5728
+ const safePanel = panel || defaultPanel;
5729
+ const preferences = useActionsPreferences();
5730
+ const { theming } = useSharedPreferences();
5731
+ const defaultWidth = theming.layout.defaults.dockingWidth;
4315
5732
  const [pref, setPref] = useState(
4316
- panel.actionKey ? preferences.actions.keys[panel.actionKey].docked || null : null
5733
+ safePanel.actionKey ? preferences.actionsKeys[safePanel.actionKey]?.docked || null : null
4317
5734
  );
4318
5735
  const actionsMap = useAppSelector((state) => state.actions.keys);
4319
5736
  const actions = useActions(actionsMap);
4320
- const previouslyCollapsed = usePrevious(panel.collapsed);
4321
- const previousWidth = actions.getDockedWidth(panel.actionKey) || null;
5737
+ const previouslyCollapsed = usePrevious(safePanel.collapsed);
5738
+ const previousWidth = actions.getDockedWidth(safePanel.actionKey) || null;
4322
5739
  const width = pref?.width || defaultWidth;
4323
5740
  const minWidth = pref?.minWidth && pref.minWidth < width ? pref.minWidth : defaultWidth < width ? defaultWidth : width;
4324
5741
  const maxWidth = pref?.maxWidth && pref.maxWidth > width ? pref.maxWidth : defaultWidth > width ? defaultWidth : width;
4325
5742
  const isPopulated = () => {
4326
- return panel.active && actions.isOpen(panel.actionKey);
5743
+ return safePanel.active && actions.isOpen(safePanel.actionKey);
4327
5744
  };
4328
5745
  const isCollapsed = () => {
4329
- return panel.collapsed;
5746
+ return safePanel.collapsed;
4330
5747
  };
4331
5748
  const forceExpand = () => {
4332
- return !!(isPopulated() && previouslyCollapsed && !panel.collapsed);
5749
+ return !!(isPopulated() && previouslyCollapsed && !safePanel.collapsed);
4333
5750
  };
4334
5751
  const currentKey = () => {
4335
- return panel.actionKey;
5752
+ return safePanel.actionKey;
4336
5753
  };
4337
5754
  const isResizable = () => {
4338
5755
  return isPopulated() ? Math.round(width) > Math.round(minWidth) && Math.round(width) < Math.round(maxWidth) : false;
@@ -4360,8 +5777,8 @@ var useResizablePanel = (panel) => {
4360
5777
  return current;
4361
5778
  }, [minWidth, maxWidth]);
4362
5779
  useEffect(() => {
4363
- setPref(panel.actionKey ? preferences.actions.keys[panel.actionKey].docked || null : null);
4364
- }, [panel.actionKey, preferences]);
5780
+ setPref(safePanel.actionKey ? preferences.actionsKeys[safePanel.actionKey]?.docked || null : null);
5781
+ }, [safePanel.actionKey, preferences]);
4365
5782
  return {
4366
5783
  currentKey,
4367
5784
  isPopulated,
@@ -4375,6 +5792,31 @@ var useResizablePanel = (panel) => {
4375
5792
  getCurrentPxWidth
4376
5793
  };
4377
5794
  };
5795
+ var useDockCleanup = (profile) => {
5796
+ const dispatch = useAppDispatch();
5797
+ const dock = useAppSelector((state) => profile ? state.actions.dock[profile] : void 0);
5798
+ const startActionKey = dock?.["dockingStart" /* start */]?.actionKey;
5799
+ const endActionKey = dock?.["dockingEnd" /* end */]?.actionKey;
5800
+ const startStatus = useActionComponentStatus({ actionKey: startActionKey || "" });
5801
+ const endStatus = useActionComponentStatus({ actionKey: endActionKey || "" });
5802
+ useEffect(() => {
5803
+ if (!profile || !dock) return;
5804
+ if (startActionKey && !startStatus.isComponentRegistered) {
5805
+ dispatch(dockAction({
5806
+ key: startActionKey,
5807
+ dockingKey: "dockingTransient" /* transient */,
5808
+ profile
5809
+ }));
5810
+ }
5811
+ if (endActionKey && !endStatus.isComponentRegistered) {
5812
+ dispatch(dockAction({
5813
+ key: endActionKey,
5814
+ dockingKey: "dockingTransient" /* transient */,
5815
+ profile
5816
+ }));
5817
+ }
5818
+ }, [profile, dock, startActionKey, endActionKey, startStatus.isComponentRegistered, endStatus.isComponentRegistered, dispatch]);
5819
+ };
4378
5820
  var DockHandle = ({
4379
5821
  flow,
4380
5822
  isResizable,
@@ -4397,7 +5839,7 @@ var DockHandle = ({
4397
5839
  className: thorium_web_docking_default.resizeHandle,
4398
5840
  disabled: !isResizable,
4399
5841
  tabIndex: isPopulated ? 0 : -1,
4400
- children: isResizable && hasDragIndicator && /* @__PURE__ */ jsx("div", { className: classNames3(thorium_web_docking_default.resizeHandleGrab, classFromFlow()) })
5842
+ children: isResizable && hasDragIndicator && /* @__PURE__ */ jsx("div", { className: classNames4(thorium_web_docking_default.resizeHandleGrab, classFromFlow()) })
4401
5843
  }
4402
5844
  ) });
4403
5845
  };
@@ -4409,7 +5851,8 @@ var DockPanel = ({
4409
5851
  isPopulated,
4410
5852
  isCollapsed,
4411
5853
  forceExpand,
4412
- hasDragIndicator
5854
+ hasDragIndicator,
5855
+ profile
4413
5856
  }) => {
4414
5857
  const { t } = useI18n();
4415
5858
  const panelRef = useRef(null);
@@ -4437,21 +5880,21 @@ var DockPanel = ({
4437
5880
  const collapsePanel = useCallback(() => {
4438
5881
  if (panelRef.current) {
4439
5882
  panelRef.current.collapse();
4440
- dispatch(collapseDockPanel(flow));
5883
+ dispatch(collapseDockPanel({ slot: flow, profile }));
4441
5884
  }
4442
- }, [dispatch, flow]);
5885
+ }, [dispatch, flow, profile]);
4443
5886
  const expandPanel = useCallback(() => {
4444
5887
  if (panelRef.current) {
4445
5888
  panelRef.current.expand();
4446
- dispatch(expandDockPanel(flow));
5889
+ dispatch(expandDockPanel({ slot: flow, profile }));
4447
5890
  }
4448
- }, [dispatch, flow]);
5891
+ }, [dispatch, flow, profile]);
4449
5892
  useEffect(() => {
4450
- dispatch(activateDockPanel(flow));
5893
+ dispatch(activateDockPanel({ slot: flow, profile }));
4451
5894
  return () => {
4452
- dispatch(deactivateDockPanel(flow));
5895
+ dispatch(deactivateDockPanel({ slot: flow, profile }));
4453
5896
  };
4454
- }, [dispatch, flow]);
5897
+ }, [dispatch, flow, profile]);
4455
5898
  useEffect(() => {
4456
5899
  isPopulated || forceExpand ? expandPanel() : collapsePanel();
4457
5900
  }, [isPopulated, forceExpand, collapsePanel, expandPanel]);
@@ -4480,7 +5923,8 @@ var DockPanel = ({
4480
5923
  onExpand: expandPanel,
4481
5924
  onResize: (size) => size !== 0 && dispatch(setDockPanelWidth({
4482
5925
  key: flow,
4483
- width: sizes.getCurrentPxWidth(size)
5926
+ width: sizes.getCurrentPxWidth(size),
5927
+ profile
4484
5928
  })),
4485
5929
  inert: isCollapsed,
4486
5930
  children: /* @__PURE__ */ jsx(
@@ -4488,7 +5932,7 @@ var DockPanel = ({
4488
5932
  {
4489
5933
  id: flow,
4490
5934
  "aria-label": makeDockLabel(),
4491
- className: classNames3(thorium_web_docking_default.panelContainer, dockClassName)
5935
+ className: classNames4(thorium_web_docking_default.panelContainer, dockClassName)
4492
5936
  }
4493
5937
  )
4494
5938
  }
@@ -4507,9 +5951,11 @@ var DockPanel = ({
4507
5951
  var StatefulDockingWrapper = ({
4508
5952
  children
4509
5953
  }) => {
4510
- const { preferences } = usePreferences();
4511
- const dockingStart = useAppSelector((state) => state.actions.dock["dockingStart" /* start */]);
4512
- const dockingEnd = useAppSelector((state) => state.actions.dock["dockingEnd" /* end */]);
5954
+ const preferences = useActionsPreferences();
5955
+ const profile = useAppSelector((state) => state.reader.profile);
5956
+ useDockCleanup(profile);
5957
+ const dockingStart = useAppSelector((state) => profile && state.actions.dock[profile] ? state.actions.dock[profile]["dockingStart" /* start */] : void 0);
5958
+ const dockingEnd = useAppSelector((state) => profile && state.actions.dock[profile] ? state.actions.dock[profile]["dockingEnd" /* end */] : void 0);
4513
5959
  const startPanel = useResizablePanel(dockingStart);
4514
5960
  const endPanel = useResizablePanel(dockingEnd);
4515
5961
  const breakpoint = useAppSelector((state) => state.theming.breakpoint);
@@ -4524,7 +5970,7 @@ var StatefulDockingWrapper = ({
4524
5970
  });
4525
5971
  const dockConfig = breakpoint && dockingMap2[breakpoint] || "both" /* both */;
4526
5972
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(PanelGroup, { direction: "horizontal", children: [
4527
- (dockConfig === "both" /* both */ || dockConfig === "start" /* start */) && /* @__PURE__ */ jsx(
5973
+ (dockConfig === "both" /* both */ || dockConfig === "start" /* start */) && profile && /* @__PURE__ */ jsx(
4528
5974
  DockPanel,
4529
5975
  {
4530
5976
  actionKey: startPanel.currentKey(),
@@ -4539,11 +5985,12 @@ var StatefulDockingWrapper = ({
4539
5985
  isPopulated: startPanel.isPopulated(),
4540
5986
  isCollapsed: startPanel.isCollapsed(),
4541
5987
  forceExpand: startPanel.forceExpand(),
4542
- hasDragIndicator: startPanel.hasDragIndicator()
5988
+ hasDragIndicator: startPanel.hasDragIndicator(),
5989
+ profile
4543
5990
  }
4544
5991
  ),
4545
5992
  /* @__PURE__ */ jsx(Panel, { id: "main-panel", order: 2, children }),
4546
- (dockConfig === "both" /* both */ || dockConfig === "end" /* end */) && /* @__PURE__ */ jsx(
5993
+ (dockConfig === "both" /* both */ || dockConfig === "end" /* end */) && profile && /* @__PURE__ */ jsx(
4547
5994
  DockPanel,
4548
5995
  {
4549
5996
  actionKey: endPanel.currentKey(),
@@ -4558,7 +6005,8 @@ var StatefulDockingWrapper = ({
4558
6005
  isPopulated: endPanel.isPopulated(),
4559
6006
  isCollapsed: endPanel.isCollapsed(),
4560
6007
  forceExpand: endPanel.forceExpand(),
4561
- hasDragIndicator: endPanel.hasDragIndicator()
6008
+ hasDragIndicator: endPanel.hasDragIndicator(),
6009
+ profile
4562
6010
  }
4563
6011
  )
4564
6012
  ] }) });
@@ -4580,18 +6028,19 @@ var StatefulBackLink = ({
4580
6028
  className
4581
6029
  }) => {
4582
6030
  const { t } = useI18n();
4583
- const { preferences } = usePreferences();
4584
- const direction = useAppSelector((state) => state.reader.direction);
6031
+ const { direction, theming } = useSharedPreferences();
6032
+ const backLinkPref = theming.header?.backLink;
6033
+ const tooltipDelay = theming.icon.tooltipDelay;
4585
6034
  const isRTL = direction === "rtl" /* rtl */;
4586
- const variant = preferences.theming.header?.backLink?.variant || "arrow" /* arrow */;
4587
- const href = preferences.theming.header?.backLink?.href;
4588
- const content = preferences.theming.header?.backLink?.content;
4589
- const visibility = preferences.theming.header?.backLink?.visibility || "partially";
4590
- const backLinkClassName = classNames3(thorium_web_backlink_default.link, visibility === "always" ? thorium_web_button_default.alwaysVisible : thorium_web_button_default.partiallyVisible);
6035
+ const variant = backLinkPref?.variant || "arrow" /* arrow */;
6036
+ const href = backLinkPref?.href;
6037
+ const content = backLinkPref?.content;
6038
+ const visibility = backLinkPref?.visibility || "partially";
6039
+ const backLinkClassName = classNames4(thorium_web_backlink_default.link, visibility === "always" ? thorium_web_button_default.alwaysVisible : thorium_web_button_default.partiallyVisible);
4591
6040
  const compounds = {
4592
6041
  tooltipTrigger: {
4593
- delay: preferences.theming.arrow.tooltipDelay,
4594
- closeDelay: preferences.theming.arrow.tooltipDelay
6042
+ delay: tooltipDelay,
6043
+ closeDelay: tooltipDelay
4595
6044
  },
4596
6045
  tooltip: {
4597
6046
  className: thorium_web_button_default.tooltip
@@ -4618,917 +6067,136 @@ var StatefulBackLink = ({
4618
6067
  className: backLinkClassName,
4619
6068
  href,
4620
6069
  "aria-label": t("reader.app.header.backLink.trigger"),
4621
- compounds
4622
- }
4623
- ) });
4624
- case "library" /* library */:
4625
- return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx(
4626
- ThLibrary,
4627
- {
4628
- className: backLinkClassName,
4629
- href,
4630
- "aria-label": t("reader.app.header.backLink.trigger"),
4631
- compounds
4632
- }
4633
- ) });
4634
- default:
4635
- if (!content) return null;
4636
- let contentNode = null;
4637
- switch (content.type) {
4638
- case "img":
4639
- contentNode = /* @__PURE__ */ jsx("img", { alt: content.alt ?? "", src: content.src });
4640
- break;
4641
- case "svg":
4642
- const parser = new DOMParser();
4643
- const doc = parser.parseFromString(content.content, "image/svg+xml");
4644
- const svgElement = doc.documentElement;
4645
- const attributes = {};
4646
- for (const { name, value } of Array.from(svgElement.attributes)) {
4647
- attributes[name] = value;
4648
- }
4649
- contentNode = React18.createElement("svg", {
4650
- ...attributes,
4651
- "aria-hidden": "true",
4652
- focusable: "false",
4653
- xmlns: "http://www.w3.org/2000/svg",
4654
- dangerouslySetInnerHTML: {
4655
- __html: svgElement.innerHTML
4656
- }
4657
- });
4658
- break;
4659
- }
4660
- return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx(
4661
- ThLink,
4662
- {
4663
- className: backLinkClassName,
4664
- href,
4665
- "aria-label": t("reader.app.header.backLink.trigger"),
4666
- compounds,
4667
- children: contentNode
4668
- }
4669
- ) });
4670
- }
4671
- };
4672
- var StatefulReaderRunningHead = ({
4673
- formatPref
4674
- }) => {
4675
- const { t } = useI18n();
4676
- const unstableTimeline = useAppSelector((state) => state.publication.unstableTimeline);
4677
- const isImmersive = useAppSelector((state) => state.reader.isImmersive);
4678
- const isHovering = useAppSelector((state) => state.reader.isHovering);
4679
- const isFullscreen = useAppSelector((state) => state.reader.isFullscreen);
4680
- const breakpoint = useAppSelector((state) => state.theming.breakpoint);
4681
- const fallbackFormat = useMemo(() => ({
4682
- variants: "title" /* title */,
4683
- displayInImmersive: true,
4684
- displayInFullscreen: true
4685
- }), []);
4686
- const breakpointsMap = useMemo(() => {
4687
- return makeBreakpointsMap({
4688
- defaultValue: formatPref?.default || fallbackFormat,
4689
- fromEnum: ThRunningHeadFormat,
4690
- pref: formatPref?.breakpoints,
4691
- validateKey: "variants"
4692
- });
4693
- }, [formatPref, fallbackFormat]);
4694
- const currentPrefs = useMemo(() => {
4695
- if (!breakpoint) return formatPref?.default || fallbackFormat;
4696
- return breakpointsMap[breakpoint] || formatPref?.default || fallbackFormat;
4697
- }, [breakpoint, breakpointsMap, formatPref?.default, fallbackFormat]);
4698
- const { variants, displayInImmersive, displayInFullscreen } = currentPrefs;
4699
- const displayFormat = useMemo(() => {
4700
- if (!variants) return "title" /* title */;
4701
- if (isImmersive && displayInImmersive === false && !isHovering) {
4702
- return "none" /* none */;
4703
- }
4704
- if (isImmersive && isFullscreen && displayInFullscreen === false && !isHovering) {
4705
- return "none" /* none */;
4706
- }
4707
- return variants;
4708
- }, [variants, isImmersive, displayInImmersive, isHovering, isFullscreen, displayInFullscreen]);
4709
- const runningHead = useMemo(() => {
4710
- if (displayFormat === "title" /* title */) {
4711
- return unstableTimeline?.title || "";
4712
- } else if (displayFormat === "chapter" /* chapter */) {
4713
- return unstableTimeline?.progression?.currentChapter || unstableTimeline?.title || "";
4714
- }
4715
- return "";
4716
- }, [displayFormat, unstableTimeline]);
4717
- if (!runningHead || displayFormat === "none" /* none */) return null;
4718
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
4719
- ThRunningHead,
4720
- {
4721
- label: runningHead,
4722
- "aria-label": t("reader.app.header.runningHead")
4723
- }
4724
- ) });
4725
- };
4726
- var StatefulReaderHeader = ({
4727
- actionKeys,
4728
- actionsOrder,
4729
- layout,
4730
- runningHeadFormatPref
4731
- }) => {
4732
- const headerRef = useRef(null);
4733
- const { preferences } = usePreferences();
4734
- const { t } = useI18n();
4735
- const { actionsComponentsMap } = usePlugins();
4736
- const actionsMap = useAppSelector((state) => state.actions.keys);
4737
- const overflowMap = useAppSelector((state) => state.actions.overflow);
4738
- const isScroll = useAppSelector((state) => state.settings.scroll);
4739
- const isImmersive = useAppSelector((state) => state.reader.isImmersive);
4740
- const isHovering = useAppSelector((state) => state.reader.isHovering);
4741
- const hasScrollAffordance = useAppSelector((state) => state.reader.hasScrollAffordance);
4742
- const positionsList = useAppSelector((state) => state.publication.positionsList);
4743
- const actions = useActions({ ...actionsMap, ...overflowMap });
4744
- const dispatch = useAppDispatch();
4745
- const { focusWithinProps } = useFocusWithin({
4746
- onFocusWithin() {
4747
- dispatch(setHovering(true));
4748
- },
4749
- onBlurWithin() {
4750
- if (actions.everyOpenDocked()) {
4751
- dispatch(setHovering(false));
4752
- }
4753
- }
4754
- });
4755
- const setHover = () => {
4756
- if (!hasScrollAffordance && actions.everyOpenDocked()) {
4757
- dispatch(setHovering(true));
4758
- }
4759
- };
4760
- const removeHover = () => {
4761
- if (!hasScrollAffordance && actions.everyOpenDocked()) {
4762
- dispatch(setHovering(false));
4763
- }
4764
- };
4765
- const listActionItems = useCallback(() => {
4766
- const actionsItems = [];
4767
- if (actionsComponentsMap && Object.keys(actionsComponentsMap).length > 0) {
4768
- actionKeys.forEach((key) => {
4769
- if (actionsComponentsMap[key]) {
4770
- actionsItems.push({
4771
- Trigger: actionsComponentsMap[key].Trigger,
4772
- Target: actionsComponentsMap[key].Target,
4773
- key
4774
- });
4775
- } else {
4776
- console.warn(`Action key "${key}" not found in the plugin registry while present in preferences.`);
4777
- }
4778
- });
4779
- }
4780
- return actionsItems.filter((item) => {
4781
- if (item.key === "jumpToPosition" /* jumpToPosition */) {
4782
- return isPositionsListValid(positionsList);
4783
- }
4784
- if (item.key === "fullscreen" /* fullscreen */) {
4785
- return document.fullscreenEnabled && !isIOSish();
4786
- }
4787
- return true;
4788
- });
4789
- }, [actionKeys, actionsComponentsMap, positionsList]);
4790
- useEffect(() => {
4791
- if (isImmersive) {
4792
- const focusElement = document.activeElement;
4793
- if (focusElement && headerRef.current?.contains(focusElement)) {
4794
- focusElement.blur();
4795
- }
4796
- }
4797
- }, [isImmersive]);
4798
- return /* @__PURE__ */ jsxs(Fragment, { children: [
4799
- /* @__PURE__ */ jsx(
4800
- ThInteractiveOverlay,
4801
- {
4802
- className: classNames3(thorium_web_reader_app_default.barOverlay, thorium_web_reader_app_default.headerOverlay),
4803
- isActive: layout === "layered-ui" /* layered */ && isImmersive && !isHovering,
4804
- onMouseEnter: setHover,
4805
- onMouseLeave: removeHover
4806
- }
4807
- ),
4808
- /* @__PURE__ */ jsxs(
4809
- ThHeader,
4810
- {
4811
- ref: headerRef,
4812
- className: classNames3(thorium_web_reader_app_default.topBar, thorium_web_reader_header_default.header),
4813
- "aria-label": t("reader.app.header.label"),
4814
- onMouseEnter: setHover,
4815
- onMouseLeave: removeHover,
4816
- ...focusWithinProps,
4817
- children: [
4818
- preferences.theming.header?.backLink && /* @__PURE__ */ jsx(StatefulBackLink, { className: thorium_web_reader_header_default.backlinkWrapper }),
4819
- /* @__PURE__ */ jsx(StatefulReaderRunningHead, { formatPref: runningHeadFormatPref }),
4820
- /* @__PURE__ */ jsx(
4821
- StatefulCollapsibleActionsBar,
4822
- {
4823
- id: "reader-header-overflowMenu",
4824
- items: listActionItems(),
4825
- prefs: {
4826
- ...preferences.actions,
4827
- displayOrder: actionsOrder
4828
- },
4829
- className: thorium_web_reader_header_default.actionsWrapper,
4830
- "aria-label": t("reader.app.header.actions"),
4831
- overflowMenuClassName: !isScroll || preferences.affordances.scroll.hintInImmersive ? thorium_web_overflow_default.hint : void 0
4832
- }
4833
- )
4834
- ]
4835
- }
4836
- )
4837
- ] });
4838
- };
4839
-
4840
- // src/components/assets/styles/thorium-web.reader.pagination.module.css
4841
- var thorium_web_reader_pagination_default = {
4842
- wrapper: "thorium_web_reader_pagination_wrapper",
4843
- listItem: "thorium_web_reader_pagination_listItem",
4844
- previousButton: "thorium_web_reader_pagination_previousButton",
4845
- progression: "thorium_web_reader_pagination_progression",
4846
- nextButton: "thorium_web_reader_pagination_nextButton",
4847
- label: "thorium_web_reader_pagination_label"
4848
- };
4849
-
4850
- // src/components/assets/styles/thorium-web.reader.progression.module.css
4851
- var thorium_web_reader_progression_default = {
4852
- wrapper: "thorium_web_reader_progression_wrapper"
4853
- };
4854
- var StatefulReaderProgression = ({
4855
- className,
4856
- formatPref,
4857
- fallbackVariant
4858
- }) => {
4859
- const { t } = useI18n();
4860
- const unstableTimeline = useAppSelector((state) => state.publication.unstableTimeline);
4861
- const isImmersive = useAppSelector((state) => state.reader.isImmersive);
4862
- const isFullscreen = useAppSelector((state) => state.reader.isFullscreen);
4863
- const isHovering = useAppSelector((state) => state.reader.isHovering);
4864
- const breakpoint = useAppSelector((state) => state.theming.breakpoint);
4865
- const [displayText, setDisplayText] = useState("");
4866
- const fallbackFormat = useMemo(() => {
4867
- return {
4868
- variants: fallbackVariant,
4869
- displayInImmersive: true,
4870
- displayInFullscreen: true
4871
- };
4872
- }, [fallbackVariant]);
4873
- const breakpointsMap = useMemo(() => {
4874
- return makeBreakpointsMap({
4875
- defaultValue: formatPref?.default || fallbackFormat,
4876
- fromEnum: ThProgressionFormat,
4877
- pref: formatPref?.breakpoints,
4878
- validateKey: "variants"
4879
- });
4880
- }, [formatPref, fallbackFormat]);
4881
- const currentPrefs = useMemo(() => {
4882
- if (!breakpoint) return formatPref?.default || fallbackFormat;
4883
- return breakpointsMap[breakpoint] || formatPref?.default || fallbackFormat;
4884
- }, [breakpoint, breakpointsMap, formatPref?.default, fallbackFormat]);
4885
- const { variants, displayInImmersive, displayInFullscreen } = currentPrefs;
4886
- const displayFormat = useMemo(() => {
4887
- if (!variants) return fallbackFormat.variants;
4888
- if (isImmersive && displayInImmersive === false && !isHovering) {
4889
- return "none" /* none */;
4890
- }
4891
- if (isImmersive && isFullscreen && displayInFullscreen === false && !isHovering) {
4892
- return "none" /* none */;
4893
- }
4894
- if (Array.isArray(variants)) {
4895
- return getBestMatchingProgressionFormat(variants, unstableTimeline?.progression) || fallbackFormat.variants;
4896
- }
4897
- return variants;
4898
- }, [variants, unstableTimeline?.progression, fallbackFormat, isImmersive, isHovering, isFullscreen, displayInImmersive, displayInFullscreen]);
4899
- useEffect(() => {
4900
- if (displayFormat === "none" /* none */ || !unstableTimeline?.progression) {
4901
- setDisplayText("");
4902
- return;
4903
- }
4904
- const {
4905
- currentPositions = [],
4906
- totalPositions,
4907
- relativeProgression,
4908
- totalProgression,
4909
- currentChapter,
4910
- positionsLeft,
4911
- totalItems,
4912
- currentIndex
4913
- } = unstableTimeline.progression;
4914
- let text = "";
4915
- const formatPositions = (positions) => {
4916
- if (positions.length === 2) {
4917
- return positions.join("\u2013");
4918
- }
4919
- return positions[0]?.toString() || "";
4920
- };
4921
- switch (displayFormat) {
4922
- case "positions" /* positions */:
4923
- if (currentPositions.length > 0) {
4924
- text = formatPositions(currentPositions);
4925
- }
4926
- break;
4927
- case "positionsOfTotal" /* positionsOfTotal */:
4928
- if (currentPositions.length > 0 && totalPositions) {
4929
- text = t("reader.progression.xOfY.compact", {
4930
- x: formatPositions(currentPositions),
4931
- y: totalPositions
4932
- });
4933
- }
4934
- break;
4935
- case "positionsPercentOfTotal" /* positionsPercentOfTotal */:
4936
- if (currentPositions.length > 0 && totalPositions) {
4937
- const percentage = Math.round((totalProgression || 0) * 100);
4938
- text = t("reader.progression.xOfY.descriptive", {
4939
- x: formatPositions(currentPositions),
4940
- y: totalPositions,
4941
- z: `${percentage}%`
4942
- });
4943
- }
4944
- break;
4945
- case "positionsLeft" /* positionsLeft */:
4946
- if (positionsLeft !== void 0) {
4947
- text = t(`reader.progression.positionsLeftInChapter.descriptive`, {
4948
- count: positionsLeft
4949
- });
4950
- }
4951
- break;
4952
- case "overallProgression" /* overallProgression */:
4953
- if (totalProgression !== void 0) {
4954
- const percentage = Math.round(totalProgression * 100);
4955
- text = `${percentage}%`;
4956
- }
4957
- break;
4958
- case "resourceProgression" /* resourceProgression */:
4959
- if (relativeProgression !== void 0) {
4960
- const percentage = Math.round(relativeProgression * 100);
4961
- text = `${percentage}%`;
6070
+ compounds
4962
6071
  }
4963
- break;
4964
- case "progressionOfResource" /* progressionOfResource */:
4965
- if (relativeProgression !== void 0) {
4966
- const percentage = Math.round(relativeProgression * 100);
4967
- text = t("reader.progression.xOfY.compact", {
4968
- x: `${percentage}%`,
4969
- y: currentChapter || t("reader.app.progression.referenceFallback")
4970
- });
6072
+ ) });
6073
+ case "library" /* library */:
6074
+ return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx(
6075
+ ThLibrary,
6076
+ {
6077
+ className: backLinkClassName,
6078
+ href,
6079
+ "aria-label": t("reader.app.header.backLink.trigger"),
6080
+ compounds
4971
6081
  }
4972
- break;
4973
- case "readingOrderIndex" /* readingOrderIndex */:
4974
- if (currentIndex !== void 0 && totalItems !== void 0) {
4975
- text = t("reader.progression.xOfY.compact", {
4976
- x: currentIndex,
4977
- y: totalItems
6082
+ ) });
6083
+ default:
6084
+ if (!content) return null;
6085
+ let contentNode = null;
6086
+ switch (content.type) {
6087
+ case "img":
6088
+ contentNode = /* @__PURE__ */ jsx("img", { alt: content.alt ?? "", src: content.src });
6089
+ break;
6090
+ case "svg":
6091
+ const parser = new DOMParser();
6092
+ const doc = parser.parseFromString(content.content, "image/svg+xml");
6093
+ const svgElement = doc.documentElement;
6094
+ const attributes = {};
6095
+ for (const { name, value } of Array.from(svgElement.attributes)) {
6096
+ attributes[name] = value;
6097
+ }
6098
+ contentNode = React24.createElement("svg", {
6099
+ ...attributes,
6100
+ "aria-hidden": "true",
6101
+ focusable: "false",
6102
+ xmlns: "http://www.w3.org/2000/svg",
6103
+ dangerouslySetInnerHTML: {
6104
+ __html: svgElement.innerHTML
6105
+ }
4978
6106
  });
4979
- }
4980
- break;
4981
- }
4982
- setDisplayText(text);
4983
- }, [displayFormat, unstableTimeline?.progression, t]);
4984
- if (!displayText || displayFormat === "none" /* none */) {
4985
- return null;
4986
- }
4987
- return /* @__PURE__ */ jsx(
4988
- ThProgression,
4989
- {
4990
- id: "current-progression",
4991
- className: classNames3(thorium_web_reader_progression_default.wrapper, className),
4992
- "aria-label": t("reader.app.progression.wrapper"),
4993
- children: displayText
4994
- }
4995
- );
4996
- };
4997
- var StatefulReaderPagination = ({
4998
- ref,
4999
- links,
5000
- compounds,
5001
- children,
5002
- ...props
5003
- }) => {
5004
- const previousButtonRef = useRef(null);
5005
- const nextButtonRef = useRef(null);
5006
- const updatedCompounds = {
5007
- ...compounds,
5008
- previousButton: {
5009
- ...compounds?.previousButton,
5010
- ref: previousButtonRef,
5011
- onKeyDown: (e) => {
5012
- if (e.key === "Escape") {
5013
- previousButtonRef.current?.blur();
5014
- }
6107
+ break;
5015
6108
  }
5016
- },
5017
- nextButton: {
5018
- ...compounds?.nextButton,
5019
- ref: nextButtonRef,
5020
- onKeyDown: (e) => {
5021
- if (e.key === "Escape") {
5022
- nextButtonRef.current?.blur();
6109
+ return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx(
6110
+ ThLink,
6111
+ {
6112
+ className: backLinkClassName,
6113
+ href,
6114
+ "aria-label": t("reader.app.header.backLink.trigger"),
6115
+ compounds,
6116
+ children: contentNode
5023
6117
  }
5024
- }
5025
- }
5026
- };
5027
- return /* @__PURE__ */ jsx(
5028
- ThPagination,
5029
- {
5030
- ref,
5031
- className: thorium_web_reader_pagination_default.wrapper,
5032
- links,
5033
- compounds: updatedCompounds,
5034
- ...props,
5035
- children
5036
- }
5037
- );
6118
+ ) });
6119
+ }
5038
6120
  };
5039
- var StatefulReaderFooter = ({
5040
- layout,
5041
- progressionFormatPref,
5042
- progressionFormatFallback
5043
- }) => {
6121
+ var useReaderHeaderBase = (actionKeys) => {
6122
+ const headerRef = useRef(null);
5044
6123
  const { t } = useI18n();
5045
- const footerRef = useRef(null);
5046
- const readerProfile = useAppSelector((state) => state.reader.profile);
6124
+ const { actionsComponentsMap } = usePlugins();
6125
+ const actionsMap = useAppSelector((state) => state.actions.keys);
6126
+ const overflowMap = useAppSelector((state) => state.actions.overflow);
6127
+ const isScroll = useAppSelector((state) => state.settings.scroll);
5047
6128
  const isImmersive = useAppSelector((state) => state.reader.isImmersive);
5048
6129
  const isHovering = useAppSelector((state) => state.reader.isHovering);
5049
6130
  const hasScrollAffordance = useAppSelector((state) => state.reader.hasScrollAffordance);
5050
- const scroll = useAppSelector((state) => state.settings.scroll);
5051
- const isFXL = useAppSelector((state) => state.publication.isFXL);
5052
- const isScroll = scroll && !isFXL;
5053
- const breakpoint = useAppSelector((state) => state.theming.breakpoint);
5054
- const reducedMotion = useAppSelector((state) => state.theming.prefersReducedMotion);
5055
- const timeline = useAppSelector((state) => state.publication.unstableTimeline);
6131
+ const positionsList = useAppSelector((state) => state.publication.positionsList);
6132
+ const actions = useActions({ ...actionsMap, ...overflowMap });
5056
6133
  const dispatch = useAppDispatch();
5057
6134
  const { focusWithinProps } = useFocusWithin({
5058
6135
  onFocusWithin() {
5059
6136
  dispatch(setHovering(true));
5060
6137
  },
5061
6138
  onBlurWithin() {
5062
- dispatch(setHovering(false));
6139
+ if (actions.everyOpenDocked()) {
6140
+ dispatch(setHovering(false));
6141
+ }
5063
6142
  }
5064
6143
  });
5065
6144
  const setHover = () => {
5066
- if (!hasScrollAffordance) {
6145
+ if (!hasScrollAffordance && actions.everyOpenDocked()) {
5067
6146
  dispatch(setHovering(true));
5068
6147
  }
5069
6148
  };
5070
6149
  const removeHover = () => {
5071
- if (!hasScrollAffordance) {
6150
+ if (!hasScrollAffordance && actions.everyOpenDocked()) {
5072
6151
  dispatch(setHovering(false));
5073
6152
  }
5074
6153
  };
5075
- const { previousLocator, nextLocator, go } = useNavigator();
5076
- const updateLinks = useCallback(() => {
5077
- const links = {
5078
- previous: void 0,
5079
- next: void 0
5080
- };
5081
- const previous = previousLocator();
5082
- const next = nextLocator();
5083
- if (previous) {
5084
- links.previous = {
5085
- node: breakpoint !== "compact" /* compact */ && breakpoint !== "medium" /* medium */ ? /* @__PURE__ */ jsxs(Fragment, { children: [
5086
- /* @__PURE__ */ jsx("span", { className: thorium_web_reader_app_default.srOnly, children: t(isFXL ? "reader.actions.goToPreviousPage.descriptive" : "reader.actions.goToPreviousChapter.descriptive") }),
5087
- /* @__PURE__ */ jsx("span", { className: thorium_web_reader_pagination_default.label, children: timeline?.previousItem?.title || previous.title || t(isFXL ? "reader.actions.goToPreviousPage.compact" : "reader.actions.goToPreviousChapter.compact") })
5088
- ] }) : /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("span", { className: thorium_web_reader_pagination_default.label, children: t(isFXL ? "reader.actions.goToPreviousPage.compact" : "reader.actions.goToPreviousChapter.compact") }) }),
5089
- onPress: () => go(previous, !reducedMotion, () => {
5090
- })
5091
- };
5092
- }
5093
- if (next) {
5094
- links.next = {
5095
- node: breakpoint !== "compact" /* compact */ && breakpoint !== "medium" /* medium */ ? /* @__PURE__ */ jsxs(Fragment, { children: [
5096
- /* @__PURE__ */ jsx("span", { className: thorium_web_reader_app_default.srOnly, children: t(isFXL ? "reader.actions.goToNextPage.descriptive" : "reader.actions.goToNextChapter.descriptive") }),
5097
- /* @__PURE__ */ jsx("span", { className: thorium_web_reader_pagination_default.label, children: timeline?.nextItem?.title || next.title || t(isFXL ? "reader.actions.goToNextPage.compact" : "reader.actions.goToNextChapter.compact") })
5098
- ] }) : /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("span", { className: thorium_web_reader_pagination_default.label, children: t(isFXL ? "reader.actions.goToNextPage.compact" : "reader.actions.goToNextChapter.compact") }) }),
5099
- onPress: () => go(next, !reducedMotion, () => {
5100
- })
5101
- };
6154
+ const listActionItems = useCallback(() => {
6155
+ const actionsItems = [];
6156
+ if (actionsComponentsMap && Object.keys(actionsComponentsMap).length > 0) {
6157
+ actionKeys.forEach((key) => {
6158
+ if (actionsComponentsMap[key]) {
6159
+ actionsItems.push({
6160
+ Trigger: actionsComponentsMap[key].Trigger,
6161
+ Target: actionsComponentsMap[key].Target,
6162
+ key
6163
+ });
6164
+ } else {
6165
+ console.warn(`Action key "${key}" not found in the plugin registry while present in preferences.`);
6166
+ }
6167
+ });
5102
6168
  }
5103
- return links;
5104
- }, [go, previousLocator, nextLocator, t, timeline, breakpoint, reducedMotion, isFXL]);
5105
- useEffect(() => {
5106
- updateLinks();
5107
- }, [timeline, updateLinks]);
6169
+ return actionsItems.filter((item) => {
6170
+ if (item.key === "jumpToPosition" /* jumpToPosition */) {
6171
+ return isPositionsListValid(positionsList);
6172
+ }
6173
+ if (item.key === "fullscreen" /* fullscreen */) {
6174
+ return document.fullscreenEnabled && !isIOSish();
6175
+ }
6176
+ return true;
6177
+ });
6178
+ }, [actionKeys, actionsComponentsMap, positionsList]);
5108
6179
  useEffect(() => {
5109
6180
  if (isImmersive) {
5110
6181
  const focusElement = document.activeElement;
5111
- if (focusElement && footerRef.current?.contains(focusElement)) {
6182
+ if (focusElement && headerRef.current?.contains(focusElement)) {
5112
6183
  focusElement.blur();
5113
6184
  }
5114
6185
  }
5115
6186
  }, [isImmersive]);
5116
- return /* @__PURE__ */ jsxs(Fragment, { children: [
5117
- /* @__PURE__ */ jsx(
5118
- ThInteractiveOverlay,
5119
- {
5120
- className: classNames3(thorium_web_reader_app_default.barOverlay, thorium_web_reader_app_default.footerOverlay),
5121
- isActive: layout === "layered-ui" /* layered */ && isImmersive && !isHovering,
5122
- onMouseEnter: setHover,
5123
- onMouseLeave: removeHover
5124
- }
5125
- ),
5126
- /* @__PURE__ */ jsx(
5127
- ThFooter,
5128
- {
5129
- ref: footerRef,
5130
- className: thorium_web_reader_app_default.bottomBar,
5131
- "aria-label": t("reader.app.footer.label"),
5132
- onMouseEnter: setHover,
5133
- onMouseLeave: removeHover,
5134
- ...focusWithinProps,
5135
- children: isScroll || readerProfile === "webPub" ? /* @__PURE__ */ jsx(
5136
- StatefulReaderPagination,
5137
- {
5138
- "aria-label": t("reader.navigation.scroll.wrapper"),
5139
- links: updateLinks(),
5140
- compounds: {
5141
- listItem: {
5142
- className: thorium_web_reader_pagination_default.listItem
5143
- },
5144
- previousButton: {
5145
- className: thorium_web_reader_pagination_default.previousButton,
5146
- preventFocusOnPress: true
5147
- },
5148
- nextButton: {
5149
- className: thorium_web_reader_pagination_default.nextButton,
5150
- preventFocusOnPress: true
5151
- }
5152
- },
5153
- children: /* @__PURE__ */ jsx(
5154
- StatefulReaderProgression,
5155
- {
5156
- className: thorium_web_reader_pagination_default.progression,
5157
- formatPref: progressionFormatPref,
5158
- fallbackVariant: progressionFormatFallback
5159
- }
5160
- )
5161
- }
5162
- ) : /* @__PURE__ */ jsx(
5163
- StatefulReaderProgression,
5164
- {
5165
- formatPref: progressionFormatPref,
5166
- fallbackVariant: progressionFormatFallback
5167
- }
5168
- )
5169
- }
5170
- )
5171
- ] });
5172
- };
5173
-
5174
- // src/core/Hooks/fonts/androidPatchCss.ts
5175
- var getAndroidPatchCss = () => {
5176
- if (typeof window === "undefined") {
5177
- return "";
5178
- }
5179
- const origin = window.location.origin;
5180
- return `/* Readium CSS
5181
- Android Fonts Patch module
5182
-
5183
- A stylesheet aligning Android generic serif and sans-serif fonts on other platforms
5184
-
5185
- Repo: https://github.com/readium/css */
5186
-
5187
- /* Android resolves sans-serif to Roboto, and serif to Droid Serif
5188
- This created issues for FXL EPUBs relying on Times (New Roman),
5189
- Helvetica or Arial while not embedding the font files in the package.
5190
-
5191
- See https://github.com/readium/css/issues/149
5192
-
5193
- Unfortunately it is no possible to target generic family using @font-face,
5194
- we have to target specific font-family names e.g. Times, Arial, etc.
5195
-
5196
- This stylesheet/patch should be loaded only for this case i.e.
5197
- a Fixed-Layout EPUB with text but no embedded font on an Android device.
5198
- The logic for checking these conditions are up to implementers.
5199
- */
5200
-
5201
- /* Serif (Times + Times New Roman) */
5202
-
5203
- @font-face {
5204
- font-family: Times;
5205
- src: url("${new URL("/fonts/AndroidPatch/serif/NimbusRoman.woff", origin).toString()}") format("woff");
5206
- font-weight: normal;
5207
- font-style: normal;
5208
- }
5209
-
5210
- @font-face {
5211
- font-family: "Times New Roman";
5212
- src: url("${new URL("/fonts/AndroidPatch/serif/NimbusRoman.woff", origin).toString()}") format("woff");
5213
- font-weight: normal;
5214
- font-style: normal;
5215
- }
5216
-
5217
- @font-face {
5218
- font-family: Times;
5219
- src: url("${new URL("/fonts/AndroidPatch/serif/NimbusRoman-Italic.woff", origin).toString()}") format("woff");
5220
- font-weight: normal;
5221
- font-style: italic;
5222
- }
5223
-
5224
- @font-face {
5225
- font-family: "Times New Roman";
5226
- src: url("${new URL("/fonts/AndroidPatch/serif/NimbusRoman-Italic.woff", origin).toString()}") format("woff");
5227
- font-weight: normal;
5228
- font-style: italic;
5229
- }
5230
-
5231
- @font-face {
5232
- font-family: Times;
5233
- src: url("${new URL("/fonts/AndroidPatch/serif/NimbusRoman-Bold.woff", origin).toString()}") format("woff");
5234
- font-weight: bold;
5235
- font-style: normal;
5236
- }
5237
-
5238
- @font-face {
5239
- font-family: "Times New Roman";
5240
- src: url("${new URL("/fonts/AndroidPatch/serif/NimbusRoman-Bold.woff", origin).toString()}") format("woff");
5241
- font-weight: bold;
5242
- font-style: normal;
5243
- }
5244
-
5245
- @font-face {
5246
- font-family: Times;
5247
- src: url("${new URL("/fonts/AndroidPatch/serif/NimbusRoman-BoldItalic.woff", origin).toString()}") format("woff");
5248
- font-weight: bold;
5249
- font-style: italic;
5250
- }
5251
-
5252
- @font-face {
5253
- font-family: "Times New Roman";
5254
- src: url("${new URL("/fonts/AndroidPatch/serif/NimbusRoman-BoldItalic.woff", origin).toString()}") format("woff");
5255
- font-weight: bold;
5256
- font-style: italic;
5257
- }
5258
-
5259
- /* Sans-serif (Helvetica + Arial) */
5260
-
5261
- @font-face {
5262
- font-family: Helvetica;
5263
- src: url("${new URL("/fonts/AndroidPatch/sans-serif/NimbusSans.woff", origin).toString()}") format("woff");
5264
- font-weight: normal;
5265
- font-style: normal;
5266
- }
5267
-
5268
- @font-face {
5269
- font-family: Arial;
5270
- src: url("${new URL("/fonts/AndroidPatch/sans-serif/NimbusSans.woff", origin).toString()}") format("woff");
5271
- font-weight: normal;
5272
- font-style: normal;
5273
- }
5274
-
5275
- @font-face {
5276
- font-family: Helvetica;
5277
- src: url("${new URL("/fonts/AndroidPatch/sans-serif/NimbusSans-Italic.woff", origin).toString()}") format("woff");
5278
- font-weight: normal;
5279
- font-style: italic;
5280
- }
5281
-
5282
- @font-face {
5283
- font-family: Arial;
5284
- src: url("${new URL("/fonts/AndroidPatch/sans-serif/NimbusSans-Italic.woff", origin).toString()}") format("woff");
5285
- font-weight: normal;
5286
- font-style: italic;
5287
- }
5288
-
5289
- @font-face {
5290
- font-family: Helvetica;
5291
- src: url("${new URL("/fonts/AndroidPatch/sans-serif/NimbusSans-Bold.woff", origin).toString()}") format("woff");
5292
- font-weight: bold;
5293
- font-style: normal;
5294
- }
5295
-
5296
- @font-face {
5297
- font-family: Arial;
5298
- src: url("${new URL("/fonts/AndroidPatch/sans-serif/NimbusSans-Bold.woff", origin).toString()}") format("woff");
5299
- font-weight: bold;
5300
- font-style: normal;
5301
- }
5302
-
5303
- @font-face {
5304
- font-family: Helvetica;
5305
- src: url("${new URL("/fonts/AndroidPatch/sans-serif/NimbusSans-BoldItalic.woff", origin).toString()}") format("woff");
5306
- font-weight: bold;
5307
- font-style: italic;
5308
- }
5309
-
5310
- @font-face {
5311
- font-family: Arial;
5312
- src: url("${new URL("/fonts/AndroidPatch/sans-serif/NimbusSans-BoldItalic.woff", origin).toString()}") format("woff");
5313
- font-weight: bold;
5314
- font-style: italic;
5315
- }`;
5316
- };
5317
-
5318
- // src/core/Hooks/fonts/useFonts.ts
5319
- var useFonts = (fontResources) => {
5320
- const injectedElementsRef = useRef({
5321
- prepend: [],
5322
- append: []
5323
- });
5324
- const createLinkElement = useCallback((resource) => {
5325
- const link = document.createElement("link");
5326
- if ("attributes" in resource && resource.attributes) {
5327
- Object.entries(resource.attributes).forEach(([key, value]) => {
5328
- link.setAttribute(key, value);
5329
- });
5330
- }
5331
- link.rel = resource.rel;
5332
- link.as = resource.as;
5333
- if ("url" in resource) {
5334
- link.href = resource.url;
5335
- } else if ("blob" in resource && resource.blob) {
5336
- link.href = URL.createObjectURL(resource.blob);
5337
- }
5338
- return link;
5339
- }, []);
5340
- const removeInjectedElements = useCallback(() => {
5341
- const { prepend, append } = injectedElementsRef.current;
5342
- [...prepend, ...append].forEach((element) => {
5343
- if (element.parentNode) {
5344
- element.parentNode.removeChild(element);
5345
- }
5346
- if (element instanceof HTMLLinkElement && element.href.startsWith("blob:")) {
5347
- URL.revokeObjectURL(element.href);
5348
- }
5349
- });
5350
- injectedElementsRef.current = {
5351
- prepend: [],
5352
- append: []
5353
- };
5354
- }, []);
5355
- const injectFontResources = useCallback((resources) => {
5356
- if (typeof document === "undefined") return;
5357
- removeInjectedElements();
5358
- if (!resources) return;
5359
- const { prepend, append } = resources;
5360
- const injectedElements = injectedElementsRef.current;
5361
- prepend.forEach((resource) => {
5362
- const element = createLinkElement(resource);
5363
- document.head.insertBefore(element, document.head.firstChild);
5364
- injectedElements.prepend.push(element);
5365
- });
5366
- append.forEach((resource) => {
5367
- const element = createLinkElement(resource);
5368
- document.head.appendChild(element);
5369
- injectedElements.append.push(element);
5370
- });
5371
- }, [createLinkElement, removeInjectedElements]);
5372
- const getAndroidFXLPatch = useCallback(() => {
5373
- const platform = getPlatform();
5374
- const isAndroid = platform === "android";
5375
- if (!isAndroid) {
5376
- return null;
5377
- }
5378
- const cssContent = getAndroidPatchCss();
5379
- const blob = new Blob([cssContent], { type: "text/css" });
5380
- return {
5381
- as: "link",
5382
- rel: "stylesheet",
5383
- blob
5384
- };
5385
- }, []);
5386
- useEffect(() => {
5387
- injectFontResources(fontResources || null);
5388
- return () => {
5389
- removeInjectedElements();
5390
- };
5391
- }, [fontResources, injectFontResources, removeInjectedElements]);
5392
6187
  return {
5393
- injectFontResources,
5394
- removeFontResources: removeInjectedElements,
5395
- getAndroidFXLPatch
6188
+ headerRef,
6189
+ focusWithinProps,
6190
+ setHover,
6191
+ removeHover,
6192
+ listActionItems,
6193
+ isImmersive,
6194
+ isHovering,
6195
+ isScroll,
6196
+ t
5396
6197
  };
5397
6198
  };
5398
6199
 
5399
- // src/helpers/peripherals.ts
5400
- var Peripherals = class {
5401
- observers = ["keydown"];
5402
- targets = [];
5403
- callbacks;
5404
- store;
5405
- actionsPref;
5406
- shortcuts;
5407
- constructor(store, actionsPref, callbacks) {
5408
- this.observers.forEach((method) => {
5409
- this["on" + method] = this["on" + method].bind(this);
5410
- });
5411
- this.store = store;
5412
- this.actionsPref = actionsPref;
5413
- this.callbacks = callbacks;
5414
- this.shortcuts = this.retrieveShortcuts();
5415
- }
5416
- getPlatformModifier() {
5417
- return this.store.getState().reader.platformModifier.modifier;
5418
- }
5419
- retrieveShortcuts() {
5420
- if (!this.actionsPref) return {};
5421
- const shortcutsObj = {};
5422
- const displayOrder = this.store.getState().publication.isFXL ? this.actionsPref.fxlOrder : this.actionsPref.reflowOrder;
5423
- for (const actionKey of displayOrder) {
5424
- const shortcutString = this.actionsPref.keys[actionKey].shortcut;
5425
- if (shortcutString) {
5426
- const shortcutObj = buildShortcut(shortcutString);
5427
- if (shortcutObj?.key) {
5428
- Object.defineProperty(shortcutsObj, shortcutObj.key, {
5429
- value: {
5430
- actionKey,
5431
- modifiers: shortcutObj.modifiers
5432
- },
5433
- writable: false,
5434
- enumerable: true
5435
- });
5436
- }
5437
- }
5438
- }
5439
- return shortcutsObj;
5440
- }
5441
- destroy() {
5442
- this.targets.forEach((t) => this.unobserve(t));
5443
- }
5444
- unobserve(item) {
5445
- if (!item) return;
5446
- this.observers.forEach((EventName) => {
5447
- item.removeEventListener(
5448
- EventName,
5449
- this["on" + EventName],
5450
- false
5451
- );
5452
- });
5453
- this.targets = this.targets.filter((t) => t !== item);
5454
- }
5455
- observe(item) {
5456
- if (!item) return;
5457
- if (this.targets.includes(item)) return;
5458
- this.observers.forEach((EventName) => {
5459
- item.addEventListener(EventName, this["on" + EventName], false);
5460
- });
5461
- this.targets.push(item);
5462
- }
5463
- onkeydown(e) {
5464
- const focusIsSafe = !isInteractiveElement(document.activeElement);
5465
- switch (e.code) {
5466
- case "Space":
5467
- focusIsSafe && this.callbacks.goProgression(e.shiftKey);
5468
- break;
5469
- case "ArrowRight":
5470
- focusIsSafe && this.callbacks.moveTo("right");
5471
- break;
5472
- case "ArrowLeft":
5473
- focusIsSafe && this.callbacks.moveTo("left");
5474
- break;
5475
- case "ArrowUp":
5476
- case "PageUp":
5477
- focusIsSafe && this.callbacks.moveTo("up");
5478
- break;
5479
- case "ArrowDown":
5480
- case "PageDown":
5481
- focusIsSafe && this.callbacks.moveTo("down");
5482
- break;
5483
- case "Home":
5484
- focusIsSafe && this.callbacks.moveTo("home");
5485
- break;
5486
- case "End":
5487
- focusIsSafe && this.callbacks.moveTo("end");
5488
- break;
5489
- default:
5490
- if (this.shortcuts.hasOwnProperty(e.code)) {
5491
- const customShortcutObj = this.shortcuts[e.code];
5492
- const sendCallback = Object.entries(customShortcutObj.modifiers).every(([modifier, value]) => {
5493
- if (modifier === "platformKey") {
5494
- return e[this.getPlatformModifier()] === value;
5495
- } else {
5496
- return e[modifier] === value;
5497
- }
5498
- });
5499
- if (sendCallback) {
5500
- e.preventDefault();
5501
- this.callbacks.toggleAction(customShortcutObj.actionKey);
5502
- }
5503
- }
5504
- break;
5505
- }
5506
- }
5507
- };
5508
- var LAYOUT_CLASSES = {
5509
- ["stacked-ui" /* stacked */]: "thorium_web_stackedUI",
5510
- ["layered-ui" /* layered */]: "thorium_web_layeredUI"
5511
- };
5512
- function getReaderClassNames(options) {
5513
- const {
5514
- layoutUI,
5515
- isScroll,
5516
- isImmersive = false,
5517
- isHovering = false,
5518
- isFXL = false,
5519
- breakpoint
5520
- } = options;
5521
- return classNames3(
5522
- thorium_web_reader_app_default.shell,
5523
- isScroll ? "thorium_web_isScroll" : "thorium_web_isPaged",
5524
- isImmersive && "thorium_web_isImmersive",
5525
- isHovering && "thorium_web_isHovering",
5526
- isFXL ? "thorium_web_isFXL" : "thorium_web_isReflow",
5527
- LAYOUT_CLASSES[layoutUI],
5528
- breakpoint ? `thorium_web_is${breakpoint.charAt(0).toUpperCase() + breakpoint.slice(1)}` : void 0
5529
- );
5530
- }
5531
-
5532
- export { NavigatorProvider, Peripherals, StatefulActionIcon, StatefulBottomSheet, StatefulCollapsibleActionsBar, StatefulColumns, StatefulDockedSheet, StatefulDockingWrapper, StatefulDropdown, StatefulFontFamily, StatefulFullScreenSheet, StatefulFullscreenTrigger, StatefulGroupWrapper, StatefulHyphens, StatefulJumpToPositionContainer, StatefulJumpToPositionTrigger, StatefulLayout, StatefulLetterSpacing, StatefulLineHeight, StatefulNumberField, StatefulOverflowMenu, StatefulOverflowMenuItem, StatefulParagraphIndent, StatefulParagraphSpacing, StatefulPopoverSheet, StatefulPreferencesProvider, StatefulPublisherStyles, StatefulRadioGroup, StatefulReaderFooter, StatefulReaderHeader, StatefulSettingsContainer, StatefulSettingsTrigger, StatefulSheetWrapper, StatefulSlider, StatefulSpacingGroup, StatefulSpacingGroupContainer, StatefulSpacingPresets, StatefulSwitch, StatefulTextAlign, StatefulTextGroup, StatefulTextGroupContainer, StatefulTextNormalize, StatefulTheme, StatefulTocContainer, StatefulTocTrigger, StatefulWordSpacing, StatefulZoom, ThPluginProvider, ThPluginRegistry, UnstableStatefulFontWeight, createDefaultPlugin, getReaderClassNames, thorium_web_button_default, thorium_web_reader_app_default, useDocking, useFonts, useGridNavigation, useGridTemplate, useLineHeight, useNavigator, usePositionStorage, usePublication, useReaderTransitions, useSettingsComponentStatus, useSpacingPresets };
5533
- //# sourceMappingURL=chunk-PXAUQJEU.mjs.map
5534
- //# sourceMappingURL=chunk-PXAUQJEU.mjs.map
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