diff --git a/cabal.project b/cabal.project index 7d2e95728..1db9e46ec 100644 --- a/cabal.project +++ b/cabal.project @@ -1,2 +1,6 @@ packages: - ./emanote + ./packages/emanote-route + ./packages/emanote-source + ./packages/emanote-pandoc + ./packages/emanote-model + ./packages/emanote diff --git a/docs/guide/html-template/external-links.md b/docs/guide/html-template/external-links.md index 64b855538..4e9b92031 100644 --- a/docs/guide/html-template/external-links.md +++ b/docs/guide/html-template/external-links.md @@ -25,7 +25,7 @@ Note that the attribute can also be used to display the icons in the [[html-temp ## URL properties -The displayed icon may depend on the link properties (e.g. the actual URI scheme). This is [[custom-style|customized using CSS]]. By default, Emanote displays a different icon if the URI scheme component is `mailto:`. Check the of [[html-template|HTML template]] for details. +The displayed icon may depend on the link properties (e.g. the actual URI scheme). This is [[custom-style|customized using CSS]]. By default, Emanote displays a different icon if the URI scheme component is `mailto:`. Check the of [[html-template|HTML template]] for details. ## Demo diff --git a/docs/guide/layer.md b/docs/guide/layer.md index 0d4309ab3..7a25d5e27 100644 --- a/docs/guide/layer.md +++ b/docs/guide/layer.md @@ -16,7 +16,7 @@ Internally, Emanote merges both `docs1` and `docs2` folders and treats them as a ## "Default" layer -Emanote *implicitly* includes what is known as the "default" layer. Its contents can be seen [here](https://github.com/srid/emanote/tree/master/emanote/default). This layer contains [[html-template]], [[yaml-config|index.yaml]] and other default assets, like the logo, favicon and [[fonts|fonts]]. When you run `emanote -L /your/notebook run`, your notebook is overlaid *on top of* this default layer. What this means, in effect, is that you override **just about any file** in the default layer, such the HTML content of [[html-template]], in your own notebook directory. As an example, see [`template/hooks`](https://github.com/srid/emanote/tree/master/docs/templates/hooks) of this documentation notbook. +Emanote *implicitly* includes what is known as the "default" layer. Its contents can be seen [here](https://github.com/srid/emanote/tree/master/packages/emanote/default). This layer contains [[html-template]], [[yaml-config|index.yaml]] and other default assets, like the logo, favicon and [[fonts|fonts]]. When you run `emanote -L /your/notebook run`, your notebook is overlaid *on top of* this default layer. What this means, in effect, is that you override **just about any file** in the default layer, such the HTML content of [[html-template]], in your own notebook directory. As an example, see [`template/hooks`](https://github.com/srid/emanote/tree/master/docs/templates/hooks) of this documentation notbook. ## Merge semantics diff --git a/docs/guide/markdown/callout.md b/docs/guide/markdown/callout.md index 5dedb4c39..b15e51150 100644 --- a/docs/guide/markdown/callout.md +++ b/docs/guide/markdown/callout.md @@ -71,4 +71,4 @@ Callouts can be nested by indenting the inner blockquote with `> >`: ## Custom callouts {#custom} -To add a new custom callout named `foo` (viz.: `[!foo] ...`), create a `/templates/filters/callout/foo.tpl` file in your [[html-template|templates]] folder. You can also change the layout and styling of existing callout types in [`/templates/filters/callout/*.tpl`](https://github.com/srid/emanote/tree/master/emanote/default/templates/filters/callout). +To add a new custom callout named `foo` (viz.: `[!foo] ...`), create a `/templates/filters/callout/foo.tpl` file in your [[html-template|templates]] folder. You can also change the layout and styling of existing callout types in [`/templates/filters/callout/*.tpl`](https://github.com/srid/emanote/tree/master/packages/emanote/default/templates/filters/callout). diff --git a/docs/guide/markdown/custom-style.md b/docs/guide/markdown/custom-style.md index 933b4af01..27d045dae 100644 --- a/docs/guide/markdown/custom-style.md +++ b/docs/guide/markdown/custom-style.md @@ -55,7 +55,7 @@ notebook. To start customizing, create a templates directory in your notebook. From there, you can override any templates you wish by copying them from Emanote's default templates into your notebook's templates directory. For example, if you want to customize the default pandoc -styling, you can copy the [pandoc.tpl](https://github.com/srid/emanote/blob/master/emanote/default/templates/components/pandoc.tpl) +styling, you can copy the [pandoc.tpl](https://github.com/srid/emanote/blob/master/packages/emanote/default/templates/components/pandoc.tpl) file from Emanote's GitHub repository into your templates/components directory and edit it accordingly. @@ -74,4 +74,3 @@ For additional information and discussion on this topic, check out [this discussion on GitHub](https://github.com/srid/emanote/discussions/438). [^mob]: If you are viewing this page on mobile or smaller screens, the embedded notes will be stacked on top of one another because we use Tailwind's [responsive classes](https://tailwindcss.com/docs/responsive-design). Incidentally, we use the `{class=".."}` syntax, rather than the `{.someClass}` syntax, only because the former is [more lenient](https://github.com/jgm/commonmark-hs/issues/76) in accepting non-standard class names, such as the Tailwind responsive classes (eg. `lg:grid-cols-2`). - diff --git a/docs/tips/js.md b/docs/tips/js.md index 1bfce6e9d..76fb0adcb 100644 --- a/docs/tips/js.md +++ b/docs/tips/js.md @@ -5,7 +5,7 @@ slug: js # JavaScript behaviours -Improve your Emanote website using custom JavaScript code. Emanote provides some predefined behaviours, like syntax highlighting or rendering of mathematical formulas. They can be accessed by including appropriate snippets in `page.headHtml` or `page.bodyHtml` of [[yaml-config|YAML configuration files]] (if adding to all or multiple routes) or Markdown frontmatter (if adding to a single route). The source code for the snippets can be found in the default [`index.yaml`](https://github.com/srid/emanote/blob/master/emanote/default/index.yaml) under the `js:` YAML map. +Improve your Emanote website using custom JavaScript code. Emanote provides some predefined behaviours, like syntax highlighting or rendering of mathematical formulas. They can be accessed by including appropriate snippets in `page.headHtml` or `page.bodyHtml` of [[yaml-config|YAML configuration files]] (if adding to all or multiple routes) or Markdown frontmatter (if adding to a single route). The source code for the snippets can be found in the default [`index.yaml`](https://github.com/srid/emanote/blob/master/packages/emanote/default/index.yaml) under the `js:` YAML map. ```query path:./* diff --git a/nix/modules/flake-parts/haskell.nix b/nix/modules/flake-parts/haskell.nix index 821569eb1..9eafc6531 100644 --- a/nix/modules/flake-parts/haskell.nix +++ b/nix/modules/flake-parts/haskell.nix @@ -13,7 +13,7 @@ projectRoot = builtins.toString (lib.fileset.toSource { inherit root; fileset = lib.fileset.unions [ - (root + /emanote) + (root + /packages) (root + /cabal.project) ]; }); diff --git a/packages/emanote-model/emanote-model.cabal b/packages/emanote-model/emanote-model.cabal new file mode 100644 index 000000000..af6847ed2 --- /dev/null +++ b/packages/emanote-model/emanote-model.cabal @@ -0,0 +1,146 @@ +cabal-version: 2.4 +name: emanote-model +version: 1.6.0.0 +license: AGPL-3.0-only +copyright: 2022 Sridhar Ratnakumar +maintainer: srid@srid.ca +author: Sridhar Ratnakumar +category: Web +homepage: https://emanote.srid.ca +synopsis: + Notebook model, notes, links, queries, graph, tasks, and search for Emanote + +bug-reports: https://github.com/srid/emanote/issues + +common haskell-common + ghc-options: + -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns + -Wmissing-deriving-strategies -Wunused-foralls -Wunused-foralls + -fprint-explicit-foralls -fprint-explicit-kinds + + default-extensions: + BangPatterns + ConstraintKinds + DataKinds + DeriveDataTypeable + DeriveFoldable + DeriveFunctor + DeriveGeneric + DeriveLift + DeriveTraversable + DerivingStrategies + DerivingVia + EmptyCase + EmptyDataDecls + EmptyDataDeriving + ExistentialQuantification + ExplicitForAll + FlexibleContexts + FlexibleInstances + GADTSyntax + GeneralisedNewtypeDeriving + ImportQualifiedPost + KindSignatures + LambdaCase + MultiParamTypeClasses + MultiWayIf + NoImplicitPrelude + NoStarIsType + NumericUnderscores + OverloadedStrings + PolyKinds + PostfixOperators + RankNTypes + ScopedTypeVariables + StandaloneDeriving + StandaloneKindSignatures + TemplateHaskell + TupleSections + TypeApplications + TypeFamilies + TypeOperators + ViewPatterns + +common library-common + import: haskell-common + default-language: Haskell2010 + build-depends: + , aeson + , aeson-extra + , aeson-optics + , base >=4.14 && <5 + , bytestring + , commonmark + , commonmark-wikilink >=0.2 + , containers + , data-default + , directory + , emanote-pandoc + , emanote-route + , emanote-source + , filepath + , filepattern + , heist >=1.1.1.0 + , heist-extra >=0.5.0.0 + , ixset-typed >=0.5.1.0 + , map-syntax + , megaparsec + , monad-logger + , mtl + , optics-core + , optics-th + , pandoc + , pandoc-link-context >=1.4.0 + , pandoc-lua-engine + , pandoc-types + , parsec + , relude >=1.0 + , tagtree + , text + , time + , unliftio + , url-slug + , xmlhtml + , yaml + +library + import: library-common + hs-source-dirs: src + exposed-modules: + Emanote.Model + Emanote.Model.Calendar + Emanote.Model.Calendar.Parser + Emanote.Model.Graph + Emanote.Model.Link.Rel + Emanote.Model.Link.Resolve + Emanote.Model.Meta + Emanote.Model.Note + Emanote.Model.Note.Filter + Emanote.Model.Query + Emanote.Model.SData + Emanote.Model.StaticFile + Emanote.Model.Task + Emanote.Model.Title + Emanote.Model.Toc + Emanote.Model.Type + +test-suite test + import: library-common + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Spec.hs + build-depends: + , containers + , emanote-model + , emanote-route + , hedgehog + , hspec + , hspec-hedgehog + , mtl + , relude + , tagtree + + other-modules: + Emanote.Model.Link.RelSpec + Emanote.Model.QuerySpec + Emanote.Model.TocSpec diff --git a/emanote/src/Emanote/Model.hs b/packages/emanote-model/src/Emanote/Model.hs similarity index 100% rename from emanote/src/Emanote/Model.hs rename to packages/emanote-model/src/Emanote/Model.hs diff --git a/emanote/src/Emanote/Model/Calendar.hs b/packages/emanote-model/src/Emanote/Model/Calendar.hs similarity index 100% rename from emanote/src/Emanote/Model/Calendar.hs rename to packages/emanote-model/src/Emanote/Model/Calendar.hs diff --git a/emanote/src/Emanote/Model/Calendar/Parser.hs b/packages/emanote-model/src/Emanote/Model/Calendar/Parser.hs similarity index 100% rename from emanote/src/Emanote/Model/Calendar/Parser.hs rename to packages/emanote-model/src/Emanote/Model/Calendar/Parser.hs diff --git a/emanote/src/Emanote/Model/Graph.hs b/packages/emanote-model/src/Emanote/Model/Graph.hs similarity index 99% rename from emanote/src/Emanote/Model/Graph.hs rename to packages/emanote-model/src/Emanote/Model/Graph.hs index f5a774523..7da4ce62c 100644 --- a/emanote/src/Emanote/Model/Graph.hs +++ b/packages/emanote-model/src/Emanote/Model/Graph.hs @@ -14,7 +14,7 @@ import Emanote.Model.Note qualified as MN import Emanote.Model.Note qualified as N import Emanote.Model.Type (Model, modelIndexRoute, modelNotes, modelRels, parentLmlRoute) import Emanote.Route qualified as R -import Emanote.Route.SiteRoute qualified as SR +import Emanote.Route.SiteRoute.Type qualified as SR import Optics.Operators as Lens ((^.)) import Relude hiding (empty) import Text.Pandoc.Definition qualified as B diff --git a/emanote/src/Emanote/Model/Link/Rel.hs b/packages/emanote-model/src/Emanote/Model/Link/Rel.hs similarity index 100% rename from emanote/src/Emanote/Model/Link/Rel.hs rename to packages/emanote-model/src/Emanote/Model/Link/Rel.hs diff --git a/emanote/src/Emanote/Model/Link/Resolve.hs b/packages/emanote-model/src/Emanote/Model/Link/Resolve.hs similarity index 92% rename from emanote/src/Emanote/Model/Link/Resolve.hs rename to packages/emanote-model/src/Emanote/Model/Link/Resolve.hs index 0024a82fa..7b7271448 100644 --- a/emanote/src/Emanote/Model/Link/Resolve.hs +++ b/packages/emanote-model/src/Emanote/Model/Link/Resolve.hs @@ -8,7 +8,7 @@ import Emanote.Model.StaticFile qualified as SF import Emanote.Model.Type (Model) import Emanote.Model.Type qualified as M import Emanote.Route qualified as R -import Emanote.Route.SiteRoute qualified as SR +import Emanote.Route.SiteRoute.Type qualified as SR import Optics.Core ((^.)) import Relude @@ -90,4 +90,8 @@ resolveModelRouteCandidates model = resourceSiteRoute :: Either (R.LMLView, MN.Note) SF.StaticFile -> SR.SiteRoute resourceSiteRoute = - either SR.noteFileSiteRoute SR.staticFileSiteRoute + SR.SiteRoute_ResourceRoute . \case + Left (view, note) -> + SR.ResourceRoute_LML view (note ^. MN.noteRoute) + Right staticFile -> + SR.ResourceRoute_StaticFile (staticFile ^. SF.staticFileRoute) diff --git a/emanote/src/Emanote/Model/Meta.hs b/packages/emanote-model/src/Emanote/Model/Meta.hs similarity index 84% rename from emanote/src/Emanote/Model/Meta.hs rename to packages/emanote-model/src/Emanote/Model/Meta.hs index 7d2de4a95..707a3b205 100644 --- a/emanote/src/Emanote/Model/Meta.hs +++ b/packages/emanote-model/src/Emanote/Model/Meta.hs @@ -8,7 +8,7 @@ module Emanote.Model.Meta ( import Data.Aeson (FromJSON) import Data.Aeson qualified as Aeson import Data.IxSet.Typed qualified as Ix -import Emanote.Model (ModelT, modelLookupNoteByRoute', modelLookupSData, modelSData) +import Emanote.Model (Model, modelLookupNoteByRoute', modelLookupSData, modelSData) import Emanote.Model.Note (_noteMeta) import Emanote.Model.SData (sdataValue) import Emanote.Model.SData qualified as SData @@ -17,19 +17,19 @@ import Optics.Operators as Lens ((^.)) import Relude -- | Look up a specific key in the meta for a given route. -lookupRouteMeta :: (FromJSON a) => a -> NonEmpty Text -> R.LMLRoute -> ModelT f -> a +lookupRouteMeta :: (FromJSON a) => a -> NonEmpty Text -> R.LMLRoute -> Model -> a lookupRouteMeta x k r = SData.lookupAeson x k . getEffectiveRouteMeta r {- | Get the (final) metadata of a note at the given route, by merging it with the defaults specified in parent routes all the way upto index.yaml. -} -getEffectiveRouteMeta :: R.LMLRoute -> ModelT f -> Aeson.Value +getEffectiveRouteMeta :: R.LMLRoute -> Model -> Aeson.Value getEffectiveRouteMeta mr model = let mNote = modelLookupNoteByRoute' mr model in getEffectiveRouteMetaWith (maybe Aeson.Null _noteMeta mNote) mr model -getEffectiveRouteMetaWith :: Aeson.Value -> R.LMLRoute -> ModelT f -> Aeson.Value +getEffectiveRouteMetaWith :: Aeson.Value -> R.LMLRoute -> Model -> Aeson.Value getEffectiveRouteMetaWith frontmatter mr model = let defaultFiles = R.routeInits @'R.Yaml (R.withLmlRoute coerce mr) defaults = flip mapMaybe (toList defaultFiles) $ \r -> do @@ -39,7 +39,7 @@ getEffectiveRouteMetaWith frontmatter mr model = metas = defaults <> maybe mempty one (guard (frontmatter /= Aeson.Null) >> pure frontmatter) in maybe Aeson.Null SData.mergeAesons $ nonEmpty metas -getYamlMeta :: R.R 'R.Yaml -> ModelT f -> Maybe Aeson.Value +getYamlMeta :: R.R 'R.Yaml -> Model -> Maybe Aeson.Value getYamlMeta r model = do s <- Ix.getOne . Ix.getEQ r $ model ^. modelSData rightToMaybe (s ^. sdataValue) @@ -51,7 +51,7 @@ A bad @subfolder/index.yaml@ contributes meta to notes under the same cascade and gathers the parse errors so callers can surface them on the affected notes (and only those notes). -} -cascadeYamlErrors :: ModelT f -> R.LMLRoute -> [Text] +cascadeYamlErrors :: Model -> R.LMLRoute -> [Text] cascadeYamlErrors model r = flip mapMaybe (toList cascade) $ \rt -> do s <- modelLookupSData rt model diff --git a/emanote/src/Emanote/Model/Note.hs b/packages/emanote-model/src/Emanote/Model/Note.hs similarity index 100% rename from emanote/src/Emanote/Model/Note.hs rename to packages/emanote-model/src/Emanote/Model/Note.hs diff --git a/emanote/src/Emanote/Model/Note/Filter.hs b/packages/emanote-model/src/Emanote/Model/Note/Filter.hs similarity index 89% rename from emanote/src/Emanote/Model/Note/Filter.hs rename to packages/emanote-model/src/Emanote/Model/Note/Filter.hs index 2157f6917..90c6ac834 100644 --- a/emanote/src/Emanote/Model/Note/Filter.hs +++ b/packages/emanote-model/src/Emanote/Model/Note/Filter.hs @@ -1,9 +1,8 @@ module Emanote.Model.Note.Filter (applyPandocFilters) where -import Control.Monad.Logger (MonadLogger) +import Control.Monad.Logger (MonadLogger, logErrorNS, logWarnNS) import Control.Monad.Writer.Strict (MonadWriter (tell)) import Data.Default (def) -import Emanote.Prelude (logE, logW) import Relude import System.Directory (doesFileExist) import System.FilePath (takeExtension) @@ -37,11 +36,11 @@ mkLuaFilter relPath = do applyPandocLuaFilters :: (MonadIO m, MonadLogger m) => ScriptingEngine -> [PF.Filter] -> Pandoc -> m (Either Text Pandoc) applyPandocLuaFilters scriptingEngine filters x = do - logW $ "[Experimental feature] Applying pandoc filters: " <> show filters + logWarnNS "emanote" $ "[Experimental feature] Applying pandoc filters: " <> show filters -- TODO: Can we constrain this to run Lua code purely (embedded) without using IO? liftIO (runIOCatchingErrors $ PF.applyFilters scriptingEngine def filters ["markdown"] x) >>= \case Left err -> do - logE $ "Error applying pandoc filters: " <> show err + logErrorNS "emanote" $ "Error applying pandoc filters: " <> show err pure $ Left (show err) Right x' -> pure $ Right x' where diff --git a/emanote/src/Emanote/Model/Query.hs b/packages/emanote-model/src/Emanote/Model/Query.hs similarity index 100% rename from emanote/src/Emanote/Model/Query.hs rename to packages/emanote-model/src/Emanote/Model/Query.hs diff --git a/emanote/src/Emanote/Model/SData.hs b/packages/emanote-model/src/Emanote/Model/SData.hs similarity index 100% rename from emanote/src/Emanote/Model/SData.hs rename to packages/emanote-model/src/Emanote/Model/SData.hs diff --git a/emanote/src/Emanote/Model/StaticFile.hs b/packages/emanote-model/src/Emanote/Model/StaticFile.hs similarity index 100% rename from emanote/src/Emanote/Model/StaticFile.hs rename to packages/emanote-model/src/Emanote/Model/StaticFile.hs diff --git a/emanote/src/Emanote/Model/Task.hs b/packages/emanote-model/src/Emanote/Model/Task.hs similarity index 100% rename from emanote/src/Emanote/Model/Task.hs rename to packages/emanote-model/src/Emanote/Model/Task.hs diff --git a/emanote/src/Emanote/Model/Title.hs b/packages/emanote-model/src/Emanote/Model/Title.hs similarity index 100% rename from emanote/src/Emanote/Model/Title.hs rename to packages/emanote-model/src/Emanote/Model/Title.hs diff --git a/emanote/src/Emanote/Model/Toc.hs b/packages/emanote-model/src/Emanote/Model/Toc.hs similarity index 100% rename from emanote/src/Emanote/Model/Toc.hs rename to packages/emanote-model/src/Emanote/Model/Toc.hs diff --git a/emanote/src/Emanote/Model/Type.hs b/packages/emanote-model/src/Emanote/Model/Type.hs similarity index 74% rename from emanote/src/Emanote/Model/Type.hs rename to packages/emanote-model/src/Emanote/Model/Type.hs index 2f7bda761..6b7017f0a 100644 --- a/emanote/src/Emanote/Model/Type.hs +++ b/packages/emanote-model/src/Emanote/Model/Type.hs @@ -6,14 +6,11 @@ module Emanote.Model.Type where import Commonmark.Extensions.WikiLink qualified as WL import Data.Aeson qualified as Aeson -import Data.Default (Default (def)) import Data.IxSet.Typed ((@=)) import Data.IxSet.Typed qualified as Ix import Data.Map.Strict qualified as Map import Data.Time (UTCTime) import Data.Tree (Forest) -import Data.UUID (UUID) -import Ema.CLI qualified import Emanote.Model.Link.Rel (IxRel) import Emanote.Model.Link.Rel qualified as Rel import Emanote.Model.Note ( @@ -28,18 +25,13 @@ import Emanote.Model.StaticFile ( StaticFile (StaticFile), StaticFileInfo, ) -import Emanote.Model.Stork.Index qualified as Stork import Emanote.Model.Task (IxTask) import Emanote.Model.Task qualified as Task import Emanote.Model.Title qualified as Tit import Emanote.Pandoc.Markdown.Syntax.HashTag qualified as HT -import Emanote.Pandoc.Renderer (EmanotePandocRenderers) import Emanote.Route (FileType (AnyExt), LMLRoute, R) import Emanote.Route qualified as R -import Emanote.Route.SiteRoute.Type (SiteRoute) import Emanote.Source.Loc (Loc) -import Heist.Extra.TemplateState (TemplateState) -import Optics.Core (Prism') import Optics.Operators ((%~), (.~), (^.)) import Optics.TH (makeLenses) import Relude @@ -47,63 +39,27 @@ import Relude data Status = Status_Loading | Status_Ready deriving stock (Eq, Show) -data ModelT encF = Model +data Model = Model { _modelStatus :: Status , _modelLayers :: Set Loc - , _modelEmaCLIAction :: Ema.CLI.Action - , _modelRoutePrism :: encF (Prism' FilePath SiteRoute) - , _modelPandocRenderers :: EmanotePandocRenderers Model LMLRoute - -- ^ Dictates how exactly to render `Pandoc` to Heist nodes. - , _modelCompileTailwind :: Bool - , _modelInstanceID :: UUID - -- ^ An unique ID for this process's model. ID changes across processes. , _modelNotes :: IxNote , _modelRels :: IxRel , _modelSData :: IxSData , _modelStaticFiles :: IxStaticFile , _modelTasks :: IxTask -- ^ A tree (forest) of all notes, based on their folder hierarchy. - , _modelHeistTemplate :: TemplateState - , _modelStorkIndex :: Stork.IndexVar , _modelFolgezettelTree :: Forest R.LMLRoute -- ^ Folgezettel tree computed once for each update to model. } deriving stock (Generic) -type Model = ModelT Identity +makeLenses ''Model -{- | A bare version of `Model` that is managed by the Ema app. - - The only difference is that this one has no `RouteEncoder`. --} -type ModelEma = ModelT (Const ()) - -deriving stock instance Generic ModelEma - -deriving stock instance Generic Model - -makeLenses ''ModelT - -withoutRoutePrism :: Model -> (Prism' FilePath SiteRoute, ModelEma) -withoutRoutePrism model@Model {..} = - let _modelRoutePrism = Const () - in (runIdentity $ model ^. modelRoutePrism, Model {..}) - -withRoutePrism :: Prism' FilePath SiteRoute -> ModelEma -> Model -withRoutePrism enc Model {..} = - let _modelRoutePrism = Identity enc - in Model {..} - -emptyModel :: Set Loc -> Ema.CLI.Action -> EmanotePandocRenderers Model LMLRoute -> Bool -> UUID -> Stork.IndexVar -> ModelEma -emptyModel layers act ren ctw instanceId storkVar = +emptyModel :: Set Loc -> Model +emptyModel layers = Model { _modelStatus = Status_Loading , _modelLayers = layers - , _modelEmaCLIAction = act - , _modelRoutePrism = Const () - , _modelPandocRenderers = ren - , _modelCompileTailwind = ctw - , _modelInstanceID = instanceId , -- Inject a placeholder `index.md` to account for the use case of emanote -- being run on an empty directory. _modelNotes = Ix.empty & injectRoot @@ -111,20 +67,14 @@ emptyModel layers act ren ctw instanceId storkVar = , _modelSData = Ix.empty , _modelStaticFiles = Ix.empty , _modelTasks = Ix.empty - , _modelHeistTemplate = def - , _modelStorkIndex = storkVar , _modelFolgezettelTree = mempty } -modelReadyForView :: ModelT f -> ModelT f +modelReadyForView :: Model -> Model modelReadyForView = modelStatus .~ Status_Ready --- | Are we running in live server, or statically generated website? -inLiveServer :: Model -> Bool -inLiveServer = Ema.CLI.isLiveServer . _modelEmaCLIAction - -modelInsertNote :: Note -> ModelT f -> ModelT f +modelInsertNote :: Note -> Model -> Model modelInsertNote note = modelNotes %~ ( Ix.updateIx r note @@ -180,7 +130,7 @@ injectAncestorPlaceholder r ns = Nothing -> Ix.updateIx (R.defaultLmlRoute r) (N.ancestorPlaceholderNote $ coerce r) ns -modelDeleteNote :: LMLRoute -> ModelT f -> ModelT f +modelDeleteNote :: LMLRoute -> Model -> Model modelDeleteNote k model = model & modelNotes @@ -221,34 +171,34 @@ deleteIxMulti r rels = let candidates = Ix.toList $ Ix.getEQ r rels in flipfoldl' Ix.delete rels candidates -modelLookupStaticFile :: FilePath -> ModelT f -> Maybe StaticFile +modelLookupStaticFile :: FilePath -> Model -> Maybe StaticFile modelLookupStaticFile fp m = do r :: R.R 'AnyExt <- R.mkRouteFromFilePath fp Ix.getOne $ Ix.getEQ r $ m ^. modelStaticFiles -modelInsertStaticFile :: UTCTime -> R.R 'AnyExt -> FilePath -> Maybe StaticFileInfo -> ModelT f -> ModelT f +modelInsertStaticFile :: UTCTime -> R.R 'AnyExt -> FilePath -> Maybe StaticFileInfo -> Model -> Model modelInsertStaticFile t r fp mInfo = modelStaticFiles %~ Ix.updateIx r staticFile where staticFile = StaticFile r fp t mInfo -modelDeleteStaticFile :: R.R 'AnyExt -> ModelT f -> ModelT f +modelDeleteStaticFile :: R.R 'AnyExt -> Model -> Model modelDeleteStaticFile r = modelStaticFiles %~ Ix.deleteIx r -modelInsertData :: SData -> ModelT f -> ModelT f +modelInsertData :: SData -> Model -> Model modelInsertData v = modelSData %~ Ix.updateIx (v ^. sdataRoute) v -modelDeleteData :: R.R 'R.Yaml -> ModelT f -> ModelT f +modelDeleteData :: R.R 'R.Yaml -> Model -> Model modelDeleteData k = modelSData %~ Ix.deleteIx k -modelLookupSData :: R.R 'R.Yaml -> ModelT f -> Maybe SData +modelLookupSData :: R.R 'R.Yaml -> Model -> Maybe SData modelLookupSData r = Ix.getOne . Ix.getEQ r . _modelSData -modelLookupNoteByRoute :: (R.LMLView, LMLRoute) -> ModelT f -> Maybe (R.LMLView, Note) +modelLookupNoteByRoute :: (R.LMLView, LMLRoute) -> Model -> Maybe (R.LMLView, Note) modelLookupNoteByRoute (view, r) (_modelNotes -> notes) = do note <- N.lookupNotesByRoute r notes case (view, noteHasFeed note) of @@ -257,7 +207,7 @@ modelLookupNoteByRoute (view, r) (_modelNotes -> notes) = do (_, _) -> pure (view, note) -- | Like `modelLookupNoteByRoute`, but for `R.Html` routes only. -modelLookupNoteByRoute' :: LMLRoute -> ModelT f -> Maybe Note +modelLookupNoteByRoute' :: LMLRoute -> Model -> Maybe Note modelLookupNoteByRoute' r = fmap snd . modelLookupNoteByRoute (R.LMLView_Html, r) @@ -266,20 +216,20 @@ given route. Uses the note's on-disk source path when the note exists; falls back to the canonical route parent for synthesized notes (and for routes that have no matching note in the model). -} -modelResolveLinkBase :: ModelT f -> LMLRoute -> Maybe (R.R 'R.Folder) +modelResolveLinkBase :: Model -> LMLRoute -> Maybe (R.R 'R.Folder) modelResolveLinkBase model r = maybe (R.withLmlRoute R.routeParent r) N.noteResolveLinkBase (modelLookupNoteByRoute' r model) -modelLookupNoteByHtmlRoute :: R 'R.Html -> ModelT f -> Rel.ResolvedRelTarget Note +modelLookupNoteByHtmlRoute :: R 'R.Html -> Model -> Rel.ResolvedRelTarget Note modelLookupNoteByHtmlRoute r = Rel.resolvedRelTargetFromCandidates . N.lookupNotesByHtmlRoute r . _modelNotes -modelLookupFeedNoteByHtmlRoute :: R 'R.Xml -> ModelT f -> Maybe Note +modelLookupFeedNoteByHtmlRoute :: R 'R.Xml -> Model -> Maybe Note modelLookupFeedNoteByHtmlRoute r model = case resolvedTarget of Rel.RRTFound note | noteHasFeed note -> pure note @@ -291,7 +241,7 @@ modelLookupFeedNoteByHtmlRoute r model = case resolvedTarget of $ N.lookupNotesByXmlRoute r $ _modelNotes model -modelLookupTitle :: LMLRoute -> ModelT f -> Tit.Title +modelLookupTitle :: LMLRoute -> Model -> Tit.Title modelLookupTitle r = maybe (Tit.fromRoute r) N._noteTitle . modelLookupNoteByRoute' r @@ -308,11 +258,11 @@ modelWikiLinkTargets wl model = @= wl in fmap Right staticFiles <> fmap Left ((R.LMLView_Html,) <$> notes) -modelLookupStaticFileByRoute :: R 'AnyExt -> ModelT f -> Maybe StaticFile +modelLookupStaticFileByRoute :: R 'AnyExt -> Model -> Maybe StaticFile modelLookupStaticFileByRoute r = Ix.getOne . Ix.getEQ r . _modelStaticFiles -modelTags :: ModelT f -> [(HT.Tag, [Note])] +modelTags :: Model -> [(HT.Tag, [Note])] modelTags = Ix.groupAscBy @HT.Tag . _modelNotes @@ -340,11 +290,11 @@ modelNoteErrors model = If index.org exist, use that. Otherwise, fallback to index.md. -} -modelIndexRoute :: ModelT f -> LMLRoute +modelIndexRoute :: Model -> LMLRoute modelIndexRoute model = do resolveLmlRoute model R.indexRoute -resolveLmlRoute :: forall lmlType f. ModelT f -> R ('R.LMLType lmlType) -> LMLRoute +resolveLmlRoute :: forall lmlType. Model -> R ('R.LMLType lmlType) -> LMLRoute resolveLmlRoute model r = fromMaybe (R.defaultLmlRoute r) $ resolveLmlRouteIfExists (model ^. modelNotes) r diff --git a/emanote/test/Emanote/Model/Link/RelSpec.hs b/packages/emanote-model/test/Emanote/Model/Link/RelSpec.hs similarity index 100% rename from emanote/test/Emanote/Model/Link/RelSpec.hs rename to packages/emanote-model/test/Emanote/Model/Link/RelSpec.hs diff --git a/emanote/test/Emanote/Model/QuerySpec.hs b/packages/emanote-model/test/Emanote/Model/QuerySpec.hs similarity index 100% rename from emanote/test/Emanote/Model/QuerySpec.hs rename to packages/emanote-model/test/Emanote/Model/QuerySpec.hs diff --git a/emanote/test/Emanote/Model/TocSpec.hs b/packages/emanote-model/test/Emanote/Model/TocSpec.hs similarity index 100% rename from emanote/test/Emanote/Model/TocSpec.hs rename to packages/emanote-model/test/Emanote/Model/TocSpec.hs diff --git a/packages/emanote-model/test/Spec.hs b/packages/emanote-model/test/Spec.hs new file mode 100644 index 000000000..1a4550cc3 --- /dev/null +++ b/packages/emanote-model/test/Spec.hs @@ -0,0 +1,14 @@ +module Main where + +import Emanote.Model.Link.RelSpec qualified +import Emanote.Model.QuerySpec qualified +import Emanote.Model.TocSpec qualified +import Relude +import Test.Hspec + +main :: IO () +main = + hspec $ do + Emanote.Model.Link.RelSpec.spec + Emanote.Model.QuerySpec.spec + Emanote.Model.TocSpec.spec diff --git a/packages/emanote-pandoc/emanote-pandoc.cabal b/packages/emanote-pandoc/emanote-pandoc.cabal new file mode 100644 index 000000000..ca0cd9bde --- /dev/null +++ b/packages/emanote-pandoc/emanote-pandoc.cabal @@ -0,0 +1,110 @@ +cabal-version: 2.4 +name: emanote-pandoc +version: 1.6.0.0 +license: AGPL-3.0-only +copyright: 2022 Sridhar Ratnakumar +maintainer: srid@srid.ca +author: Sridhar Ratnakumar +category: Web +homepage: https://emanote.srid.ca +synopsis: + Markdown parsing and generic Pandoc renderer plumbing for Emanote + +bug-reports: https://github.com/srid/emanote/issues + +common haskell-common + ghc-options: + -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns + -Wmissing-deriving-strategies -Wunused-foralls -Wunused-foralls + -fprint-explicit-foralls -fprint-explicit-kinds + + default-extensions: + BangPatterns + ConstraintKinds + DataKinds + DeriveDataTypeable + DeriveFoldable + DeriveFunctor + DeriveGeneric + DeriveLift + DeriveTraversable + DerivingStrategies + DerivingVia + EmptyCase + EmptyDataDecls + EmptyDataDeriving + ExistentialQuantification + ExplicitForAll + FlexibleContexts + FlexibleInstances + GADTSyntax + GeneralisedNewtypeDeriving + ImportQualifiedPost + KindSignatures + LambdaCase + MultiParamTypeClasses + MultiWayIf + NoImplicitPrelude + NoStarIsType + NumericUnderscores + OverloadedStrings + PolyKinds + PostfixOperators + RankNTypes + ScopedTypeVariables + StandaloneDeriving + StandaloneKindSignatures + TupleSections + TypeApplications + TypeFamilies + TypeOperators + ViewPatterns + +common library-common + import: haskell-common + default-language: Haskell2010 + build-depends: + , aeson + , base >=4.14 && <5 + , commonmark + , commonmark-extensions >=0.2.3.4 + , commonmark-pandoc + , commonmark-simple >=0.2 + , commonmark-wikilink >=0.2 + , containers + , emanote-route + , heist >=1.1.1.0 + , heist-extra >=0.5.0.0 + , pandoc + , pandoc-types + , parsec + , relude >=1.0 + , tagtree + , text + +library + import: library-common + hs-source-dirs: src + exposed-modules: + Emanote.Pandoc.BuiltinFilters + Emanote.Pandoc.ExternalLink + Emanote.Pandoc.Link + Emanote.Pandoc.Markdown.Parser + Emanote.Pandoc.Markdown.Syntax.HashTag + Emanote.Pandoc.Markdown.Syntax.Highlight + Emanote.Pandoc.Renderer + +test-suite test + import: library-common + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Spec.hs + build-depends: + , emanote-pandoc + , hedgehog + , hspec + , hspec-hedgehog + , pandoc-types + , relude + + other-modules: Emanote.Pandoc.ExternalLinkSpec diff --git a/emanote/src/Emanote/Pandoc/BuiltinFilters.hs b/packages/emanote-pandoc/src/Emanote/Pandoc/BuiltinFilters.hs similarity index 100% rename from emanote/src/Emanote/Pandoc/BuiltinFilters.hs rename to packages/emanote-pandoc/src/Emanote/Pandoc/BuiltinFilters.hs diff --git a/emanote/src/Emanote/Pandoc/ExternalLink.hs b/packages/emanote-pandoc/src/Emanote/Pandoc/ExternalLink.hs similarity index 100% rename from emanote/src/Emanote/Pandoc/ExternalLink.hs rename to packages/emanote-pandoc/src/Emanote/Pandoc/ExternalLink.hs diff --git a/emanote/src/Emanote/Pandoc/Link.hs b/packages/emanote-pandoc/src/Emanote/Pandoc/Link.hs similarity index 100% rename from emanote/src/Emanote/Pandoc/Link.hs rename to packages/emanote-pandoc/src/Emanote/Pandoc/Link.hs diff --git a/emanote/src/Emanote/Pandoc/Markdown/Parser.hs b/packages/emanote-pandoc/src/Emanote/Pandoc/Markdown/Parser.hs similarity index 100% rename from emanote/src/Emanote/Pandoc/Markdown/Parser.hs rename to packages/emanote-pandoc/src/Emanote/Pandoc/Markdown/Parser.hs diff --git a/emanote/src/Emanote/Pandoc/Markdown/Syntax/HashTag.hs b/packages/emanote-pandoc/src/Emanote/Pandoc/Markdown/Syntax/HashTag.hs similarity index 100% rename from emanote/src/Emanote/Pandoc/Markdown/Syntax/HashTag.hs rename to packages/emanote-pandoc/src/Emanote/Pandoc/Markdown/Syntax/HashTag.hs diff --git a/emanote/src/Emanote/Pandoc/Markdown/Syntax/Highlight.hs b/packages/emanote-pandoc/src/Emanote/Pandoc/Markdown/Syntax/Highlight.hs similarity index 100% rename from emanote/src/Emanote/Pandoc/Markdown/Syntax/Highlight.hs rename to packages/emanote-pandoc/src/Emanote/Pandoc/Markdown/Syntax/Highlight.hs diff --git a/emanote/src/Emanote/Pandoc/Renderer.hs b/packages/emanote-pandoc/src/Emanote/Pandoc/Renderer.hs similarity index 100% rename from emanote/src/Emanote/Pandoc/Renderer.hs rename to packages/emanote-pandoc/src/Emanote/Pandoc/Renderer.hs diff --git a/emanote/test/Emanote/Pandoc/ExternalLinkSpec.hs b/packages/emanote-pandoc/test/Emanote/Pandoc/ExternalLinkSpec.hs similarity index 100% rename from emanote/test/Emanote/Pandoc/ExternalLinkSpec.hs rename to packages/emanote-pandoc/test/Emanote/Pandoc/ExternalLinkSpec.hs diff --git a/packages/emanote-pandoc/test/Spec.hs b/packages/emanote-pandoc/test/Spec.hs new file mode 100644 index 000000000..1ad168f9c --- /dev/null +++ b/packages/emanote-pandoc/test/Spec.hs @@ -0,0 +1,9 @@ +module Main where + +import Emanote.Pandoc.ExternalLinkSpec qualified +import Relude +import Test.Hspec + +main :: IO () +main = + hspec Emanote.Pandoc.ExternalLinkSpec.spec diff --git a/packages/emanote-route/emanote-route.cabal b/packages/emanote-route/emanote-route.cabal new file mode 100644 index 000000000..4f12eec14 --- /dev/null +++ b/packages/emanote-route/emanote-route.cabal @@ -0,0 +1,96 @@ +cabal-version: 2.4 +name: emanote-route +version: 1.6.0.0 +license: AGPL-3.0-only +copyright: 2022 Sridhar Ratnakumar +maintainer: srid@srid.ca +author: Sridhar Ratnakumar +category: Web +homepage: https://emanote.srid.ca +synopsis: Pure route, path, and generated URL types for Emanote +bug-reports: https://github.com/srid/emanote/issues + +common haskell-common + ghc-options: + -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns + -Wmissing-deriving-strategies -Wunused-foralls -Wunused-foralls + -fprint-explicit-foralls -fprint-explicit-kinds + + default-extensions: + AllowAmbiguousTypes + BangPatterns + ConstraintKinds + DataKinds + DeriveDataTypeable + DeriveFoldable + DeriveFunctor + DeriveGeneric + DeriveLift + DeriveTraversable + DerivingStrategies + DerivingVia + EmptyCase + EmptyDataDecls + EmptyDataDeriving + ExistentialQuantification + ExplicitForAll + FlexibleContexts + FlexibleInstances + GADTSyntax + GeneralisedNewtypeDeriving + ImportQualifiedPost + KindSignatures + LambdaCase + MultiParamTypeClasses + MultiWayIf + NoImplicitPrelude + NoStarIsType + NumericUnderscores + OverloadedStrings + PolyKinds + PostfixOperators + RankNTypes + ScopedTypeVariables + StandaloneDeriving + StandaloneKindSignatures + TupleSections + TypeApplications + TypeFamilies + TypeOperators + ViewPatterns + +common library-common + import: haskell-common + default-language: Haskell2010 + build-depends: + , aeson + , base >=4.14 && <5 + , filepath + , relude >=1.0 + , tagtree + , text + , url-slug + +library + import: library-common + hs-source-dirs: src + exposed-modules: + Emanote.Route + Emanote.Route.Ext + Emanote.Route.ModelRoute + Emanote.Route.R + Emanote.Route.SiteRoute.Type + +test-suite test + import: library-common + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Spec.hs + build-depends: + , emanote-route + , hedgehog + , hspec + , hspec-hedgehog + , relude + + other-modules: Emanote.Route.RSpec diff --git a/emanote/src/Emanote/Route.hs b/packages/emanote-route/src/Emanote/Route.hs similarity index 100% rename from emanote/src/Emanote/Route.hs rename to packages/emanote-route/src/Emanote/Route.hs diff --git a/emanote/src/Emanote/Route/Ext.hs b/packages/emanote-route/src/Emanote/Route/Ext.hs similarity index 100% rename from emanote/src/Emanote/Route/Ext.hs rename to packages/emanote-route/src/Emanote/Route/Ext.hs diff --git a/emanote/src/Emanote/Route/ModelRoute.hs b/packages/emanote-route/src/Emanote/Route/ModelRoute.hs similarity index 100% rename from emanote/src/Emanote/Route/ModelRoute.hs rename to packages/emanote-route/src/Emanote/Route/ModelRoute.hs diff --git a/emanote/src/Emanote/Route/R.hs b/packages/emanote-route/src/Emanote/Route/R.hs similarity index 100% rename from emanote/src/Emanote/Route/R.hs rename to packages/emanote-route/src/Emanote/Route/R.hs diff --git a/emanote/src/Emanote/Route/SiteRoute/Type.hs b/packages/emanote-route/src/Emanote/Route/SiteRoute/Type.hs similarity index 87% rename from emanote/src/Emanote/Route/SiteRoute/Type.hs rename to packages/emanote-route/src/Emanote/Route/SiteRoute/Type.hs index 07fd613e0..96a481ca0 100644 --- a/emanote/src/Emanote/Route/SiteRoute/Type.hs +++ b/packages/emanote-route/src/Emanote/Route/SiteRoute/Type.hs @@ -11,7 +11,7 @@ module Emanote.Route.SiteRoute.Type ( ) where import Data.Aeson (ToJSON) -import Emanote.Pandoc.Markdown.Syntax.HashTag qualified as HT +import Data.TagTree qualified as TT import Emanote.Route.Ext qualified as Ext import Emanote.Route.ModelRoute (LMLRoute, LMLView, StaticFileRoute, lmlRouteCase) import Emanote.Route.R qualified as R @@ -28,7 +28,7 @@ data ExportFormat -- | A route to a virtual resource (not in `Model`) data VirtualRoute = VirtualRoute_Index - | VirtualRoute_TagIndex [HT.TagNode] + | VirtualRoute_TagIndex [TT.TagNode] | VirtualRoute_Export ExportFormat | VirtualRoute_StorkIndex | VirtualRoute_TaskIndex @@ -37,11 +37,10 @@ data VirtualRoute {- | A route to a resource in `Model` - This is *mostly isomorphic* to `ModelRoute`, except for containing the - absolute path to the static file. + This is *mostly isomorphic* to `ModelRoute`. -} data ResourceRoute - = ResourceRoute_StaticFile StaticFileRoute FilePath + = ResourceRoute_StaticFile StaticFileRoute | ResourceRoute_LML LMLView LMLRoute deriving stock (Eq, Show, Ord, Generic) deriving anyclass (ToJSON) @@ -61,7 +60,7 @@ instance Show SiteRoute where "Amb: " <> urlPath SiteRoute_ResourceRoute rr -> case rr of - ResourceRoute_StaticFile r _fp -> + ResourceRoute_StaticFile r -> show r ResourceRoute_LML _view r -> show $ lmlRouteCase r @@ -94,10 +93,10 @@ decodeStorkIndexR fp = do "-" :| ["stork.st"] <- R.unRoute <$> R.decodeAnyRoute fp pass -decodeTagIndexR :: FilePath -> Maybe [HT.TagNode] +decodeTagIndexR :: FilePath -> Maybe [TT.TagNode] decodeTagIndexR fp = do "-" :| "tags" : tagPath <- pure $ R.unRoute $ R.decodeHtmlRoute fp - pure $ fmap (HT.TagNode . Slug.unSlug) tagPath + pure $ fmap (TT.TagNode . Slug.unSlug) tagPath decodeTaskIndexR :: FilePath -> Maybe () decodeTaskIndexR fp = do @@ -124,6 +123,6 @@ encodeExportR = \case ExportFormat_Metadata -> fromString "export.json" ExportFormat_Content -> fromString "export.md" -encodeTagIndexR :: [HT.TagNode] -> R.R 'Ext.Html +encodeTagIndexR :: [TT.TagNode] -> R.R 'Ext.Html encodeTagIndexR tagNodes = - R.R $ "-" :| "tags" : fmap (fromString . toString . HT.unTagNode) tagNodes + R.R $ "-" :| "tags" : fmap (fromString . toString . TT.unTagNode) tagNodes diff --git a/emanote/test/Emanote/Route/RSpec.hs b/packages/emanote-route/test/Emanote/Route/RSpec.hs similarity index 100% rename from emanote/test/Emanote/Route/RSpec.hs rename to packages/emanote-route/test/Emanote/Route/RSpec.hs diff --git a/packages/emanote-route/test/Spec.hs b/packages/emanote-route/test/Spec.hs new file mode 100644 index 000000000..23eb891b2 --- /dev/null +++ b/packages/emanote-route/test/Spec.hs @@ -0,0 +1,9 @@ +module Main where + +import Emanote.Route.RSpec qualified +import Relude +import Test.Hspec + +main :: IO () +main = + hspec Emanote.Route.RSpec.spec diff --git a/packages/emanote-source/emanote-source.cabal b/packages/emanote-source/emanote-source.cabal new file mode 100644 index 000000000..acef543d9 --- /dev/null +++ b/packages/emanote-source/emanote-source.cabal @@ -0,0 +1,78 @@ +cabal-version: 2.4 +name: emanote-source +version: 1.6.0.0 +license: AGPL-3.0-only +copyright: 2022 Sridhar Ratnakumar +maintainer: srid@srid.ca +author: Sridhar Ratnakumar +category: Web +homepage: https://emanote.srid.ca +synopsis: Source locations and source file pattern policy for Emanote +bug-reports: https://github.com/srid/emanote/issues + +common haskell-common + ghc-options: + -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns + -Wmissing-deriving-strategies -Wunused-foralls -Wunused-foralls + -fprint-explicit-foralls -fprint-explicit-kinds + + default-extensions: + BangPatterns + ConstraintKinds + DataKinds + DeriveDataTypeable + DeriveFoldable + DeriveFunctor + DeriveGeneric + DeriveLift + DeriveTraversable + DerivingStrategies + DerivingVia + EmptyCase + EmptyDataDecls + EmptyDataDeriving + ExistentialQuantification + ExplicitForAll + FlexibleContexts + FlexibleInstances + GADTSyntax + GeneralisedNewtypeDeriving + ImportQualifiedPost + KindSignatures + LambdaCase + MultiParamTypeClasses + MultiWayIf + NoImplicitPrelude + NoStarIsType + NumericUnderscores + OverloadedStrings + PolyKinds + PostfixOperators + RankNTypes + ScopedTypeVariables + StandaloneDeriving + StandaloneKindSignatures + TupleSections + TypeApplications + TypeFamilies + TypeOperators + ViewPatterns + +common library-common + import: haskell-common + default-language: Haskell2010 + build-depends: + , base >=4.14 && <5 + , containers + , deriving-aeson + , emanote-route + , filepath + , filepattern + , relude >=1.0 + +library + import: library-common + hs-source-dirs: src + exposed-modules: + Emanote.Source.Loc + Emanote.Source.Pattern diff --git a/emanote/src/Emanote/Source/Loc.hs b/packages/emanote-source/src/Emanote/Source/Loc.hs similarity index 100% rename from emanote/src/Emanote/Source/Loc.hs rename to packages/emanote-source/src/Emanote/Source/Loc.hs diff --git a/emanote/src/Emanote/Source/Pattern.hs b/packages/emanote-source/src/Emanote/Source/Pattern.hs similarity index 100% rename from emanote/src/Emanote/Source/Pattern.hs rename to packages/emanote-source/src/Emanote/Source/Pattern.hs diff --git a/emanote/CHANGELOG.md b/packages/emanote/CHANGELOG.md similarity index 97% rename from emanote/CHANGELOG.md rename to packages/emanote/CHANGELOG.md index 0053cf36c..9c314ad6d 100644 --- a/emanote/CHANGELOG.md +++ b/packages/emanote/CHANGELOG.md @@ -29,6 +29,10 @@ - TOC sidebar: tightened entry padding and styled the overflow scrollbar (Firefox `scrollbar-width: thin` + WebKit pseudo-element) so long tables of contents no longer surface the chunky OS-default bar (closes [#668](https://github.com/srid/emanote/issues/668)). - Live server: assets bundled under `_emanote-static/` (skylighting CSS, self-hosted fonts, inverted-tree CSS, emanote-logo, Stork CSS+JS) now cache-bust with `?t=` instead of being served bare. Edits to any of these files in `emanote run` invalidate the browser cache without a manual restart. Templates use a new `${url}` splice; the older `${ema:emanoteStaticLayerUrl}` continues to work for third-party templates but skips the cache buster (closes [#666](https://github.com/srid/emanote/issues/666)). +**Developer changes** + +- Split the Haskell codebase into acyclic Cabal packages under `packages/`: `emanote-route`, `emanote-source`, `emanote-pandoc`, `emanote-model`, and the app package `emanote` ([#600](https://github.com/srid/emanote/issues/600)). + ## 1.4.0.0 (2025-08-18) **Notable features** diff --git a/emanote/LICENSE b/packages/emanote/LICENSE similarity index 100% rename from emanote/LICENSE rename to packages/emanote/LICENSE diff --git a/emanote/README.md b/packages/emanote/README.md similarity index 100% rename from emanote/README.md rename to packages/emanote/README.md diff --git a/emanote/default/LICENSE b/packages/emanote/default/LICENSE similarity index 100% rename from emanote/default/LICENSE rename to packages/emanote/default/LICENSE diff --git a/emanote/default/README b/packages/emanote/default/README similarity index 100% rename from emanote/default/README rename to packages/emanote/default/README diff --git a/emanote/default/_emanote-live-server/tailwind/README.txt b/packages/emanote/default/_emanote-live-server/tailwind/README.txt similarity index 100% rename from emanote/default/_emanote-live-server/tailwind/README.txt rename to packages/emanote/default/_emanote-live-server/tailwind/README.txt diff --git a/emanote/default/_emanote-live-server/tailwind/tailwind.cdn.js b/packages/emanote/default/_emanote-live-server/tailwind/tailwind.cdn.js similarity index 100% rename from emanote/default/_emanote-live-server/tailwind/tailwind.cdn.js rename to packages/emanote/default/_emanote-live-server/tailwind/tailwind.cdn.js diff --git a/emanote/default/_emanote-live-server/tailwind/update.sh b/packages/emanote/default/_emanote-live-server/tailwind/update.sh similarity index 100% rename from emanote/default/_emanote-live-server/tailwind/update.sh rename to packages/emanote/default/_emanote-live-server/tailwind/update.sh diff --git a/emanote/default/_emanote-static/emanote-logo.svg b/packages/emanote/default/_emanote-static/emanote-logo.svg similarity index 100% rename from emanote/default/_emanote-static/emanote-logo.svg rename to packages/emanote/default/_emanote-static/emanote-logo.svg diff --git a/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LIftL.woff2 b/packages/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LIftL.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LIftL.woff2 rename to packages/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LIftL.woff2 diff --git a/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LJftLp_A.woff2 b/packages/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LJftLp_A.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LJftLp_A.woff2 rename to packages/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LJftLp_A.woff2 diff --git a/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LLPtLp_A.woff2 b/packages/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LLPtLp_A.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LLPtLp_A.woff2 rename to packages/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LLPtLp_A.woff2 diff --git a/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LL_tLp_A.woff2 b/packages/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LL_tLp_A.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LL_tLp_A.woff2 rename to packages/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LL_tLp_A.woff2 diff --git a/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LLvtLp_A.woff2 b/packages/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LLvtLp_A.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LLvtLp_A.woff2 rename to packages/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LLvtLp_A.woff2 diff --git a/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LT_tLp_A.woff2 b/packages/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LT_tLp_A.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LT_tLp_A.woff2 rename to packages/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LT_tLp_A.woff2 diff --git a/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LXftLp_A.woff2 b/packages/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LXftLp_A.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LXftLp_A.woff2 rename to packages/emanote/default/_emanote-static/fonts/0QIhMX1D_JOuMw_LXftLp_A.woff2 diff --git a/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuM2T7I-NP.woff2 b/packages/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuM2T7I-NP.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/0QIvMX1D_JOuM2T7I-NP.woff2 rename to packages/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuM2T7I-NP.woff2 diff --git a/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuM3b7I-NP.woff2 b/packages/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuM3b7I-NP.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/0QIvMX1D_JOuM3b7I-NP.woff2 rename to packages/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuM3b7I-NP.woff2 diff --git a/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMw77I-NP.woff2 b/packages/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMw77I-NP.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMw77I-NP.woff2 rename to packages/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMw77I-NP.woff2 diff --git a/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMwT7I-NP.woff2 b/packages/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMwT7I-NP.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMwT7I-NP.woff2 rename to packages/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMwT7I-NP.woff2 diff --git a/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMwX7I-NP.woff2 b/packages/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMwX7I-NP.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMwX7I-NP.woff2 rename to packages/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMwX7I-NP.woff2 diff --git a/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMwf7I-NP.woff2 b/packages/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMwf7I-NP.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMwf7I-NP.woff2 rename to packages/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMwf7I-NP.woff2 diff --git a/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMwr7Iw.woff2 b/packages/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMwr7Iw.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMwr7Iw.woff2 rename to packages/emanote/default/_emanote-static/fonts/0QIvMX1D_JOuMwr7Iw.woff2 diff --git a/emanote/default/_emanote-static/fonts/V8mDoQDjQSkFtoMM3T6r8E7mPb54C-s0.woff2 b/packages/emanote/default/_emanote-static/fonts/V8mDoQDjQSkFtoMM3T6r8E7mPb54C-s0.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/V8mDoQDjQSkFtoMM3T6r8E7mPb54C-s0.woff2 rename to packages/emanote/default/_emanote-static/fonts/V8mDoQDjQSkFtoMM3T6r8E7mPb54C-s0.woff2 diff --git a/emanote/default/_emanote-static/fonts/V8mDoQDjQSkFtoMM3T6r8E7mPb94C-s0.woff2 b/packages/emanote/default/_emanote-static/fonts/V8mDoQDjQSkFtoMM3T6r8E7mPb94C-s0.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/V8mDoQDjQSkFtoMM3T6r8E7mPb94C-s0.woff2 rename to packages/emanote/default/_emanote-static/fonts/V8mDoQDjQSkFtoMM3T6r8E7mPb94C-s0.woff2 diff --git a/emanote/default/_emanote-static/fonts/V8mDoQDjQSkFtoMM3T6r8E7mPbF4Cw.woff2 b/packages/emanote/default/_emanote-static/fonts/V8mDoQDjQSkFtoMM3T6r8E7mPbF4Cw.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/V8mDoQDjQSkFtoMM3T6r8E7mPbF4Cw.woff2 rename to packages/emanote/default/_emanote-static/fonts/V8mDoQDjQSkFtoMM3T6r8E7mPbF4Cw.woff2 diff --git a/emanote/default/_emanote-static/fonts/fonts.css b/packages/emanote/default/_emanote-static/fonts/fonts.css similarity index 100% rename from emanote/default/_emanote-static/fonts/fonts.css rename to packages/emanote/default/_emanote-static/fonts/fonts.css diff --git a/emanote/default/_emanote-static/fonts/i7dMIFZifjKcF5UAWdDRaPpZUFWaHg.woff2 b/packages/emanote/default/_emanote-static/fonts/i7dMIFZifjKcF5UAWdDRaPpZUFWaHg.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/i7dMIFZifjKcF5UAWdDRaPpZUFWaHg.woff2 rename to packages/emanote/default/_emanote-static/fonts/i7dMIFZifjKcF5UAWdDRaPpZUFWaHg.woff2 diff --git a/emanote/default/_emanote-static/fonts/i7dMIFZifjKcF5UAWdDRaPpZUFqaHjyV.woff2 b/packages/emanote/default/_emanote-static/fonts/i7dMIFZifjKcF5UAWdDRaPpZUFqaHjyV.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/i7dMIFZifjKcF5UAWdDRaPpZUFqaHjyV.woff2 rename to packages/emanote/default/_emanote-static/fonts/i7dMIFZifjKcF5UAWdDRaPpZUFqaHjyV.woff2 diff --git a/emanote/default/_emanote-static/fonts/i7dMIFZifjKcF5UAWdDRaPpZUFuaHjyV.woff2 b/packages/emanote/default/_emanote-static/fonts/i7dMIFZifjKcF5UAWdDRaPpZUFuaHjyV.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/i7dMIFZifjKcF5UAWdDRaPpZUFuaHjyV.woff2 rename to packages/emanote/default/_emanote-static/fonts/i7dMIFZifjKcF5UAWdDRaPpZUFuaHjyV.woff2 diff --git a/emanote/default/_emanote-static/fonts/i7dNIFZifjKcF5UAWdDRYERMR3K_.woff2 b/packages/emanote/default/_emanote-static/fonts/i7dNIFZifjKcF5UAWdDRYERMR3K_.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/i7dNIFZifjKcF5UAWdDRYERMR3K_.woff2 rename to packages/emanote/default/_emanote-static/fonts/i7dNIFZifjKcF5UAWdDRYERMR3K_.woff2 diff --git a/emanote/default/_emanote-static/fonts/i7dNIFZifjKcF5UAWdDRYERMSHK_IwU.woff2 b/packages/emanote/default/_emanote-static/fonts/i7dNIFZifjKcF5UAWdDRYERMSHK_IwU.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/i7dNIFZifjKcF5UAWdDRYERMSHK_IwU.woff2 rename to packages/emanote/default/_emanote-static/fonts/i7dNIFZifjKcF5UAWdDRYERMSHK_IwU.woff2 diff --git a/emanote/default/_emanote-static/fonts/i7dNIFZifjKcF5UAWdDRYERMSXK_IwU.woff2 b/packages/emanote/default/_emanote-static/fonts/i7dNIFZifjKcF5UAWdDRYERMSXK_IwU.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/i7dNIFZifjKcF5UAWdDRYERMSXK_IwU.woff2 rename to packages/emanote/default/_emanote-static/fonts/i7dNIFZifjKcF5UAWdDRYERMSXK_IwU.woff2 diff --git a/emanote/default/_emanote-static/fonts/i7dPIFZifjKcF5UAWdDRYE58RWq7.woff2 b/packages/emanote/default/_emanote-static/fonts/i7dPIFZifjKcF5UAWdDRYE58RWq7.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/i7dPIFZifjKcF5UAWdDRYE58RWq7.woff2 rename to packages/emanote/default/_emanote-static/fonts/i7dPIFZifjKcF5UAWdDRYE58RWq7.woff2 diff --git a/emanote/default/_emanote-static/fonts/i7dPIFZifjKcF5UAWdDRYE98RWq7.woff2 b/packages/emanote/default/_emanote-static/fonts/i7dPIFZifjKcF5UAWdDRYE98RWq7.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/i7dPIFZifjKcF5UAWdDRYE98RWq7.woff2 rename to packages/emanote/default/_emanote-static/fonts/i7dPIFZifjKcF5UAWdDRYE98RWq7.woff2 diff --git a/emanote/default/_emanote-static/fonts/i7dPIFZifjKcF5UAWdDRYEF8RQ.woff2 b/packages/emanote/default/_emanote-static/fonts/i7dPIFZifjKcF5UAWdDRYEF8RQ.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/i7dPIFZifjKcF5UAWdDRYEF8RQ.woff2 rename to packages/emanote/default/_emanote-static/fonts/i7dPIFZifjKcF5UAWdDRYEF8RQ.woff2 diff --git a/emanote/default/_emanote-static/fonts/i7dSIFZifjKcF5UAWdDRYERE_FeqEiSRV3U.woff2 b/packages/emanote/default/_emanote-static/fonts/i7dSIFZifjKcF5UAWdDRYERE_FeqEiSRV3U.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/i7dSIFZifjKcF5UAWdDRYERE_FeqEiSRV3U.woff2 rename to packages/emanote/default/_emanote-static/fonts/i7dSIFZifjKcF5UAWdDRYERE_FeqEiSRV3U.woff2 diff --git a/emanote/default/_emanote-static/fonts/i7dSIFZifjKcF5UAWdDRYERE_FeqEySRV3U.woff2 b/packages/emanote/default/_emanote-static/fonts/i7dSIFZifjKcF5UAWdDRYERE_FeqEySRV3U.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/i7dSIFZifjKcF5UAWdDRYERE_FeqEySRV3U.woff2 rename to packages/emanote/default/_emanote-static/fonts/i7dSIFZifjKcF5UAWdDRYERE_FeqEySRV3U.woff2 diff --git a/emanote/default/_emanote-static/fonts/i7dSIFZifjKcF5UAWdDRYERE_FeqHCSR.woff2 b/packages/emanote/default/_emanote-static/fonts/i7dSIFZifjKcF5UAWdDRYERE_FeqHCSR.woff2 similarity index 100% rename from emanote/default/_emanote-static/fonts/i7dSIFZifjKcF5UAWdDRYERE_FeqHCSR.woff2 rename to packages/emanote/default/_emanote-static/fonts/i7dSIFZifjKcF5UAWdDRYERE_FeqHCSR.woff2 diff --git a/emanote/default/_emanote-static/fonts/update.sh b/packages/emanote/default/_emanote-static/fonts/update.sh similarity index 100% rename from emanote/default/_emanote-static/fonts/update.sh rename to packages/emanote/default/_emanote-static/fonts/update.sh diff --git a/emanote/default/_emanote-static/inverted-tree.css b/packages/emanote/default/_emanote-static/inverted-tree.css similarity index 100% rename from emanote/default/_emanote-static/inverted-tree.css rename to packages/emanote/default/_emanote-static/inverted-tree.css diff --git a/emanote/default/_emanote-static/js/code-copy.js b/packages/emanote/default/_emanote-static/js/code-copy.js similarity index 100% rename from emanote/default/_emanote-static/js/code-copy.js rename to packages/emanote/default/_emanote-static/js/code-copy.js diff --git a/emanote/default/_emanote-static/js/footnote-popup.js b/packages/emanote/default/_emanote-static/js/footnote-popup.js similarity index 100% rename from emanote/default/_emanote-static/js/footnote-popup.js rename to packages/emanote/default/_emanote-static/js/footnote-popup.js diff --git a/emanote/default/_emanote-static/js/main.js b/packages/emanote/default/_emanote-static/js/main.js similarity index 91% rename from emanote/default/_emanote-static/js/main.js rename to packages/emanote/default/_emanote-static/js/main.js index 4b5637dce..55730f21c 100644 --- a/emanote/default/_emanote-static/js/main.js +++ b/packages/emanote/default/_emanote-static/js/main.js @@ -3,7 +3,7 @@ // doesn't block first paint. // // Single source of truth: the importmap rendered alongside this script -// (built from `emanoteJsModuleNames` in `emanote/src/Emanote/View/JsBundle.hs`). +// (built from `emanoteJsModuleNames` in `packages/emanote/src/Emanote/View/JsBundle.hs`). // We read each bare specifier from it and dynamically import — so adding // a behavior is a one-line change on the Haskell side, no second list // to keep in sync. Browser dedupes module-loads by URL; behaviors that diff --git a/emanote/default/_emanote-static/js/morph.js b/packages/emanote/default/_emanote-static/js/morph.js similarity index 100% rename from emanote/default/_emanote-static/js/morph.js rename to packages/emanote/default/_emanote-static/js/morph.js diff --git a/emanote/default/_emanote-static/js/stork.js b/packages/emanote/default/_emanote-static/js/stork.js similarity index 100% rename from emanote/default/_emanote-static/js/stork.js rename to packages/emanote/default/_emanote-static/js/stork.js diff --git a/emanote/default/_emanote-static/js/theme-toggle.js b/packages/emanote/default/_emanote-static/js/theme-toggle.js similarity index 100% rename from emanote/default/_emanote-static/js/theme-toggle.js rename to packages/emanote/default/_emanote-static/js/theme-toggle.js diff --git a/emanote/default/_emanote-static/js/toc-spy.js b/packages/emanote/default/_emanote-static/js/toc-spy.js similarity index 100% rename from emanote/default/_emanote-static/js/toc-spy.js rename to packages/emanote/default/_emanote-static/js/toc-spy.js diff --git a/emanote/default/_emanote-static/skylighting.css b/packages/emanote/default/_emanote-static/skylighting.css similarity index 100% rename from emanote/default/_emanote-static/skylighting.css rename to packages/emanote/default/_emanote-static/skylighting.css diff --git a/emanote/default/_emanote-static/stork/README.txt b/packages/emanote/default/_emanote-static/stork/README.txt similarity index 100% rename from emanote/default/_emanote-static/stork/README.txt rename to packages/emanote/default/_emanote-static/stork/README.txt diff --git a/emanote/default/_emanote-static/stork/edible-dark.css b/packages/emanote/default/_emanote-static/stork/edible-dark.css similarity index 100% rename from emanote/default/_emanote-static/stork/edible-dark.css rename to packages/emanote/default/_emanote-static/stork/edible-dark.css diff --git a/emanote/default/_emanote-static/stork/edible.css b/packages/emanote/default/_emanote-static/stork/edible.css similarity index 100% rename from emanote/default/_emanote-static/stork/edible.css rename to packages/emanote/default/_emanote-static/stork/edible.css diff --git a/emanote/default/_emanote-static/stork/stork.js b/packages/emanote/default/_emanote-static/stork/stork.js similarity index 100% rename from emanote/default/_emanote-static/stork/stork.js rename to packages/emanote/default/_emanote-static/stork/stork.js diff --git a/emanote/default/_emanote-static/stork/stork.js.map b/packages/emanote/default/_emanote-static/stork/stork.js.map similarity index 100% rename from emanote/default/_emanote-static/stork/stork.js.map rename to packages/emanote/default/_emanote-static/stork/stork.js.map diff --git a/emanote/default/_emanote-static/stork/stork.wasm b/packages/emanote/default/_emanote-static/stork/stork.wasm similarity index 100% rename from emanote/default/_emanote-static/stork/stork.wasm rename to packages/emanote/default/_emanote-static/stork/stork.wasm diff --git a/emanote/default/favicon.svg b/packages/emanote/default/favicon.svg similarity index 100% rename from emanote/default/favicon.svg rename to packages/emanote/default/favicon.svg diff --git a/emanote/default/index.yaml b/packages/emanote/default/index.yaml similarity index 100% rename from emanote/default/index.yaml rename to packages/emanote/default/index.yaml diff --git a/emanote/default/templates/README b/packages/emanote/default/templates/README similarity index 100% rename from emanote/default/templates/README rename to packages/emanote/default/templates/README diff --git a/emanote/default/templates/base.tpl b/packages/emanote/default/templates/base.tpl similarity index 100% rename from emanote/default/templates/base.tpl rename to packages/emanote/default/templates/base.tpl diff --git a/emanote/default/templates/components/backlinks.tpl b/packages/emanote/default/templates/components/backlinks.tpl similarity index 100% rename from emanote/default/templates/components/backlinks.tpl rename to packages/emanote/default/templates/components/backlinks.tpl diff --git a/emanote/default/templates/components/body.tpl b/packages/emanote/default/templates/components/body.tpl similarity index 100% rename from emanote/default/templates/components/body.tpl rename to packages/emanote/default/templates/components/body.tpl diff --git a/emanote/default/templates/components/breadcrumbs.tpl b/packages/emanote/default/templates/components/breadcrumbs.tpl similarity index 100% rename from emanote/default/templates/components/breadcrumbs.tpl rename to packages/emanote/default/templates/components/breadcrumbs.tpl diff --git a/emanote/default/templates/components/checkbox-checked.tpl b/packages/emanote/default/templates/components/checkbox-checked.tpl similarity index 100% rename from emanote/default/templates/components/checkbox-checked.tpl rename to packages/emanote/default/templates/components/checkbox-checked.tpl diff --git a/emanote/default/templates/components/checkbox-unchecked.tpl b/packages/emanote/default/templates/components/checkbox-unchecked.tpl similarity index 100% rename from emanote/default/templates/components/checkbox-unchecked.tpl rename to packages/emanote/default/templates/components/checkbox-unchecked.tpl diff --git a/emanote/default/templates/components/context.tpl b/packages/emanote/default/templates/components/context.tpl similarity index 100% rename from emanote/default/templates/components/context.tpl rename to packages/emanote/default/templates/components/context.tpl diff --git a/emanote/default/templates/components/footer.tpl b/packages/emanote/default/templates/components/footer.tpl similarity index 100% rename from emanote/default/templates/components/footer.tpl rename to packages/emanote/default/templates/components/footer.tpl diff --git a/emanote/default/templates/components/footnote-popup.tpl b/packages/emanote/default/templates/components/footnote-popup.tpl similarity index 100% rename from emanote/default/templates/components/footnote-popup.tpl rename to packages/emanote/default/templates/components/footnote-popup.tpl diff --git a/emanote/default/templates/components/metadata.tpl b/packages/emanote/default/templates/components/metadata.tpl similarity index 100% rename from emanote/default/templates/components/metadata.tpl rename to packages/emanote/default/templates/components/metadata.tpl diff --git a/emanote/default/templates/components/note-body.tpl b/packages/emanote/default/templates/components/note-body.tpl similarity index 100% rename from emanote/default/templates/components/note-body.tpl rename to packages/emanote/default/templates/components/note-body.tpl diff --git a/emanote/default/templates/components/note-title.tpl b/packages/emanote/default/templates/components/note-title.tpl similarity index 100% rename from emanote/default/templates/components/note-title.tpl rename to packages/emanote/default/templates/components/note-title.tpl diff --git a/emanote/default/templates/components/note-uptree-recurse.tpl b/packages/emanote/default/templates/components/note-uptree-recurse.tpl similarity index 100% rename from emanote/default/templates/components/note-uptree-recurse.tpl rename to packages/emanote/default/templates/components/note-uptree-recurse.tpl diff --git a/emanote/default/templates/components/note-uptree.tpl b/packages/emanote/default/templates/components/note-uptree.tpl similarity index 100% rename from emanote/default/templates/components/note-uptree.tpl rename to packages/emanote/default/templates/components/note-uptree.tpl diff --git a/emanote/default/templates/components/pandoc.tpl b/packages/emanote/default/templates/components/pandoc.tpl similarity index 100% rename from emanote/default/templates/components/pandoc.tpl rename to packages/emanote/default/templates/components/pandoc.tpl diff --git a/emanote/default/templates/components/sidebar-tree.tpl b/packages/emanote/default/templates/components/sidebar-tree.tpl similarity index 100% rename from emanote/default/templates/components/sidebar-tree.tpl rename to packages/emanote/default/templates/components/sidebar-tree.tpl diff --git a/emanote/default/templates/components/sidebar.tpl b/packages/emanote/default/templates/components/sidebar.tpl similarity index 100% rename from emanote/default/templates/components/sidebar.tpl rename to packages/emanote/default/templates/components/sidebar.tpl diff --git a/emanote/default/templates/components/stork/stork-icon.tpl b/packages/emanote/default/templates/components/stork/stork-icon.tpl similarity index 100% rename from emanote/default/templates/components/stork/stork-icon.tpl rename to packages/emanote/default/templates/components/stork/stork-icon.tpl diff --git a/emanote/default/templates/components/stork/stork-search-head.tpl b/packages/emanote/default/templates/components/stork/stork-search-head.tpl similarity index 100% rename from emanote/default/templates/components/stork/stork-search-head.tpl rename to packages/emanote/default/templates/components/stork/stork-search-head.tpl diff --git a/emanote/default/templates/components/stork/stork-search.tpl b/packages/emanote/default/templates/components/stork/stork-search.tpl similarity index 100% rename from emanote/default/templates/components/stork/stork-search.tpl rename to packages/emanote/default/templates/components/stork/stork-search.tpl diff --git a/emanote/default/templates/components/timeline.tpl b/packages/emanote/default/templates/components/timeline.tpl similarity index 100% rename from emanote/default/templates/components/timeline.tpl rename to packages/emanote/default/templates/components/timeline.tpl diff --git a/emanote/default/templates/components/toc.tpl b/packages/emanote/default/templates/components/toc.tpl similarity index 100% rename from emanote/default/templates/components/toc.tpl rename to packages/emanote/default/templates/components/toc.tpl diff --git a/emanote/default/templates/error.tpl b/packages/emanote/default/templates/error.tpl similarity index 100% rename from emanote/default/templates/error.tpl rename to packages/emanote/default/templates/error.tpl diff --git a/emanote/default/templates/experimental.tpl b/packages/emanote/default/templates/experimental.tpl similarity index 100% rename from emanote/default/templates/experimental.tpl rename to packages/emanote/default/templates/experimental.tpl diff --git a/emanote/default/templates/filters/callout/_base.tpl b/packages/emanote/default/templates/filters/callout/_base.tpl similarity index 100% rename from emanote/default/templates/filters/callout/_base.tpl rename to packages/emanote/default/templates/filters/callout/_base.tpl diff --git a/emanote/default/templates/filters/callout/failure.tpl b/packages/emanote/default/templates/filters/callout/failure.tpl similarity index 100% rename from emanote/default/templates/filters/callout/failure.tpl rename to packages/emanote/default/templates/filters/callout/failure.tpl diff --git a/emanote/default/templates/filters/callout/info.tpl b/packages/emanote/default/templates/filters/callout/info.tpl similarity index 100% rename from emanote/default/templates/filters/callout/info.tpl rename to packages/emanote/default/templates/filters/callout/info.tpl diff --git a/emanote/default/templates/filters/callout/note.tpl b/packages/emanote/default/templates/filters/callout/note.tpl similarity index 100% rename from emanote/default/templates/filters/callout/note.tpl rename to packages/emanote/default/templates/filters/callout/note.tpl diff --git a/emanote/default/templates/filters/callout/quote.tpl b/packages/emanote/default/templates/filters/callout/quote.tpl similarity index 100% rename from emanote/default/templates/filters/callout/quote.tpl rename to packages/emanote/default/templates/filters/callout/quote.tpl diff --git a/emanote/default/templates/filters/callout/tip.tpl b/packages/emanote/default/templates/filters/callout/tip.tpl similarity index 100% rename from emanote/default/templates/filters/callout/tip.tpl rename to packages/emanote/default/templates/filters/callout/tip.tpl diff --git a/emanote/default/templates/filters/callout/warning.tpl b/packages/emanote/default/templates/filters/callout/warning.tpl similarity index 100% rename from emanote/default/templates/filters/callout/warning.tpl rename to packages/emanote/default/templates/filters/callout/warning.tpl diff --git a/emanote/default/templates/filters/embed-audio.tpl b/packages/emanote/default/templates/filters/embed-audio.tpl similarity index 100% rename from emanote/default/templates/filters/embed-audio.tpl rename to packages/emanote/default/templates/filters/embed-audio.tpl diff --git a/emanote/default/templates/filters/embed-code.tpl b/packages/emanote/default/templates/filters/embed-code.tpl similarity index 100% rename from emanote/default/templates/filters/embed-code.tpl rename to packages/emanote/default/templates/filters/embed-code.tpl diff --git a/emanote/default/templates/filters/embed-image.tpl b/packages/emanote/default/templates/filters/embed-image.tpl similarity index 100% rename from emanote/default/templates/filters/embed-image.tpl rename to packages/emanote/default/templates/filters/embed-image.tpl diff --git a/emanote/default/templates/filters/embed-note.tpl b/packages/emanote/default/templates/filters/embed-note.tpl similarity index 100% rename from emanote/default/templates/filters/embed-note.tpl rename to packages/emanote/default/templates/filters/embed-note.tpl diff --git a/emanote/default/templates/filters/embed-pdf.tpl b/packages/emanote/default/templates/filters/embed-pdf.tpl similarity index 100% rename from emanote/default/templates/filters/embed-pdf.tpl rename to packages/emanote/default/templates/filters/embed-pdf.tpl diff --git a/emanote/default/templates/filters/embed-video.tpl b/packages/emanote/default/templates/filters/embed-video.tpl similarity index 100% rename from emanote/default/templates/filters/embed-video.tpl rename to packages/emanote/default/templates/filters/embed-video.tpl diff --git a/emanote/default/templates/filters/query-default.tpl b/packages/emanote/default/templates/filters/query-default.tpl similarity index 100% rename from emanote/default/templates/filters/query-default.tpl rename to packages/emanote/default/templates/filters/query-default.tpl diff --git a/emanote/default/templates/filters/query-timeline.tpl b/packages/emanote/default/templates/filters/query-timeline.tpl similarity index 100% rename from emanote/default/templates/filters/query-timeline.tpl rename to packages/emanote/default/templates/filters/query-timeline.tpl diff --git a/emanote/default/templates/hooks/after-note.tpl b/packages/emanote/default/templates/hooks/after-note.tpl similarity index 100% rename from emanote/default/templates/hooks/after-note.tpl rename to packages/emanote/default/templates/hooks/after-note.tpl diff --git a/emanote/default/templates/hooks/before-note.tpl b/packages/emanote/default/templates/hooks/before-note.tpl similarity index 100% rename from emanote/default/templates/hooks/before-note.tpl rename to packages/emanote/default/templates/hooks/before-note.tpl diff --git a/emanote/default/templates/hooks/more-head.tpl b/packages/emanote/default/templates/hooks/more-head.tpl similarity index 100% rename from emanote/default/templates/hooks/more-head.tpl rename to packages/emanote/default/templates/hooks/more-head.tpl diff --git a/emanote/default/templates/hooks/note-end.tpl b/packages/emanote/default/templates/hooks/note-end.tpl similarity index 100% rename from emanote/default/templates/hooks/note-end.tpl rename to packages/emanote/default/templates/hooks/note-end.tpl diff --git a/emanote/default/templates/layouts/default.tpl b/packages/emanote/default/templates/layouts/default.tpl similarity index 100% rename from emanote/default/templates/layouts/default.tpl rename to packages/emanote/default/templates/layouts/default.tpl diff --git a/emanote/default/templates/special/base.tpl b/packages/emanote/default/templates/special/base.tpl similarity index 100% rename from emanote/default/templates/special/base.tpl rename to packages/emanote/default/templates/special/base.tpl diff --git a/emanote/default/templates/special/index.tpl b/packages/emanote/default/templates/special/index.tpl similarity index 100% rename from emanote/default/templates/special/index.tpl rename to packages/emanote/default/templates/special/index.tpl diff --git a/emanote/default/templates/special/tagindex.tpl b/packages/emanote/default/templates/special/tagindex.tpl similarity index 100% rename from emanote/default/templates/special/tagindex.tpl rename to packages/emanote/default/templates/special/tagindex.tpl diff --git a/emanote/default/templates/special/tasks.tpl b/packages/emanote/default/templates/special/tasks.tpl similarity index 100% rename from emanote/default/templates/special/tasks.tpl rename to packages/emanote/default/templates/special/tasks.tpl diff --git a/emanote/default/templates/styles.tpl b/packages/emanote/default/templates/styles.tpl similarity index 100% rename from emanote/default/templates/styles.tpl rename to packages/emanote/default/templates/styles.tpl diff --git a/emanote/emanote.cabal b/packages/emanote/emanote.cabal similarity index 80% rename from emanote/emanote.cabal rename to packages/emanote/emanote.cabal index 338ee117b..93471d29c 100644 --- a/emanote/emanote.cabal +++ b/packages/emanote/emanote.cabal @@ -111,6 +111,10 @@ common library-common , deriving-aeson , directory , ema >=0.10.1 + , emanote-model + , emanote-pandoc + , emanote-route + , emanote-source , feed , filepath , filepattern @@ -166,48 +170,19 @@ library Emanote Emanote.CLI Emanote.MCP - Emanote.Model - Emanote.Model.Calendar - Emanote.Model.Calendar.Parser - Emanote.Model.Graph - Emanote.Model.Link.Rel - Emanote.Model.Link.Resolve - Emanote.Model.Meta - Emanote.Model.Note - Emanote.Model.Note.Filter - Emanote.Model.Query - Emanote.Model.SData - Emanote.Model.StaticFile - Emanote.Model.Stork - Emanote.Model.Stork.Index - Emanote.Model.Task - Emanote.Model.Title - Emanote.Model.Toc - Emanote.Model.Type - Emanote.Pandoc.BuiltinFilters - Emanote.Pandoc.ExternalLink - Emanote.Pandoc.Link - Emanote.Pandoc.Markdown.Parser - Emanote.Pandoc.Markdown.Syntax.HashTag - Emanote.Pandoc.Markdown.Syntax.Highlight - Emanote.Pandoc.Renderer Emanote.Pandoc.Renderer.Callout Emanote.Pandoc.Renderer.Embed Emanote.Pandoc.Renderer.Query Emanote.Pandoc.Renderer.Url Emanote.Prelude - Emanote.Route - Emanote.Route.Ext - Emanote.Route.ModelRoute - Emanote.Route.R Emanote.Route.SiteRoute Emanote.Route.SiteRoute.Class - Emanote.Route.SiteRoute.Type + Emanote.Site.Model Emanote.Source.Dynamic - Emanote.Source.Loc Emanote.Source.Patch - Emanote.Source.Pattern Emanote.Static.Sources + Emanote.Stork + Emanote.Stork.Index Emanote.View Emanote.View.Common Emanote.View.Export @@ -231,12 +206,7 @@ executable emanote hs-source-dirs: exe main-is: Main.hs ghc-options: -threaded -rtsopts -with-rtsopts=-N - - if flag(ghcid) - hs-source-dirs: src - - else - build-depends: emanote + build-depends: emanote test-suite test import: library-common @@ -244,19 +214,10 @@ test-suite test hs-source-dirs: test main-is: Spec.hs build-depends: + , emanote , hspec , relude - if flag(ghcid) - hs-source-dirs: src - - else - build-depends: emanote - other-modules: - Emanote.Model.Link.RelSpec - Emanote.Model.QuerySpec - Emanote.Model.TocSpec - Emanote.Pandoc.ExternalLinkSpec - Emanote.Pandoc.Renderer.CalloutSpec - Emanote.Route.RSpec - Emanote.View.FeedSpec + other-modules: + Emanote.Pandoc.Renderer.CalloutSpec + Emanote.View.FeedSpec diff --git a/emanote/exe/Main.hs b/packages/emanote/exe/Main.hs similarity index 100% rename from emanote/exe/Main.hs rename to packages/emanote/exe/Main.hs diff --git a/emanote/src/Emanote.hs b/packages/emanote/src/Emanote.hs similarity index 96% rename from emanote/src/Emanote.hs rename to packages/emanote/src/Emanote.hs index ae0d16d78..55f41999a 100644 --- a/emanote/src/Emanote.hs +++ b/packages/emanote/src/Emanote.hs @@ -25,8 +25,6 @@ import Emanote.CLI qualified as CLI import Emanote.MCP qualified as MCP import Emanote.Model.Graph qualified as G import Emanote.Model.Link.Rel (ResolvedRelTarget (..)) -import Emanote.Model.Type (modelCompileTailwind) -import Emanote.Model.Type qualified as Model import Emanote.Pandoc.Renderer import Emanote.Pandoc.Renderer.Callout qualified as PF import Emanote.Pandoc.Renderer.Embed qualified as PF @@ -36,6 +34,7 @@ import Emanote.Prelude (log, logE, logW) import Emanote.Route.ModelRoute (LMLRoute, lmlRouteCase) import Emanote.Route.SiteRoute.Class (emanoteGeneratableRoutes, emanoteRouteEncoder) import Emanote.Route.SiteRoute.Type (SiteRoute) +import Emanote.Site.Model qualified as Model import Emanote.Source.Dynamic (EmanoteConfig (..), emanoteSiteInput) import Emanote.View.Export qualified as Export import Emanote.View.Export.JSON qualified as ExportJSON @@ -63,7 +62,7 @@ modelUpdateCachedFields :: Model.ModelEma -> Model.ModelEma modelUpdateCachedFields model = model & Model.modelFolgezettelTree - .~ G.folgezettelTreesFrom (unModelEma model) (Model.modelIndexRoute model) + .~ G.folgezettelTreesFrom (unModelEma model ^. Model.modelCore) (Model.modelIndexRoute model) defaultEmanoteConfig :: CLI.Cli -> EmanoteConfig defaultEmanoteConfig cli = @@ -107,7 +106,7 @@ run cfg@EmanoteConfig {..} = do postRun :: EmanoteConfig -> (Model.ModelEma, (FilePath, [FilePath])) -> IO () postRun EmanoteConfig {..} (unModelEma -> model0, (outPath, genPaths)) = do - when (model0 ^. modelCompileTailwind) + when (model0 ^. Model.modelCompileTailwind) $ compileTailwindCss (outPath generatedCssFile) genPaths checkBrokenLinks _emanoteConfigCli $ ExportJSON.modelRels model0 checkBadMarkdownFiles $ Model.modelNoteErrors model0 diff --git a/emanote/src/Emanote/CLI.hs b/packages/emanote/src/Emanote/CLI.hs similarity index 100% rename from emanote/src/Emanote/CLI.hs rename to packages/emanote/src/Emanote/CLI.hs diff --git a/emanote/src/Emanote/MCP.hs b/packages/emanote/src/Emanote/MCP.hs similarity index 100% rename from emanote/src/Emanote/MCP.hs rename to packages/emanote/src/Emanote/MCP.hs diff --git a/emanote/src/Emanote/Pandoc/Renderer/Callout.hs b/packages/emanote/src/Emanote/Pandoc/Renderer/Callout.hs similarity index 99% rename from emanote/src/Emanote/Pandoc/Renderer/Callout.hs rename to packages/emanote/src/Emanote/Pandoc/Renderer/Callout.hs index 8767cb773..29e4643e9 100644 --- a/emanote/src/Emanote/Pandoc/Renderer/Callout.hs +++ b/packages/emanote/src/Emanote/Pandoc/Renderer/Callout.hs @@ -19,10 +19,10 @@ module Emanote.Pandoc.Renderer.Callout ( import Data.Default (Default (def)) import Data.Map.Syntax ((##)) import Data.Text qualified as T -import Emanote.Model (Model) import Emanote.Model.Title qualified as Tit import Emanote.Pandoc.Renderer (PandocBlockRenderer) import Emanote.Route (LMLRoute) +import Emanote.Site.Model (Model) import Heist.Extra qualified as HE import Heist.Extra.Splices.Pandoc qualified as HP import Heist.Interpreted qualified as HI diff --git a/emanote/src/Emanote/Pandoc/Renderer/Embed.hs b/packages/emanote/src/Emanote/Pandoc/Renderer/Embed.hs similarity index 93% rename from emanote/src/Emanote/Pandoc/Renderer/Embed.hs rename to packages/emanote/src/Emanote/Pandoc/Renderer/Embed.hs index 0e2903ddf..2d79de91d 100644 --- a/emanote/src/Emanote/Pandoc/Renderer/Embed.hs +++ b/packages/emanote/src/Emanote/Pandoc/Renderer/Embed.hs @@ -2,8 +2,6 @@ module Emanote.Pandoc.Renderer.Embed where import Commonmark.Extensions.WikiLink qualified as WL import Data.Map.Syntax ((##)) -import Emanote.Model (Model) -import Emanote.Model qualified as M import Emanote.Model.Link.Rel qualified as Rel import Emanote.Model.Link.Resolve qualified as Resolve import Emanote.Model.Note qualified as MN @@ -15,9 +13,10 @@ import Emanote.Pandoc.Link qualified as Link import Emanote.Pandoc.Renderer (PandocBlockRenderer, PandocInlineRenderer) import Emanote.Pandoc.Renderer.Url qualified as RendererUrl import Emanote.Route.ModelRoute qualified as R -import Emanote.Route.R qualified as R import Emanote.Route.SiteRoute qualified as SF import Emanote.Route.SiteRoute qualified as SR +import Emanote.Site.Model (Model) +import Emanote.Site.Model qualified as M import Heist qualified as H import Heist.Extra qualified as HE import Heist.Extra.Splices.Pandoc (pandocSplice) @@ -36,7 +35,7 @@ embedBlockWikiLinkResolvingSplice model _nf ctx noteRoute node = do -- TODO: Use anchor to embed a section? (Rel.URTWikiLink (WL.WikiLinkEmbed, wl), _mAnchor) <- Rel.parseUnresolvedRelTarget parentR (otherAttrs <> one ("title", tit)) url - let rRel = Resolve.resolveWikiLinkMustExist model noteRoute wl + let rRel = Resolve.resolveWikiLinkMustExist (model ^. M.modelCore) noteRoute wl RendererUrl.renderSomeInlineRefWith Resolve.resourceSiteRoute (is, (url, tit)) rRel model ctx inl $ \case Left (R.LMLView_Html, r) -> embedResourceRoute model ctx r Right sf @@ -52,7 +51,7 @@ embedBlockRegularLinkResolvingSplice model _nf ctx noteRoute node = do let parentR = M.modelResolveLinkBase model noteRoute (Rel.URTResource candidates, _mAnchor) <- Rel.parseUnresolvedRelTarget parentR (otherAttrs <> one ("title", tit)) url - let rRel = Resolve.resolveModelRouteCandidates model candidates + let rRel = Resolve.resolveModelRouteCandidates (model ^. M.modelCore) candidates RendererUrl.renderSomeInlineRefWith Resolve.resourceSiteRoute (is, (url, tit)) rRel model ctx inl $ either (const Nothing) (embedStaticFileRoute model $ WL.plainify is) @@ -62,7 +61,7 @@ embedInlineWikiLinkResolvingSplice model _nf ctx noteRoute inl = do guard $ inlRef == Link.InlineLink let parentR = M.modelResolveLinkBase model noteRoute (Rel.URTWikiLink (WL.WikiLinkEmbed, wl), _mAnchor) <- Rel.parseUnresolvedRelTarget parentR (otherAttrs <> one ("title", tit)) url - let rRel = Resolve.resolveWikiLinkMustExist model noteRoute wl + let rRel = Resolve.resolveWikiLinkMustExist (model ^. M.modelCore) noteRoute wl RendererUrl.renderSomeInlineRefWith Resolve.resourceSiteRoute (is, (url, tit)) rRel model ctx inl $ either (const Nothing) (embedStaticFileRoute model $ show wl) diff --git a/emanote/src/Emanote/Pandoc/Renderer/Query.hs b/packages/emanote/src/Emanote/Pandoc/Renderer/Query.hs similarity index 92% rename from emanote/src/Emanote/Pandoc/Renderer/Query.hs rename to packages/emanote/src/Emanote/Pandoc/Renderer/Query.hs index 8b6470172..825859775 100644 --- a/emanote/src/Emanote/Pandoc/Renderer/Query.hs +++ b/packages/emanote/src/Emanote/Pandoc/Renderer/Query.hs @@ -6,13 +6,14 @@ module Emanote.Pandoc.Renderer.Query ( import Data.List qualified as List import Data.Map.Syntax ((##)) import Data.Text qualified as T -import Emanote.Model (Model) import Emanote.Model.Note qualified as MN import Emanote.Model.Query qualified as Q import Emanote.Model.Title qualified as Tit import Emanote.Pandoc.Renderer (PandocBlockRenderer) import Emanote.Route (LMLRoute, LMLView (LMLView_Html)) import Emanote.Route.SiteRoute qualified as SR +import Emanote.Site.Model (Model) +import Emanote.Site.Model qualified as M import Heist qualified as H import Heist.Extra qualified as HE import Heist.Extra.Splices.Pandoc (RenderCtx) @@ -38,7 +39,7 @@ queryResolvingSplice model _nr ctx noteRoute blk = do HI.textSplice (show q) "result" ## (HI.runChildrenWith . noteSpliceMap ($ ctx) model) - `foldMapM` Q.runQuery noteRoute model q + `foldMapM` Q.runQuery noteRoute (model ^. M.modelCore) q -- TODO: Reuse this elsewhere noteSpliceMap :: diff --git a/emanote/src/Emanote/Pandoc/Renderer/Url.hs b/packages/emanote/src/Emanote/Pandoc/Renderer/Url.hs similarity index 96% rename from emanote/src/Emanote/Pandoc/Renderer/Url.hs rename to packages/emanote/src/Emanote/Pandoc/Renderer/Url.hs index cc7419d37..01a0a4632 100644 --- a/emanote/src/Emanote/Pandoc/Renderer/Url.hs +++ b/packages/emanote/src/Emanote/Pandoc/Renderer/Url.hs @@ -6,8 +6,6 @@ module Emanote.Pandoc.Renderer.Url ( import Commonmark.Extensions.WikiLink qualified as WL import Data.Text qualified as T -import Emanote.Model (Model) -import Emanote.Model qualified as M import Emanote.Model.Link.Rel qualified as Rel import Emanote.Model.Link.Resolve qualified as Resolve import Emanote.Model.Note qualified as MN @@ -16,11 +14,14 @@ import Emanote.Pandoc.Link qualified as Link import Emanote.Pandoc.Renderer (PandocInlineRenderer) import Emanote.Route qualified as R import Emanote.Route.SiteRoute qualified as SR +import Emanote.Site.Model (Model) +import Emanote.Site.Model qualified as M import Heist.Extra.Splices.Pandoc qualified as HP import Heist.Extra.Splices.Pandoc qualified as Splices import Heist.Extra.Splices.Pandoc.Ctx (ctxSansCustomSplicing) import Heist.Interpreted qualified as HI import Optics.Core (review) +import Optics.Operators ((^.)) import Relude import Text.Pandoc.Definition qualified as B import Text.Pandoc.Walk qualified as W @@ -31,7 +32,7 @@ urlResolvingSplice model _nf (ctxSansCustomSplicing -> ctx) noteRoute inl = do (inlRef, attr@(id', cls, otherAttrs), is, (url, tit)) <- Link.parseInlineRef inl let parentR = M.modelResolveLinkBase model noteRoute (uRel, mAnchor) <- Rel.parseUnresolvedRelTarget parentR (otherAttrs <> one ("title", tit)) url - let rRel = Resolve.resolveUnresolvedRelTarget model noteRoute uRel + let rRel = Resolve.resolveUnresolvedRelTarget (model ^. M.modelCore) noteRoute uRel renderSomeInlineRefWith id (is, (url, tit)) rRel model ctx inl $ \sr -> case inlRef of Link.InlineLink -> do @@ -167,6 +168,6 @@ siteRouteDefaultInnerText model url = \case SR.ResourceRoute_LML R.LMLView_Html lmlR -> Tit.toInlines . MN._noteTitle <$> M.modelLookupNoteByRoute' lmlR model SR.ResourceRoute_LML R.LMLView_Atom _ -> Nothing - SR.ResourceRoute_StaticFile _ _ -> + SR.ResourceRoute_StaticFile _ -> -- Just append a file: prefix, to existing wiki-link. pure $ B.Str "File:" : [B.Str url] diff --git a/emanote/src/Emanote/Prelude.hs b/packages/emanote/src/Emanote/Prelude.hs similarity index 100% rename from emanote/src/Emanote/Prelude.hs rename to packages/emanote/src/Emanote/Prelude.hs diff --git a/emanote/src/Emanote/Route/SiteRoute.hs b/packages/emanote/src/Emanote/Route/SiteRoute.hs similarity index 100% rename from emanote/src/Emanote/Route/SiteRoute.hs rename to packages/emanote/src/Emanote/Route/SiteRoute.hs diff --git a/emanote/src/Emanote/Route/SiteRoute/Class.hs b/packages/emanote/src/Emanote/Route/SiteRoute/Class.hs similarity index 90% rename from emanote/src/Emanote/Route/SiteRoute/Class.hs rename to packages/emanote/src/Emanote/Route/SiteRoute/Class.hs index e41b15c52..963f2724b 100644 --- a/emanote/src/Emanote/Route/SiteRoute/Class.hs +++ b/packages/emanote/src/Emanote/Route/SiteRoute/Class.hs @@ -22,16 +22,16 @@ import Data.List.NonEmpty qualified as NE import Data.Set qualified as Set import Data.Time.Format (defaultTimeLocale, formatTime) import Ema (UrlStrategy (..), routeUrlWith) -import Emanote.Model qualified as M import Emanote.Model.Link.Rel qualified as Rel -import Emanote.Model.Meta qualified as Model +import Emanote.Model.Link.Resolve qualified as Resolve import Emanote.Model.Note qualified as N import Emanote.Model.StaticFile qualified as SF -import Emanote.Model.Type (Model, ModelEma, ModelT) import Emanote.Pandoc.Markdown.Syntax.HashTag qualified as HT import Emanote.Route qualified as R import Emanote.Route.ModelRoute (LMLRoute, StaticFileRoute) import Emanote.Route.SiteRoute.Type +import Emanote.Site.Model (Model, ModelEma) +import Emanote.Site.Model qualified as M import Emanote.View.LiveServerFiles qualified as LiveServerFile import Optics.Core (Prism', prism') import Optics.Operators ((^.)) @@ -117,7 +117,7 @@ encodeResourceRoute model = \case R.LMLView_Atom -> R.encodeRoute <$> N.noteXmlRoute note R.LMLView_Html -> pure $ R.encodeRoute $ N.noteHtmlRoute note ) - ResourceRoute_StaticFile r _fpAbs -> + ResourceRoute_StaticFile r -> R.encodeRoute r -- | Decode a route that is known to refer to a resource in the model @@ -147,11 +147,12 @@ decodeGeneratedRoute model fp = SiteRoute_AmbiguousR ("/" <> fp) (N._noteRoute <$> ns) noteFeedSiteRoute :: N.Note -> SiteRoute -noteFeedSiteRoute = SiteRoute_ResourceRoute . ResourceRoute_LML R.LMLView_Atom . N._noteRoute +noteFeedSiteRoute = + Resolve.resourceSiteRoute . Left . (R.LMLView_Atom,) noteFileSiteRoute :: (R.LMLView, N.Note) -> SiteRoute noteFileSiteRoute = - lmlSiteRoute . fmap N._noteRoute + Resolve.resourceSiteRoute . Left noteFileSiteRoute' :: N.Note -> SiteRoute noteFileSiteRoute' = @@ -166,10 +167,7 @@ lmlResourceRoute = ResourceRoute_LML staticFileSiteRoute :: SF.StaticFile -> SiteRoute staticFileSiteRoute = - (SiteRoute_ResourceRoute . staticResourceRoute) . (SF._staticFileRoute &&& SF._staticFilePath) - where - staticResourceRoute :: (StaticFileRoute, FilePath) -> ResourceRoute - staticResourceRoute = uncurry ResourceRoute_StaticFile + Resolve.resourceSiteRoute . Right {- | Generate the static URL for a site route, including special handling for html-proofer compatibility. @@ -194,7 +192,7 @@ siteRouteUrlStatic model sr = getBaseUrl :: Model -> Text getBaseUrl m = - Model.lookupRouteMeta "/" ("template" :| ["baseUrl"]) (M.modelIndexRoute m) m + M.lookupRouteMeta "/" ("template" :| ["baseUrl"]) (M.modelIndexRoute m) m -- | Like siteRouteUrlStatic but live-server friendly siteRouteUrl :: (HasCallStack) => Model -> SiteRoute -> Text @@ -218,7 +216,7 @@ siteRouteUrl model sr = Nothing SiteRoute_ResourceRoute rr -> case rr of - ResourceRoute_StaticFile sfR _fp -> + ResourceRoute_StaticFile sfR -> Just sfR ResourceRoute_LML _ _ -> Nothing @@ -230,9 +228,9 @@ urlStrategySuffix model = Ema.UrlDirect -> ".html" Ema.UrlPretty -> "" -urlStrategy :: ModelT f -> UrlStrategy +urlStrategy :: M.ModelT f -> UrlStrategy urlStrategy model = - Model.lookupRouteMeta Ema.UrlDirect ("template" :| one "urlStrategy") (M.modelIndexRoute model) model + M.lookupRouteMeta Ema.UrlDirect ("template" :| one "urlStrategy") (M.modelIndexRoute model) model indexRoute :: SiteRoute indexRoute = diff --git a/packages/emanote/src/Emanote/Site/Model.hs b/packages/emanote/src/Emanote/Site/Model.hs new file mode 100644 index 000000000..fc1c4468b --- /dev/null +++ b/packages/emanote/src/Emanote/Site/Model.hs @@ -0,0 +1,235 @@ +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TemplateHaskell #-} + +module Emanote.Site.Model where + +import Commonmark.Extensions.WikiLink qualified as WL +import Data.Aeson (FromJSON) +import Data.Aeson qualified as Aeson +import Data.Default (Default (def)) +import Data.Time (UTCTime) +import Data.Tree (Forest) +import Data.UUID (UUID) +import Emanote.Model.Link.Rel (IxRel) +import Emanote.Model.Link.Rel qualified as Rel +import Emanote.Model.Meta qualified as Meta +import Emanote.Model.Note (IxNote, Note) +import Emanote.Model.SData (IxSData, SData) +import Emanote.Model.StaticFile (IxStaticFile, StaticFile, StaticFileInfo) +import Emanote.Model.Task (IxTask) +import Emanote.Model.Title qualified as Tit +import Emanote.Model.Type qualified as Core +import Emanote.Pandoc.Markdown.Syntax.HashTag qualified as HT +import Emanote.Pandoc.Renderer (EmanotePandocRenderers) +import Emanote.Route (FileType (AnyExt), LMLRoute, R) +import Emanote.Route qualified as R +import Emanote.Route.SiteRoute.Type (SiteRoute) +import Emanote.Source.Loc (Loc) +import Emanote.Stork.Index qualified as Stork +import Heist.Extra.TemplateState (TemplateState) +import Optics.Core (Lens', Prism', (%)) +import Optics.Operators ((%~), (^.)) +import Optics.TH (makeLenses) +import Relude + +type Status = Core.Status + +pattern Status_Loading :: Status +pattern Status_Loading = Core.Status_Loading + +pattern Status_Ready :: Status +pattern Status_Ready = Core.Status_Ready + +{-# COMPLETE Status_Loading, Status_Ready #-} + +data ModelT encF = Model + { _modelCore :: Core.Model + , _modelIsLiveServer :: Bool + , _modelRoutePrism :: encF (Prism' FilePath SiteRoute) + , _modelPandocRenderers :: EmanotePandocRenderers Model LMLRoute + -- ^ Dictates how exactly to render `Pandoc` to Heist nodes. + , _modelCompileTailwind :: Bool + , _modelInstanceID :: UUID + -- ^ An unique ID for this process's model. ID changes across processes. + , _modelHeistTemplate :: TemplateState + , _modelStorkIndex :: Stork.IndexVar + } + deriving stock (Generic) + +type Model = ModelT Identity + +{- | A bare version of `Model` that is managed by the Ema app. + + The only difference is that this one has no `RouteEncoder`. +-} +type ModelEma = ModelT (Const ()) + +deriving stock instance Generic ModelEma + +deriving stock instance Generic Model + +makeLenses ''ModelT + +modelStatus :: Lens' (ModelT f) Status +modelStatus = modelCore % Core.modelStatus + +modelLayers :: Lens' (ModelT f) (Set Loc) +modelLayers = modelCore % Core.modelLayers + +modelNotes :: Lens' (ModelT f) IxNote +modelNotes = modelCore % Core.modelNotes + +modelRels :: Lens' (ModelT f) IxRel +modelRels = modelCore % Core.modelRels + +modelSData :: Lens' (ModelT f) IxSData +modelSData = modelCore % Core.modelSData + +modelStaticFiles :: Lens' (ModelT f) IxStaticFile +modelStaticFiles = modelCore % Core.modelStaticFiles + +modelTasks :: Lens' (ModelT f) IxTask +modelTasks = modelCore % Core.modelTasks + +modelFolgezettelTree :: Lens' (ModelT f) (Forest R.LMLRoute) +modelFolgezettelTree = modelCore % Core.modelFolgezettelTree + +withoutRoutePrism :: Model -> (Prism' FilePath SiteRoute, ModelEma) +withoutRoutePrism model@Model {..} = + let _modelRoutePrism = Const () + in (runIdentity $ model ^. modelRoutePrism, Model {..}) + +withRoutePrism :: Prism' FilePath SiteRoute -> ModelEma -> Model +withRoutePrism enc Model {..} = + let _modelRoutePrism = Identity enc + in Model {..} + +emptyModel :: Set Loc -> Bool -> EmanotePandocRenderers Model LMLRoute -> Bool -> UUID -> Stork.IndexVar -> ModelEma +emptyModel layers isLiveServer ren ctw instanceId storkVar = + Model + { _modelCore = Core.emptyModel layers + , _modelIsLiveServer = isLiveServer + , _modelRoutePrism = Const () + , _modelPandocRenderers = ren + , _modelCompileTailwind = ctw + , _modelInstanceID = instanceId + , _modelHeistTemplate = def + , _modelStorkIndex = storkVar + } + +modelReadyForView :: ModelT f -> ModelT f +modelReadyForView = + modelCore %~ Core.modelReadyForView + +-- | Are we running in live server, or statically generated website? +inLiveServer :: ModelT f -> Bool +inLiveServer = _modelIsLiveServer + +modelInsertNote :: Note -> ModelT f -> ModelT f +modelInsertNote note = + modelCore %~ Core.modelInsertNote note + +modelDeleteNote :: LMLRoute -> ModelT f -> ModelT f +modelDeleteNote r = + modelCore %~ Core.modelDeleteNote r + +modelLookupStaticFile :: FilePath -> ModelT f -> Maybe StaticFile +modelLookupStaticFile fp = + Core.modelLookupStaticFile fp . _modelCore + +modelInsertStaticFile :: UTCTime -> R 'AnyExt -> FilePath -> Maybe StaticFileInfo -> ModelT f -> ModelT f +modelInsertStaticFile t r fp mInfo = + modelCore %~ Core.modelInsertStaticFile t r fp mInfo + +modelDeleteStaticFile :: R 'AnyExt -> ModelT f -> ModelT f +modelDeleteStaticFile r = + modelCore %~ Core.modelDeleteStaticFile r + +modelInsertData :: SData -> ModelT f -> ModelT f +modelInsertData v = + modelCore %~ Core.modelInsertData v + +modelDeleteData :: R 'R.Yaml -> ModelT f -> ModelT f +modelDeleteData k = + modelCore %~ Core.modelDeleteData k + +modelLookupSData :: R 'R.Yaml -> ModelT f -> Maybe SData +modelLookupSData r = + Core.modelLookupSData r . _modelCore + +modelLookupNoteByRoute :: (R.LMLView, LMLRoute) -> ModelT f -> Maybe (R.LMLView, Note) +modelLookupNoteByRoute route = + Core.modelLookupNoteByRoute route . _modelCore + +modelLookupNoteByRoute' :: LMLRoute -> ModelT f -> Maybe Note +modelLookupNoteByRoute' r = + Core.modelLookupNoteByRoute' r . _modelCore + +modelResolveLinkBase :: ModelT f -> LMLRoute -> Maybe (R 'R.Folder) +modelResolveLinkBase model = + Core.modelResolveLinkBase (_modelCore model) + +modelLookupNoteByHtmlRoute :: R 'R.Html -> ModelT f -> Rel.ResolvedRelTarget Note +modelLookupNoteByHtmlRoute r = + Core.modelLookupNoteByHtmlRoute r . _modelCore + +modelLookupFeedNoteByHtmlRoute :: R 'R.Xml -> ModelT f -> Maybe Note +modelLookupFeedNoteByHtmlRoute r = + Core.modelLookupFeedNoteByHtmlRoute r . _modelCore + +modelLookupTitle :: LMLRoute -> ModelT f -> Tit.Title +modelLookupTitle r = + Core.modelLookupTitle r . _modelCore + +modelWikiLinkTargets :: WL.WikiLink -> ModelT f -> [Either (R.LMLView, Note) StaticFile] +modelWikiLinkTargets wl = + Core.modelWikiLinkTargets wl . _modelCore + +modelLookupStaticFileByRoute :: R 'AnyExt -> ModelT f -> Maybe StaticFile +modelLookupStaticFileByRoute r = + Core.modelLookupStaticFileByRoute r . _modelCore + +modelTags :: ModelT f -> [(HT.Tag, [Note])] +modelTags = + Core.modelTags . _modelCore + +modelNoteRels :: ModelT f -> [Rel.Rel] +modelNoteRels = + Core.modelNoteRels . _modelCore + +modelNoteMetas :: ModelT f -> Map LMLRoute (Tit.Title, LMLRoute, Aeson.Value) +modelNoteMetas = + Core.modelNoteMetas . _modelCore + +modelNoteErrors :: ModelT f -> Map LMLRoute [Text] +modelNoteErrors = + Core.modelNoteErrors . _modelCore + +modelIndexRoute :: ModelT f -> LMLRoute +modelIndexRoute = + Core.modelIndexRoute . _modelCore + +resolveLmlRoute :: forall lmlType f. ModelT f -> R ('R.LMLType lmlType) -> LMLRoute +resolveLmlRoute model = + Core.resolveLmlRoute (_modelCore model) + +parentLmlRoute :: ModelT f -> R.LMLRoute -> Maybe R.LMLRoute +parentLmlRoute model = + Core.parentLmlRoute (_modelCore model) + +lookupRouteMeta :: (FromJSON a) => a -> NonEmpty Text -> R.LMLRoute -> ModelT f -> a +lookupRouteMeta x k r = + Meta.lookupRouteMeta x k r . _modelCore + +getEffectiveRouteMeta :: R.LMLRoute -> ModelT f -> Aeson.Value +getEffectiveRouteMeta r = + Meta.getEffectiveRouteMeta r . _modelCore + +getEffectiveRouteMetaWith :: Aeson.Value -> R.LMLRoute -> ModelT f -> Aeson.Value +getEffectiveRouteMetaWith frontmatter r = + Meta.getEffectiveRouteMetaWith frontmatter r . _modelCore + +cascadeYamlErrors :: ModelT f -> R.LMLRoute -> [Text] +cascadeYamlErrors model = + Meta.cascadeYamlErrors (_modelCore model) diff --git a/emanote/src/Emanote/Source/Dynamic.hs b/packages/emanote/src/Emanote/Source/Dynamic.hs similarity index 91% rename from emanote/src/Emanote/Source/Dynamic.hs rename to packages/emanote/src/Emanote/Source/Dynamic.hs index 53c2ffac4..081bdff52 100644 --- a/emanote/src/Emanote/Source/Dynamic.hs +++ b/packages/emanote/src/Emanote/Source/Dynamic.hs @@ -18,15 +18,15 @@ import Ema (Dynamic (..)) import Ema.CLI qualified import Emanote.CLI qualified as CLI import Emanote.Model.Note (Note) -import Emanote.Model.Stork.Index qualified as Stork -import Emanote.Model.Type qualified as Model import Emanote.Pandoc.Renderer (EmanotePandocRenderers) import Emanote.Prelude (chainM) import Emanote.Route (LMLRoute) +import Emanote.Site.Model qualified as Model import Emanote.Source.Loc (Loc) import Emanote.Source.Loc qualified as Loc import Emanote.Source.Patch qualified as Patch import Emanote.Source.Pattern qualified as Pattern +import Emanote.Stork.Index qualified as Stork import Optics.TH (makeLenses) import Paths_emanote qualified import Relude @@ -56,7 +56,14 @@ emanoteSiteInput cliAct EmanoteConfig {..} = do instanceId <- liftIO UUID.nextRandom storkIndex <- Stork.newIndex let layers = Loc.userLayers ((CLI.path &&& CLI.mountPoint) <$> CLI.layers _emanoteConfigCli) <> one defaultLayer - initialModel = Model.emptyModel layers cliAct _emanoteConfigPandocRenderers _emanoteCompileTailwind instanceId storkIndex + initialModel = + Model.emptyModel + layers + (Ema.CLI.isLiveServer cliAct) + _emanoteConfigPandocRenderers + _emanoteCompileTailwind + instanceId + storkIndex scriptingEngine <- getEngine Dynamic <$> UM.unionMount diff --git a/emanote/src/Emanote/Source/Patch.hs b/packages/emanote/src/Emanote/Source/Patch.hs similarity index 98% rename from emanote/src/Emanote/Source/Patch.hs rename to packages/emanote/src/Emanote/Source/Patch.hs index 67c54ca85..69acc1bef 100644 --- a/emanote/src/Emanote/Source/Patch.hs +++ b/packages/emanote/src/Emanote/Source/Patch.hs @@ -9,20 +9,20 @@ import Control.Monad.Logger (LoggingT (runLoggingT), MonadLogger, MonadLoggerIO import Data.ByteString qualified as BS import Data.List.NonEmpty qualified as NEL import Data.Time (defaultTimeLocale, formatTime, getCurrentTime) -import Emanote.Model qualified as M import Emanote.Model.Note qualified as N import Emanote.Model.SData qualified as SD import Emanote.Model.StaticFile (readStaticFileInfo) -import Emanote.Model.Stork.Index qualified as Stork -import Emanote.Model.Type (ModelEma) import Emanote.Prelude ( log, logD, logE, ) import Emanote.Route qualified as R +import Emanote.Site.Model (ModelEma) +import Emanote.Site.Model qualified as M import Emanote.Source.Loc (Loc, locResolve, userLayersToSearch) import Emanote.Source.Pattern (filePatterns, ignorePatterns) +import Emanote.Stork.Index qualified as Stork import Heist.Extra.TemplateState qualified as T import Optics.Operators ((%~), (^.)) import Relude diff --git a/emanote/src/Emanote/Static/Sources.hs b/packages/emanote/src/Emanote/Static/Sources.hs similarity index 100% rename from emanote/src/Emanote/Static/Sources.hs rename to packages/emanote/src/Emanote/Static/Sources.hs diff --git a/emanote/src/Emanote/Model/Stork.hs b/packages/emanote/src/Emanote/Stork.hs similarity index 72% rename from emanote/src/Emanote/Model/Stork.hs rename to packages/emanote/src/Emanote/Stork.hs index 06b7c5e6f..4d2aaea00 100644 --- a/emanote/src/Emanote/Model/Stork.hs +++ b/packages/emanote/src/Emanote/Stork.hs @@ -1,4 +1,4 @@ -module Emanote.Model.Stork ( +module Emanote.Stork ( renderStorkIndex, ) where @@ -7,31 +7,31 @@ import Data.Default (Default (def)) import Data.IxSet.Typed qualified as Ix import Emanote.Model.Meta (lookupRouteMeta) import Emanote.Model.Note qualified as N -import Emanote.Model.Stork.Index ( +import Emanote.Model.Title qualified as Tit +import Emanote.Model.Type (Model) +import Emanote.Model.Type qualified as M +import Emanote.Route qualified as R +import Emanote.Source.Loc qualified as Loc +import Emanote.Stork.Index ( Config (Config), File (File), FileType (FileType_Markdown, FileType_PlainText), Handling, + IndexVar, Input (Input), readOrBuildStorkIndex, ) -import Emanote.Model.Title qualified as Tit -import Emanote.Model.Type (Model) -import Emanote.Model.Type qualified as M -import Emanote.Route qualified as R -import Emanote.Route.SiteRoute qualified as SR -import Emanote.Source.Loc qualified as Loc import Optics.Core ((^.)) import Relude import System.FilePath (()) -renderStorkIndex :: (MonadIO m, MonadLoggerIO m) => Model -> m LByteString -renderStorkIndex model = do - let config = Config $ Input (storkFiles model) (frontmatterHandling model) - readOrBuildStorkIndex (model ^. M.modelStorkIndex) config +renderStorkIndex :: (MonadIO m, MonadLoggerIO m) => IndexVar -> (R.LMLRoute -> Text) -> Model -> m LByteString +renderStorkIndex storkIndex renderUrl model = do + let config = Config $ Input (storkFiles renderUrl model) (frontmatterHandling model) + readOrBuildStorkIndex storkIndex config -storkFiles :: Model -> [File] -storkFiles model = +storkFiles :: (R.LMLRoute -> Text) -> Model -> [File] +storkFiles renderUrl model = flip mapMaybe (Ix.toList (model ^. M.modelNotes)) $ \note -> do baseDir <- fst . Loc.locPath . fst <$> note ^. N.noteSource let fp = ((baseDir ) $ R.withLmlRoute R.encodeRoute $ note ^. N.noteRoute) @@ -41,7 +41,7 @@ storkFiles model = pure $ File fp - (SR.siteRouteUrl model $ SR.lmlSiteRoute (R.LMLView_Html, note ^. N.noteRoute)) + (renderUrl $ note ^. N.noteRoute) (Tit.toPlain $ note ^. N.noteTitle) ft diff --git a/emanote/src/Emanote/Model/Stork/Index.hs b/packages/emanote/src/Emanote/Stork/Index.hs similarity index 93% rename from emanote/src/Emanote/Model/Stork/Index.hs rename to packages/emanote/src/Emanote/Stork/Index.hs index c28359867..505cfeb45 100644 --- a/emanote/src/Emanote/Model/Stork/Index.hs +++ b/packages/emanote/src/Emanote/Stork/Index.hs @@ -1,7 +1,7 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE TemplateHaskell #-} -module Emanote.Model.Stork.Index ( +module Emanote.Stork.Index ( IndexVar, newIndex, clearStorkIndex, @@ -13,12 +13,11 @@ module Emanote.Model.Stork.Index ( FileType (..), ) where -import Control.Monad.Logger (MonadLoggerIO) +import Control.Monad.Logger (MonadLoggerIO, logDebugNS, logInfoNS, logWarnNS) import Data.Default (Default (..)) import Data.Text qualified as T import Data.Time (NominalDiffTime, diffUTCTime, getCurrentTime) import Deriving.Aeson -import Emanote.Prelude (log, logD, logW) import Numeric (showGFloat) import Relude import System.Process.ByteString (readProcessWithExitCode) @@ -39,14 +38,14 @@ readOrBuildStorkIndex :: (MonadIO m, MonadLoggerIO m) => IndexVar -> Config -> m readOrBuildStorkIndex (IndexVar indexVar) config = do readTVarIO indexVar >>= \case Just index -> do - logD "STORK: Returning cached search index" + logDebugNS "emanote" "STORK: Returning cached search index" pure index Nothing -> do -- TODO: What if there are concurrent reads? We probably need a lock. -- And we want to encapsulate this whole thing. - logW "STORK: Generating search index (this may be expensive)" + logWarnNS "emanote" "STORK: Generating search index (this may be expensive)" (diff, !index) <- timeIt $ runStork config - log $ toText $ "STORK: Done generating search index in " <> showGFloat (Just 2) diff "" <> " seconds" + logInfoNS "emanote" $ toText $ "STORK: Done generating search index in " <> showGFloat (Just 2) diff "" <> " seconds" atomically $ modifyTVar' indexVar $ \_ -> Just index pure index where diff --git a/emanote/src/Emanote/View.hs b/packages/emanote/src/Emanote/View.hs similarity index 100% rename from emanote/src/Emanote/View.hs rename to packages/emanote/src/Emanote/View.hs diff --git a/emanote/src/Emanote/View/Common.hs b/packages/emanote/src/Emanote/View/Common.hs similarity index 98% rename from emanote/src/Emanote/View/Common.hs rename to packages/emanote/src/Emanote/View/Common.hs index 52b31e9b0..46b157e5c 100644 --- a/emanote/src/Emanote/View/Common.hs +++ b/packages/emanote/src/Emanote/View/Common.hs @@ -17,16 +17,15 @@ import Data.Map.Syntax ((##)) import Data.Text qualified as T import Data.Version (showVersion) import Ema.Server.Common qualified as Ema -import Emanote.Model.Meta qualified as Meta import Emanote.Model.SData qualified as SData import Emanote.Model.Title qualified as Tit -import Emanote.Model.Type (Model) -import Emanote.Model.Type qualified as M import Emanote.Pandoc.Renderer (EmanotePandocRenderers (..), PandocRenderers (..)) import Emanote.Pandoc.Renderer qualified as Renderer import Emanote.Route (LMLRoute) import Emanote.Route qualified as R import Emanote.Route.SiteRoute.Class qualified as SR +import Emanote.Site.Model (Model) +import Emanote.Site.Model qualified as M import Emanote.View.JsBundle qualified as JsBundle import Emanote.View.LiveServerFiles qualified as LiveServerFiles import Emanote.View.StaticUrl qualified as StaticUrl @@ -110,7 +109,7 @@ mkTemplateRenderCtx model r meta = defaultRouteMeta :: Model -> (LMLRoute, Aeson.Value) defaultRouteMeta model = let r = M.modelIndexRoute model - meta = Meta.getEffectiveRouteMeta r model + meta = M.getEffectiveRouteMeta r model in (r, meta) commonSplices :: diff --git a/emanote/src/Emanote/View/Export.hs b/packages/emanote/src/Emanote/View/Export.hs similarity index 94% rename from emanote/src/Emanote/View/Export.hs rename to packages/emanote/src/Emanote/View/Export.hs index 87a7003df..7a2392892 100644 --- a/emanote/src/Emanote/View/Export.hs +++ b/packages/emanote/src/Emanote/View/Export.hs @@ -4,8 +4,8 @@ module Emanote.View.Export ( renderExport, ) where -import Emanote.Model (Model) import Emanote.Route.SiteRoute.Type (ExportFormat (..)) +import Emanote.Site.Model (Model) import Emanote.View.Export.Content import Emanote.View.Export.JSON import Relude diff --git a/emanote/src/Emanote/View/Export/Content.hs b/packages/emanote/src/Emanote/View/Export/Content.hs similarity index 98% rename from emanote/src/Emanote/View/Export/Content.hs rename to packages/emanote/src/Emanote/View/Export/Content.hs index 556c650f6..b1f52a729 100644 --- a/emanote/src/Emanote/View/Export/Content.hs +++ b/packages/emanote/src/Emanote/View/Export/Content.hs @@ -12,13 +12,13 @@ module Emanote.View.Export.Content ( import Commonmark.Extensions.WikiLink qualified as WL import Data.Text qualified as T -import Emanote.Model (Model) -import Emanote.Model qualified as M import Emanote.Model.Note qualified as Note import Emanote.Model.Title qualified as Tit import Emanote.Route qualified as R import Emanote.Route.SiteRoute qualified as SR import Emanote.Route.SiteRoute.Class (lmlSiteRoute) +import Emanote.Site.Model (Model) +import Emanote.Site.Model qualified as M import Emanote.Source.Loc (locResolve) import Emanote.View.Export.JSON (getBaseUrlFromModel, lmlSourcePath) import NeatInterpolation (text) diff --git a/emanote/src/Emanote/View/Export/JSON.hs b/packages/emanote/src/Emanote/View/Export/JSON.hs similarity index 93% rename from emanote/src/Emanote/View/Export/JSON.hs rename to packages/emanote/src/Emanote/View/Export/JSON.hs index 9b7675ade..c33c05f33 100644 --- a/emanote/src/Emanote/View/Export/JSON.hs +++ b/packages/emanote/src/Emanote/View/Export/JSON.hs @@ -16,17 +16,16 @@ module Emanote.View.Export.JSON ( import Data.Aeson (ToJSON) import Data.Aeson qualified as Aeson import Data.Map.Strict qualified as Map -import Emanote.Model (Model) -import Emanote.Model qualified as M import Emanote.Model.Link.Rel qualified as Rel import Emanote.Model.Link.Resolve qualified as Resolve -import Emanote.Model.Meta (getEffectiveRouteMeta) import Emanote.Model.SData (lookupAeson) import Emanote.Model.Title qualified as Tit import Emanote.Route (LMLRoute) import Emanote.Route qualified as R import Emanote.Route.SiteRoute qualified as SR import Emanote.Route.SiteRoute.Class (lmlSiteRoute) +import Emanote.Site.Model (Model) +import Emanote.Site.Model qualified as M import Optics.Operators ((^.)) import Relude @@ -90,7 +89,7 @@ modelRels model = let from_ = rel ^. Rel.relFrom to_ = rel ^. Rel.relTo toTarget = - Resolve.resolveUnresolvedRelTarget model from_ to_ + Resolve.resolveUnresolvedRelTarget (model ^. M.modelCore) from_ to_ <&> SR.siteRouteUrlStatic model in (from_, one $ Link to_ toTarget) @@ -110,5 +109,5 @@ lmlSourcePath = getBaseUrlFromModel :: Model -> Maybe Text getBaseUrlFromModel model = let indexRoute = M.modelIndexRoute model - feedMeta = getEffectiveRouteMeta indexRoute model + feedMeta = M.getEffectiveRouteMeta indexRoute model in lookupAeson Nothing ("page" :| ["siteUrl"]) feedMeta diff --git a/emanote/src/Emanote/View/Feed.hs b/packages/emanote/src/Emanote/View/Feed.hs similarity index 94% rename from emanote/src/Emanote/View/Feed.hs rename to packages/emanote/src/Emanote/View/Feed.hs index 404ce6768..b8cf6b95c 100644 --- a/emanote/src/Emanote/View/Feed.hs +++ b/packages/emanote/src/Emanote/View/Feed.hs @@ -4,15 +4,15 @@ module Emanote.View.Feed where import Data.Aeson qualified as Aeson import Data.Aeson.Optics (key, _String) -import Emanote.Model (Model) -import Emanote.Model.Meta (getEffectiveRouteMeta) import Emanote.Model.Note (Feed (..), Note (..), lookupMeta) import Emanote.Model.Query (Query, parseQuery, runQuery) import Emanote.Model.SData (lookupAeson) import Emanote.Model.Title (toPlain) import Emanote.Route.SiteRoute import Emanote.Route.SiteRoute.Class (noteFeedSiteRoute) -import Optics.Operators ((^?)) +import Emanote.Site.Model (Model) +import Emanote.Site.Model qualified as M +import Optics.Operators ((^.), (^?)) import Optics.Optic ((%)) import Relude import Text.Atom.Feed qualified as Atom @@ -77,14 +77,14 @@ renderFeed model baseNote = encodeUtf8 <$> eFeedText eFeedText = do feed <- maybeToRight "feed attribute missing" $ _noteFeed baseNote feedQuery <- getNoteQuery baseNote - case nonEmpty (runQuery (_noteRoute baseNote) model feedQuery) of + case nonEmpty (runQuery (_noteRoute baseNote) (model ^. M.modelCore) feedQuery) of Nothing -> Right (renderEmptyFeed baseNote) Just notes -> renderPopulatedFeed model baseNote feed notes renderPopulatedFeed :: Model -> Note -> Feed -> NonEmpty Note -> Either LText LText renderPopulatedFeed model baseNote feed notes = do let feedMeta :: Aeson.Value - feedMeta = getEffectiveRouteMeta (_noteRoute baseNote) model + feedMeta = M.getEffectiveRouteMeta (_noteRoute baseNote) model let mFeedUrl :: Maybe Text mFeedUrl = lookupAeson Nothing ("page" :| ["siteUrl"]) feedMeta feedUrl <- maybeToRight "index.yaml or note doesn't have page.siteUrl" mFeedUrl diff --git a/emanote/src/Emanote/View/JsBundle.hs b/packages/emanote/src/Emanote/View/JsBundle.hs similarity index 99% rename from emanote/src/Emanote/View/JsBundle.hs rename to packages/emanote/src/Emanote/View/JsBundle.hs index d2f42268c..f0378f301 100644 --- a/emanote/src/Emanote/View/JsBundle.hs +++ b/packages/emanote/src/Emanote/View/JsBundle.hs @@ -33,7 +33,7 @@ module Emanote.View.JsBundle ( import Data.Aeson qualified as Aeson import Data.Aeson.Key qualified as AesonKey import Data.Text qualified as T -import Emanote.Model.Type (Model) +import Emanote.Site.Model (Model) import Emanote.View.StaticUrl (emanoteStaticUrl) import Relude import System.FilePath (()) diff --git a/emanote/src/Emanote/View/LiveServerFiles.hs b/packages/emanote/src/Emanote/View/LiveServerFiles.hs similarity index 94% rename from emanote/src/Emanote/View/LiveServerFiles.hs rename to packages/emanote/src/Emanote/View/LiveServerFiles.hs index 2c2a2daed..146fb3610 100644 --- a/emanote/src/Emanote/View/LiveServerFiles.hs +++ b/packages/emanote/src/Emanote/View/LiveServerFiles.hs @@ -7,7 +7,7 @@ where import Data.Text qualified as T import Emanote.Model.StaticFile (StaticFile) -import Emanote.Model.Type qualified as M +import Emanote.Site.Model qualified as M import Relude -- TODO: Check this compile-time using TH? diff --git a/emanote/src/Emanote/View/StaticUrl.hs b/packages/emanote/src/Emanote/View/StaticUrl.hs similarity index 95% rename from emanote/src/Emanote/View/StaticUrl.hs rename to packages/emanote/src/Emanote/View/StaticUrl.hs index 4f9fc84b4..422338ea6 100644 --- a/emanote/src/Emanote/View/StaticUrl.hs +++ b/packages/emanote/src/Emanote/View/StaticUrl.hs @@ -21,9 +21,9 @@ module Emanote.View.StaticUrl ( ) where import Data.Map.Syntax ((##)) -import Emanote.Model.Type (Model) -import Emanote.Model.Type qualified as M import Emanote.Route.SiteRoute.Class qualified as SR +import Emanote.Site.Model (Model) +import Emanote.Site.Model qualified as M import Heist qualified as H import Heist.Interpreted qualified as HI import Relude diff --git a/emanote/src/Emanote/View/TagIndex.hs b/packages/emanote/src/Emanote/View/TagIndex.hs similarity index 98% rename from emanote/src/Emanote/View/TagIndex.hs rename to packages/emanote/src/Emanote/View/TagIndex.hs index 0cae15bd8..22f0a8bc1 100644 --- a/emanote/src/Emanote/View/TagIndex.hs +++ b/packages/emanote/src/Emanote/View/TagIndex.hs @@ -6,12 +6,12 @@ import Data.Map.Strict qualified as Map import Data.Map.Syntax ((##)) import Data.Tree (Forest, Tree) import Data.Tree qualified as Tree -import Emanote.Model (Model) -import Emanote.Model qualified as M import Emanote.Model.Note qualified as MN import Emanote.Pandoc.Markdown.Syntax.HashTag qualified as HT import Emanote.Pandoc.Renderer.Query qualified as PF import Emanote.Route.SiteRoute.Class qualified as SR +import Emanote.Site.Model (Model) +import Emanote.Site.Model qualified as M import Emanote.View.Common ( TemplateRenderCtx (withInlineCtx), commonSplices, diff --git a/emanote/src/Emanote/View/Tailwind.hs b/packages/emanote/src/Emanote/View/Tailwind.hs similarity index 100% rename from emanote/src/Emanote/View/Tailwind.hs rename to packages/emanote/src/Emanote/View/Tailwind.hs diff --git a/emanote/src/Emanote/View/TaskIndex.hs b/packages/emanote/src/Emanote/View/TaskIndex.hs similarity index 96% rename from emanote/src/Emanote/View/TaskIndex.hs rename to packages/emanote/src/Emanote/View/TaskIndex.hs index d79d7c7b9..3b7e151b3 100644 --- a/emanote/src/Emanote/View/TaskIndex.hs +++ b/packages/emanote/src/Emanote/View/TaskIndex.hs @@ -4,12 +4,12 @@ import Data.IxSet.Typed qualified as Ix import Data.List.NonEmpty qualified as NE import Data.Map.Strict qualified as Map import Data.Map.Syntax ((##)) -import Emanote.Model (Model) import Emanote.Model.Task (Task) import Emanote.Model.Task qualified as Task -import Emanote.Model.Type qualified as M import Emanote.Route qualified as R import Emanote.Route.SiteRoute qualified as SR +import Emanote.Site.Model (Model) +import Emanote.Site.Model qualified as M import Emanote.View.Common qualified as Common import Heist.Extra.Splices.List qualified as Splices import Heist.Extra.Splices.Pandoc qualified as Splices diff --git a/emanote/src/Emanote/View/Template.hs b/packages/emanote/src/Emanote/View/Template.hs similarity index 88% rename from emanote/src/Emanote/View/Template.hs rename to packages/emanote/src/Emanote/View/Template.hs index 586110fd6..ddf99b975 100644 --- a/emanote/src/Emanote/View/Template.hs +++ b/packages/emanote/src/Emanote/View/Template.hs @@ -8,19 +8,19 @@ import Data.Set qualified as Set import Data.Text qualified as T import Data.Tree qualified as Tree import Ema qualified -import Emanote.Model (Model, ModelEma) -import Emanote.Model qualified as M import Emanote.Model.Calendar qualified as Calendar import Emanote.Model.Graph qualified as G -import Emanote.Model.Meta qualified as Meta import Emanote.Model.Note qualified as MN import Emanote.Model.SData qualified as SData -import Emanote.Model.Stork (renderStorkIndex) +import Emanote.Model.StaticFile qualified as SF import Emanote.Model.Toc (newToc, renderToc, tocUnnecessaryToRender) import Emanote.Route qualified as R import Emanote.Route.SiteRoute (SiteRoute) import Emanote.Route.SiteRoute qualified as SR import Emanote.Route.SiteRoute.Class (indexRoute) +import Emanote.Site.Model (Model, ModelEma) +import Emanote.Site.Model qualified as M +import Emanote.Stork (renderStorkIndex) import Emanote.View.Common qualified as C import Emanote.View.Export (renderExport) import Emanote.View.Feed (feedDiscoveryLink, renderFeed) @@ -101,8 +101,12 @@ renderResourceRoute m = \case Nothing -> -- This should never be reached because decodeRoute looks up the model. error $ "Bad route: " <> show r - SR.ResourceRoute_StaticFile _ fpAbs -> - Ema.AssetStatic fpAbs + SR.ResourceRoute_StaticFile staticFileRoute -> + case M.modelLookupStaticFileByRoute staticFileRoute m of + Just staticFile -> + Ema.AssetStatic $ staticFile ^. SF.staticFilePath + Nothing -> + error $ "Bad static file route: " <> show staticFileRoute renderVirtualRoute :: (MonadIO m, MonadLoggerIO m) => Model -> SR.VirtualRoute -> m (Ema.Asset LByteString) renderVirtualRoute m = \case @@ -114,7 +118,11 @@ renderVirtualRoute m = \case content <- liftIO $ renderExport exportFormat m pure $ Ema.AssetGenerated Ema.Other content SR.VirtualRoute_StorkIndex -> - Ema.AssetGenerated Ema.Other <$> renderStorkIndex m + Ema.AssetGenerated Ema.Other + <$> renderStorkIndex + (m ^. M.modelStorkIndex) + (SR.siteRouteUrl m . SR.lmlSiteRoute . (R.LMLView_Html,)) + (m ^. M.modelCore) SR.VirtualRoute_TaskIndex -> pure $ Ema.AssetGenerated Ema.Html $ TaskIndex.renderTasks m @@ -149,8 +157,8 @@ patchMeta meta = renderLmlHtml :: Model -> MN.Note -> LByteString renderLmlHtml model note = do let r = note ^. MN.noteRoute - meta = patchMeta $ Meta.getEffectiveRouteMetaWith (note ^. MN.noteMeta) r model - doc = prependDataErrors (Meta.cascadeYamlErrors model r) (note ^. MN.noteDoc) + meta = patchMeta $ M.getEffectiveRouteMetaWith (note ^. MN.noteMeta) r model + doc = prependDataErrors (M.cascadeYamlErrors model r) (note ^. MN.noteDoc) toc = newToc doc sourcePath = fromMaybe (R.withLmlRoute R.encodeRoute r) $ do fmap snd $ note ^. MN.noteSource @@ -166,7 +174,7 @@ renderLmlHtml model note = do C.commonSplices (C.withLinkInlineCtx ctx) model meta (note ^. MN.noteTitle) -- Template flags forM_ ["uptree", "breadcrumbs", "sidebar", "toc"] $ \flag -> do - let hasFlag' = Meta.lookupRouteMeta @Bool False ("template" :| [flag, "enable"]) r model + let hasFlag' = M.lookupRouteMeta @Bool False ("template" :| [flag, "enable"]) r model hasFlag = if flag == "toc" then hasFlag' && not (tocUnnecessaryToRender toc) else hasFlag' "ema:has:" <> flag ## Heist.ifElseISplice hasFlag -- Sidebar navigation @@ -188,13 +196,13 @@ renderLmlHtml model note = do then feedDiscoveryLink model note else mempty "ema:note:backlinks" ## - backlinksSplice model (G.modelLookupBacklinks r model) - let (backlinksDaily, backlinksNoDaily) = partition (Calendar.isDailyNote . fst) $ G.modelLookupBacklinks r model + backlinksSplice model (G.modelLookupBacklinks r (model ^. M.modelCore)) + let (backlinksDaily, backlinksNoDaily) = partition (Calendar.isDailyNote . fst) $ G.modelLookupBacklinks r (model ^. M.modelCore) "ema:note:backlinks:daily" ## backlinksSplice model backlinksDaily "ema:note:backlinks:nodaily" ## backlinksSplice model backlinksNoDaily - let folgeAnc = G.modelFolgezettelAncestorTree model r + let folgeAnc = G.modelFolgezettelAncestorTree (model ^. M.modelCore) r "ema:note:uptree" ## Splices.treeSplice (\_ _ -> ()) folgeAnc $ \(last -> nodeRoute) children -> do @@ -216,7 +224,7 @@ backlinksSplice model (bs :: [(R.LMLRoute, NonEmpty [B.Block])]) = Splices.listSplice bs "backlink" $ \(source, contexts) -> do let bnote = fromMaybe (error "backlink note missing - impossible") $ M.modelLookupNoteByRoute' source model - bmeta = Meta.getEffectiveRouteMetaWith (bnote ^. MN.noteMeta) source model + bmeta = M.getEffectiveRouteMetaWith (bnote ^. MN.noteMeta) source model bctx = C.mkTemplateRenderCtx model source bmeta -- TODO: reuse note splice "backlink:note:title" ## C.titleSplice bctx (M.modelLookupTitle source model) @@ -239,7 +247,7 @@ routeTreeSplices tCtx mCurrentRoute model = do "ema:route-tree" ## Splices.treeSplice getOrder (model ^. M.modelFolgezettelTree) $ \(last -> nodeRoute) children -> do - let shortTitle = Meta.lookupRouteMeta @(Maybe Text) Nothing ("short-title" :| []) nodeRoute model + let shortTitle = M.lookupRouteMeta @(Maybe Text) Nothing ("short-title" :| []) nodeRoute model "node:text" ## maybe (C.titleSplice tCtx $ M.modelLookupTitle nodeRoute model) HI.textSplice shortTitle "node:url" ## HI.textSplice $ SR.siteRouteUrl model $ SR.lmlSiteRoute (R.LMLView_Html, nodeRoute) let isActiveNode = Just nodeRoute == mCurrentRoute @@ -248,7 +256,7 @@ routeTreeSplices tCtx mCurrentRoute model = do -- active route (i.e., mr is a Just) flip (maybe True) mCurrentRoute $ \r -> -- FIXME: Performance! (exponential complexity) - let folgeAnc = Set.fromList $ concatMap Tree.flatten $ G.modelFolgezettelAncestorTree model r + let folgeAnc = Set.fromList $ concatMap Tree.flatten $ G.modelFolgezettelAncestorTree (model ^. M.modelCore) r isFolgeAnc = Set.member nodeRoute folgeAnc in r == nodeRoute || isFolgeAnc openTree = @@ -262,17 +270,17 @@ routeTreeSplices tCtx mCurrentRoute model = do "has-current-route" ## Heist.ifElseISplice (isJust mCurrentRoute) where getFoldersFirst tr = - Meta.lookupRouteMeta @Bool False ("template" :| ["sidebar", "folders-first"]) tr model + M.lookupRouteMeta @Bool False ("template" :| ["sidebar", "folders-first"]) tr model getOrder path children = let tr = last path isLeaf = null children priority = if getFoldersFirst tr && isLeaf then 1 else 0 :: Int in ( priority - , Meta.lookupRouteMeta @Int 0 (one "order") tr model + , M.lookupRouteMeta @Int 0 (one "order") tr model , tr ) getCollapsed tr = - Meta.lookupRouteMeta @Bool True ("template" :| ["sidebar", "collapsed"]) tr model + M.lookupRouteMeta @Bool True ("template" :| ["sidebar", "collapsed"]) tr model lookupTemplateName :: (ConvertUtf8 Text b) => Aeson.Value -> b lookupTemplateName meta = diff --git a/emanote/test/Emanote/Pandoc/Renderer/CalloutSpec.hs b/packages/emanote/test/Emanote/Pandoc/Renderer/CalloutSpec.hs similarity index 100% rename from emanote/test/Emanote/Pandoc/Renderer/CalloutSpec.hs rename to packages/emanote/test/Emanote/Pandoc/Renderer/CalloutSpec.hs diff --git a/emanote/test/Emanote/View/FeedSpec.hs b/packages/emanote/test/Emanote/View/FeedSpec.hs similarity index 100% rename from emanote/test/Emanote/View/FeedSpec.hs rename to packages/emanote/test/Emanote/View/FeedSpec.hs diff --git a/emanote/test/Spec.hs b/packages/emanote/test/Spec.hs similarity index 100% rename from emanote/test/Spec.hs rename to packages/emanote/test/Spec.hs