Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions KMReader.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@
"CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "KMReader/KMReader-macOS.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 376;
CURRENT_PROJECT_VERSION = 377;
DEVELOPMENT_TEAM = M777UHWZA4;
ENABLE_APP_SANDBOX = YES;
ENABLE_HARDENED_RUNTIME = YES;
Expand Down Expand Up @@ -496,7 +496,7 @@
"CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = "KMReader/KMReader-macOS.entitlements";
CODE_SIGN_IDENTITY = "Apple Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 376;
CURRENT_PROJECT_VERSION = 377;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=appletvos*]" = M777UHWZA4;
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = M777UHWZA4;
Expand Down Expand Up @@ -556,7 +556,7 @@
CODE_SIGN_ENTITLEMENTS = KMReaderWidgets/KMReaderWidgets.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 376;
CURRENT_PROJECT_VERSION = 377;
DEVELOPMENT_TEAM = M777UHWZA4;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = KMReaderWidgets/Info.plist;
Expand Down Expand Up @@ -590,7 +590,7 @@
CODE_SIGN_IDENTITY = "Apple Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 376;
CURRENT_PROJECT_VERSION = 377;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = M777UHWZA4;
GENERATE_INFOPLIST_FILE = YES;
Expand Down
27 changes: 27 additions & 0 deletions KMReader/Features/Reader/Models/ReaderContainerInsets.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// ReaderContainerInsets.swift
//
//

import CoreGraphics

#if os(iOS)
import UIKit
#endif

struct ReaderContainerInsets: Equatable {
var top: CGFloat
var left: CGFloat
var bottom: CGFloat
var right: CGFloat

static let zero = ReaderContainerInsets(top: 0, left: 0, bottom: 0, right: 0)
}

#if os(iOS)
extension ReaderContainerInsets {
var uiEdgeInsets: UIEdgeInsets {
UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)
}
}
#endif
62 changes: 51 additions & 11 deletions KMReader/Features/Reader/ViewModels/EpubReaderViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
//
//

#if os(iOS)
#if os(iOS) || os(macOS)
import Foundation
import Observation
import SwiftUI
import UIKit
#if os(iOS)
import UIKit
#elseif os(macOS)
import AppKit
#endif

struct WebPubLocation: Equatable {
let href: String
Expand Down Expand Up @@ -614,6 +618,10 @@
chapterPageCounts[index]
}

var resolvedViewportSize: CGSize? {
viewportSize.width > 0 && viewportSize.height > 0 ? viewportSize : nil
}

func initialProgression(for chapterIndex: Int) -> Double? {
guard initialChapterIndex == chapterIndex else { return nil }
return initialProgression
Expand Down Expand Up @@ -652,20 +660,39 @@
updateLocation(chapterIndex: currentChapterIndex, pageIndex: currentPageIndex)

// Store in memory cache only (no file persistence)
let effectiveViewport = viewportSize.width > 0 ? viewportSize : UIScreen.main.bounds.size
let effectiveViewport = viewportSize.width > 0 ? viewportSize : Self.defaultViewportSize
let href = readingOrder[chapterIndex].href
let cacheKey = pageCountCacheKey(for: href, viewport: effectiveViewport)
pageCountCache[cacheKey] = normalizedCount
}

var labelTopOffset: CGFloat { UIDevice.current.userInterfaceIdiom == .pad ? 24 : 8 }
var labelBottomOffset: CGFloat { UIDevice.current.userInterfaceIdiom == .pad ? 16 : 8 }
var useSafeArea: Bool { UIDevice.current.userInterfaceIdiom != .pad }
var labelTopOffset: CGFloat {
#if os(iOS)
return PlatformHelper.isPad ? 24 : 8
#else
return 8
#endif
}
var labelBottomOffset: CGFloat {
#if os(iOS)
return PlatformHelper.isPad ? 16 : 8
#else
return 8
#endif
}
var useSafeArea: Bool {
#if os(iOS)
return !PlatformHelper.isPad
#else
return true
#endif
}

func containerInsetsForLabels() -> UIEdgeInsets {
let topPadding = labelTopOffset + 24
let bottomPadding = labelBottomOffset + 24
return UIEdgeInsets(top: topPadding, left: 0, bottom: bottomPadding, right: 0)
func containerInsetsForLabels() -> ReaderContainerInsets {
WebPubInfoOverlaySupport.containerInsets(
topOffset: labelTopOffset,
bottomOffset: labelBottomOffset
)
}

// MARK: - Private Methods
Expand Down Expand Up @@ -900,7 +927,7 @@

var effectiveViewport = viewportSize
if effectiveViewport.width <= 0 {
effectiveViewport = UIScreen.main.bounds.size
effectiveViewport = Self.defaultViewportSize
}

for (index, link) in readingOrder.enumerated() {
Expand Down Expand Up @@ -1168,6 +1195,19 @@
}
return trimmed.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
}

private static var defaultViewportSize: CGSize {
#if os(iOS)
return UIScreen.main.bounds.size
#elseif os(macOS)
if let screen = NSScreen.main {
return screen.frame.size
}
return CGSize(width: 1280, height: 800)
#else
return CGSize(width: 1280, height: 800)
#endif
}
}

#endif
4 changes: 2 additions & 2 deletions KMReader/Features/Reader/Views/BookReaderView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ struct BookReaderView: View {
onClose: closeReader
)
} else {
#if os(iOS)
#if os(iOS) || os(macOS)
EpubReaderView(
sessionID: sessionID,
book: book,
Expand All @@ -76,7 +76,7 @@ struct BookReaderView: View {
title: "EPUB Reader Not Available",
message: String(
localized:
"EPUB reading is only supported on iOS."
"EPUB reading is only supported on iOS and macOS."
),
onClose: closeReader
)
Expand Down
51 changes: 33 additions & 18 deletions KMReader/Features/Reader/Views/DivinaReaderView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,9 @@ struct DivinaReaderView: View {
KeyboardHelpOverlay(
readingDirection: readingDirection,
hasTOC: !viewModel.tableOfContents.isEmpty,
supportsLiveText: true,
supportsJumpToPage: true,
supportsToggleControls: true,
hasNextBook: currentSegmentNextBook != nil,
onDismiss: {
hideKeyboardHelp()
Expand All @@ -820,120 +823,126 @@ struct DivinaReaderView: View {
.animation(.default, value: showKeyboardHelp)
}

private func handleKeyCode(_ keyCode: UInt16, flags: NSEvent.ModifierFlags) {
private func handleKeyCode(_ keyCode: UInt16, flags: NSEvent.ModifierFlags) -> Bool {
// Handle ESC key to close window
if keyCode == 53 { // ESC key
closeReader()
return
return true
}

// Handle ? key and H key for keyboard help
if keyCode == 44 { // ? key (Shift + /)
showKeyboardHelp.toggle()
return
return true
}

// Handle Return/Enter key for fullscreen toggle
if keyCode == 36 { // Return/Enter key
if let window = NSApplication.shared.keyWindow {
window.toggleFullScreen(nil)
}
return
return true
}

// Handle Space key for toggle controls
if keyCode == 49 { // Space key
toggleControls()
return
return true
}

// Ignore if modifier keys are pressed (except for system shortcuts)
guard flags.intersection([.command, .option, .control]).isEmpty else { return }
guard flags.intersection([.command, .option, .control]).isEmpty else { return false }

// Handle F key for fullscreen toggle
if keyCode == 3 { // F key
if let window = NSApplication.shared.keyWindow {
window.toggleFullScreen(nil)
}
return
return true
}

// Handle H key for keyboard help
if keyCode == 4 { // H key
showKeyboardHelp.toggle()
return
return true
}

// Handle C key for toggle controls
if keyCode == 8 { // C key
toggleControls()
return
return true
}

if keyCode == 37 { // L key
enableLiveText.toggle()
let message = enableLiveText ? String(localized: "Live Text: ON") : String(localized: "Live Text: OFF")
ErrorManager.shared.notify(message: message)
return
return true
}

// Handle T key for TOC
if keyCode == 17 { // T key
if !viewModel.tableOfContents.isEmpty {
showingTOCSheet = true
}
return
return true
}

// Handle J key for jump to page
if keyCode == 38 { // J key
if viewModel.hasPages {
showingPageJumpSheet = true
}
return
return true
}

// Handle N key for next book
if keyCode == 45 { // N key
if let nextBook = currentSegmentNextBook {
openNextBook(nextBookId: nextBook.id)
}
return
return true
}

guard viewModel.hasPages else { return }
guard viewModel.hasPages else { return false }

switch readingDirection {
case .ltr:
switch keyCode {
case 124: // Right arrow
goToNextPage()
return true
case 123: // Left arrow
goToPreviousPage()
return true
default:
break
return false
}
case .rtl:
switch keyCode {
case 123: // Left arrow
goToNextPage()
return true
case 124: // Right arrow
goToPreviousPage()
return true
default:
break
return false
}
case .vertical:
switch keyCode {
case 125: // Down arrow
goToNextPage()
return true
case 126: // Up arrow
goToPreviousPage()
return true
default:
break
return false
}
case .webtoon:
// Webtoon scrolling is handled by WebtoonReaderView's own keyboard monitor
break
return false
}
}
#endif
Expand Down Expand Up @@ -1337,14 +1346,20 @@ struct DivinaReaderView: View {

return ReaderPresentationManager.MacReaderCommandState(
isActive: true,
supportsReaderSettings: true,
supportsBookDetails: currentSegmentBook != nil,
hasPages: viewModel.hasPages,
hasTableOfContents: !viewModel.tableOfContents.isEmpty,
supportsPageJump: viewModel.hasPages,
supportsBookNavigation: true,
canOpenPreviousBook: currentSegmentPreviousBook != nil,
canOpenNextBook: currentSegmentNextBook != nil,
readingDirection: readingDirection,
pageLayout: pageLayout,
isolateCoverPage: isolateCoverPage,
splitWidePageMode: splitWidePageMode,
supportsReadingDirectionSelection: true,
supportsPageLayoutSelection: true,
supportsDualPageOptions: supportsDualPageOptions,
supportsSplitWidePageMode: supportsSplitWidePageMode
)
Expand Down
2 changes: 1 addition & 1 deletion KMReader/Features/Reader/Views/Epub/EpubEndPageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//
//

#if os(iOS)
#if os(iOS) || os(macOS)
import SwiftUI

struct EpubEndPageView: View {
Expand Down
Loading
Loading