forked from marko/svitak
147 lines
4.3 KiB
Haskell
147 lines
4.3 KiB
Haskell
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
|
|
|
-- | Shared types for the whole reader. The parser ('EpubParser') produces a
|
|
-- 'BookStructure' cheaply and renders each 'Chapter' on demand; the UI
|
|
-- consumes both through the 'BookInfo' interface.
|
|
module Types
|
|
( -- * Identifiers
|
|
ChapterIndex (..),
|
|
InternalPath (..),
|
|
|
|
-- * Book content
|
|
Chapter (..),
|
|
ChapterRef (..),
|
|
TocEntry (..),
|
|
BookStructure (..),
|
|
|
|
-- * The book interface the UI talks to
|
|
BookInfo (..),
|
|
|
|
-- * UI state
|
|
AppState (..),
|
|
UserAction (..),
|
|
|
|
-- * EPUB environment
|
|
EpubEnv (..),
|
|
EpubAction,
|
|
ReadMode(..),
|
|
Html(..),
|
|
PlainText(..),
|
|
)
|
|
where
|
|
|
|
import Codec.Archive.Zip (Archive)
|
|
import qualified Codec.Epub.Data.Manifest as DMan
|
|
import qualified Codec.Epub.Data.Metadata as DMeta
|
|
import Control.Monad.Except (ExceptT)
|
|
import Control.Monad.Reader (ReaderT)
|
|
import qualified Data.Map.Strict as M
|
|
import qualified Data.Text as T
|
|
-- | Position of a chapter within the linear reading order (the spine).
|
|
newtype ChapterIndex = ChapterIndex Int
|
|
deriving (Show, Eq, Ord, Num)
|
|
|
|
-- | A fully-resolved path to a file inside the EPUB zip.
|
|
newtype InternalPath = InternalPath FilePath
|
|
deriving (Show, Eq, Ord)
|
|
|
|
-- | A lightweight pointer to a chapter: enough to navigate and label it in the
|
|
-- sidebar without having rendered its (potentially large) content yet.
|
|
data ChapterRef = ChapterRef
|
|
{ refPath :: InternalPath,
|
|
refTitle :: T.Text,
|
|
refIndex :: ChapterIndex
|
|
}
|
|
deriving (Show)
|
|
|
|
-- | A chapter rendered to self-contained HTML, ready for WebKit.
|
|
data Chapter = Chapter
|
|
{ chapterPath :: InternalPath,
|
|
chapterTitle :: T.Text,
|
|
chapterHtml :: Html,
|
|
chapterIndex :: ChapterIndex
|
|
}
|
|
deriving (Show)
|
|
|
|
newtype Html = Html T.Text deriving (Show, Eq)
|
|
newtype PlainText = PlainText T.Text deriving (Show, Eq)
|
|
|
|
-- | A node in the (possibly nested) table of contents. @tocTarget@ is the
|
|
-- resolved chapter path; @tocFragment@ is the optional in-page anchor.
|
|
data TocEntry = TocEntry
|
|
{ tocLabel :: T.Text,
|
|
tocTarget :: InternalPath,
|
|
tocFragment :: T.Text,
|
|
tocChildren :: [TocEntry]
|
|
}
|
|
deriving (Show)
|
|
|
|
-- | The cheap-to-compute skeleton of a book: reading order + navigation tree.
|
|
data BookStructure = BookStructure
|
|
{ structRefs :: [ChapterRef],
|
|
structToc :: [TocEntry]
|
|
}
|
|
|
|
-- | The UI only ever sees a book through this interface, so it stays
|
|
-- format-agnostic. The single instance ('EpubEnv') lives in 'EpubParser'.
|
|
class BookInfo a where
|
|
bookTitle :: a -> T.Text
|
|
bookAuthors :: a -> [T.Text]
|
|
bookFilePath :: a -> FilePath
|
|
|
|
-- | Resolve the spine and TOC. Fast: does not render chapter bodies.
|
|
loadStructure :: a -> IO (Either String BookStructure)
|
|
|
|
-- | Render a single chapter's body to self-contained HTML, on demand.
|
|
renderChapter :: a -> ChapterRef -> IO (Either String Chapter)
|
|
|
|
-- | Extract a single chapter's plain text (tags stripped), for search.
|
|
chapterText :: a -> ChapterRef -> IO (Either String PlainText)
|
|
|
|
-- | Mutable application state, held in a 'Control.Concurrent.STM.TVar'.
|
|
-- Rendered chapters accumulate in 'stCache' as the reader visits them.
|
|
data AppState = AppState
|
|
{ stRefs :: [ChapterRef],
|
|
stToc :: [TocEntry],
|
|
stCache :: M.Map ChapterIndex Chapter,
|
|
stIndex :: ChapterIndex,
|
|
stZoom :: Double,
|
|
stTitle :: T.Text,
|
|
stBookPath :: FilePath,
|
|
stGen :: Int,
|
|
stUse :: M.Map ChapterIndex Int,
|
|
stTick :: Int,
|
|
stSearchIndex :: M.Map ChapterIndex PlainText,
|
|
stMode :: ReadMode
|
|
}
|
|
|
|
-- Enable toggling between paginated (2 pages on the screen) or scrolling (single, flowing page)
|
|
data ReadMode = Paginated | Scrolling
|
|
deriving (Eq)
|
|
|
|
-- | A user request, produced by the keyboard / sidebar and handled centrally.
|
|
-- 'GoToChapter' carries an optional anchor to scroll to within the chapter.
|
|
data UserAction
|
|
= NextChapter
|
|
| PrevChapter
|
|
| ZoomIn
|
|
| ZoomOut
|
|
| GoToChapter ChapterIndex T.Text
|
|
| NextPage
|
|
| PrevPage
|
|
| ToggleMode
|
|
| SearchFocus
|
|
|
|
-- | Parsed EPUB, carried through the parser as a reader environment.
|
|
data EpubEnv = EpubEnv
|
|
{ archive :: Archive,
|
|
opfXml :: String,
|
|
baseDir :: FilePath,
|
|
bookPath :: FilePath,
|
|
eMetadata :: DMeta.Metadata,
|
|
eManifest :: DMan.Manifest,
|
|
eVersion :: T.Text
|
|
}
|
|
|
|
-- | The parser monad: read-only access to the 'EpubEnv', with string errors.
|
|
type EpubAction a = ReaderT EpubEnv (ExceptT String IO) a
|