@edrlab/thorium-web 1.0.0-beta.2 → 1.0.0-beta.4

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 (64) hide show
  1. package/LICENSE +1 -1
  2. package/dist/{ThSettingsWrapper-De4qvjsh.d.mts → ThSettingsWrapper-CA2_0nQz.d.mts} +1 -1
  3. package/dist/{actionsReducer-e808e6V_.d.mts → actionsReducer-D4Mphfuv.d.mts} +1 -1
  4. package/dist/chunk-BNPR6V7V.mjs.map +1 -1
  5. package/dist/chunk-IEYR7QV7.mjs.map +1 -1
  6. package/dist/chunk-MLEYTQGK.mjs.map +1 -1
  7. package/dist/chunk-NA3KMUS4.mjs +417 -0
  8. package/dist/chunk-NA3KMUS4.mjs.map +1 -0
  9. package/dist/{chunk-GHEGQBCW.mjs → chunk-NOV43IG4.mjs} +4 -18
  10. package/dist/chunk-NOV43IG4.mjs.map +1 -0
  11. package/dist/chunk-NQ2ZSGCX.mjs.map +1 -1
  12. package/dist/{chunk-O4MBY6P4.mjs → chunk-OCASVHBV.mjs} +91 -36
  13. package/dist/chunk-OCASVHBV.mjs.map +1 -0
  14. package/dist/{chunk-TWGRY5SW.mjs → chunk-RQFPGXWN.mjs} +29 -22
  15. package/dist/chunk-RQFPGXWN.mjs.map +1 -0
  16. package/dist/{chunk-BVSROK7Z.mjs → chunk-SDVDRPT5.mjs} +116 -6
  17. package/dist/chunk-SDVDRPT5.mjs.map +1 -0
  18. package/dist/chunk-TTGURRX3.mjs.map +1 -1
  19. package/dist/{chunk-PGZF5NO2.mjs → chunk-TXILKP4F.mjs} +16 -12
  20. package/dist/chunk-TXILKP4F.mjs.map +1 -0
  21. package/dist/components/Epub/index.d.mts +48 -25
  22. package/dist/components/Epub/index.mjs +878 -408
  23. package/dist/components/Epub/index.mjs.map +1 -1
  24. package/dist/{const-DGYvRLhk.d.mts → const-IAfi9t_g.d.mts} +14 -11
  25. package/dist/core/Components/index.d.mts +52 -6
  26. package/dist/core/Components/index.mjs +2 -2
  27. package/dist/core/Helpers/index.d.mts +3 -9
  28. package/dist/core/Helpers/index.mjs +2 -3
  29. package/dist/core/Hooks/index.d.mts +15 -5
  30. package/dist/core/Hooks/index.mjs +2 -3
  31. package/dist/{enums-DxbWWvn7.d.mts → enums-DqGQ66r1.d.mts} +1 -23
  32. package/dist/lib/index.d.mts +355 -11
  33. package/dist/lib/index.mjs +2 -3
  34. package/dist/{overflowMenu.module-XQRI7RJJ.module.css → overflowMenu.module-FO27A2A3.module.css} +12 -0
  35. package/dist/preferences/index.d.mts +6 -6
  36. package/dist/preferences/index.mjs +2 -3
  37. package/dist/publicationGrid.module-T6ISNRGM.module.css +70 -0
  38. package/dist/{reader-QFK7DGLX.css → reader-NGCQJKLX.css} +74 -36
  39. package/dist/{readerArrowButton.module-NHAUIQXS.module.css → readerArrowButton.module-EFLOIADG.module.css} +4 -0
  40. package/dist/{readerHeader.module-OBKZATSW.module.css → readerHeader.module-K7OLOIJP.module.css} +2 -7
  41. package/dist/{readerLoader.module-U6LLXOVQ.module.css → readerLoader.module-KDBPCQZJ.module.css} +1 -1
  42. package/dist/readerPagination.module-W4IAEOSH.module.css +73 -0
  43. package/dist/readerProgression.module-7PU7L74S.module.css +5 -0
  44. package/dist/{readerSharedUI.module-MCLGNG22.module.css → readerSharedUI.module-Y2VDWFS5.module.css} +1 -5
  45. package/dist/{settings.module-5WB3OOEF.module.css → settings.module-O2SOKTIN.module.css} +4 -0
  46. package/dist/{sheets.module-DDN7GPMG.module.css → sheets.module-NA32WOSZ.module.css} +4 -4
  47. package/dist/{toc.module-XJLXQM7G.module.css → toc.module-KNW5CKIY.module.css} +1 -0
  48. package/dist/{useBreakpoints-I7vHrywa.d.mts → useBreakpoints-BQaiwecQ.d.mts} +1 -1
  49. package/dist/{useEpubNavigator-BrV4MYJy.d.mts → useEpubNavigator-DFRJ_tHa.d.mts} +9 -10
  50. package/dist/{usePreferences-B55XqFto.d.mts → usePreferences-BMyBhq7F.d.mts} +1 -1
  51. package/dist/useTimeline-Bid5B7AQ.d.mts +39 -0
  52. package/package.json +31 -22
  53. package/dist/bookUrlConverter.module-J46O27CR.module.css +0 -63
  54. package/dist/chunk-3QS3WKRC.mjs +0 -29
  55. package/dist/chunk-3QS3WKRC.mjs.map +0 -1
  56. package/dist/chunk-BVSROK7Z.mjs.map +0 -1
  57. package/dist/chunk-GHEGQBCW.mjs.map +0 -1
  58. package/dist/chunk-JS5WI5D4.mjs +0 -600
  59. package/dist/chunk-JS5WI5D4.mjs.map +0 -1
  60. package/dist/chunk-O4MBY6P4.mjs.map +0 -1
  61. package/dist/chunk-PGZF5NO2.mjs.map +0 -1
  62. package/dist/chunk-TWGRY5SW.mjs.map +0 -1
  63. package/dist/index-ClB-1iNN.d.mts +0 -356
  64. package/dist/readerProgression.module-S5R3Y6JI.module.css +0 -11
@@ -1,42 +1,42 @@
1
- import { require_debounce, useAppSelector, useAppDispatch, setColumnCount, setFontFamily, setFontWeight, setHyphens, setScroll, setLetterSpacing, setPublisherStyles, setLineHeight, setParagraphIndent, setParagraphSpacing, setSettingsContainer, setTextAlign, setTextNormalization, setTheme, setWordSpacing, setFontSize, toggleImmersive, setFullscreen, setActionOpen, dockAction, setReducedTransparency, setReducedMotion, setMonochrome, setForcedColors, setContrast, setColorScheme, setBreakpoint, setImmersive, setHovering, setArrows, setProgression, setTocEntry, useAppStore, toggleActionOpen, setDirection, setPlatformModifier, setRTL, setFXL, setRunningHead, setTocTree, setOverflow, setPositionsList, setLoading, collapseDockPanel, expandDockPanel, activateDockPanel, deactivateDockPanel, setDockPanelWidth } from '../../chunk-O4MBY6P4.mjs';
2
- export { ThStoreProvider, actionsSlice, activateDockPanel, collapseDockPanel, deactivateDockPanel, dockAction, expandDockPanel, makeStore, publicationSlice, readerSlice, setActionOpen, setArrows, setBreakpoint, setColorScheme, setColumnCount, setContrast, setDirection, setDockPanelWidth, setFXL, setFontFamily, setFontSize, setFontWeight, setForcedColors, setFullscreen, setHovering, setHyphens, setImmersive, setLayoutStrategy, setLetterSpacing, setLineHeight, setLineLength, setLoading, setMonochrome, setOverflow, setParagraphIndent, setParagraphSpacing, setPlatformModifier, setPositionsList, setProgression, setPublicationEnd, setPublicationStart, setPublisherStyles, setRTL, setReducedMotion, setReducedTransparency, setRunningHead, setScroll, setSettingsContainer, setTextAlign, setTextNormalization, setTheme, setTocEntry, setTocTree, setWordSpacing, settingsSlice, themeSlice, toggleActionOpen, toggleImmersive, useAppDispatch, useAppSelector, useAppStore } from '../../chunk-O4MBY6P4.mjs';
3
- import { defaultFontFamilyOptions, usePreferences, defaultLetterSpacing, defaultLineHeights, defaultParagraphIndent, defaultParagraphSpacing, defaultSpacingSettingsSubpanel, defaultSpacingSettingsMain, defaultTextSettingsSubpanel, defaultTextSettingsMain, usePreferenceKeys, buildThemeObject, defaultWordSpacing, defaultFontSize, useTheming } from '../../chunk-PGZF5NO2.mjs';
4
- export { ThPreferencesProvider, usePreferences, useTheming } from '../../chunk-PGZF5NO2.mjs';
5
- import { ThRadioGroup, ThDropdown, ThSlider, ThSwitch, ThNumberField, ThSettingsWrapper, ThActionButton, ThMenu, ThCollapsibleActionsBar, ThMenuItem, ThPopover, ThContainerHeader, ThNavigationButton, ThContainerBody, ThBottomSheet, ThCloseButton, ThModal, ThDockedPanel, ThTypedComponentRenderer, useActions, ThForm, ThFormNumberField, ThFormSearchField, ThFormTextField, ThLoader, ThHeader, ThRunningHead, ThFooter, ThProgression } from '../../chunk-BVSROK7Z.mjs';
1
+ import { require_debounce, useAppSelector, useAppDispatch, setColumnCount, setFontFamily, setFontWeight, setHyphens, setScroll, setLetterSpacing, setPublisherStyles, setLineHeight, setParagraphIndent, setParagraphSpacing, setSettingsContainer, setTextAlign, setTextNormalization, setTheme, setWordSpacing, setFontSize, setFullscreen, setActionOpen, dockAction, setReducedTransparency, setReducedMotion, setMonochrome, setForcedColors, setContrast, setColorScheme, setBreakpoint, setTimeline, setImmersive, setHovering, setArrows, toggleImmersive, setProgression, useAppStore, toggleActionOpen, setDirection, setPlatformModifier, setRTL, setFXL, setRunningHead, setOverflow, setScrollAffordance, setPublicationStart, setPublicationEnd, setPositionsList, setLoading, setTocEntry, collapseDockPanel, expandDockPanel, activateDockPanel, deactivateDockPanel, setDockPanelWidth } from '../../chunk-OCASVHBV.mjs';
2
+ export { ThStoreProvider, actionsSlice, activateDockPanel, collapseDockPanel, deactivateDockPanel, dockAction, expandDockPanel, makeStore, publicationSlice, readerSlice, setActionOpen, setArrows, setBreakpoint, setColorScheme, setColumnCount, setContrast, setDirection, setDockPanelWidth, setFXL, setFontFamily, setFontSize, setFontWeight, setForcedColors, setFullscreen, setHovering, setHyphens, setImmersive, setLetterSpacing, setLineHeight, setLineLength, setLoading, setMonochrome, setOverflow, setParagraphIndent, setParagraphSpacing, setPlatformModifier, setPositionsList, setProgression, setPublicationEnd, setPublicationStart, setPublisherStyles, setRTL, setReducedMotion, setReducedTransparency, setRunningHead, setScroll, setScrollAffordance, setSettingsContainer, setTextAlign, setTextNormalization, setTheme, setTimeline, setTocEntry, setTocTree, setWordSpacing, settingsSlice, themeSlice, toggleActionOpen, toggleImmersive, useAppDispatch, useAppSelector, useAppStore } from '../../chunk-OCASVHBV.mjs';
3
+ import { defaultFontFamilyOptions, usePreferences, defaultLetterSpacing, defaultLineHeights, defaultParagraphIndent, defaultParagraphSpacing, defaultSpacingSettingsSubpanel, defaultSpacingSettingsMain, defaultTextSettingsSubpanel, defaultTextSettingsMain, usePreferenceKeys, buildThemeObject, defaultWordSpacing, defaultFontSize, useTheming } from '../../chunk-TXILKP4F.mjs';
4
+ export { ThPreferencesProvider, usePreferences, useTheming } from '../../chunk-TXILKP4F.mjs';
5
+ import { ThRadioGroup, ThDropdown, ThSlider, ThSwitch, ThNumberField, ThSettingsWrapper, ThActionButton, ThMenu, ThCollapsibleActionsBar, ThMenuItem, ThPopover, ThContainerHeader, ThNavigationButton, ThContainerBody, ThBottomSheet, ThCloseButton, ThModal, ThDockedPanel, ThTypedComponentRenderer, useActions, ThForm, ThFormNumberField, ThFormSearchField, ThLoader, ThGrid, ThInteractiveOverlay, ThHeader, ThRunningHead, ThFooter, ThPagination, ThProgression } from '../../chunk-SDVDRPT5.mjs';
6
6
  import '../../chunk-MLEYTQGK.mjs';
7
- import { makeBreakpointsMap, localData, isKeyboardTriggered, isActiveElement, isInteractiveElement } from '../../chunk-GHEGQBCW.mjs';
8
- export { isActiveElement, isInteractiveElement, isKeyboardTriggered, localData, makeBreakpointsMap } from '../../chunk-GHEGQBCW.mjs';
7
+ import { makeBreakpointsMap, isKeyboardTriggered, isActiveElement, isInteractiveElement } from '../../chunk-NOV43IG4.mjs';
8
+ export { isActiveElement, isInteractiveElement, isKeyboardTriggered, makeBreakpointsMap } from '../../chunk-NOV43IG4.mjs';
9
9
  import { propsToCSSVars } from '../../chunk-TTGURRX3.mjs';
10
10
  export { propsToCSSVars } from '../../chunk-TTGURRX3.mjs';
11
11
  import { isIOSish, getPlatformModifier, buildShortcut, metaKeys } from '../../chunk-IEYR7QV7.mjs';
12
12
  export { UnstableShortcutMetaKeywords, UnstableShortcutRepresentation, buildShortcut, defaultPlatformModifier, getPlatform, getPlatformModifier, isIOSish, isIpadOS, isMacish, metaKeys } from '../../chunk-IEYR7QV7.mjs';
13
- import { useEpubNavigator, en_default, useFullscreen, usePrevious, TH_CUSTOM_SCHEME, ThScrollActions } from '../../chunk-JS5WI5D4.mjs';
14
- export { useEpubNavigator } from '../../chunk-JS5WI5D4.mjs';
13
+ import { useEpubNavigator, useFullscreen, usePrevious, useLocalStorage, useTimeline } from '../../chunk-NA3KMUS4.mjs';
14
+ export { useEpubNavigator } from '../../chunk-NA3KMUS4.mjs';
15
15
  import '../../chunk-BNPR6V7V.mjs';
16
16
  import '../../chunk-NQ2ZSGCX.mjs';
17
- import { ThDockingTypes, ThSheetTypes } from '../../chunk-TWGRY5SW.mjs';
18
- import { __commonJS, __toESM } from '../../chunk-3QS3WKRC.mjs';
19
- import React48, { createContext, useCallback, useRef, useContext, useEffect, useMemo, useState } from 'react';
17
+ import { __commonJS, __toESM, ThDockingTypes, ThSheetTypes } from '../../chunk-RQFPGXWN.mjs';
18
+ import React49, { createContext, useState, useMemo, useCallback, useEffect, useRef, useContext, isValidElement, cloneElement } from 'react';
20
19
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
21
- import settingsStyles3 from '../../settings.module-5WB3OOEF.module.css';
22
- import { ListBox, ListBoxItem, Radio, Text, useFilter, Tree, TreeItem, TreeItemContent, Button, Collection, Keyboard, Toolbar } from 'react-aria-components';
20
+ import settingsStyles3 from '../../settings.module-O2SOKTIN.module.css';
21
+ import { ListBox, ListBoxItem, Radio, Text, useFilter, Tree, TreeItem, TreeItemContent, Button, Collection, Link, Keyboard, Toolbar } from 'react-aria-components';
23
22
  import { fontWeightRangeConfig, TextAlignment } from '@readium/navigator';
24
- import classNames9 from 'classnames';
25
- import readerSharedUI3 from '../../readerSharedUI.module-MCLGNG22.module.css';
26
- import '../../reader-QFK7DGLX.css';
27
- import arrowStyles2 from '../../readerArrowButton.module-NHAUIQXS.module.css';
28
- import { I18nProvider } from 'react-aria';
29
- import { Link, EPUBLayout, Locator, HttpFetcher, Manifest, Publication, ReadingProgression } from '@readium/shared';
23
+ import classNames10 from 'classnames';
24
+ import readerSharedUI3 from '../../readerSharedUI.module-Y2VDWFS5.module.css';
25
+ import '../../reader-NGCQJKLX.css';
26
+ import arrowStyles2 from '../../readerArrowButton.module-EFLOIADG.module.css';
27
+ import { I18nProvider, useFocusWithin } from 'react-aria';
28
+ import { HttpFetcher, Manifest, Publication, ReadingProgression, EPUBLayout, Link as Link$1 } from '@readium/shared';
30
29
  import dockingStyles from '../../docking.module-TDNYZX4H.module.css';
31
30
  import { PanelGroup, Panel, PanelResizeHandle } from 'react-resizable-panels';
32
- import readerHeaderStyles from '../../readerHeader.module-OBKZATSW.module.css';
33
- import overflowMenuStyles from '../../overflowMenu.module-XQRI7RJJ.module.css';
34
- import progressionStyles from '../../readerProgression.module-S5R3Y6JI.module.css';
31
+ import readerHeaderStyles from '../../readerHeader.module-K7OLOIJP.module.css';
32
+ import overflowMenuStyles from '../../overflowMenu.module-FO27A2A3.module.css';
33
+ import readerPaginationStyles2 from '../../readerPagination.module-W4IAEOSH.module.css';
34
+ import progressionStyles from '../../readerProgression.module-7PU7L74S.module.css';
35
35
  import jumpToPositionStyles from '../../jumpToPosition.module-4C47UYPE.module.css';
36
- import sheetStyles2 from '../../sheets.module-DDN7GPMG.module.css';
37
- import tocStyles from '../../toc.module-XJLXQM7G.module.css';
38
- import bookUrlConverterStyles from '../../bookUrlConverter.module-J46O27CR.module.css';
39
- import readerLoaderStyles from '../../readerLoader.module-U6LLXOVQ.module.css';
36
+ import sheetStyles2 from '../../sheets.module-NA32WOSZ.module.css';
37
+ import tocStyles from '../../toc.module-KNW5CKIY.module.css';
38
+ import readerLoaderStyles from '../../readerLoader.module-KDBPCQZJ.module.css';
39
+ import publicationGridStyles from '../../publicationGrid.module-T6ISNRGM.module.css';
40
40
 
41
41
  // node_modules/.pnpm/object-path@0.11.8/node_modules/object-path/index.js
42
42
  var require_object_path = __commonJS({
@@ -434,6 +434,251 @@ var require_dist = __commonJS({
434
434
  });
435
435
  }
436
436
  });
437
+
438
+ // src/resources/locales/en.json
439
+ var en_default = {
440
+ reader: {
441
+ app: {
442
+ loading: "Loading",
443
+ publicationWrapper: "You\u2019re now in the publication.",
444
+ docking: {
445
+ dockingLeft: "Docked panel left",
446
+ dockingRight: "Docked panel right",
447
+ dockingEmpty: "Currently empty",
448
+ dockingClosed: "Is closed, contains {{ action }}",
449
+ dockingCollapsed: "Is Collapsed, contains {{ action }}"
450
+ },
451
+ header: {
452
+ label: "Top bar",
453
+ runningHead: "Running head",
454
+ runningHeadFallback: "...",
455
+ actions: "Actions"
456
+ },
457
+ footer: {
458
+ label: "Bottom bar"
459
+ },
460
+ progression: {
461
+ wrapper: "Current progression",
462
+ of: "{{ current }} of {{ reference }}",
463
+ referenceFallback: "current chapter",
464
+ pubFallback: "the publication"
465
+ },
466
+ docker: {
467
+ wrapper: "Docking options",
468
+ dockToLeft: {
469
+ trigger: "Dock to the left",
470
+ tooltip: "Dock left"
471
+ },
472
+ dockToRight: {
473
+ trigger: "Dock to the right",
474
+ tooltip: "Dock Right"
475
+ },
476
+ popover: {
477
+ trigger: "Display in a window",
478
+ tooltip: "Pop over"
479
+ },
480
+ fullscreen: {
481
+ trigger: "Display over contents",
482
+ tooltip: "Overlay"
483
+ },
484
+ close: {
485
+ trigger: "Close",
486
+ tooltip: "Close"
487
+ }
488
+ },
489
+ back: {
490
+ trigger: "Back"
491
+ }
492
+ },
493
+ navigation: {
494
+ goForward: "Go forward",
495
+ goBackward: "Go backward",
496
+ scroll: {
497
+ wrapper: "Navigate through the publication",
498
+ prevLabel: "Previous",
499
+ nextLabel: "Next",
500
+ prevA11yLabel: "Go to the previous resource",
501
+ nextA11yLabel: "Go to the next resource"
502
+ }
503
+ },
504
+ fullscreen: {
505
+ tooltip: "Fullscreen",
506
+ trigger: "Toggle fullscreen mode",
507
+ close: "Exit fullscreen mode"
508
+ },
509
+ toc: {
510
+ tooltip: "Table of contents",
511
+ trigger: "Table of contents",
512
+ close: "Close table of contents",
513
+ heading: "Table of contents",
514
+ entries: "Entries in the table",
515
+ empty: "The table of contents was not provided for this publication.",
516
+ search: {
517
+ label: "Search",
518
+ placeholder: "Search",
519
+ clear: "Clear"
520
+ }
521
+ },
522
+ jumpToPosition: {
523
+ tooltip: "Jump to\u2026",
524
+ trigger: "Jump to position",
525
+ heading: "Jump to position",
526
+ go: "Go",
527
+ close: "Close jump to position",
528
+ label: "Select a position from {{ positionStart }} to {{ positionEnd }}:"
529
+ },
530
+ overflowMenu: {
531
+ active: {
532
+ tooltip: "More actions",
533
+ trigger: "Toggle menu"
534
+ },
535
+ hint: {
536
+ tooltip: "Toggle actions",
537
+ trigger: "Display actions"
538
+ }
539
+ },
540
+ layoutStrategy: {
541
+ tooltip: "Layout",
542
+ trigger: "Layout strategy",
543
+ close: "Close layout strategy menu",
544
+ heading: "Layout strategy",
545
+ title: "Strategy",
546
+ margin: "Margin",
547
+ lineLength: "Line length",
548
+ columns: "Columns",
549
+ minChars: "Disable minimal line length",
550
+ maxChars: "Disable maximal line length",
551
+ minimalLineLength: {
552
+ title: "Minimal line length",
553
+ increase: "Increase minimal line length",
554
+ decrease: "Decrease maximal line length"
555
+ },
556
+ optimalLineLength: {
557
+ title: "Optimal line length",
558
+ increase: "Increase optimal line length",
559
+ decrease: "Decrease optimal line length"
560
+ },
561
+ maximalLineLength: {
562
+ title: "Maximal line length",
563
+ increase: "Increase maximal line length",
564
+ decrease: "Decrease maximal line length"
565
+ }
566
+ },
567
+ settings: {
568
+ tooltip: "Settings",
569
+ trigger: "Settings menu",
570
+ close: "Close settings menu",
571
+ heading: "Display settings",
572
+ themes: {
573
+ title: "Themes",
574
+ auto: "Auto",
575
+ light: "Light",
576
+ sepia: "Sepia",
577
+ paper: "Paper",
578
+ dark: "Dark",
579
+ contrast1: "Contrast 1",
580
+ contrast2: "Contrast 2",
581
+ contrast3: "Contrast 3"
582
+ },
583
+ zoom: {
584
+ title: "Zoom",
585
+ increase: "Increase zoom",
586
+ decrease: "Decrease zoom"
587
+ },
588
+ fontSize: {
589
+ title: "Font size",
590
+ increase: "Increase font size",
591
+ decrease: "Decrease font size"
592
+ },
593
+ fontFamily: {
594
+ title: "Font family",
595
+ labels: {
596
+ publisher: "Publisher\u2019s font",
597
+ oldStyle: "Old style serif",
598
+ modern: "Modern serif",
599
+ sans: "Sans-serif",
600
+ humanist: "Humanist sans-serif",
601
+ monospace: "Monospace"
602
+ }
603
+ },
604
+ fontWeight: {
605
+ title: "Font weight",
606
+ increase: "Increase font weight",
607
+ decrease: "Decrease font weight"
608
+ },
609
+ normalizeText: {
610
+ title: "Text normalization",
611
+ label: "Remove all text formatting"
612
+ },
613
+ align: {
614
+ title: "Text alignment",
615
+ publisher: "Default",
616
+ left: "Left",
617
+ justify: "Justify",
618
+ right: "Right"
619
+ },
620
+ hyphens: {
621
+ title: "Hyphenation",
622
+ label: "Enable hyphens"
623
+ },
624
+ text: {
625
+ title: "Text",
626
+ advanced: {
627
+ tooltip: "More options",
628
+ trigger: "More formatting options"
629
+ }
630
+ },
631
+ lineHeight: {
632
+ title: "Line height",
633
+ publisher: "Default",
634
+ small: "Tight",
635
+ medium: "Balanced",
636
+ large: "Loose"
637
+ },
638
+ paraSpacing: {
639
+ title: "Paragraph spacing",
640
+ increase: "Increase paragraph spacing",
641
+ decrease: "Decrease paragraph spacing"
642
+ },
643
+ paraIndent: {
644
+ title: "Paragraph indent",
645
+ increase: "Increase paragraph indent",
646
+ decrease: "Decrease paragraph indent"
647
+ },
648
+ wordSpacing: {
649
+ title: "Word spacing",
650
+ increase: "Increase word spacing",
651
+ decrease: "Decrease word spacing"
652
+ },
653
+ letterSpacing: {
654
+ title: "Letter spacing",
655
+ increase: "Increase letter spacing",
656
+ decrease: "Decrease letter spacing"
657
+ },
658
+ publisherStyles: {
659
+ label: "Use original formatting"
660
+ },
661
+ spacing: {
662
+ title: "Spacing",
663
+ advanced: {
664
+ tooltip: "More options",
665
+ trigger: "More spacing options"
666
+ }
667
+ },
668
+ layout: {
669
+ title: "Layout",
670
+ scrolled: "Scrollable",
671
+ paginated: "Paginated"
672
+ },
673
+ column: {
674
+ title: "Columns",
675
+ auto: "Auto",
676
+ one: "1 col",
677
+ two: "2 cols"
678
+ }
679
+ }
680
+ }
681
+ };
437
682
  var SvgDocumentScanner = (props) => /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 -960 960 960", width: "24px", fill: "inherit", ...props, children: /* @__PURE__ */ jsx("path", { d: "M80-720v-200h200v80H160v120H80Zm720 0v-120H680v-80h200v200h-80ZM80-40v-200h80v120h120v80H80Zm600 0v-80h120v-120h80v200H680ZM280-240h400v-480H280v480Zm0 80q-33 0-56.5-23.5T200-240v-480q0-33 23.5-56.5T280-800h400q33 0 56.5 23.5T760-720v480q0 33-23.5 56.5T680-160H280Zm80-400h240v-80H360v80Zm0 120h240v-80H360v80Zm0 120h240v-80H360v80Zm-80 80v-480 480Z" }) });
438
683
  var document_scanner_default = SvgDocumentScanner;
439
684
  var SvgArticle = (props) => /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 -960 960 960", width: "24px", fill: "inherit", ...props, children: /* @__PURE__ */ jsx("path", { d: "M280-280h280v-80H280v80Zm0-160h400v-80H280v80Zm0-160h400v-80H280v80Zm-80 480q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z" }) });
@@ -467,26 +712,61 @@ var StatefulRadioGroup = ({
467
712
  }
468
713
  ) });
469
714
  };
715
+
716
+ // src/components/Epub/Settings/StatefulColumns.tsx
717
+ var import_debounce = __toESM(require_debounce());
470
718
  var StatefulColumns = () => {
471
- const isScroll = useAppSelector((state) => state.settings.scroll);
719
+ const scroll = useAppSelector((state) => state.settings.scroll);
472
720
  const isFXL = useAppSelector((state) => state.publication.isFXL);
721
+ const isScroll = scroll && !isFXL;
473
722
  const columnCount = useAppSelector((state) => state.settings.columnCount) || "auto";
723
+ const [effectiveValue, setEffectiveValue] = useState(columnCount);
724
+ const fontSize = useAppSelector((state) => state.settings.fontSize);
725
+ const fontFamily = useAppSelector((state) => state.settings.fontFamily);
726
+ const wordSpacing = useAppSelector((state) => state.settings.wordSpacing);
727
+ const letterSpacing = useAppSelector((state) => state.settings.letterSpacing);
728
+ const publisherStyles = useAppSelector((state) => state.settings.publisherStyles);
729
+ const layoutSettings = useMemo(() => {
730
+ return {
731
+ fontSize,
732
+ fontFamily,
733
+ wordSpacing,
734
+ letterSpacing,
735
+ publisherStyles
736
+ };
737
+ }, [fontSize, fontFamily, wordSpacing, letterSpacing, publisherStyles]);
474
738
  const dispatch = useAppDispatch();
475
- const { submitPreferences } = useEpubNavigator();
739
+ const { submitPreferences, getSetting } = useEpubNavigator();
740
+ const updateEffectiveValue = useCallback((preference, setting) => {
741
+ const derivedValue = preference === "auto" || setting === null ? "auto" : setting.toString();
742
+ setEffectiveValue(derivedValue);
743
+ }, []);
476
744
  const updatePreference = useCallback(async (value) => {
477
745
  const colCount = value === "auto" ? null : Number(value);
478
746
  await submitPreferences({ columnCount: colCount });
747
+ updateEffectiveValue(value, getSetting("columnCount"));
479
748
  dispatch(setColumnCount(value));
480
- }, [submitPreferences, dispatch]);
749
+ }, [submitPreferences, getSetting, updateEffectiveValue, dispatch]);
750
+ const debouncedUpdate = useCallback(() => {
751
+ const update = () => updateEffectiveValue(columnCount, getSetting("columnCount"));
752
+ (0, import_debounce.default)(update, 50)();
753
+ }, [columnCount, layoutSettings, getSetting, updateEffectiveValue]);
754
+ useEffect(() => {
755
+ debouncedUpdate();
756
+ window.addEventListener("resize", debouncedUpdate);
757
+ return () => {
758
+ window.removeEventListener("resize", debouncedUpdate);
759
+ };
760
+ }, [debouncedUpdate]);
481
761
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
482
762
  StatefulRadioGroup,
483
763
  {
484
764
  standalone: true,
485
765
  label: en_default.reader.settings.column.title,
486
766
  orientation: "horizontal",
487
- value: columnCount,
767
+ value: effectiveValue,
488
768
  onChange: async (val) => await updatePreference(val),
489
- isDisabled: isScroll && !isFXL,
769
+ isDisabled: isScroll,
490
770
  items: [
491
771
  {
492
772
  icon: document_scanner_default,
@@ -501,7 +781,11 @@ var StatefulColumns = () => {
501
781
  {
502
782
  icon: menu_book_default,
503
783
  label: en_default.reader.settings.column.two,
504
- value: "2"
784
+ value: "2",
785
+ // This is subpar when the columnCount is 1 though because
786
+ // it won’t be disabled, but it’s the best we can do with
787
+ // the preferences API at the moment
788
+ isDisabled: effectiveValue === "1" && columnCount === "2"
505
789
  }
506
790
  ]
507
791
  }
@@ -582,10 +866,10 @@ var StatefulSlider = ({
582
866
  {
583
867
  ...props,
584
868
  ...standalone ? { label } : { "aria-label": label },
585
- className: classNames9(settingsStyles3.readerSettingsSlider, standalone ? settingsStyles3.readerSettingsGroup : ""),
869
+ className: classNames10(settingsStyles3.readerSettingsSlider, standalone ? settingsStyles3.readerSettingsGroup : ""),
586
870
  compounds: {
587
871
  label: {
588
- className: classNames9(settingsStyles3.readerSettingsLabel, settingsStyles3.readerSettingsSliderLabel)
872
+ className: classNames10(settingsStyles3.readerSettingsLabel, settingsStyles3.readerSettingsSliderLabel)
589
873
  },
590
874
  output: {
591
875
  className: settingsStyles3.readerSettingsSliderOutput
@@ -678,15 +962,16 @@ var contract_default = SvgContract;
678
962
  var SvgDocs = (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: "M320-440h320v-80H320v80Zm0 120h320v-80H320v80Zm0 120h200v-80H320v80ZM240-80q-33 0-56.5-23.5T160-160v-640q0-33 23.5-56.5T240-880h320l240 240v480q0 33-23.5 56.5T720-80H240Zm280-520v-200H240v640h480v-440H520ZM240-800v200-200 640-640Z" }) });
679
963
  var docs_default = SvgDocs;
680
964
  var StatefulLayout = () => {
681
- const isScroll = useAppSelector((state) => state.settings.scroll);
965
+ const scroll = useAppSelector((state) => state.settings.scroll);
966
+ const isFXL = useAppSelector((state) => state.publication.isFXL);
967
+ const isScroll = scroll && !isFXL;
682
968
  const dispatch = useAppDispatch();
683
- const { getSetting, submitPreferences, handleScrollAffordances } = useEpubNavigator();
969
+ const { getSetting, submitPreferences } = useEpubNavigator();
684
970
  const updatePreference = useCallback(async (value) => {
685
971
  const derivedValue = value === "scroll_option" /* scroll */;
686
972
  await submitPreferences({ scroll: derivedValue });
687
973
  dispatch(setScroll(getSetting("scroll")));
688
- handleScrollAffordances(derivedValue);
689
- }, [submitPreferences, getSetting, dispatch, handleScrollAffordances]);
974
+ }, [submitPreferences, getSetting, dispatch]);
690
975
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
691
976
  StatefulRadioGroup,
692
977
  {
@@ -720,7 +1005,7 @@ var StatefulNumberField = ({
720
1005
  {
721
1006
  ...props,
722
1007
  ...standalone ? { label } : { "aria-label": label },
723
- className: classNames9(settingsStyles3.readerSettingsNumbfield, standalone ? settingsStyles3.readerSettingsGroup : ""),
1008
+ className: classNames10(settingsStyles3.readerSettingsNumbfield, standalone ? settingsStyles3.readerSettingsGroup : ""),
724
1009
  compounds: {
725
1010
  group: {
726
1011
  className: settingsStyles3.readerSettingsGroupWrapper
@@ -1026,16 +1311,16 @@ var StatefulGroupWrapper = ({
1026
1311
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
1027
1312
  ThSettingsWrapper,
1028
1313
  {
1029
- className: classNames9(settingsStyles3.readerSettingsGroup, settingsStyles3.readerSettingsAdvancedGroup),
1314
+ className: classNames10(settingsStyles3.readerSettingsGroup, settingsStyles3.readerSettingsAdvancedGroup),
1030
1315
  items: componentsMap,
1031
1316
  prefs: resolvedPrefs,
1032
1317
  compounds: {
1033
1318
  label: heading,
1034
1319
  heading: {
1035
- className: classNames9(settingsStyles3.readerSettingsLabel, settingsStyles3.readerSettingsGroupLabel)
1320
+ className: classNames10(settingsStyles3.readerSettingsLabel, settingsStyles3.readerSettingsGroupLabel)
1036
1321
  },
1037
1322
  button: {
1038
- className: classNames9(readerSharedUI3.icon, settingsStyles3.readerSettingsAdvancedIcon),
1323
+ className: classNames10(readerSharedUI3.icon, settingsStyles3.readerSettingsAdvancedIcon),
1039
1324
  "aria-label": moreLabel,
1040
1325
  compounds: {
1041
1326
  tooltipTrigger: {
@@ -1398,11 +1683,11 @@ var StatefulTheme = ({ mapArrowNav }) => {
1398
1683
  label: en_default.reader.settings.themes.title,
1399
1684
  value: theme,
1400
1685
  onChange: async (val) => await updatePreference(val),
1401
- children: /* @__PURE__ */ jsx("div", { className: classNames9(settingsStyles3.readerSettingsRadioWrapper, settingsStyles3.readerSettingsThemesWrapper), children: themeItems.current.map(
1686
+ children: /* @__PURE__ */ jsx("div", { className: classNames10(settingsStyles3.readerSettingsRadioWrapper, settingsStyles3.readerSettingsThemesWrapper), children: themeItems.current.map(
1402
1687
  (t) => /* @__PURE__ */ jsx(
1403
1688
  Radio,
1404
1689
  {
1405
- className: classNames9(
1690
+ className: classNames10(
1406
1691
  settingsStyles3.readerSettingsRadio,
1407
1692
  settingsStyles3.readerSettingsThemeRadio
1408
1693
  ),
@@ -1639,7 +1924,7 @@ var DockHandle = ({
1639
1924
  className: dockingStyles.dockResizeHandle,
1640
1925
  disabled: !isResizable,
1641
1926
  tabIndex: isPopulated ? 0 : -1,
1642
- children: isResizable && hasDragIndicator && /* @__PURE__ */ jsx("div", { className: classNames9(dockingStyles.dockResizeHandleGrab, classFromFlow()) })
1927
+ children: isResizable && hasDragIndicator && /* @__PURE__ */ jsx("div", { className: classNames10(dockingStyles.dockResizeHandleGrab, classFromFlow()) })
1643
1928
  }
1644
1929
  ) });
1645
1930
  };
@@ -1731,7 +2016,7 @@ var DockPanel = ({
1731
2016
  {
1732
2017
  id: flow,
1733
2018
  "aria-label": makeDockLabel(),
1734
- className: classNames9(dockingStyles.dockPanelContainer, dockClassName)
2019
+ className: classNames10(dockingStyles.dockPanelContainer, dockClassName)
1735
2020
  }
1736
2021
  )
1737
2022
  }
@@ -1818,8 +2103,6 @@ var StatefulActionIcon = ({
1818
2103
  }) => {
1819
2104
  const RSPrefs = usePreferences();
1820
2105
  const triggerRef = useRef(null);
1821
- useAppSelector((state) => state.reader.isImmersive);
1822
- useAppSelector((state) => state.reader.isHovering);
1823
2106
  const dispatch = useAppDispatch();
1824
2107
  const handleClassNameFromState = () => {
1825
2108
  let className = "";
@@ -1850,7 +2133,7 @@ var StatefulActionIcon = ({
1850
2133
  ThActionButton,
1851
2134
  {
1852
2135
  ref: triggerRef,
1853
- className: classNames9(readerSharedUI3.icon, handleClassNameFromState(), props.className),
2136
+ className: classNames10(readerSharedUI3.icon, handleClassNameFromState(), props.className),
1854
2137
  onPress: props.onPress || defaultOnPressFunc,
1855
2138
  onKeyDown: blurOnEsc,
1856
2139
  onFocus: handleImmersive,
@@ -1874,8 +2157,6 @@ var StatefulActionIcon = ({
1874
2157
  var StatefulOverflowMenu = ({
1875
2158
  id,
1876
2159
  className,
1877
- actionFallback,
1878
- display,
1879
2160
  items,
1880
2161
  triggerRef
1881
2162
  }) => {
@@ -1886,7 +2167,7 @@ var StatefulOverflowMenu = ({
1886
2167
  isOpen: value
1887
2168
  }));
1888
2169
  };
1889
- if (items.length > 0 && display) {
2170
+ if (items.length > 0) {
1890
2171
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
1891
2172
  ThMenu,
1892
2173
  {
@@ -1907,7 +2188,7 @@ var StatefulOverflowMenu = ({
1907
2188
  button: /* @__PURE__ */ jsx(
1908
2189
  StatefulActionIcon,
1909
2190
  {
1910
- className: className ? className : overflowMenuStyles.activeButton,
2191
+ className: classNames10(className, overflowMenuStyles.activeButton),
1911
2192
  "aria-label": en_default.reader.overflowMenu.active.trigger,
1912
2193
  placement: "bottom",
1913
2194
  tooltipLabel: en_default.reader.overflowMenu.active.tooltip,
@@ -1918,34 +2199,12 @@ var StatefulOverflowMenu = ({
1918
2199
  }
1919
2200
  }
1920
2201
  ) });
1921
- } else {
1922
- if (actionFallback) {
1923
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
1924
- StatefulActionIcon,
1925
- {
1926
- className: className ? className : overflowMenuStyles.hintButton,
1927
- "aria-label": en_default.reader.overflowMenu.hint.trigger,
1928
- placement: "bottom",
1929
- tooltipLabel: en_default.reader.overflowMenu.hint.tooltip,
1930
- visibility: "always" /* always */,
1931
- onPress: () => {
1932
- dispatch(toggleImmersive());
1933
- },
1934
- preventFocusOnPress: true,
1935
- children: /* @__PURE__ */ jsx(more_vert_default, { "aria-hidden": "true", focusable: "false" })
1936
- }
1937
- ) });
1938
- } else {
1939
- return /* @__PURE__ */ jsx(Fragment, {});
1940
- }
1941
2202
  }
1942
2203
  };
1943
2204
  var StatefulCollapsibleActionsBar = ({
1944
2205
  id,
1945
2206
  items,
1946
- overflowActionCallback,
1947
2207
  overflowMenuClassName,
1948
- overflowMenuDisplay,
1949
2208
  ...props
1950
2209
  }) => {
1951
2210
  const ref = useRef(null);
@@ -1963,9 +2222,7 @@ var StatefulCollapsibleActionsBar = ({
1963
2222
  {
1964
2223
  id,
1965
2224
  triggerRef: ref,
1966
- display: overflowMenuDisplay || true,
1967
2225
  className: overflowMenuClassName,
1968
- actionFallback: overflowActionCallback,
1969
2226
  items: []
1970
2227
  }
1971
2228
  )
@@ -1974,20 +2231,42 @@ var StatefulCollapsibleActionsBar = ({
1974
2231
  }
1975
2232
  ) });
1976
2233
  };
1977
- var StatefulReaderHeader = () => {
2234
+ var StatefulReaderHeader = ({
2235
+ layout
2236
+ }) => {
2237
+ const headerRef = useRef(null);
1978
2238
  const { reflowActionKeys, fxlActionKeys } = usePreferenceKeys();
1979
2239
  const RSPrefs = usePreferences();
1980
2240
  const { actionsComponentsMap } = usePlugins();
2241
+ const actionsMap = useAppSelector((state) => state.actions.keys);
2242
+ const overflowMap = useAppSelector((state) => state.actions.overflow);
1981
2243
  const isFXL = useAppSelector((state) => state.publication.isFXL);
2244
+ const isScroll = useAppSelector((state) => state.settings.scroll);
1982
2245
  const runningHead = useAppSelector((state) => state.publication.runningHead);
1983
2246
  const isImmersive = useAppSelector((state) => state.reader.isImmersive);
1984
2247
  const isHovering = useAppSelector((state) => state.reader.isHovering);
2248
+ const hasScrollAffordance = useAppSelector((state) => state.reader.hasScrollAffordance);
2249
+ const actions = useActions({ ...actionsMap, ...overflowMap });
1985
2250
  const dispatch = useAppDispatch();
2251
+ const { focusWithinProps } = useFocusWithin({
2252
+ onFocusWithin() {
2253
+ dispatch(setHovering(true));
2254
+ },
2255
+ onBlurWithin() {
2256
+ if (actions.everyOpenDocked()) {
2257
+ dispatch(setHovering(false));
2258
+ }
2259
+ }
2260
+ });
1986
2261
  const setHover = () => {
1987
- dispatch(setHovering(true));
2262
+ if (!hasScrollAffordance && actions.everyOpenDocked()) {
2263
+ dispatch(setHovering(true));
2264
+ }
1988
2265
  };
1989
2266
  const removeHover = () => {
1990
- dispatch(setHovering(false));
2267
+ if (!hasScrollAffordance && actions.everyOpenDocked()) {
2268
+ dispatch(setHovering(false));
2269
+ }
1991
2270
  };
1992
2271
  const listActionItems = useCallback(() => {
1993
2272
  const actionKeys = isFXL ? fxlActionKeys : reflowActionKeys;
@@ -2007,38 +2286,62 @@ var StatefulReaderHeader = () => {
2007
2286
  }
2008
2287
  return actionsItems;
2009
2288
  }, [isFXL, fxlActionKeys, reflowActionKeys, actionsComponentsMap]);
2010
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
2011
- ThHeader,
2012
- {
2013
- className: readerHeaderStyles.header,
2014
- id: "top-bar",
2015
- "aria-label": en_default.reader.app.header.label,
2016
- onMouseEnter: setHover,
2017
- onMouseLeave: removeHover,
2018
- children: [
2019
- /* @__PURE__ */ jsx(
2020
- ThRunningHead,
2021
- {
2022
- label: runningHead || en_default.reader.app.header.runningHeadFallback,
2023
- syncDocTitle: true,
2024
- "aria-label": en_default.reader.app.header.runningHead
2025
- }
2026
- ),
2027
- /* @__PURE__ */ jsx(
2028
- StatefulCollapsibleActionsBar,
2029
- {
2030
- id: "reader-header-overflowMenu",
2031
- items: listActionItems(),
2032
- prefs: { ...RSPrefs.actions, displayOrder: isFXL ? RSPrefs.actions.fxlOrder : RSPrefs.actions.reflowOrder },
2033
- className: readerHeaderStyles.actionsWrapper,
2034
- "aria-label": en_default.reader.app.header.actions,
2035
- overflowActionCallback: isImmersive && !isHovering,
2036
- overflowMenuDisplay: !isImmersive || isHovering
2037
- }
2038
- )
2039
- ]
2289
+ useEffect(() => {
2290
+ if (isImmersive) {
2291
+ const focusElement = document.activeElement;
2292
+ if (focusElement && headerRef.current?.contains(focusElement)) {
2293
+ focusElement.blur();
2294
+ }
2040
2295
  }
2041
- ) });
2296
+ }, [isImmersive]);
2297
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2298
+ /* @__PURE__ */ jsx(
2299
+ ThInteractiveOverlay,
2300
+ {
2301
+ id: "reader-header-overlay",
2302
+ className: "bar-overlay",
2303
+ isActive: layout === "layered-ui" /* layered */ && isImmersive && !isHovering,
2304
+ onMouseEnter: setHover,
2305
+ onMouseLeave: removeHover
2306
+ }
2307
+ ),
2308
+ /* @__PURE__ */ jsxs(
2309
+ ThHeader,
2310
+ {
2311
+ ref: headerRef,
2312
+ className: readerHeaderStyles.header,
2313
+ id: "top-bar",
2314
+ "aria-label": en_default.reader.app.header.label,
2315
+ onMouseEnter: setHover,
2316
+ onMouseLeave: removeHover,
2317
+ ...focusWithinProps,
2318
+ children: [
2319
+ /* @__PURE__ */ jsx(
2320
+ ThRunningHead,
2321
+ {
2322
+ label: runningHead || en_default.reader.app.header.runningHeadFallback,
2323
+ syncDocTitle: true,
2324
+ "aria-label": en_default.reader.app.header.runningHead
2325
+ }
2326
+ ),
2327
+ /* @__PURE__ */ jsx(
2328
+ StatefulCollapsibleActionsBar,
2329
+ {
2330
+ id: "reader-header-overflowMenu",
2331
+ items: listActionItems(),
2332
+ prefs: {
2333
+ ...RSPrefs.actions,
2334
+ displayOrder: isFXL ? RSPrefs.actions.fxlOrder : RSPrefs.actions.reflowOrder
2335
+ },
2336
+ className: readerHeaderStyles.actionsWrapper,
2337
+ "aria-label": en_default.reader.app.header.actions,
2338
+ overflowMenuClassName: !isScroll || RSPrefs.affordances.scroll.hintInImmersive ? overflowMenuStyles.hintButton : void 0
2339
+ }
2340
+ )
2341
+ ]
2342
+ }
2343
+ )
2344
+ ] });
2042
2345
  };
2043
2346
  var StatefulReaderArrowButton = ({
2044
2347
  direction,
@@ -2052,7 +2355,9 @@ var StatefulReaderArrowButton = ({
2052
2355
  const buttonRef = useRef(null);
2053
2356
  const isRTL = useAppSelector((state) => state.publication.isRTL);
2054
2357
  const hasArrows = useAppSelector((state) => state.reader.hasArrows);
2055
- const isScroll = useAppSelector((state) => state.settings.scroll);
2358
+ const scroll = useAppSelector((state) => state.settings.scroll);
2359
+ const isFXL = useAppSelector((state) => state.publication.isFXL);
2360
+ const isScroll = scroll && !isFXL;
2056
2361
  const wasScroll = usePrevious(isScroll);
2057
2362
  const dispatch = useAppDispatch();
2058
2363
  const [isHovering, setIsHovering] = useState(false);
@@ -2097,7 +2402,7 @@ var StatefulReaderArrowButton = ({
2097
2402
  onPress: (e) => onPress && handleOnPress(e, onPress),
2098
2403
  onHoverChange: (isHovering2) => setIsHovering(isHovering2),
2099
2404
  onKeyDown: blurOnEsc,
2100
- className: classNames9(className, handleClassNameFromSpaceProp(), handleClassNameFromState()),
2405
+ className: classNames10(className, handleClassNameFromSpaceProp(), handleClassNameFromState()),
2101
2406
  isDisabled,
2102
2407
  preventFocusOnPress: true,
2103
2408
  ...props,
@@ -2116,7 +2421,9 @@ var StatefulReaderArrowButton = ({
2116
2421
  ) });
2117
2422
  };
2118
2423
  var import_json_templates2 = __toESM(require_dist());
2119
- var StatefulReaderProgression = () => {
2424
+ var StatefulReaderProgression = ({
2425
+ className
2426
+ }) => {
2120
2427
  const jsonTemplate = (0, import_json_templates2.default)(en_default.reader.app.progression.of);
2121
2428
  const progression = useAppSelector((state) => state.publication.progression);
2122
2429
  const [current, setCurrent] = useState("");
@@ -2141,32 +2448,174 @@ var StatefulReaderProgression = () => {
2141
2448
  {
2142
2449
  id: progressionStyles.current,
2143
2450
  "aria-label": en_default.reader.app.progression.wrapper,
2451
+ className,
2144
2452
  children: jsonTemplate({ current, reference })
2145
2453
  }
2146
2454
  ) });
2147
2455
  };
2148
- var StatefulReaderFooter = () => {
2456
+ var StatefulPagination = ({
2457
+ ref,
2458
+ links,
2459
+ compounds,
2460
+ children,
2461
+ ...props
2462
+ }) => {
2463
+ const previousButtonRef = useRef(null);
2464
+ const nextButtonRef = useRef(null);
2465
+ const updatedCompounds = {
2466
+ ...compounds,
2467
+ previousButton: {
2468
+ ...compounds?.previousButton,
2469
+ ref: previousButtonRef,
2470
+ onKeyDown: (e) => {
2471
+ if (e.key === "Escape") {
2472
+ previousButtonRef.current?.blur();
2473
+ }
2474
+ }
2475
+ },
2476
+ nextButton: {
2477
+ ...compounds?.nextButton,
2478
+ ref: nextButtonRef,
2479
+ onKeyDown: (e) => {
2480
+ if (e.key === "Escape") {
2481
+ nextButtonRef.current?.blur();
2482
+ }
2483
+ }
2484
+ }
2485
+ };
2486
+ return /* @__PURE__ */ jsx(
2487
+ ThPagination,
2488
+ {
2489
+ ref,
2490
+ className: readerPaginationStyles2.pagination,
2491
+ links,
2492
+ compounds: updatedCompounds,
2493
+ ...props,
2494
+ children
2495
+ }
2496
+ );
2497
+ };
2498
+ var StatefulReaderFooter = ({
2499
+ layout
2500
+ }) => {
2501
+ const footerRef = useRef(null);
2502
+ const isImmersive = useAppSelector((state) => state.reader.isImmersive);
2503
+ const isHovering = useAppSelector((state) => state.reader.isHovering);
2504
+ const hasScrollAffordance = useAppSelector((state) => state.reader.hasScrollAffordance);
2505
+ const scroll = useAppSelector((state) => state.settings.scroll);
2506
+ const isFXL = useAppSelector((state) => state.publication.isFXL);
2507
+ const isScroll = scroll && !isFXL;
2508
+ const breakpoint = useAppSelector((state) => state.theming.breakpoint);
2509
+ const reducedMotion = useAppSelector((state) => state.theming.prefersReducedMotion);
2510
+ const timeline = useAppSelector((state) => state.publication.unstableTimeline);
2149
2511
  const dispatch = useAppDispatch();
2512
+ const { focusWithinProps } = useFocusWithin({
2513
+ onFocusWithin() {
2514
+ dispatch(setHovering(true));
2515
+ },
2516
+ onBlurWithin() {
2517
+ dispatch(setHovering(false));
2518
+ }
2519
+ });
2150
2520
  const setHover = () => {
2151
- dispatch(setHovering(true));
2521
+ if (!hasScrollAffordance) {
2522
+ dispatch(setHovering(true));
2523
+ }
2152
2524
  };
2153
2525
  const removeHover = () => {
2154
- dispatch(setHovering(false));
2526
+ if (!hasScrollAffordance) {
2527
+ dispatch(setHovering(false));
2528
+ }
2155
2529
  };
2156
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
2157
- ThFooter,
2158
- {
2159
- id: "bottom-bar",
2160
- "aria-label": en_default.reader.app.footer.label,
2161
- onMouseEnter: setHover,
2162
- onMouseLeave: removeHover,
2163
- children: /* @__PURE__ */ jsx(StatefulReaderProgression, {})
2530
+ const { previousLocator, nextLocator, go } = useEpubNavigator();
2531
+ const updateLinks = useCallback(() => {
2532
+ const links = {
2533
+ previous: void 0,
2534
+ next: void 0
2535
+ };
2536
+ const previous = previousLocator();
2537
+ const next = nextLocator();
2538
+ if (previous) {
2539
+ links.previous = {
2540
+ node: breakpoint !== "compact" /* compact */ && breakpoint !== "medium" /* medium */ ? /* @__PURE__ */ jsxs(Fragment, { children: [
2541
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: en_default.reader.navigation.scroll.prevA11yLabel }),
2542
+ /* @__PURE__ */ jsx("span", { className: readerPaginationStyles2.paginationLabel, children: timeline?.previousItem?.title || previous.title || en_default.reader.navigation.scroll.prevLabel })
2543
+ ] }) : /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("span", { className: readerPaginationStyles2.paginationLabel, children: en_default.reader.navigation.scroll.prevLabel }) }),
2544
+ onPress: () => go(previous, !reducedMotion, () => {
2545
+ })
2546
+ };
2164
2547
  }
2165
- ) });
2548
+ if (next) {
2549
+ links.next = {
2550
+ node: breakpoint !== "compact" /* compact */ && breakpoint !== "medium" /* medium */ ? /* @__PURE__ */ jsxs(Fragment, { children: [
2551
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: en_default.reader.navigation.scroll.nextA11yLabel }),
2552
+ /* @__PURE__ */ jsx("span", { className: readerPaginationStyles2.paginationLabel, children: timeline?.nextItem?.title || next.title || en_default.reader.navigation.scroll.nextLabel })
2553
+ ] }) : /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("span", { className: readerPaginationStyles2.paginationLabel, children: en_default.reader.navigation.scroll.nextLabel }) }),
2554
+ onPress: () => go(next, !reducedMotion, () => {
2555
+ })
2556
+ };
2557
+ }
2558
+ return links;
2559
+ }, [go, previousLocator, nextLocator, timeline, breakpoint, reducedMotion]);
2560
+ useEffect(() => {
2561
+ updateLinks();
2562
+ }, [timeline, updateLinks]);
2563
+ useEffect(() => {
2564
+ if (isImmersive) {
2565
+ const focusElement = document.activeElement;
2566
+ if (focusElement && footerRef.current?.contains(focusElement)) {
2567
+ focusElement.blur();
2568
+ }
2569
+ }
2570
+ }, [isImmersive]);
2571
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2572
+ /* @__PURE__ */ jsx(
2573
+ ThInteractiveOverlay,
2574
+ {
2575
+ id: "reader-footer-overlay",
2576
+ className: "bar-overlay",
2577
+ isActive: layout === "layered-ui" /* layered */ && isImmersive && !isHovering,
2578
+ onMouseEnter: setHover,
2579
+ onMouseLeave: removeHover
2580
+ }
2581
+ ),
2582
+ /* @__PURE__ */ jsx(
2583
+ ThFooter,
2584
+ {
2585
+ ref: footerRef,
2586
+ id: "bottom-bar",
2587
+ "aria-label": en_default.reader.app.footer.label,
2588
+ onMouseEnter: setHover,
2589
+ onMouseLeave: removeHover,
2590
+ ...focusWithinProps,
2591
+ children: isScroll && !isFXL ? /* @__PURE__ */ jsx(
2592
+ StatefulPagination,
2593
+ {
2594
+ "aria-label": en_default.reader.navigation.scroll.wrapper,
2595
+ links: updateLinks(),
2596
+ compounds: {
2597
+ listItem: {
2598
+ className: readerPaginationStyles2.paginationListItem
2599
+ },
2600
+ previousButton: {
2601
+ className: readerPaginationStyles2.previousButton,
2602
+ preventFocusOnPress: true
2603
+ },
2604
+ nextButton: {
2605
+ className: readerPaginationStyles2.nextButton,
2606
+ preventFocusOnPress: true
2607
+ }
2608
+ },
2609
+ children: /* @__PURE__ */ jsx(StatefulReaderProgression, { className: readerPaginationStyles2.progression })
2610
+ }
2611
+ ) : /* @__PURE__ */ jsx(StatefulReaderProgression, {})
2612
+ }
2613
+ )
2614
+ ] });
2166
2615
  };
2167
2616
 
2168
2617
  // src/components/Epub/StatefulReader.tsx
2169
- var import_debounce = __toESM(require_debounce());
2618
+ var import_debounce2 = __toESM(require_debounce());
2170
2619
  var SvgFullscreen = (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-120v-200h80v120h120v80H120Zm520 0v-80h120v-120h80v200H640ZM120-640v-200h200v80H200v120h-80Zm640 0v-120H640v-80h200v200h-80Z" }) });
2171
2620
  var fullscreen_default = SvgFullscreen;
2172
2621
  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" }) });
@@ -2500,6 +2949,34 @@ var StatefulDocker = ({
2500
2949
  )
2501
2950
  ] }) });
2502
2951
  };
2952
+ var useWebkitPatch = (isOpen) => {
2953
+ const scroll = useAppSelector((state) => state.settings.scroll);
2954
+ const isFXL = useAppSelector((state) => state.publication.isFXL);
2955
+ const isScroll = scroll && !isFXL;
2956
+ const {
2957
+ getCframes
2958
+ } = useEpubNavigator();
2959
+ useEffect(() => {
2960
+ if (isScroll && !isOpen) {
2961
+ const container = document.getElementById("container");
2962
+ if (!container) return;
2963
+ const currentHeight = container.offsetHeight;
2964
+ container.style.height = `${currentHeight - 1}px`;
2965
+ setTimeout(() => {
2966
+ container.style.height = "";
2967
+ const frame = getCframes()?.[0];
2968
+ if (!frame?.window?.document?.scrollingElement) return;
2969
+ const currentScrollTop = frame.window.document.scrollingElement.scrollTop;
2970
+ if (currentScrollTop > 1) {
2971
+ frame.window.document.scrollingElement.scrollTop = currentScrollTop - 1;
2972
+ } else {
2973
+ frame.window.document.scrollingElement.scrollTop = currentScrollTop + 1;
2974
+ }
2975
+ frame.window.document.scrollingElement.scrollTop = currentScrollTop;
2976
+ }, 0);
2977
+ }
2978
+ }, [isScroll, isOpen, getCframes]);
2979
+ };
2503
2980
  var StatefulPopoverSheet = ({
2504
2981
  id,
2505
2982
  triggerRef,
@@ -2521,7 +2998,8 @@ var StatefulPopoverSheet = ({
2521
2998
  const popoverHeaderRef = useRef(null);
2522
2999
  const popoverBodyRef = useRef(null);
2523
3000
  const popoverCloseRef = useRef(null);
2524
- if (React48.Children.toArray(children).length > 0) {
3001
+ useWebkitPatch(!!isOpen);
3002
+ if (React49.Children.toArray(children).length > 0) {
2525
3003
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
2526
3004
  ThPopover,
2527
3005
  {
@@ -2534,7 +3012,7 @@ var StatefulPopoverSheet = ({
2534
3012
  updateState: resetFocus
2535
3013
  },
2536
3014
  placement: placement || "bottom",
2537
- className: classNames9(sheetStyles2.popOverSheet, className),
3015
+ className: classNames10(sheetStyles2.popOverSheet, className),
2538
3016
  isOpen,
2539
3017
  onOpenChange,
2540
3018
  isKeyboardDismissDisabled: dismissEscapeKeyClose,
@@ -2564,7 +3042,7 @@ var StatefulPopoverSheet = ({
2564
3042
  direction: direction === "ltr" ? "left" : "right",
2565
3043
  label: en_default.reader.app.back.trigger,
2566
3044
  ref: popoverCloseRef,
2567
- className: classNames9(className, readerSharedUI3.backButton),
3045
+ className: classNames10(className, readerSharedUI3.backButton),
2568
3046
  "aria-label": en_default.reader.app.back.trigger,
2569
3047
  onPress: onClosePress
2570
3048
  }
@@ -2752,7 +3230,7 @@ var StatefulBottomSheet = ({
2752
3230
  const scrimClassName = useMemo(() => {
2753
3231
  return scrimPref.active ? sheetStyles2.bottomSheetScrim : "";
2754
3232
  }, [scrimPref]);
2755
- if (React48.Children.toArray(children).length > 0) {
3233
+ if (React49.Children.toArray(children).length > 0) {
2756
3234
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
2757
3235
  ThBottomSheet,
2758
3236
  {
@@ -2780,7 +3258,7 @@ var StatefulBottomSheet = ({
2780
3258
  prefersReducedMotion,
2781
3259
  compounds: {
2782
3260
  container: {
2783
- className: classNames9(sheetStyles2.bottomSheetModal, detentClassName),
3261
+ className: classNames10(sheetStyles2.bottomSheetModal, detentClassName),
2784
3262
  ref: sheetContainerRef,
2785
3263
  style: {
2786
3264
  maxWidth: maxWidthPref
@@ -2792,15 +3270,15 @@ var StatefulBottomSheet = ({
2792
3270
  onKeyDown: onDragKeyCallback
2793
3271
  },
2794
3272
  content: {
2795
- className: classNames9(sheetStyles2.bottomSheet, className),
3273
+ className: classNames10(sheetStyles2.bottomSheet, className),
2796
3274
  disableDrag: true
2797
3275
  },
2798
3276
  scroller: {
2799
- className: classNames9(sheetStyles2.bottomSheetScroller, sheetStyles2.sheetBody),
3277
+ className: classNames10(sheetStyles2.bottomSheetScroller, sheetStyles2.sheetBody),
2800
3278
  draggable: false
2801
3279
  },
2802
3280
  backdrop: {
2803
- className: classNames9(sheetStyles2.bottomSheetBackdrop, scrimClassName),
3281
+ className: classNames10(sheetStyles2.bottomSheetBackdrop, scrimClassName),
2804
3282
  style: { "--defaults-scrim": scrimPref.override }
2805
3283
  }
2806
3284
  },
@@ -2821,7 +3299,7 @@ var StatefulBottomSheet = ({
2821
3299
  direction: direction === "ltr" ? "left" : "right",
2822
3300
  label: en_default.reader.app.back.trigger,
2823
3301
  ref: bottomSheetCloseRef,
2824
- className: classNames9(className, readerSharedUI3.backButton),
3302
+ className: classNames10(className, readerSharedUI3.backButton),
2825
3303
  "aria-label": en_default.reader.app.back.trigger,
2826
3304
  onPress: onClosePress
2827
3305
  }
@@ -2864,7 +3342,8 @@ var StatefulFullScreenSheet = ({
2864
3342
  const fullScreenHeaderRef = useRef(null);
2865
3343
  const fullScreenBodyRef = useRef(null);
2866
3344
  const fullScreenCloseRef = useRef(null);
2867
- if (React48.Children.toArray(children).length > 0) {
3345
+ useWebkitPatch(!!isOpen);
3346
+ if (React49.Children.toArray(children).length > 0) {
2868
3347
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
2869
3348
  ThModal,
2870
3349
  {
@@ -2883,7 +3362,7 @@ var StatefulFullScreenSheet = ({
2883
3362
  isOpen,
2884
3363
  onOpenChange,
2885
3364
  isDismissable: true,
2886
- className: classNames9(sheetStyles2.fullScreenSheet, className),
3365
+ className: classNames10(sheetStyles2.fullScreenSheet, className),
2887
3366
  isKeyboardDismissDisabled: dismissEscapeKeyClose,
2888
3367
  style: {
2889
3368
  "--sheet-sticky-header": fullScreenHeaderRef.current ? `${fullScreenHeaderRef.current.clientHeight}px` : void 0
@@ -2906,7 +3385,7 @@ var StatefulFullScreenSheet = ({
2906
3385
  direction: direction === "ltr" ? "left" : "right",
2907
3386
  label: en_default.reader.app.back.trigger,
2908
3387
  ref: fullScreenCloseRef,
2909
- className: classNames9(className, readerSharedUI3.backButton),
3388
+ className: classNames10(className, readerSharedUI3.backButton),
2910
3389
  "aria-label": en_default.reader.app.back.trigger,
2911
3390
  onPress: onClosePress
2912
3391
  }
@@ -2959,7 +3438,7 @@ var StatefulDockedSheet = ({
2959
3438
  return direction === "ltr" /* ltr */ ? sheetStyles2.dockedSheetRightBorder : sheetStyles2.dockedSheetLeftBorder;
2960
3439
  }
2961
3440
  }, [flow, direction]);
2962
- if (React48.Children.toArray(children).length > 0) {
3441
+ if (React49.Children.toArray(children).length > 0) {
2963
3442
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
2964
3443
  ThDockedPanel,
2965
3444
  {
@@ -2972,7 +3451,7 @@ var StatefulDockedSheet = ({
2972
3451
  autoFocus: false,
2973
3452
  updateState: resetFocus
2974
3453
  },
2975
- className: classNames9(sheetStyles2.dockedSheet, className, classFromFlow()),
3454
+ className: classNames10(sheetStyles2.dockedSheet, className, classFromFlow()),
2976
3455
  style: {
2977
3456
  "--sheet-sticky-header": dockedSheetHeaderRef.current ? `${dockedSheetHeaderRef.current.clientHeight}px` : void 0
2978
3457
  },
@@ -2994,7 +3473,7 @@ var StatefulDockedSheet = ({
2994
3473
  direction: direction === "ltr" ? "left" : "right",
2995
3474
  label: en_default.reader.app.back.trigger,
2996
3475
  ref: dockedSheetCloseRef,
2997
- className: classNames9(className, readerSharedUI3.backButton),
3476
+ className: classNames10(className, readerSharedUI3.backButton),
2998
3477
  "aria-label": en_default.reader.app.back.trigger,
2999
3478
  onPress: onClosePress
3000
3479
  }
@@ -3526,11 +4005,12 @@ var SvgChevronRight = (props) => /* @__PURE__ */ jsx("svg", { xmlns: "http://www
3526
4005
  var chevron_right_default = SvgChevronRight;
3527
4006
  var StatefulTocContainer = ({ triggerRef }) => {
3528
4007
  const treeRef = useRef(null);
3529
- const tocEntry = useAppSelector((state) => state.publication.tocEntry);
4008
+ const unstableTimeline = useAppSelector((state) => state.publication.unstableTimeline);
4009
+ const tocEntry = unstableTimeline?.toc?.currentEntry;
4010
+ const tocTree = unstableTimeline?.toc?.tree;
3530
4011
  const direction = useAppSelector((state) => state.reader.direction);
3531
4012
  const isRTL = direction === "rtl" /* rtl */;
3532
4013
  const actionState = useAppSelector((state) => state.actions.keys["toc" /* toc */]);
3533
- const tocTree = useAppSelector((state) => state.publication.tocTree);
3534
4014
  const dispatch = useAppDispatch();
3535
4015
  const previousTocEntry = usePrevious(tocEntry);
3536
4016
  const [forceRerender, setForceRerender] = useState(0);
@@ -3538,8 +4018,8 @@ var StatefulTocContainer = ({ triggerRef }) => {
3538
4018
  const docking = useDocking("toc" /* toc */);
3539
4019
  const sheetType = docking.sheetType;
3540
4020
  const { contains } = useFilter({ sensitivity: "base" });
3541
- const [filterValue, setFilterValue] = React48.useState("");
3542
- const searchInputRef = React48.useRef(null);
4021
+ const [filterValue, setFilterValue] = React49.useState("");
4022
+ const searchInputRef = React49.useRef(null);
3543
4023
  const filterTocTree = (items, filterValue2) => {
3544
4024
  if (!filterValue2) {
3545
4025
  return items;
@@ -3572,15 +4052,13 @@ var StatefulTocContainer = ({ triggerRef }) => {
3572
4052
  const el = document.querySelector(`[data-key=${key}]`);
3573
4053
  const href = el?.getAttribute("data-href");
3574
4054
  if (!href) return;
3575
- const link = new Link({ href });
4055
+ const link = new Link$1({ href });
3576
4056
  const cb = actionState?.isOpen && (sheetType === "docked start" /* dockedStart */ || sheetType === "docked end" /* dockedEnd */) ? () => {
3577
4057
  dispatch(setTocEntry(key));
3578
4058
  dispatch(setImmersive(true));
3579
- dispatch(setHovering(false));
3580
4059
  } : () => {
3581
4060
  dispatch(setTocEntry(key));
3582
4061
  dispatch(setImmersive(true));
3583
- dispatch(setHovering(false));
3584
4062
  setOpen(false);
3585
4063
  };
3586
4064
  goLink(link, true, cb);
@@ -3599,7 +4077,7 @@ var StatefulTocContainer = ({ triggerRef }) => {
3599
4077
  }
3600
4078
  }, [actionState, setOpen, filterValue]);
3601
4079
  useEffect(() => {
3602
- if ((sheetType === "docked start" /* dockedStart */ || sheetType === "docked end" /* dockedEnd */) && tocEntry !== void 0 && previousTocEntry === void 0) {
4080
+ if ((sheetType === "docked start" /* dockedStart */ || sheetType === "docked end" /* dockedEnd */) && tocEntry !== void 0 && (!previousTocEntry || previousTocEntry !== tocEntry)) {
3603
4081
  setForceRerender(Math.random());
3604
4082
  }
3605
4083
  }, [sheetType, tocEntry, previousTocEntry]);
@@ -3675,15 +4153,15 @@ var StatefulTocContainer = ({ triggerRef }) => {
3675
4153
  textValue: item.title || "",
3676
4154
  children: [
3677
4155
  /* @__PURE__ */ jsxs(TreeItemContent, { children: [
3678
- item.children ? /* @__PURE__ */ jsx(
4156
+ item.children && /* @__PURE__ */ jsx(
3679
4157
  Button,
3680
4158
  {
3681
4159
  slot: "chevron",
3682
4160
  className: tocStyles.tocTreeItemButton,
3683
- ...isRTL ? { style: { transform: "scaleX(-1)" } } : {},
4161
+ style: { transform: isRTL ? "scaleX(-1)" : "none" },
3684
4162
  children: /* @__PURE__ */ jsx(chevron_right_default, { "aria-hidden": "true", focusable: "false" })
3685
4163
  }
3686
- ) : null,
4164
+ ),
3687
4165
  /* @__PURE__ */ jsxs("div", { className: tocStyles.tocTreeItemText, children: [
3688
4166
  /* @__PURE__ */ jsx("div", { className: tocStyles.tocTreeItemTextTitle, children: item.title }),
3689
4167
  item.position && /* @__PURE__ */ jsx("div", { className: tocStyles.tocTreeItemTextPosition, children: item.position })
@@ -3908,32 +4386,6 @@ var Peripherals = class {
3908
4386
  }
3909
4387
  };
3910
4388
 
3911
- // src/helpers/createTocTree.ts
3912
- function createTocTree(links, idGenerator, positionsList) {
3913
- return links.map((link) => {
3914
- const newId = idGenerator();
3915
- let href = link.href;
3916
- const fragmentIndex = href.indexOf("#");
3917
- if (fragmentIndex !== -1) {
3918
- const baseHref = href.substring(0, fragmentIndex);
3919
- const duplicateLink = links.find((l) => l.href.startsWith(baseHref) && l.href !== href);
3920
- if (!duplicateLink) {
3921
- href = baseHref;
3922
- }
3923
- }
3924
- const treeNode = {
3925
- id: newId,
3926
- href,
3927
- title: link.title,
3928
- position: positionsList?.find((position) => position.href === href)?.locations.position
3929
- };
3930
- if (link.children) {
3931
- treeNode.children = createTocTree(link.children.items, idGenerator, positionsList);
3932
- }
3933
- return treeNode;
3934
- });
3935
- }
3936
-
3937
4389
  // src/helpers/deserializePositions.ts
3938
4390
  var deserializePositions = (positionsList) => {
3939
4391
  return positionsList?.map((locator) => ({
@@ -3960,22 +4412,19 @@ var StatefulReader = ({
3960
4412
  }
3961
4413
  const { fxlThemeKeys, reflowThemeKeys } = usePreferenceKeys();
3962
4414
  const RSPrefs = usePreferences();
4415
+ const [publication, setPublication] = useState(null);
3963
4416
  const container = useRef(null);
3964
- const publication = useRef(null);
3965
4417
  const localDataKey = useRef(`${selfHref}-current-location`);
3966
4418
  const arrowsWidth = useRef(2 * ((RSPrefs.theming.arrow.size || 40) + (RSPrefs.theming.arrow.offset || 0)));
3967
4419
  const isFXL = useAppSelector((state) => state.publication.isFXL);
3968
4420
  const positionsList = useAppSelector((state) => state.publication.positionsList);
3969
- const tocTree = useAppSelector((state) => state.publication.tocTree);
3970
- const tocEntry = useAppSelector((state) => state.publication.tocEntry);
3971
- const layoutUI = isFXL ? RSPrefs.theming.layout.ui?.fxl || "layered-ui" /* layered */ : "stacked-ui" /* stacked */;
4421
+ const progression = useAppSelector((state) => state.publication.progression);
3972
4422
  const textAlign = useAppSelector((state) => state.settings.textAlign);
3973
4423
  const columnCount = useAppSelector((state) => state.settings.columnCount);
3974
4424
  const fontFamily = useAppSelector((state) => state.settings.fontFamily);
3975
4425
  const fontSize = useAppSelector((state) => state.settings.fontSize);
3976
4426
  const fontWeight = useAppSelector((state) => state.settings.fontWeight);
3977
4427
  const hyphens = useAppSelector((state) => state.settings.hyphens);
3978
- const layoutStrategy = useAppSelector((state) => state.settings.layoutStrategy);
3979
4428
  const letterSpacing = useAppSelector((state) => state.settings.letterSpacing);
3980
4429
  const lineLength = useAppSelector((state) => state.settings.lineLength);
3981
4430
  const lineHeight = useAppSelector((state) => state.settings.lineHeight);
@@ -3983,7 +4432,7 @@ var StatefulReader = ({
3983
4432
  const paragraphSpacing = useAppSelector((state) => state.settings.paragraphSpacing);
3984
4433
  const publisherStyles = useAppSelector((state) => state.settings.publisherStyles);
3985
4434
  const scroll = useAppSelector((state) => state.settings.scroll);
3986
- const isPaged = !scroll;
4435
+ const isScroll = scroll && !isFXL;
3987
4436
  const textNormalization = useAppSelector((state) => state.settings.textNormalization);
3988
4437
  const wordSpacing = useAppSelector((state) => state.settings.wordSpacing);
3989
4438
  const themeObject = useAppSelector((state) => state.theming.theme);
@@ -3992,9 +4441,10 @@ var StatefulReader = ({
3992
4441
  const colorScheme = useAppSelector((state) => state.theming.colorScheme);
3993
4442
  const reducedMotion = useAppSelector((state) => state.theming.prefersReducedMotion);
3994
4443
  const breakpoint = useAppSelector((state) => state.theming.breakpoint);
3995
- const arrowsOccupySpace = breakpoint && (breakpoint === "large" /* large */ || breakpoint === "xLarge" /* xLarge */);
4444
+ const arrowsOccupySpace = !isScroll && breakpoint && (breakpoint === "large" /* large */ || breakpoint === "xLarge" /* xLarge */);
3996
4445
  const isImmersive = useAppSelector((state) => state.reader.isImmersive);
3997
4446
  const isHovering = useAppSelector((state) => state.reader.isHovering);
4447
+ const layoutUI = isFXL ? RSPrefs.theming.layout.ui?.fxl || "layered-ui" /* layered */ : isScroll ? RSPrefs.theming.layout.ui?.reflow || "layered-ui" /* layered */ : "stacked-ui" /* stacked */;
3998
4448
  useTheming({
3999
4449
  theme,
4000
4450
  themeKeys: RSPrefs.theming.themes.keys,
@@ -4013,9 +4463,45 @@ var StatefulReader = ({
4013
4463
  onReducedMotionChange: (reducedMotion2) => dispatch(setReducedMotion(reducedMotion2)),
4014
4464
  onReducedTransparencyChange: (reducedTransparency) => dispatch(setReducedTransparency(reducedTransparency))
4015
4465
  });
4466
+ const atPublicationStart = useAppSelector((state) => state.publication.atPublicationStart);
4467
+ const atPublicationEnd = useAppSelector((state) => state.publication.atPublicationEnd);
4468
+ const dispatch = useAppDispatch();
4469
+ const onFsChange = useCallback((isFullscreen) => {
4470
+ dispatch(setFullscreen(isFullscreen));
4471
+ }, [dispatch]);
4472
+ const fs = useFullscreen(onFsChange);
4473
+ const {
4474
+ EpubNavigatorLoad,
4475
+ EpubNavigatorDestroy,
4476
+ goLeft,
4477
+ goRight,
4478
+ goBackward,
4479
+ goForward,
4480
+ navLayout,
4481
+ currentLocator,
4482
+ currentPositions,
4483
+ canGoBackward,
4484
+ canGoForward,
4485
+ isScrollStart,
4486
+ isScrollEnd,
4487
+ getCframes,
4488
+ onFXLPositionChange,
4489
+ submitPreferences
4490
+ } = useEpubNavigator();
4491
+ const { setLocalData, getLocalData, localData } = useLocalStorage(localDataKey.current);
4492
+ useTimeline({
4493
+ publication,
4494
+ currentLocation: localData,
4495
+ currentPositions: progression.currentPositions || [],
4496
+ positionsList,
4497
+ onChange: (timeline2) => {
4498
+ dispatch(setTimeline(timeline2));
4499
+ }
4500
+ });
4016
4501
  const cache = useRef({
4017
4502
  layoutUI,
4018
4503
  isImmersive,
4504
+ isHovering,
4019
4505
  arrowsOccupySpace: arrowsOccupySpace || false,
4020
4506
  settings: {
4021
4507
  columnCount,
@@ -4023,47 +4509,22 @@ var StatefulReader = ({
4023
4509
  fontSize,
4024
4510
  fontWeight,
4025
4511
  hyphens,
4026
- layoutStrategy,
4027
4512
  letterSpacing,
4028
4513
  lineHeight,
4029
4514
  lineLength,
4030
4515
  paragraphIndent,
4031
4516
  paragraphSpacing,
4032
4517
  publisherStyles,
4033
- scroll,
4518
+ scroll: isScroll,
4034
4519
  textAlign,
4035
4520
  textNormalization,
4036
4521
  theme,
4037
4522
  wordSpacing
4038
4523
  },
4039
- tocTree,
4040
4524
  positionsList: positionsList || [],
4041
4525
  colorScheme,
4042
4526
  reducedMotion
4043
4527
  });
4044
- const atPublicationStart = useAppSelector((state) => state.publication.atPublicationStart);
4045
- const atPublicationEnd = useAppSelector((state) => state.publication.atPublicationEnd);
4046
- const dispatch = useAppDispatch();
4047
- const onFsChange = useCallback((isFullscreen) => {
4048
- dispatch(setFullscreen(isFullscreen));
4049
- }, [dispatch]);
4050
- const fs = useFullscreen(onFsChange);
4051
- const {
4052
- EpubNavigatorLoad,
4053
- EpubNavigatorDestroy,
4054
- goLeft,
4055
- goRight,
4056
- goBackward,
4057
- goForward,
4058
- scrollBackTo,
4059
- navLayout,
4060
- currentLocator,
4061
- currentPositions,
4062
- getCframes,
4063
- onFXLPositionChange,
4064
- handleScrollAffordances,
4065
- submitPreferences
4066
- } = useEpubNavigator();
4067
4528
  const activateImmersiveOnAction = useCallback(() => {
4068
4529
  if (!cache.current.isImmersive) dispatch(setImmersive(true));
4069
4530
  }, [dispatch]);
@@ -4072,22 +4533,23 @@ var StatefulReader = ({
4072
4533
  dispatch(setArrows(false));
4073
4534
  dispatch(toggleImmersive());
4074
4535
  }, [dispatch]);
4075
- useEffect(() => {
4076
- cache.current.isImmersive = isImmersive;
4077
- }, [isImmersive]);
4078
4536
  const handleTap = (event) => {
4079
4537
  const _cframes = getCframes();
4080
- if (_cframes && !cache.current.settings.scroll) {
4081
- const oneQuarter = (_cframes.length === 2 ? _cframes[0].window.innerWidth + _cframes[1].window.innerWidth : _cframes[0].window.innerWidth) * window.devicePixelRatio / 4;
4082
- if (event.x < oneQuarter) {
4083
- goLeft(!cache.current.reducedMotion, activateImmersiveOnAction);
4084
- } else if (event.x > oneQuarter * 3) {
4085
- goRight(!cache.current.reducedMotion, activateImmersiveOnAction);
4086
- } else if (oneQuarter <= event.x && event.x <= oneQuarter * 3) {
4087
- toggleIsImmersive();
4538
+ if (_cframes) {
4539
+ if (!cache.current.settings.scroll) {
4540
+ const oneQuarter = (_cframes.length === 2 ? _cframes[0].window.innerWidth + _cframes[1].window.innerWidth : _cframes[0].window.innerWidth) * window.devicePixelRatio / 4;
4541
+ if (event.x < oneQuarter) {
4542
+ goLeft(!cache.current.reducedMotion, activateImmersiveOnAction);
4543
+ } else if (event.x > oneQuarter * 3) {
4544
+ goRight(!cache.current.reducedMotion, activateImmersiveOnAction);
4545
+ } else if (oneQuarter <= event.x && event.x <= oneQuarter * 3) {
4546
+ toggleIsImmersive();
4547
+ }
4548
+ } else {
4549
+ if (RSPrefs.affordances.scroll.toggleOnMiddlePointer.includes("tap")) {
4550
+ toggleIsImmersive();
4551
+ }
4088
4552
  }
4089
- } else {
4090
- toggleIsImmersive();
4091
4553
  }
4092
4554
  };
4093
4555
  const handleProgression = useCallback((locator) => {
@@ -4099,49 +4561,10 @@ var StatefulReader = ({
4099
4561
  totalProgression: locator.locations.totalProgression
4100
4562
  }));
4101
4563
  }, [dispatch, currentPositions]);
4102
- const handleTocEntryOnNav = useCallback((locator) => {
4103
- if (!locator) return;
4104
- if (cache.current.tocTree) {
4105
- const findMatch = (items, link) => {
4106
- for (const item of items) {
4107
- if (item.href === link?.href) {
4108
- return item;
4109
- }
4110
- if (item.children) {
4111
- const match = findMatch(item.children, link);
4112
- if (match) {
4113
- return match;
4114
- }
4115
- }
4116
- }
4117
- return void 0;
4118
- };
4119
- const currentMatch = findMatch(cache.current.tocTree, new Link(locator));
4120
- if (currentMatch) {
4121
- dispatch(setTocEntry(currentMatch.id));
4122
- return;
4123
- }
4124
- if (navLayout() === EPUBLayout.fixed) {
4125
- const positions = currentPositions();
4126
- if (positions && positions.length === 2) {
4127
- const otherPosition = positions[0] === locator.locations.position ? positions[1] : positions[0];
4128
- const otherPositionInList = cache.current.positionsList.find((pos) => pos.locations.position === otherPosition);
4129
- if (otherPositionInList) {
4130
- const match = findMatch(cache.current.tocTree, new Link(otherPositionInList));
4131
- if (match) {
4132
- dispatch(setTocEntry(match.id));
4133
- return;
4134
- }
4135
- }
4136
- }
4137
- }
4138
- }
4139
- }, [dispatch, currentPositions, navLayout]);
4140
4564
  const handleFXLProgression = useCallback((locator) => {
4141
4565
  handleProgression(locator);
4142
- handleTocEntryOnNav(locator);
4143
- localData.set(localDataKey.current, locator);
4144
- }, [handleProgression, handleTocEntryOnNav]);
4566
+ setLocalData(locator);
4567
+ }, [handleProgression, setLocalData]);
4145
4568
  onFXLPositionChange(handleFXLProgression);
4146
4569
  const initReadingEnv = async () => {
4147
4570
  if (navLayout() === EPUBLayout.fixed) {
@@ -4149,8 +4572,6 @@ var StatefulReader = ({
4149
4572
  if (cLoc) {
4150
4573
  handleFXLProgression(cLoc);
4151
4574
  }
4152
- } else {
4153
- handleScrollAffordances(cache.current.settings.scroll);
4154
4575
  }
4155
4576
  };
4156
4577
  const p = new Peripherals(useAppStore(), RSPrefs.actions, {
@@ -4162,21 +4583,11 @@ var StatefulReader = ({
4162
4583
  case "left":
4163
4584
  if (!cache.current.settings.scroll) goLeft(!cache.current.reducedMotion, activateImmersiveOnAction);
4164
4585
  break;
4165
- case "up":
4166
- case "home":
4167
- if (cache.current.settings.scroll) activateImmersiveOnAction();
4168
- break;
4169
- case "down":
4170
- case "end":
4171
- if (cache.current.settings.scroll) activateImmersiveOnAction();
4172
- break;
4173
4586
  }
4174
4587
  },
4175
4588
  goProgression: (shiftKey) => {
4176
4589
  if (!cache.current.settings?.scroll) {
4177
4590
  shiftKey ? goBackward(!cache.current.reducedMotion, activateImmersiveOnAction) : goForward(!cache.current.reducedMotion, activateImmersiveOnAction);
4178
- } else {
4179
- activateImmersiveOnAction();
4180
4591
  }
4181
4592
  },
4182
4593
  toggleAction: (actionKey) => {
@@ -4205,51 +4616,71 @@ var StatefulReader = ({
4205
4616
  p.observe(window);
4206
4617
  },
4207
4618
  positionChanged: async function(locator) {
4208
- const currentLocator2 = localData.get(localDataKey.current);
4209
- if (currentLocator2?.href !== locator.href) {
4210
- handleTocEntryOnNav(locator);
4211
- }
4212
4619
  if (navLayout() === EPUBLayout.reflowable) {
4213
- const debouncedHandleProgression = (0, import_debounce.default)(
4620
+ const debouncedHandleProgression = (0, import_debounce2.default)(
4214
4621
  async () => {
4215
- if (currentLocator2?.href !== locator.href) {
4216
- handleScrollAffordances(cache.current.settings.scroll);
4217
- }
4218
4622
  handleProgression(locator);
4219
- localData.set(localDataKey.current, locator);
4623
+ setLocalData(locator);
4220
4624
  },
4221
4625
  250
4222
4626
  );
4223
4627
  debouncedHandleProgression();
4224
4628
  }
4629
+ if (canGoBackward()) {
4630
+ dispatch(setPublicationStart(false));
4631
+ } else {
4632
+ dispatch(setPublicationStart(true));
4633
+ }
4634
+ if (canGoForward()) {
4635
+ dispatch(setPublicationEnd(false));
4636
+ } else {
4637
+ dispatch(setPublicationEnd(true));
4638
+ }
4225
4639
  },
4226
4640
  tap: function(_e) {
4227
4641
  handleTap(_e);
4228
4642
  return true;
4229
4643
  },
4230
4644
  click: function(_e) {
4231
- cache.current.layoutUI === "layered-ui" /* layered */ && handleTap(_e);
4645
+ if (cache.current.layoutUI === "layered-ui" /* layered */) {
4646
+ if (!cache.current.settings.scroll || RSPrefs.affordances.scroll.toggleOnMiddlePointer.includes("click")) {
4647
+ handleTap(_e);
4648
+ }
4649
+ }
4232
4650
  return true;
4233
4651
  },
4234
4652
  zoom: function(_scale) {
4235
4653
  },
4236
4654
  miscPointer: function(_amount) {
4237
4655
  },
4656
+ scroll: function(_delta) {
4657
+ if (cache.current.settings.scroll && navLayout() === EPUBLayout.reflowable) {
4658
+ if (isScrollStart() || isScrollEnd()) {
4659
+ if (
4660
+ // Keep consistent with pagination behavior
4661
+ cache.current.layoutUI === "layered-ui" /* layered */
4662
+ ) {
4663
+ dispatch(setScrollAffordance(true));
4664
+ }
4665
+ } else if (!cache.current.isImmersive && _delta > 20) {
4666
+ if (RSPrefs.affordances.scroll.hideOnForwardScroll) {
4667
+ dispatch(setImmersive(true));
4668
+ }
4669
+ } else if (cache.current.isImmersive && _delta < -20) {
4670
+ if (
4671
+ // Keep consistent with pagination behavior
4672
+ cache.current.layoutUI === "layered-ui" /* layered */ && RSPrefs.affordances.scroll.showOnBackwardScroll
4673
+ ) {
4674
+ dispatch(setImmersive(false));
4675
+ }
4676
+ }
4677
+ }
4678
+ },
4238
4679
  customEvent: function(_key, _data) {
4239
4680
  },
4240
4681
  handleLocator: function(locator) {
4241
4682
  const href = locator.href;
4242
- if (href.includes(TH_CUSTOM_SCHEME)) {
4243
- if (href.includes(ThScrollActions.prev)) {
4244
- goLeft(false, () => {
4245
- scrollBackTo(RSPrefs.scroll.backTo);
4246
- });
4247
- } else if (href.includes(ThScrollActions.next)) {
4248
- goRight(false, () => {
4249
- scrollBackTo("top" /* top */);
4250
- });
4251
- }
4252
- } else if (href.startsWith("http://") || href.startsWith("https://") || href.startsWith("mailto:") || href.startsWith("tel:")) {
4683
+ if (href.startsWith("http://") || href.startsWith("https://") || href.startsWith("mailto:") || href.startsWith("tel:")) {
4253
4684
  if (confirm(`Open "${href}" ?`)) window.open(href, "_blank");
4254
4685
  } else {
4255
4686
  console.warn("Unhandled locator", locator);
@@ -4264,20 +4695,27 @@ var StatefulReader = ({
4264
4695
  constraint: value
4265
4696
  });
4266
4697
  }, [submitPreferences]);
4698
+ useEffect(() => {
4699
+ cache.current.isImmersive = isImmersive;
4700
+ }, [isImmersive]);
4701
+ useEffect(() => {
4702
+ cache.current.isHovering = isHovering;
4703
+ }, [isHovering]);
4267
4704
  useEffect(() => {
4268
4705
  cache.current.layoutUI = layoutUI;
4269
4706
  }, [layoutUI]);
4270
4707
  useEffect(() => {
4271
- cache.current.settings.scroll = scroll;
4708
+ cache.current.settings.scroll = isScroll;
4709
+ dispatch(setImmersive(false));
4272
4710
  const handleConstraint = async (value) => {
4273
4711
  await applyConstraint(value);
4274
4712
  };
4275
- if (!scroll) {
4713
+ if (!isScroll) {
4276
4714
  handleConstraint(arrowsOccupySpace ? arrowsWidth.current : 0).catch(console.error);
4277
4715
  } else {
4278
4716
  handleConstraint(0).catch(console.error);
4279
4717
  }
4280
- }, [scroll, arrowsOccupySpace, applyConstraint]);
4718
+ }, [isScroll, arrowsOccupySpace, applyConstraint, dispatch]);
4281
4719
  useEffect(() => {
4282
4720
  cache.current.settings.columnCount = columnCount;
4283
4721
  }, [columnCount]);
@@ -4293,9 +4731,6 @@ var StatefulReader = ({
4293
4731
  useEffect(() => {
4294
4732
  cache.current.settings.hyphens = hyphens;
4295
4733
  }, [hyphens]);
4296
- useEffect(() => {
4297
- cache.current.settings.layoutStrategy = layoutStrategy;
4298
- }, [layoutStrategy]);
4299
4734
  useEffect(() => {
4300
4735
  cache.current.settings.letterSpacing = letterSpacing;
4301
4736
  }, [letterSpacing]);
@@ -4326,19 +4761,6 @@ var StatefulReader = ({
4326
4761
  useEffect(() => {
4327
4762
  cache.current.positionsList = positionsList || [];
4328
4763
  }, [positionsList]);
4329
- useEffect(() => {
4330
- cache.current.tocTree = tocTree;
4331
- if (tocEntry) return;
4332
- const knownPosition = localData.get(localDataKey.current);
4333
- if (knownPosition) {
4334
- handleTocEntryOnNav(knownPosition);
4335
- } else {
4336
- const initialTocEntry = tocTree?.[0];
4337
- if (initialTocEntry) {
4338
- handleTocEntryOnNav(new Locator({ href: initialTocEntry.href, type: "" }));
4339
- }
4340
- }
4341
- }, [tocTree, tocEntry, handleTocEntryOnNav]);
4342
4764
  useEffect(() => {
4343
4765
  cache.current.arrowsOccupySpace = arrowsOccupySpace || false;
4344
4766
  }, [arrowsOccupySpace]);
@@ -4382,18 +4804,21 @@ var StatefulReader = ({
4382
4804
  const fetcher = new HttpFetcher(void 0, selfHref);
4383
4805
  const manifest = Manifest.deserialize(rawManifest);
4384
4806
  manifest.setSelfLink(selfHref);
4385
- publication.current = new Publication({
4807
+ setPublication(new Publication({
4386
4808
  manifest,
4387
4809
  fetcher
4388
- });
4389
- dispatch(setRTL(publication.current.metadata.effectiveReadingProgression === ReadingProgression.rtl));
4390
- dispatch(setFXL(publication.current.metadata.getPresentation()?.layout === EPUBLayout.fixed));
4391
- const pubTitle = publication.current.metadata.title.getTranslation("en");
4810
+ }));
4811
+ }, [rawManifest, selfHref]);
4812
+ useEffect(() => {
4813
+ if (!publication) return;
4814
+ dispatch(setRTL(publication.metadata.effectiveReadingProgression === ReadingProgression.rtl));
4815
+ dispatch(setFXL(publication.metadata.getPresentation()?.layout === EPUBLayout.fixed));
4816
+ const pubTitle = publication.metadata.title.getTranslation("en");
4392
4817
  dispatch(setRunningHead(pubTitle));
4393
4818
  dispatch(setProgression({ currentPublication: pubTitle }));
4394
4819
  let positionsList2;
4395
4820
  const fetchPositions = async () => {
4396
- positionsList2 = await publication.current?.positionsFromManifest();
4821
+ positionsList2 = await publication.positionsFromManifest();
4397
4822
  if (positionsList2 && positionsList2.length > 0) {
4398
4823
  dispatch(setProgression({ totalPositions: positionsList2.length }));
4399
4824
  }
@@ -4401,12 +4826,8 @@ var StatefulReader = ({
4401
4826
  dispatch(setPositionsList(deserializedPositionsList));
4402
4827
  };
4403
4828
  fetchPositions().catch(console.error).then(() => {
4404
- const isFXL2 = publication.current?.metadata.getPresentation()?.layout === EPUBLayout.fixed;
4405
- const initialPosition = localData.get(localDataKey.current);
4406
- let idCounter = 0;
4407
- const idGenerator = () => `toc-${++idCounter}`;
4408
- const tocTree2 = createTocTree(publication.current?.tableOfContents?.items || [], idGenerator, positionsList2);
4409
- dispatch(setTocTree(tocTree2));
4829
+ const isFXL2 = publication.metadata.getPresentation()?.layout === EPUBLayout.fixed;
4830
+ const initialPosition = getLocalData();
4410
4831
  const initialConstraint = cache.current.arrowsOccupySpace ? arrowsWidth.current : 0;
4411
4832
  const themeKeys = isFXL2 ? fxlThemeKeys : reflowThemeKeys;
4412
4833
  const theme2 = themeKeys.includes(cache.current.settings.theme) ? cache.current.settings.theme : "auto";
@@ -4429,7 +4850,6 @@ var StatefulReader = ({
4429
4850
  fontSize: cache.current.settings.fontSize,
4430
4851
  fontWeight: cache.current.settings.fontWeight,
4431
4852
  hyphens: cache.current.settings.hyphens,
4432
- layoutStrategy: cache.current.settings.layoutStrategy,
4433
4853
  letterSpacing: cache.current.settings.publisherStyles ? void 0 : cache.current.settings.letterSpacing,
4434
4854
  lineHeight: cache.current.settings.publisherStyles ? void 0 : cache.current.settings.lineHeight === null ? null : lineHeightOptions[cache.current.settings.lineHeight],
4435
4855
  optimalLineLength: cache.current.settings.lineLength?.optimal,
@@ -4444,24 +4864,21 @@ var StatefulReader = ({
4444
4864
  ...themeProps
4445
4865
  };
4446
4866
  const defaults = isFXL2 ? {} : {
4447
- layoutStrategy: RSPrefs.typography.layoutStrategy,
4448
4867
  maximalLineLength: RSPrefs.typography.maximalLineLength,
4449
4868
  minimalLineLength: RSPrefs.typography.minimalLineLength,
4450
4869
  optimalLineLength: RSPrefs.typography.optimalLineLength,
4451
- pageGutter: RSPrefs.typography.pageGutter
4870
+ pageGutter: RSPrefs.typography.pageGutter,
4871
+ scrollPaddingTop: RSPrefs.theming.layout.ui?.reflow === "layered-ui" /* layered */ ? (RSPrefs.theming.icon.size || 24) * 3 : RSPrefs.theming.icon.size || 24,
4872
+ scrollPaddingBottom: RSPrefs.theming.layout.ui?.reflow === "layered-ui" /* layered */ ? (RSPrefs.theming.icon.size || 24) * 5 : RSPrefs.theming.icon.size || 24
4452
4873
  };
4453
4874
  EpubNavigatorLoad({
4454
4875
  container: container.current,
4455
- publication: publication.current,
4876
+ publication,
4456
4877
  listeners,
4457
4878
  positionsList: positionsList2,
4458
- initialPosition,
4879
+ initialPosition: initialPosition ?? void 0,
4459
4880
  preferences,
4460
- defaults,
4461
- scrollAffordances: {
4462
- top: RSPrefs.scroll.topAffordance,
4463
- bottom: RSPrefs.scroll.bottomAffordance
4464
- }
4881
+ defaults
4465
4882
  }, () => p.observe(window));
4466
4883
  }).finally(() => {
4467
4884
  const setLoadingThunk = (dispatch2) => {
@@ -4472,21 +4889,23 @@ var StatefulReader = ({
4472
4889
  return () => {
4473
4890
  EpubNavigatorDestroy(() => p.destroy());
4474
4891
  };
4475
- }, [rawManifest, selfHref, RSPrefs, fxlThemeKeys, reflowThemeKeys]);
4892
+ }, [publication, RSPrefs, fxlThemeKeys, reflowThemeKeys]);
4476
4893
  if (!breakpoint) return null;
4477
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(I18nProvider, { locale: RSPrefs.locale, children: /* @__PURE__ */ jsx(ThPluginProvider, { children: /* @__PURE__ */ jsx("main", { children: /* @__PURE__ */ jsx(StatefulDockingWrapper, { children: /* @__PURE__ */ jsxs(
4894
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(I18nProvider, { locale: RSPrefs.locale, children: /* @__PURE__ */ jsx(ThPluginProvider, { children: /* @__PURE__ */ jsx("main", { id: "reader-main", children: /* @__PURE__ */ jsx(StatefulDockingWrapper, { children: /* @__PURE__ */ jsxs(
4478
4895
  "div",
4479
4896
  {
4480
- id: "reader-main",
4481
- className: classNames9(
4897
+ id: "reader-shell",
4898
+ className: classNames10(
4482
4899
  isFXL ? "isFXL" : "isReflow",
4483
4900
  isImmersive ? "isImmersive" : "",
4484
4901
  isHovering ? "isHovering" : "",
4485
- layoutUI
4902
+ isScroll ? "isScroll" : "isPaged",
4903
+ layoutUI,
4904
+ breakpoint
4486
4905
  ),
4487
4906
  children: [
4488
- /* @__PURE__ */ jsx(StatefulReaderHeader, {}),
4489
- isPaged ? /* @__PURE__ */ jsx("nav", { className: arrowStyles2.container, id: arrowStyles2.left, children: /* @__PURE__ */ jsx(
4907
+ /* @__PURE__ */ jsx(StatefulReaderHeader, { layout: layoutUI }),
4908
+ !isScroll ? /* @__PURE__ */ jsx("nav", { className: arrowStyles2.container, id: arrowStyles2.left, children: /* @__PURE__ */ jsx(
4490
4909
  StatefulReaderArrowButton,
4491
4910
  {
4492
4911
  direction: "left",
@@ -4496,7 +4915,7 @@ var StatefulReader = ({
4496
4915
  }
4497
4916
  ) }) : /* @__PURE__ */ jsx(Fragment, {}),
4498
4917
  /* @__PURE__ */ jsx("article", { id: "wrapper", "aria-label": en_default.reader.app.publicationWrapper, children: /* @__PURE__ */ jsx("div", { id: "container", ref: container }) }),
4499
- isPaged ? /* @__PURE__ */ jsx("nav", { className: arrowStyles2.container, id: arrowStyles2.right, children: /* @__PURE__ */ jsx(
4918
+ !isScroll ? /* @__PURE__ */ jsx("nav", { className: arrowStyles2.container, id: arrowStyles2.right, children: /* @__PURE__ */ jsx(
4500
4919
  StatefulReaderArrowButton,
4501
4920
  {
4502
4921
  direction: "right",
@@ -4505,68 +4924,11 @@ var StatefulReader = ({
4505
4924
  onPress: () => goRight(!reducedMotion, activateImmersiveOnAction)
4506
4925
  }
4507
4926
  ) }) : /* @__PURE__ */ jsx(Fragment, {}),
4508
- isPaged ? /* @__PURE__ */ jsx(StatefulReaderFooter, {}) : /* @__PURE__ */ jsx(Fragment, {})
4927
+ /* @__PURE__ */ jsx(StatefulReaderFooter, { layout: layoutUI })
4509
4928
  ]
4510
4929
  }
4511
4930
  ) }) }) }) }) });
4512
4931
  };
4513
- var BookUrlConverter = () => {
4514
- const [bookUrl, setBookUrl] = useState("");
4515
- const handleAction = (e) => {
4516
- e.preventDefault();
4517
- if (!bookUrl) return;
4518
- try {
4519
- const bytes = new TextEncoder().encode(bookUrl);
4520
- const binString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("");
4521
- const base64 = btoa(binString);
4522
- const base64Url = base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
4523
- window.location.href = new URL(
4524
- `read?book=https://publication-server.readium.org/${encodeURIComponent(base64Url)}/manifest.json`,
4525
- window.location.href
4526
- ).href;
4527
- } catch (error) {
4528
- console.error("Error encoding URL:", error);
4529
- }
4530
- };
4531
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
4532
- ThForm,
4533
- {
4534
- label: en_default.bookUrlConverter.button,
4535
- className: bookUrlConverterStyles.bookConverterForm,
4536
- onSubmit: handleAction,
4537
- compounds: {
4538
- button: {
4539
- className: bookUrlConverterStyles.bookConverterFormButton,
4540
- isDisabled: !bookUrl
4541
- }
4542
- },
4543
- children: /* @__PURE__ */ jsx(
4544
- ThFormTextField,
4545
- {
4546
- label: en_default.bookUrlConverter.label,
4547
- name: "book-url-converter",
4548
- className: bookUrlConverterStyles.bookConverterFormTextField,
4549
- type: "url",
4550
- errorMessage: en_default.bookUrlConverter.error,
4551
- compounds: {
4552
- label: {
4553
- className: bookUrlConverterStyles.bookConverterFormLabel
4554
- },
4555
- input: {
4556
- className: bookUrlConverterStyles.bookConverterFormInput,
4557
- value: bookUrl,
4558
- onChange: (e) => setBookUrl(e.target.value),
4559
- placeholder: en_default.bookUrlConverter.placeholder
4560
- },
4561
- fieldError: {
4562
- className: bookUrlConverterStyles.bookConverterFormFieldError
4563
- }
4564
- }
4565
- }
4566
- )
4567
- }
4568
- ) });
4569
- };
4570
4932
  var StatefulLoader = ({ isLoading, children }) => {
4571
4933
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
4572
4934
  ThLoader,
@@ -4578,7 +4940,115 @@ var StatefulLoader = ({ isLoading, children }) => {
4578
4940
  }
4579
4941
  ) });
4580
4942
  };
4943
+ var DefaultImage = ({
4944
+ src,
4945
+ alt = ""
4946
+ }) => /* @__PURE__ */ jsx(
4947
+ "img",
4948
+ {
4949
+ src,
4950
+ alt,
4951
+ className: publicationGridStyles.publicationImage,
4952
+ loading: "lazy"
4953
+ }
4954
+ );
4955
+ var PublicationGrid = ({
4956
+ publications,
4957
+ columnWidth = 400,
4958
+ gap = "1.5rem",
4959
+ renderCover = (publication) => /* @__PURE__ */ jsx(
4960
+ DefaultImage,
4961
+ {
4962
+ src: publication.cover,
4963
+ alt: ""
4964
+ }
4965
+ )
4966
+ }) => {
4967
+ const renderCoverWithClass = (publication) => {
4968
+ const cover = renderCover(publication);
4969
+ if (!isValidElement(cover)) {
4970
+ return /* @__PURE__ */ jsx(
4971
+ DefaultImage,
4972
+ {
4973
+ src: publication.cover,
4974
+ alt: ""
4975
+ }
4976
+ );
4977
+ }
4978
+ return cloneElement(cover, {
4979
+ className: classNames10(
4980
+ publicationGridStyles.publicationImage,
4981
+ cover.props.className
4982
+ )
4983
+ });
4984
+ };
4985
+ return /* @__PURE__ */ jsx(
4986
+ ThGrid,
4987
+ {
4988
+ className: publicationGridStyles.publicationGrid,
4989
+ items: publications,
4990
+ columnWidth,
4991
+ gap,
4992
+ renderItem: (publication, index) => /* @__PURE__ */ jsxs(
4993
+ Link,
4994
+ {
4995
+ href: publication.url,
4996
+ className: publicationGridStyles.publicationCard,
4997
+ children: [
4998
+ /* @__PURE__ */ jsx("figure", { className: publicationGridStyles.publicationCover, children: renderCoverWithClass(publication) }),
4999
+ /* @__PURE__ */ jsxs("div", { className: publicationGridStyles.publicationInfo, children: [
5000
+ /* @__PURE__ */ jsx("h2", { className: publicationGridStyles.publicationTitle, children: publication.title }),
5001
+ /* @__PURE__ */ jsx("p", { className: publicationGridStyles.publicationAuthor, children: publication.author }),
5002
+ publication.rendition && /* @__PURE__ */ jsx("p", { className: publicationGridStyles.publicationRendition, children: publication.rendition })
5003
+ ] })
5004
+ ]
5005
+ },
5006
+ index
5007
+ )
5008
+ }
5009
+ );
5010
+ };
5011
+ function usePublication({ url, onError = () => {
5012
+ } }) {
5013
+ const [error, setError] = useState("");
5014
+ const [manifest, setManifest] = useState(void 0);
5015
+ const [selfLink, setSelfLink] = useState(void 0);
5016
+ useEffect(() => {
5017
+ if (!url) {
5018
+ setError("Manifest URL is required");
5019
+ return;
5020
+ }
5021
+ const decodedUrl = decodeURIComponent(url);
5022
+ const manifestLink = new Link$1({ href: decodedUrl });
5023
+ const fetcher = new HttpFetcher(void 0);
5024
+ try {
5025
+ const fetched = fetcher.get(manifestLink);
5026
+ fetched.link().then((link) => {
5027
+ setSelfLink(link.toURL(decodedUrl));
5028
+ });
5029
+ fetched.readAsJSON().then((manifestData) => {
5030
+ setManifest(manifestData);
5031
+ }).catch((error2) => {
5032
+ console.error("Error loading manifest:", error2);
5033
+ setError(`Failed loading manifest ${decodedUrl}: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
5034
+ });
5035
+ } catch (error2) {
5036
+ console.error("Error loading manifest:", error2);
5037
+ setError(`Failed loading manifest ${decodedUrl}: ${error2 instanceof Error ? error2.message : "Unknown error"}`);
5038
+ }
5039
+ }, [url]);
5040
+ useEffect(() => {
5041
+ if (error) {
5042
+ onError(error);
5043
+ }
5044
+ }, [error, onError]);
5045
+ return {
5046
+ error,
5047
+ manifest,
5048
+ selfLink
5049
+ };
5050
+ }
4581
5051
 
4582
- export { BookUrlConverter, StatefulActionIcon, StatefulBottomSheet, StatefulCollapsibleActionsBar, StatefulColumns, StatefulDockedSheet, StatefulFontFamily, StatefulFontWeight, StatefulFullScreenSheet, StatefulFullscreenTrigger, StatefulGroupWrapper, StatefulHyphens, StatefulJumpToPositionContainer, StatefulJumpToPositionTrigger, StatefulLayout, StatefulLetterSpacing, StatefulLineHeight, StatefulLoader, StatefulNumberField, StatefulOverflowMenu, StatefulOverflowMenuItem, StatefulParagraphIndent, StatefulParagraphSpacing, StatefulPopoverSheet, StatefulPublisherStyles, StatefulRadioGroup, StatefulReader, StatefulSettingsContainer, StatefulSettingsTrigger, StatefulSheetWrapper, StatefulSlider, StatefulSpacingGroup, StatefulSpacingGroupContainer, StatefulSwitch, StatefulTextAlign, StatefulTextGroup, StatefulTextGroupContainer, StatefulTextNormalize, StatefulTheme, StatefulTocContainer, StatefulTocTrigger, StatefulWordSpacing, StatefulZoom, createDefaultPlugin, useDocking };
5052
+ export { DefaultImage, PublicationGrid, StatefulActionIcon, StatefulBottomSheet, StatefulCollapsibleActionsBar, StatefulColumns, StatefulDockedSheet, StatefulFontFamily, StatefulFontWeight, StatefulFullScreenSheet, StatefulFullscreenTrigger, StatefulGroupWrapper, StatefulHyphens, StatefulJumpToPositionContainer, StatefulJumpToPositionTrigger, StatefulLayout, StatefulLetterSpacing, StatefulLineHeight, StatefulLoader, StatefulNumberField, StatefulOverflowMenu, StatefulOverflowMenuItem, StatefulParagraphIndent, StatefulParagraphSpacing, StatefulPopoverSheet, StatefulPublisherStyles, StatefulRadioGroup, StatefulReader, StatefulSettingsContainer, StatefulSettingsTrigger, StatefulSheetWrapper, StatefulSlider, StatefulSpacingGroup, StatefulSpacingGroupContainer, StatefulSwitch, StatefulTextAlign, StatefulTextGroup, StatefulTextGroupContainer, StatefulTextNormalize, StatefulTheme, StatefulTocContainer, StatefulTocTrigger, StatefulWordSpacing, StatefulZoom, createDefaultPlugin, useDocking, usePublication };
4583
5053
  //# sourceMappingURL=index.mjs.map
4584
5054
  //# sourceMappingURL=index.mjs.map