From 400ea83f7d1ea0c6d56e29bab270cc5b5e6b5366 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 15 Jul 2015 09:49:36 -0500 Subject: [PATCH] Refactor to use current style recommendations. --- App.elm | 440 +++++++++++++++---------- Component/Editor.elm | 293 +++++++++++----- Component/LeftSidebar.elm | 329 ++++++++++++------ Component/LeftSidebar/OpenMenuView.elm | 54 +-- Component/Page.elm | 177 ++++++---- Component/RightSidebar.elm | 188 +++++++---- Dreamwriter.elm | 99 +++--- Dreamwriter/Mailboxes.elm | 172 ++++++---- 8 files changed, 1128 insertions(+), 624 deletions(-) diff --git a/App.elm b/App.elm index f1fca9e..fa759cf 100644 --- a/App.elm +++ b/App.elm @@ -5,18 +5,17 @@ import Dreamwriter.Mailboxes exposing (signals, addresses) import Dreamwriter.Mailboxes as Mailboxes import Component.Page as Page -import Component.LeftSidebar as LeftSidebar +import Component.LeftSidebar as LeftSidebar import Component.RightSidebar as RightSidebar -import Component.Editor as Editor +import Component.Editor as Editor import String -import Graphics.Element exposing (Element, container, midTop) import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (..) import Html.Lazy exposing (..) import Signal -import Signal exposing (Signal, sampleOn, dropRepeats, mergeMany, foldp, forwardTo) +import Signal exposing (Signal) import Time exposing (Time, since) import List exposing (..) import Maybe @@ -24,200 +23,278 @@ import Window import Dict import Set + debounce : Time -> Signal a -> Signal a -debounce wait signal = sampleOn (since wait signal |> dropRepeats) signal +debounce wait signal = + Signal.sampleOn (since wait signal |> Signal.dropRepeats) signal + -- UPDATES -- type Update - = NoOp - | LoadAsCurrentDoc Doc - | OpenDocId Identifier - | ListDocs (List Doc) - | OpenNoteId Identifier - | ListNotes (List Note) - | SetCurrentNote (Maybe Note) - | SetChapters (List Chapter) - | UpdateChapter Chapter - | SetTitle (String, Int) - | SetDescription (String, Int) - | SetFullscreen FullscreenState - | PutSnapshot Snapshot - | SetPage Page.Model + = NoOp + | LoadAsCurrentDoc Doc + | OpenDocId Identifier + | ListDocs (List Doc) + | OpenNoteId Identifier + | ListNotes (List Note) + | SetCurrentNote (Maybe Note) + | SetChapters (List Chapter) + | UpdateChapter Chapter + | SetTitle (String, Int) + | SetDescription (String, Int) + | SetFullscreen FullscreenState + | PutSnapshot Snapshot + | SetPage Page.Model + -- updates from user input updates : Signal.Mailbox Update -updates = Signal.mailbox NoOp +updates = + Signal.mailbox NoOp + + +type alias AppState = + { + page : Page.Model, -type alias AppState = { - page : Page.Model, + fullscreen : FullscreenState, - fullscreen : FullscreenState, + currentDoc : Maybe Doc, + currentDocId : Maybe Identifier, + currentNote : Maybe Note, - currentDoc : Maybe Doc, - currentDocId : Maybe Identifier, - currentNote : Maybe Note, + docs : List Doc, + notes : List Note, + snapshots : Dict.Dict Identifier Snapshot + } - docs : List Doc, - notes : List Note, - snapshots : Dict.Dict Identifier Snapshot -} initialState : AppState -initialState = { - page = Page.initialModel, +initialState = + { + page = Page.initialModel, - fullscreen = False, + fullscreen = False, - currentDoc = Nothing, - currentDocId = Nothing, - currentNote = Nothing, + currentDoc = Nothing, + currentDocId = Nothing, + currentNote = Nothing, + + docs = [], + notes = [], + snapshots = Dict.empty + } - docs = [], - notes = [], - snapshots = Dict.empty - } transition : Update -> AppState -> AppState transition action state = - case action of - NoOp -> state - - OpenDocId id -> - let initialPage = Page.initialModel - page' = { initialPage | - currentDocId <- Just id, - currentDoc <- state.currentDoc, - docs <- state.docs, - notes <- state.notes, - fullscreen <- state.fullscreen - } - in { state | - currentDocId <- Just id, - page <- page' - } - - LoadAsCurrentDoc doc -> - let stateAfterOpenDocId = transition (OpenDocId doc.id) state - newState = {stateAfterOpenDocId | currentDoc <- Just doc} - in - updateCurrentDoc (\_ -> doc) newState - |> pruneSnapshots - - ListDocs docs -> - {state | docs <- docs} - - ListNotes notes -> - {state | notes <- notes} - - SetCurrentNote currentNote -> - {state | currentNote <- currentNote} - - SetChapters chapters -> - updateCurrentDoc (\doc -> {doc | chapters <- chapters}) state - |> pruneSnapshots - - UpdateChapter chapter -> - updateCurrentDoc (\doc -> {doc | chapters <- (map (preferById chapter) doc.chapters)}) state - |> pruneSnapshots - - SetTitle (title, words) -> - updateCurrentDoc (\doc -> {doc | title <- title, titleWords <- words}) state - - SetDescription (description, words) -> - updateCurrentDoc (\doc -> {doc | description <- description, descriptionWords <- words}) state - - SetFullscreen enabled -> - {state | fullscreen <- enabled} - - PutSnapshot snapshot -> - {state | snapshots <- Dict.insert snapshot.id snapshot state.snapshots} - - SetPage model -> { state | - page <- model, - currentDocId <- model.currentDocId, - currentNote <- model.currentNote - } + case action of + NoOp -> + state + + OpenDocId id -> + let + initialPage = + Page.initialModel + + page = + { initialPage | + currentDocId <- Just id, + currentDoc <- state.currentDoc, + docs <- state.docs, + notes <- state.notes, + fullscreen <- state.fullscreen + } + in + { state | + currentDocId <- Just id, + page <- page + } + + LoadAsCurrentDoc doc -> + let + stateAfterOpenDocId = + transition (OpenDocId doc.id) state + + newState = + { stateAfterOpenDocId | currentDoc <- Just doc } + in + updateCurrentDoc (always doc) newState + |> pruneSnapshots + + ListDocs docs -> + { state | docs <- docs } + + ListNotes notes -> + { state | notes <- notes } + + SetCurrentNote currentNote -> + { state | currentNote <- currentNote } + + SetChapters chapters -> + let + updateDoc doc = + { doc | chapters <- chapters } + in + updateCurrentDoc updateDoc state + |> pruneSnapshots + + UpdateChapter chapter -> + let + updateDoc doc = + { doc | + chapters <- List.map (preferById chapter) doc.chapters + } + in + updateCurrentDoc updateDoc state + |> pruneSnapshots + + SetTitle (title, words) -> + let + updateDoc doc = + { doc | title <- title, titleWords <- words } + in + updateCurrentDoc updateDoc state + + SetDescription (description, words) -> + let + updateDoc doc = + { doc | + description <- description, + descriptionWords <- words + } + in + updateCurrentDoc updateDoc state + + SetFullscreen enabled -> + { state | fullscreen <- enabled } + + PutSnapshot snapshot -> + { state | + snapshots <- Dict.insert snapshot.id snapshot state.snapshots + } + + SetPage model -> + { state | + page <- model, + currentDocId <- model.currentDocId, + currentNote <- model.currentNote + } + -- Throw out any snapshots that are no longer relevant, so they can be GC'd. pruneSnapshots : AppState -> AppState pruneSnapshots state = - case state.currentDoc of - Nothing -> state - Just currentDoc -> - let allSnapshotIds = Set.fromList <| map .snapshotId currentDoc.chapters - newSnapshots = state.snapshots - |> Dict.filter (\id _ -> Set.member id allSnapshotIds) - in - {state | snapshots <- newSnapshots} + case state.currentDoc of + Nothing -> + state + + Just currentDoc -> + let + allSnapshotIds = + currentDoc.chapters + |> List.map .snapshotId + |> Set.fromList + + newSnapshots = + state.snapshots + |> Dict.filter (\id _ -> Set.member id allSnapshotIds) + in + { state | snapshots <- newSnapshots } + updateCurrentDoc : (Doc -> Doc) -> AppState -> AppState updateCurrentDoc transformation state = - case state.currentDoc of - Nothing -> state - Just currentDoc -> - let newCurrentDoc = transformation currentDoc - newDocs = map (preferById newCurrentDoc) state.docs - in - {state | currentDoc <- Just newCurrentDoc - , docs <- newDocs - } + case state.currentDoc of + Nothing -> + state + + Just currentDoc -> + let + newCurrentDoc = + transformation currentDoc + + newDocs = + List.map (preferById newCurrentDoc) state.docs + in + { state | + currentDoc <- Just newCurrentDoc, + docs <- newDocs + } + preferById : { a | id : b } -> { a | id : b } -> { a | id : b } preferById preferred given = - if preferred.id == given.id - then preferred - else given + if preferred.id == given.id then + preferred + else + given + + +main : Signal Html +main = + Signal.map scene state -main : Signal Element -main = Signal.map2 scene state Window.dimensions userInput : Signal Update userInput = - mergeMany [ - Signal.map LoadAsCurrentDoc loadAsCurrentDoc, - Signal.map ListDocs listDocs, - Signal.map ListNotes listNotes, - Signal.map SetChapters setChapters, - Signal.map UpdateChapter updateChapter, - Signal.map SetTitle setTitle, - Signal.map SetDescription setDescription, - Signal.map SetFullscreen setFullscreen, - Signal.map PutSnapshot putSnapshot, - Signal.map SetCurrentNote setCurrentNote, - updates.signal - ] + Signal.mergeMany + [ + Signal.map LoadAsCurrentDoc loadAsCurrentDoc, + Signal.map ListDocs listDocs, + Signal.map ListNotes listNotes, + Signal.map SetChapters setChapters, + Signal.map UpdateChapter updateChapter, + Signal.map SetTitle setTitle, + Signal.map SetDescription setDescription, + Signal.map SetFullscreen setFullscreen, + Signal.map PutSnapshot putSnapshot, + Signal.map SetCurrentNote setCurrentNote, + updates.signal + ] + generalizePageUpdate : AppState -> Page.Update -> Update -generalizePageUpdate state pageUpdate = SetPage (Page.transition pageUpdate state.page) +generalizePageUpdate state pageUpdate = + SetPage (Page.transition pageUpdate state.page) + modelPage : AppState -> Page.Model -modelPage state = { - leftSidebar = state.page.leftSidebar, - rightSidebar = state.page.rightSidebar, - editor = state.page.editor, +modelPage state = + { + leftSidebar = state.page.leftSidebar, + rightSidebar = state.page.rightSidebar, + editor = state.page.editor, + + fullscreen = state.fullscreen, - fullscreen = state.fullscreen, + currentDocId = state.currentDocId, + currentDoc = state.currentDoc, + currentNote = state.currentNote, - currentDocId = state.currentDocId, - currentDoc = state.currentDoc, - currentNote = state.currentNote, + docs = state.docs, + notes = state.notes + } - docs = state.docs, - notes = state.notes - } +scene : AppState -> Html +scene state = + let + pageUpdate = + Signal.forwardTo updates.address (generalizePageUpdate state) + + addresses = + Mailboxes.addresses + + in + Page.view { addresses | update = pageUpdate } (modelPage state) -scene : AppState -> (Int, Int) -> Element -scene state (w, h) = - let pageUpdate = forwardTo updates.address (generalizePageUpdate state) - addresses = Mailboxes.addresses - html = Page.view { addresses | update = pageUpdate } (modelPage state) - in - container w h midTop (toElement w h html) -- manage the state of our application over time state : Signal AppState -state = foldp transition initialState userInput +state = + Signal.foldp transition initialState userInput + -- PORTS -- @@ -232,44 +309,73 @@ port listDocs : Signal (List Doc) port listNotes : Signal (List Note) port putSnapshot : Signal Snapshot + port setCurrentDocId : Signal (Maybe Identifier) -port setCurrentDocId = Signal.map .currentDocId state +port setCurrentDocId = + Signal.map .currentDocId state + port newDoc : Signal () -port newDoc = signals.newDoc +port newDoc = + signals.newDoc + port openFromFile : Signal () -port openFromFile = signals.openFromFile +port openFromFile = + signals.openFromFile + port downloadDoc : Signal DownloadOptions -port downloadDoc = signals.download +port downloadDoc = + signals.download + port printDoc : Signal () -port printDoc = signals.print +port printDoc = + signals.print + port navigateToChapterId : Signal Identifier -port navigateToChapterId = signals.navigateToChapterId +port navigateToChapterId = + signals.navigateToChapterId + port navigateToTitle : Signal () -port navigateToTitle = signals.navigateToTitle +port navigateToTitle = + signals.navigateToTitle + port newNote : Signal () -port newNote = signals.newNote +port newNote = + signals.newNote + port openNoteId : Signal Identifier -port openNoteId = signals.openNoteId +port openNoteId = + signals.openNoteId + port newChapter : Signal () -port newChapter = signals.newChapter +port newChapter = + signals.newChapter + port searchNotes : Signal String -port searchNotes = debounce 500 <| signals.searchNotes +port searchNotes = + signals.searchNotes + |> debounce 500 + port fullscreen : Signal Bool -port fullscreen = signals.fullscreen +port fullscreen = + signals.fullscreen + port execCommand : Signal String -port execCommand = signals.execCommand +port execCommand = + signals.execCommand + port remoteSync : Signal () -port remoteSync = signals.remoteSync \ No newline at end of file +port remoteSync = + signals.remoteSync \ No newline at end of file diff --git a/Component/Editor.elm b/Component/Editor.elm index bcbc348..a47ae64 100644 --- a/Component/Editor.elm +++ b/Component/Editor.elm @@ -12,120 +12,241 @@ import List exposing (..) import Signal exposing (Address, mailbox) import Json.Encode exposing (string) -type alias Addresses a = { a | - fullscreen : Address FullscreenState, - remoteSync : Address (), - execCommand : Address String -} -type alias Model = { - currentDoc : Doc, - fullscreen : FullscreenState -} +type alias Addresses a = + { a | + fullscreen : Address FullscreenState, + remoteSync : Address (), + execCommand : Address String + } + + +type alias Model = + { + currentDoc : Doc, + fullscreen : FullscreenState + } + initialModel : Model -initialModel = { - currentDoc = emptyDoc, - fullscreen = False - } +initialModel = + { + currentDoc = emptyDoc, + fullscreen = False + } + view : Addresses a -> Model -> Html view channels model = - lazy3 viewEditor channels model.currentDoc model.fullscreen + lazy3 viewEditor channels model.currentDoc model.fullscreen + viewEditor : Addresses a -> Doc -> FullscreenState -> Html viewEditor channels currentDoc fullscreen = - div [id "editor-container"] [ - div [id "editor-frame"] [ - div [id "editor-header"] [ - div [class "toolbar-section toolbar-button flaticon-zoom19"] [], - div [class "toolbar-section"] [ - viewFontControl channels.execCommand "toggle-bold" "B" "bold", - viewFontControl channels.execCommand "toggle-italics" "I" "italic", - viewFontControl channels.execCommand "toggle-strikethrough" "\xA0S\xA0" "strikethrough" - ], - lazy2 viewFullscreenButton channels.fullscreen fullscreen - ], - - div [id "document-page"] <| [ - h1 [id "edit-title" ] [], - div [id "edit-description"] [] - ] ++ concatMap (lazyViewChapter << .id) currentDoc.chapters, - - div [id "editor-footer"] [ - let wordCount = currentDoc.titleWords + currentDoc.descriptionWords + - (sum <| map (\chap -> chap.headingWords + chap.bodyWords) currentDoc.chapters) - in - div [id "doc-word-count"] [text <| (pluralize "word" wordCount) ++ " saved"], - div [id "dropbox-sync"] [ - input [ - id "toggle-dropbox-sync", - property "type" (string "checkbox"), - onClick channels.remoteSync () - ] [], - label [for "toggle-dropbox-sync"] [ - text " sync to Dropbox" - ] + div + [ id "editor-container" ] + [ + div + [ id "editor-frame" ] + [ + viewEditorHeader channels currentDoc fullscreen, + viewOutline channels currentDoc fullscreen, + viewEditorFooter channels currentDoc fullscreen + ] ] - ] - ] - ] + + +viewEditorHeader : Addresses a -> Doc -> FullscreenState -> Html +viewEditorHeader channels currentDoc fullscreen = + div + [ id "editor-header" ] + [ + div [ class "toolbar-section toolbar-button flaticon-zoom19" ] [], + + div + [ class "toolbar-section" ] + [ + viewFontControl + channels.execCommand + "toggle-bold" + "B" + "bold" + , + + viewFontControl + channels.execCommand + "toggle-italics" + "I" + "italic" + , + + viewFontControl + channels.execCommand + "toggle-strikethrough" + "\xA0S\xA0" + "strikethrough" + ] + , + + lazy2 viewFullscreenButton channels.fullscreen fullscreen + ] + + +viewEditorFooter : Addresses a -> Doc -> FullscreenState -> Html +viewEditorFooter channels currentDoc fullscreen = + let + countChapterWords chapter = + chapter.headingWords + chapter.bodyWords + + chapterWords = + currentDoc.chapters + |> List.map countChapterWords + |> List.sum + + wordCount = + currentDoc.titleWords + + currentDoc.descriptionWords + + chapterWords + + wordCountLabel = + (pluralize "word" wordCount) ++ " saved" + in + div + [ id "editor-footer" ] + [ + div [ id "doc-word-count" ] [ text wordCountLabel ], + + div + [ id "dropbox-sync" ] + [ + input + [ + id "toggle-dropbox-sync", + property "type" (string "checkbox"), + onClick channels.remoteSync () + ] + [] + , + + label + [ for "toggle-dropbox-sync" ] + [ text " sync to Dropbox" ] + ] + ] + + +viewOutline : Addresses a -> Doc -> FullscreenState -> Html +viewOutline channels currentDoc fullscreen = + let + outlineHeadingNodes = + [ + h1 [ id "edit-title" ] [], + div [ id "edit-description" ] [] + ] + + outlineChapterNodes = + List.concatMap + (.id >> lazyViewChapter) + currentDoc.chapters + in + div + [ id "document-page" ] + (outlineHeadingNodes ++ outlineChapterNodes) + withCommas : Int -> String withCommas num = - if num >= 1000 - then - let prefix = withCommas <| floor (num / 1000) - in - prefix ++ "," ++ (String.right 3 <| toString num) + if num >= 1000 then + let + prefix = + (num / 1000) + |> floor + |> withCommas + + suffix = + num + |> toString + |> String.right 3 + + in + prefix ++ "," ++ suffix else - toString num + toString num + pluralize : String -> Int -> String pluralize noun quantity = - if quantity == 1 - then "1 " ++ noun - else (withCommas quantity) ++ " " ++ noun ++ "s" + if quantity == 1 then + "1 " ++ noun + else + (withCommas quantity) ++ " " ++ noun ++ "s" + viewFullscreenButton : Address FullscreenState -> FullscreenState -> Html viewFullscreenButton fullscreenChannel fullscreen = - let {fullscreenClass, targetMode, fullscreenTitle} = case fullscreen of - True -> - { fullscreenClass = "flaticon-collapsing" - , targetMode = False - , fullscreenTitle = "Leave Fullscreen Mode" - } - False -> - { fullscreenClass = "flaticon-expand" - , targetMode = True - , fullscreenTitle = "Enter Fullscreen Mode" - } - in - div [class ("toolbar-section toolbar-button " ++ fullscreenClass), - title fullscreenTitle, - onClick fullscreenChannel targetMode - ] [] + let + {fullscreenClass, targetMode, fullscreenTitle} = + case fullscreen of + True -> + { + fullscreenClass = "flaticon-collapsing", + targetMode = False, + fullscreenTitle = "Leave Fullscreen Mode" + } + + False -> + { + fullscreenClass = "flaticon-expand", + targetMode = True, + fullscreenTitle = "Enter Fullscreen Mode" + } + in + div + [ + class ("toolbar-section toolbar-button " ++ fullscreenClass), + title fullscreenTitle, + onClick fullscreenChannel targetMode + ] + [] + lazyViewChapter : Identifier -> List Html -lazyViewChapter chapterId = [ - lazy viewChapterHeading chapterId, - lazy viewChapterBody chapterId - ] +lazyViewChapter chapterId = + [ + lazy viewChapterHeading chapterId, + lazy viewChapterBody chapterId + ] + viewChapterBody : Identifier -> Html viewChapterBody chapterId = - div [key ("chapter-body-" ++ chapterId), - id ("edit-chapter-body-" ++ chapterId), - class "chapter-body"] [] + div + [ + key ("chapter-body-" ++ chapterId), + id ("edit-chapter-body-" ++ chapterId), + class "chapter-body" + ] + [] + viewChapterHeading : Identifier -> Html viewChapterHeading chapterId = - h2 [key ("chapter-heading-" ++ chapterId), - id ("edit-chapter-heading-" ++ chapterId), - class "chapter-heading"] [] + h2 + [ + key ("chapter-heading-" ++ chapterId), + id ("edit-chapter-heading-" ++ chapterId), + class "chapter-heading" + ] + [] + viewFontControl : Address String -> String -> String -> String -> Html viewFontControl execCommandChannel idAttr label command = - span [class "font-control toolbar-button toolbar-font-button", id idAttr, - (property "unselectable" (string "on")), - onClick execCommandChannel command] [text label] \ No newline at end of file + span + [ + class "font-control toolbar-button toolbar-font-button", + id idAttr, + (property "unselectable" (string "on")), + onClick execCommandChannel command + ] + [ text label ] \ No newline at end of file diff --git a/Component/LeftSidebar.elm b/Component/LeftSidebar.elm index cb5682d..96f5357 100644 --- a/Component/LeftSidebar.elm +++ b/Component/LeftSidebar.elm @@ -10,137 +10,260 @@ import Html.Attributes exposing (..) import Html.Events exposing (..) import Html.Lazy exposing (..) import Maybe -import Regex exposing (..) -import Signal exposing (Address, forwardTo) - -type ViewMode = CurrentDocMode | OpenMenuMode | SettingsMode - -type alias Addresses a = { a | - print : Address (), - newDoc : Address (), - newChapter : Address (), - openFromFile : Address (), - navigateToTitle : Address (), - navigateToChapterId : Address Identifier, - download : Address DownloadOptions, - update : Address Update -} - -type alias Model = { - viewMode : ViewMode, - docs : List Doc, - currentDocId : Maybe Identifier, - currentDoc : Doc -} +import Regex +import Signal exposing (Address) + + +type ViewMode = + CurrentDocMode | OpenMenuMode | SettingsMode + + +type alias Addresses a = + { a | + print : Address (), + newDoc : Address (), + newChapter : Address (), + openFromFile : Address (), + navigateToTitle : Address (), + navigateToChapterId : Address Identifier, + download : Address DownloadOptions, + update : Address Update + } + + +type alias Model = + { + viewMode : ViewMode, + docs : List Doc, + currentDocId : Maybe Identifier, + currentDoc : Doc + } + initialModel : Model -initialModel = { - viewMode = CurrentDocMode, - docs = [], - currentDocId = Nothing, - currentDoc = emptyDoc - } +initialModel = + { + viewMode = CurrentDocMode, + docs = [], + currentDocId = Nothing, + currentDoc = emptyDoc + } + type Update - = NoOp - | SetViewMode ViewMode - | OpenDocId Identifier + = NoOp + | SetViewMode ViewMode + | OpenDocId Identifier + transition : Update -> Model -> Model transition update model = - case update of - NoOp -> model + case update of + NoOp -> + model - SetViewMode mode -> { model | viewMode <- mode } + SetViewMode mode -> + { model | viewMode <- mode } + + OpenDocId id -> + { model | + currentDocId <- Just id, + viewMode <- CurrentDocMode + } - OpenDocId id -> { model | - currentDocId <- Just id, - viewMode <- CurrentDocMode - } -- Replace illegal filename characters with underscores -illegalFilenameCharMatcher = regex "[/\\<>?|\":*]" +illegalFilenameCharMatcher = + Regex.regex "[/\\<>?|\":*]" + legalizeFilename : String -> String -legalizeFilename = replace All illegalFilenameCharMatcher (\_ -> "_") +legalizeFilename = + Regex.replace Regex.All illegalFilenameCharMatcher (always "_") + + +downloadContentType = + "text/plain;charset=UTF-8" -downloadContentType = "text/plain;charset=UTF-8" view : Addresses a -> Model -> Html view addresses model = - let openDoc = forwardTo addresses.update OpenDocId - {sidebarHeader, sidebarBody, sidebarFooter} = case model.viewMode of - OpenMenuMode -> { - sidebarHeader = lazy viewOpenMenuHeader addresses.update, - sidebarBody = lazy2 (OpenMenu.view addresses.openFromFile openDoc) model.docs model.currentDoc, - sidebarFooter = viewOpenMenuFooter - } + let + openDoc = + Signal.forwardTo addresses.update OpenDocId - CurrentDocMode -> { - sidebarHeader = lazy2 viewCurrentDocHeader model.currentDoc addresses, - sidebarBody = lazy3 CurrentDoc.view addresses.navigateToTitle addresses.navigateToChapterId model.currentDoc, - sidebarFooter = lazy viewCurrentDocFooter addresses - } + {sidebarHeader, sidebarBody, sidebarFooter} = + case model.viewMode of + OpenMenuMode -> + { + sidebarHeader = + lazy + viewOpenMenuHeader addresses.update + , - SettingsMode -> { -- TODO make this different than CurrentDocMode - sidebarHeader = lazy2 viewCurrentDocHeader model.currentDoc addresses, - sidebarBody = lazy3 CurrentDoc.view addresses.navigateToTitle addresses.navigateToChapterId model.currentDoc, - sidebarFooter = lazy viewCurrentDocFooter addresses - } + sidebarBody = + lazy2 + (OpenMenu.view addresses.openFromFile openDoc) + model.docs + model.currentDoc + , - in - div [id "left-sidebar-container", class "sidebar"] [ - sidebarHeader, - div [id "left-sidebar-body", class "sidebar-body"] [sidebarBody], - sidebarFooter - ] + sidebarFooter = + viewOpenMenuFooter + } + + CurrentDocMode -> + { + sidebarHeader = + lazy2 + viewCurrentDocHeader model.currentDoc addresses, + + sidebarBody = + lazy3 + CurrentDoc.view + addresses.navigateToTitle + addresses.navigateToChapterId + model.currentDoc, + + sidebarFooter = + lazy + viewCurrentDocFooter addresses + } + + SettingsMode -> + { -- TODO make this different than CurrentDocMode + sidebarHeader = + lazy2 + viewCurrentDocHeader + model.currentDoc + addresses + , + + sidebarBody = + lazy3 + CurrentDoc.view + addresses.navigateToTitle + addresses.navigateToChapterId + model.currentDoc + , + + sidebarFooter = + lazy + viewCurrentDocFooter addresses + } + + in + div + [ id "left-sidebar-container", class "sidebar" ] + [ + sidebarHeader, + + div + [ id "left-sidebar-body", class "sidebar-body" ] + [ sidebarBody ] + , + + sidebarFooter + ] + + +sidebarHeaderId = + "left-sidebar-header" + + +sidebarHeaderClass = + "sidebar-header" -sidebarHeaderId = "left-sidebar-header" -sidebarHeaderClass = "sidebar-header" viewOpenMenuFooter : Html -viewOpenMenuFooter = span [] [] +viewOpenMenuFooter = + span [] [] + viewCurrentDocFooter : Addresses a -> Html viewCurrentDocFooter addresses = - div [id "left-sidebar-footer", class "sidebar-footer"] [ - span [id "add-chapter", - title "Add Chapter", - onClick addresses.newChapter (), - class "flaticon-plus81"] []] + div + [ id "left-sidebar-footer", class "sidebar-footer" ] + [ + span + [ + id "add-chapter", + title "Add Chapter", + onClick addresses.newChapter (), + class "flaticon-plus81"] [] + ] + viewOpenMenuHeader updateChannel = - div [key "open-menu-header", id sidebarHeaderId, class sidebarHeaderClass] [ - span [class "sidebar-header-control", - onClick updateChannel (SetViewMode CurrentDocMode)] [text "cancel"] - ] + div + [ + key "open-menu-header", + id sidebarHeaderId, + class sidebarHeaderClass + ] + [ + span + [ + class "sidebar-header-control", + onClick updateChannel (SetViewMode CurrentDocMode) + ] + [ text "cancel" ] + ] + viewCurrentDocHeader : Doc -> Addresses a -> Html viewCurrentDocHeader currentDoc addresses = - let downloadOptions = { - filename = (legalizeFilename currentDoc.title) ++ ".html", - contentType = downloadContentType - } - in - menu [id sidebarHeaderId, class sidebarHeaderClass] [ - menuitem [ - title "New", - class "sidebar-header-control flaticon-add26", - onClick addresses.newDoc ()] [], - menuitem [ - title "Open", - class "sidebar-header-control flaticon-folder63", - onClick addresses.update (SetViewMode OpenMenuMode)] [], - menuitem [ - title "Download", - class "sidebar-header-control flaticon-cloud134", - onClick addresses.download downloadOptions] [], - menuitem [ - title "Print", - class "sidebar-header-control flaticon-printer70", - onClick addresses.print ()] [], - menuitem [ - title "Settings", - class "sidebar-header-control flaticon-gear33", - onClick addresses.update (SetViewMode SettingsMode)] [] - ] \ No newline at end of file + let + downloadOptions = + { + filename = (legalizeFilename currentDoc.title) ++ ".html", + contentType = downloadContentType + } + in + menu + [ id sidebarHeaderId, class sidebarHeaderClass ] + [ + menuitem + [ + title "New", + class "sidebar-header-control flaticon-add26", + onClick addresses.newDoc () + ] + [] + , + + menuitem + [ + title "Open", + class "sidebar-header-control flaticon-folder63", + onClick addresses.update (SetViewMode OpenMenuMode) + ] + [] + , + + menuitem + [ + title "Download", + class "sidebar-header-control flaticon-cloud134", + onClick addresses.download downloadOptions + ] + [] + , + + menuitem + [ + title "Print", + class "sidebar-header-control flaticon-printer70", + onClick addresses.print () + ] + [] + , + + menuitem + [ + title "Settings", + class "sidebar-header-control flaticon-gear33", + onClick addresses.update (SetViewMode SettingsMode) + ] + [] + ] \ No newline at end of file diff --git a/Component/LeftSidebar/OpenMenuView.elm b/Component/LeftSidebar/OpenMenuView.elm index cb93e8c..75622d8 100644 --- a/Component/LeftSidebar/OpenMenuView.elm +++ b/Component/LeftSidebar/OpenMenuView.elm @@ -8,32 +8,42 @@ import Html.Events exposing (..) import List exposing (..) import Signal exposing (Message, Address) + view : Address () -> Address Identifier -> List Doc -> Doc -> Html view openFromFile openDoc docs currentDoc = - let sortedDocs : List Doc - sortedDocs = sortBy (negate << .lastModifiedTime) docs + let + docNodes : List Html + docNodes = + docs + |> List.sortBy (.lastModifiedTime >> negate) + |> List.map (viewOpenDocEntryFor openDoc currentDoc) - docNodes : List Html - docNodes = map (viewOpenDocEntryFor openDoc currentDoc) sortedDocs + openFileNode : Html + openFileNode = + div + [ class "open-entry from-file", onClick openFromFile () ] + [ + span [] [ text "A " ], + b [] [ text ".html" ], + span [] [ text " file from your computer..." ] + ] + in + div [ key "open-menu-view", id "open" ] (openFileNode :: docNodes) - openFileNodes : List Html - openFileNodes = [ - div [class "open-entry from-file", - onClick openFromFile () - ] [ - span [] [text "A "], - b [] [text ".html"], - span [] [text " file from your computer..."] - ] - ] - in - div [key "open-menu-view", id "open"] (openFileNodes ++ docNodes) viewOpenDocEntryFor : Address Identifier -> Doc -> Doc -> Html viewOpenDocEntryFor openDoc currentDoc doc = - let className = if doc.id == currentDoc.id - then "open-entry current" - else "open-entry" - in - div [key ("#open-doc-" ++ doc.id), class className, - onClick openDoc doc.id] [text doc.title] + let + className = + if doc.id == currentDoc.id then + "open-entry current" + else + "open-entry" + in + div + [ + key ("#open-doc-" ++ doc.id), + class className, + onClick openDoc doc.id + ] + [ text doc.title ] \ No newline at end of file diff --git a/Component/Page.elm b/Component/Page.elm index 4b112d0..f784016 100644 --- a/Component/Page.elm +++ b/Component/Page.elm @@ -2,106 +2,147 @@ module Component.Page where import Dreamwriter exposing (..) -import Component.LeftSidebar as LeftSidebar +import Component.LeftSidebar as LeftSidebar import Component.RightSidebar as RightSidebar -import Component.Editor as Editor +import Component.Editor as Editor import String import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (..) -import Signal exposing (forwardTo) +import Signal + type Update - = NoOp - | SetLeftSidebar LeftSidebar.Model - | SetRightSidebar RightSidebar.Model - | SetEditor Editor.Model + = NoOp + | SetLeftSidebar LeftSidebar.Model + | SetRightSidebar RightSidebar.Model + | SetEditor Editor.Model + -type alias Model = { - leftSidebar : LeftSidebar.Model, - rightSidebar : RightSidebar.Model, - editor : Editor.Model, +type alias Model = + { + leftSidebar : LeftSidebar.Model, + rightSidebar : RightSidebar.Model, + editor : Editor.Model, - fullscreen : FullscreenState, + fullscreen : FullscreenState, - currentDocId : Maybe Identifier, - currentDoc : Maybe Doc, - currentNote : Maybe Note, + currentDocId : Maybe Identifier, + currentDoc : Maybe Doc, + currentNote : Maybe Note, + + docs : List Doc, + notes : List Note + } - docs : List Doc, - notes : List Note -} initialModel : Model -initialModel = { - leftSidebar = LeftSidebar.initialModel, - rightSidebar = RightSidebar.initialModel, - editor = Editor.initialModel, +initialModel = + { + leftSidebar = LeftSidebar.initialModel, + rightSidebar = RightSidebar.initialModel, + editor = Editor.initialModel, - fullscreen = False, + fullscreen = False, - currentDocId = Nothing, - currentDoc = Nothing, - currentNote = Nothing, + currentDocId = Nothing, + currentDoc = Nothing, + currentNote = Nothing, + + docs = [], + notes = [] + } - docs = [], - notes = [] - } transition : Update -> Model -> Model transition update model = - case update of - NoOp -> model + case update of + NoOp -> + model - SetLeftSidebar childModel -> { model | - leftSidebar <- childModel, - currentDocId <- childModel.currentDocId - } + SetLeftSidebar childModel -> + { model | + leftSidebar <- childModel, + currentDocId <- childModel.currentDocId + } - SetRightSidebar childModel -> { model | - rightSidebar <- childModel, - currentNote <- childModel.currentNote - } + SetRightSidebar childModel -> + { model | + rightSidebar <- childModel, + currentNote <- childModel.currentNote + } + + SetEditor childModel -> + { model | editor <- childModel } - SetEditor childModel -> { model | editor <- childModel } view addresses model = - let updateLeftSidebar = forwardTo addresses.update (generalizeLeftSidebarUpdate model) - leftSidebarChannels = { addresses | update <- updateLeftSidebar } - rightSidebarChannels = addresses - editorChannels = addresses - in div [id "page"] <| - case model.currentDoc of - Nothing -> [] - Just currentDoc -> - [ - LeftSidebar.view leftSidebarChannels (modelLeftSidebar currentDoc model), - Editor.view editorChannels (modelEditor currentDoc model), - RightSidebar.view rightSidebarChannels (modelRightSidebar model) - ] + let + updateLeftSidebar = + Signal.forwardTo + addresses.update + (generalizeLeftSidebarUpdate model) + + leftSidebarChannels = + { addresses | update <- updateLeftSidebar } + + rightSidebarChannels = + addresses + + editorChannels = + addresses + in + div [ id "page" ] <| + case model.currentDoc of + Nothing -> + [] + + Just currentDoc -> + [ + LeftSidebar.view + leftSidebarChannels + (modelLeftSidebar currentDoc model) + , + + Editor.view + editorChannels + (modelEditor currentDoc model) + , + + RightSidebar.view + rightSidebarChannels + (modelRightSidebar model) + ] + modelLeftSidebar : Doc -> Model -> LeftSidebar.Model -modelLeftSidebar currentDoc model = { - docs = model.docs, - currentDoc = currentDoc, - currentDocId = model.currentDocId, - viewMode = model.leftSidebar.viewMode - } +modelLeftSidebar currentDoc model = + { + docs = model.docs, + currentDoc = currentDoc, + currentDocId = model.currentDocId, + viewMode = model.leftSidebar.viewMode + } + modelEditor : Doc -> Model -> Editor.Model -modelEditor currentDoc model = { - currentDoc = currentDoc, - fullscreen = model.fullscreen - } +modelEditor currentDoc model = + { + currentDoc = currentDoc, + fullscreen = model.fullscreen + } + modelRightSidebar : Model -> RightSidebar.Model -modelRightSidebar model = { - currentNote = model.currentNote, - notes = model.notes - } +modelRightSidebar model = + { + currentNote = model.currentNote, + notes = model.notes + } + generalizeLeftSidebarUpdate : Model -> LeftSidebar.Update -> Update generalizeLeftSidebarUpdate model leftSidebarUpdate = - SetLeftSidebar (LeftSidebar.transition leftSidebarUpdate model.leftSidebar) + SetLeftSidebar (LeftSidebar.transition leftSidebarUpdate model.leftSidebar) diff --git a/Component/RightSidebar.elm b/Component/RightSidebar.elm index 15e3803..22bbdde 100644 --- a/Component/RightSidebar.elm +++ b/Component/RightSidebar.elm @@ -10,85 +10,147 @@ import Maybe import Signal exposing (Address) import List exposing (..) -type alias Addresses a = { a | - newNote : Address (), - searchNotes : Address String, - openNoteId : Address Identifier -} -type alias Model = { - currentNote : Maybe Note, - notes : List Note -} +type alias Addresses a = + { a | + newNote : Address (), + searchNotes : Address String, + openNoteId : Address Identifier + } + + +type alias Model = + { + currentNote : Maybe Note, + notes : List Note + } + initialModel : Model -initialModel = { - currentNote = Nothing, - notes = [] - } +initialModel = + { + currentNote = Nothing, + notes = [] + } view : Addresses a -> Model -> Html view addresses model = - let {sidebarBody, sidebarFooter} = case model.currentNote of - Nothing -> - { sidebarBody = lazy2 viewNoteListings addresses.openNoteId model.notes - , sidebarFooter = span [] [] - } - Just currentNote -> - { sidebarBody = lazy viewCurrentNoteBody currentNote - , sidebarFooter = lazy2 viewCurrentNoteFooter addresses currentNote - } - in - div [id "right-sidebar-container", class "sidebar"] [ - div [id "right-sidebar-header", class "sidebar-header"] [ - input [id "notes-search-text", class "sidebar-header-control", placeholder "search notes", - onInput addresses.searchNotes targetValue] [], - span [id "notes-search-button", class "sidebar-header-control flaticon-pencil90", - onClick addresses.newNote ()] [] - ], - div [id "right-sidebar-body", class "sidebar-body"] [ - sidebarBody - ], - sidebarFooter - ] + let + {sidebarBody, sidebarFooter} = + case model.currentNote of + Nothing -> + { + sidebarBody = + lazy2 + viewNoteListings + addresses.openNoteId + model.notes + , + + sidebarFooter = + span [] [] + } + + Just currentNote -> + { + sidebarBody = + lazy viewCurrentNoteBody currentNote + , + + sidebarFooter = + lazy2 + viewCurrentNoteFooter + addresses currentNote + } + in + div + [ id "right-sidebar-container", class "sidebar" ] + [ + div + [ id "right-sidebar-body", class "sidebar-body" ] + [ sidebarBody ] + , + + sidebarFooter + ] + +viewHeader : Addresses a -> Model -> Html +viewHeader addresses model = + div + [ id "right-sidebar-header", class "sidebar-header" ] + [ + input + [ + id "notes-search-text", + class "sidebar-header-control", + placeholder "search notes", + onInput addresses.searchNotes targetValue + ] + [] + , + + span + [ + id "notes-search-button", + class "sidebar-header-control flaticon-pencil90", + onClick addresses.newNote () + ] + [] + ] + + viewNoteListings openNoteIdChannel notes = - div [id "note-listings"] <| map (viewNoteListing openNoteIdChannel) notes + div + [ id "note-listings" ] + (List.map (viewNoteListing openNoteIdChannel) notes) + viewNoteListing : Address Identifier -> Note -> Html viewNoteListing openNoteId note = - div [key ("note-" ++ note.id), class "note-listing", - onClick openNoteId note.id] [ - div [class "flaticon-document127 note-listing-icon"] [], - div [class "note-listing-title"] [text note.title] - ] + div + [ + key ("note-" ++ note.id), class "note-listing", + onClick openNoteId note.id + ] + [ + div [ class "flaticon-document127 note-listing-icon" ] [], + div [ class "note-listing-title" ] [ text note.title ] + ] + viewCurrentNoteBody : Note -> Html viewCurrentNoteBody note = - div [key ("current-note-" ++ note.id), id "current-note"] [ - div [id "current-note-title-container"] [ - div [id "current-note-title"] [] - ], - div [id "current-note-body"] [] - ] + div + [ key ("current-note-" ++ note.id), id "current-note" ] + [ + div + [ id "current-note-title-container" ] + [ div [ id "current-note-title" ] [] ] + , + + div [ id "current-note-body" ] [] + ] + onInput address decoder = - on "input" decoder (Signal.message address) + on "input" decoder (Signal.message address) + viewCurrentNoteFooter : Addresses a -> Note -> Html viewCurrentNoteFooter addresses note = - div [id "current-note-controls", class "sidebar-footer"] [] - --div [id "current-note-controls", class "sidebar-footer"] [ - -- span [id "download-current-note", - -- title "Download Note", - -- class "flaticon-cloud134 current-note-control"] [], - -- span [id "print-current-note", - -- title "Print Note", - -- class "flaticon-printer70 current-note-control"] [], - -- span [id "current-note-settings", - -- title "Note Settings", - -- class "flaticon-gear33 current-note-control"] [], - -- span [id "delete-current-note", - -- title "Delete Note", - -- class "flaticon-closed18 current-note-control"] [] - --] \ No newline at end of file + div [ id "current-note-controls", class "sidebar-footer" ] [] + --div [ id "current-note-controls", class "sidebar-footer" ] [ + -- span [ id "download-current-note", + -- title "Download Note", + -- class "flaticon-cloud134 current-note-control" ] [], + -- span [ id "print-current-note", + -- title "Print Note", + -- class "flaticon-printer70 current-note-control" ] [], + -- span [ id "current-note-settings", + -- title "Note Settings", + -- class "flaticon-gear33 current-note-control" ] [], + -- span [ id "delete-current-note", + -- title "Delete Note", + -- class "flaticon-closed18 current-note-control" ] [] + --] \ No newline at end of file diff --git a/Dreamwriter.elm b/Dreamwriter.elm index fd427df..f071623 100644 --- a/Dreamwriter.elm +++ b/Dreamwriter.elm @@ -7,53 +7,60 @@ type alias Identifier = String type alias MsSinceEpoch = Int type alias HtmlString = String -type alias DownloadOptions = { - filename : String, - contentType : String -} - -type alias Note = { - id : Identifier, - title : String, - snapshotId : HtmlString, - creationTime : MsSinceEpoch, - lastModifiedTime : MsSinceEpoch -} - -type alias Doc = { - id : Identifier, - title : String, - description : HtmlString, - chapters : List Chapter, - titleWords : Int, - descriptionWords : Int, - creationTime : MsSinceEpoch, - lastModifiedTime : MsSinceEpoch -} - -emptyDoc = { - id = "", - title = "", - description = "", - chapters = [], - titleWords = 0, +type alias DownloadOptions = + { + filename : String, + contentType : String + } + +type alias Note = + { + id : Identifier, + title : String, + snapshotId : HtmlString, + creationTime : MsSinceEpoch, + lastModifiedTime : MsSinceEpoch + } + +type alias Doc = + { + id : Identifier, + title : String, + description : HtmlString, + chapters : List Chapter, + titleWords : Int, + descriptionWords : Int, + creationTime : MsSinceEpoch, + lastModifiedTime : MsSinceEpoch + } + +emptyDoc = + { + id = "", + title = "", + description = "", + chapters = [], + titleWords = 0, descriptionWords = 0, - creationTime = 0, + creationTime = 0, lastModifiedTime = 0 } -type alias Chapter = { - id : Identifier, - heading : String, - headingWords : Int, - bodyWords : Int, - creationTime : MsSinceEpoch, - lastModifiedTime : MsSinceEpoch, - snapshotId : Identifier -} - -type alias Snapshot = { - id : Identifier, - html : String, - text : String -} +type alias Chapter = + { + id : Identifier, + heading : String, + headingWords : Int, + bodyWords : Int, + creationTime : MsSinceEpoch, + lastModifiedTime : MsSinceEpoch, + snapshotId : Identifier + } + +type alias Snapshot = + { + id : Identifier, + html : String, + text : String + } + \ No newline at end of file diff --git a/Dreamwriter/Mailboxes.elm b/Dreamwriter/Mailboxes.elm index 26c7989..0dbcee7 100644 --- a/Dreamwriter/Mailboxes.elm +++ b/Dreamwriter/Mailboxes.elm @@ -5,107 +5,141 @@ import Dreamwriter exposing (..) import Signal exposing (Mailbox, Address, mailbox) import Signal -type alias Addresses = { - newNote : Address (), - openNoteId : Address Identifier, - searchNotes : Address String, - fullscreen : Address FullscreenState, - execCommand : Address String, - remoteSync : Address (), - print : Address (), - newDoc : Address (), - newChapter : Address (), - openFromFile : Address (), - navigateToTitle : Address (), - navigateToChapterId : Address Identifier, - download : Address DownloadOptions -} + +type alias Addresses = + { + newNote : Address (), + openNoteId : Address Identifier, + searchNotes : Address String, + fullscreen : Address FullscreenState, + execCommand : Address String, + remoteSync : Address (), + print : Address (), + newDoc : Address (), + newChapter : Address (), + openFromFile : Address (), + navigateToTitle : Address (), + navigateToChapterId : Address Identifier, + download : Address DownloadOptions + } + addresses : Addresses -addresses = { - fullscreen = fullscreen.address, - execCommand = execCommand.address, - remoteSync = remoteSync.address, - newNote = newNote.address, - openNoteId = openNoteId.address, - searchNotes = searchNotes.address, - print = print.address, - newDoc = newDoc.address, - newChapter = newChapter.address, - download = download.address, - openFromFile = openFromFile.address, - navigateToTitle = navigateToTitle.address, +addresses = + { + fullscreen = fullscreen.address, + execCommand = execCommand.address, + remoteSync = remoteSync.address, + newNote = newNote.address, + openNoteId = openNoteId.address, + searchNotes = searchNotes.address, + print = print.address, + newDoc = newDoc.address, + newChapter = newChapter.address, + download = download.address, + openFromFile = openFromFile.address, + navigateToTitle = navigateToTitle.address, navigateToChapterId = navigateToChapterId.address } -type alias Signals = { - newNote : Signal (), - openNoteId : Signal Identifier, - searchNotes : Signal String, - fullscreen : Signal FullscreenState, - execCommand : Signal String, - remoteSync : Signal (), - print : Signal (), - newDoc : Signal (), - newChapter : Signal (), - openFromFile : Signal (), - navigateToTitle : Signal (), - navigateToChapterId : Signal Identifier, - download : Signal DownloadOptions -} + +type alias Signals = + { + newNote : Signal (), + openNoteId : Signal Identifier, + searchNotes : Signal String, + fullscreen : Signal FullscreenState, + execCommand : Signal String, + remoteSync : Signal (), + print : Signal (), + newDoc : Signal (), + newChapter : Signal (), + openFromFile : Signal (), + navigateToTitle : Signal (), + navigateToChapterId : Signal Identifier, + download : Signal DownloadOptions + } + signals : Signals -signals = { - fullscreen = fullscreen.signal, - execCommand = execCommand.signal, - remoteSync = remoteSync.signal, - newNote = newNote.signal, - openNoteId = openNoteId.signal, - searchNotes = searchNotes.signal, - print = print.signal, - newDoc = newDoc.signal, - newChapter = newChapter.signal, - download = download.signal, - openFromFile = openFromFile.signal, - navigateToTitle = navigateToTitle.signal, +signals = + { + fullscreen = fullscreen.signal, + execCommand = execCommand.signal, + remoteSync = remoteSync.signal, + newNote = newNote.signal, + openNoteId = openNoteId.signal, + searchNotes = searchNotes.signal, + print = print.signal, + newDoc = newDoc.signal, + newChapter = newChapter.signal, + download = download.signal, + openFromFile = openFromFile.signal, + navigateToTitle = navigateToTitle.signal, navigateToChapterId = navigateToChapterId.signal } + download : Signal.Mailbox DownloadOptions -download = Signal.mailbox { filename = "", contentType = "" } +download = + Signal.mailbox { filename = "", contentType = "" } + newDoc : Signal.Mailbox () -newDoc = Signal.mailbox () +newDoc = + Signal.mailbox () + openFromFile : Signal.Mailbox () -openFromFile = Signal.mailbox () +openFromFile = + Signal.mailbox () + navigateToChapterId : Signal.Mailbox Identifier -navigateToChapterId = Signal.mailbox "" +navigateToChapterId = + Signal.mailbox "" + navigateToTitle : Signal.Mailbox () -navigateToTitle = Signal.mailbox () +navigateToTitle = + Signal.mailbox () + print : Signal.Mailbox () -print = Signal.mailbox () +print = + Signal.mailbox () + searchNotes : Signal.Mailbox String -searchNotes = Signal.mailbox "" +searchNotes = + Signal.mailbox "" + newNote : Signal.Mailbox () -newNote = Signal.mailbox () +newNote = + Signal.mailbox () + openNoteId : Signal.Mailbox Identifier -openNoteId = Signal.mailbox "" +openNoteId = + Signal.mailbox "" + newChapter : Signal.Mailbox () -newChapter = Signal.mailbox () +newChapter = + Signal.mailbox () + fullscreen : Signal.Mailbox Bool -fullscreen = Signal.mailbox False +fullscreen = + Signal.mailbox False + execCommand : Signal.Mailbox String -execCommand = Signal.mailbox "" +execCommand = + Signal.mailbox "" + remoteSync : Signal.Mailbox () -remoteSync = Signal.mailbox () \ No newline at end of file +remoteSync = + Signal.mailbox () \ No newline at end of file