From aa2bdcf8fe8da74238643f90ba995b0902bca3eb Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Sun, 17 May 2020 21:05:47 -0400 Subject: [PATCH 01/14] Semantic -> Fomantic --- src/app/Main.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/Main.hs b/src/app/Main.hs index 5fec71ef9..3d7c00066 100644 --- a/src/app/Main.hs +++ b/src/app/Main.hs @@ -45,7 +45,7 @@ renderPage config r val = elAttr "html" ("lang" =: "en") $ do blank _ -> do forM_ - [ "https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css", + [ "https://cdn.jsdelivr.net/npm/fomantic-ui@2.8.4/dist/semantic.min.css", "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css" ] $ \url -> From 6756157fecf6712209cd847b1a631ffc8f74028c Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Sun, 17 May 2020 21:05:59 -0400 Subject: [PATCH 02/14] Misc UI improvements * Show footer in all pages * Flip Up/Down link groups horizontally * Rename connection pane link group titles --- src/app/Neuron/Web/View.hs | 61 ++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/src/app/Neuron/Web/View.hs b/src/app/Neuron/Web/View.hs index 347cdebfb..85294ab00 100644 --- a/src/app/Neuron/Web/View.hs +++ b/src/app/Neuron/Web/View.hs @@ -128,14 +128,14 @@ renderRouteBody config r (g, x) = do Route_ZIndex -> renderIndex config g Route_Search {} -> - renderSearch g + renderSearch config g Route_Zettel _ -> renderZettel config (g, x) Route_Redirect _ -> elAttr "meta" ("http-equiv" =: "Refresh" <> "content" =: ("0; url=" <> (Rib.routeUrlRel $ Route_Zettel x))) blank renderIndex :: DomBuilder t m => Config -> ZettelGraph -> m () -renderIndex Config {..} graph = do +renderIndex config@Config {..} graph = do let neuronTheme = Theme.mkTheme theme elClass "h1" "header" $ text "Zettel Index" divClass "z-index" $ do @@ -151,17 +151,18 @@ renderIndex Config {..} graph = do text $ "There " <> countNounBe "cluster" "clusters" (length clusters) <> " in the Zettelkasten graph. " text "Each cluster is rendered as a forest, with their roots (mother zettels) highlighted." forM_ clusters $ \forest -> - divClass ("ui stacked " <> Theme.semanticColor neuronTheme <> " segment") $ do + divClass ("ui " <> Theme.semanticColor neuronTheme <> " segment") $ do -- Forest of zettels, beginning with mother vertices. el "ul" $ renderForest True Nothing (Just graph) forest - renderBrandFooter True + renderFooter config graph Nothing + renderBrandFooter where countNounBe noun nounPlural = \case 1 -> "is 1 " <> noun n -> "are " <> show n <> " " <> nounPlural -renderSearch :: DomBuilder t m => ZettelGraph -> m () -renderSearch graph = do +renderSearch :: DomBuilder t m => Config -> ZettelGraph -> m () +renderSearch config graph = do elClass "h1" "header" $ text "Search" divClass "ui fluid icon input search" $ do elAttr "input" ("type" =: "text" <> "id" =: "search-input") blank @@ -181,6 +182,8 @@ renderSearch graph = do elAttr "ul" ("id" =: "search-results" <> "class" =: "zettel-list") blank el "script" $ text $ "let index = " <> toText (Aeson.encodeToLazyText index) <> ";" el "script" $ text searchScript + renderFooter config graph Nothing + renderBrandFooter renderZettel :: PandocBuilder t m => Config -> (ZettelGraph, Zettel) -> m () renderZettel config (graph, z@Zettel {..}) = do @@ -189,30 +192,40 @@ renderZettel config (graph, z@Zettel {..}) = do renderZettelPanel config graph z renderZettelPanel :: DomBuilder t m => Config -> ZettelGraph -> Zettel -> m () -renderZettelPanel Config {..} graph z@Zettel {..} = do +renderZettelPanel config@Config {..} graph z@Zettel {..} = do let neuronTheme = Theme.mkTheme theme divClass ("ui inverted " <> Theme.semanticColor neuronTheme <> " top attached connections segment") $ do divClass "ui two column grid" $ do divClass "column" $ do - divClass "ui header" $ text "Down" - el "ul" $ renderForest True (Just 2) Nothing $ - G.frontlinkForest Folgezettel z graph - divClass "column" $ do - divClass "ui header" $ text "Up" + elAttr "div" ("class" =: "ui header" <> title =: "The following zettels branch to this zettel") $ + text "Uplinks" el "ul" $ do renderForest True Nothing Nothing $ G.backlinkForest Folgezettel z graph - divClass "ui header" $ text "Other backlinks" - el "ul" $ do - renderForest True Nothing Nothing - $ fmap (flip Node []) - $ G.backlinks OrdinaryConnection z graph - divClass "ui inverted black bottom attached footer segment" $ do + let cfBacklinks = G.backlinks OrdinaryConnection z graph + whenNotNull cfBacklinks $ \_ -> do + elAttr "div" ("class" =: "ui header" <> title =: "Zettels that link here, but without branching") $ + text "Backlinks" + el "ul" $ do + renderForest True Nothing Nothing $ + fmap (flip Node []) cfBacklinks + divClass "column" $ do + elAttr "div" ("class" =: "ui header" <> title =: "This zettel branches to the following zettels") $ + text "Downlinks" + el "ul" $ renderForest True (Just 2) Nothing $ + G.frontlinkForest Folgezettel z graph + renderFooter config graph (Just z) + renderBrandFooter + +renderFooter :: DomBuilder t m => Config -> ZettelGraph -> Maybe Zettel -> m () +renderFooter Config {..} graph mzettel = do + let attachClass = maybe "" (const "bottom attached") mzettel + divClass ("ui inverted black " <> attachClass <> " footer segment") $ do divClass "ui equal width grid" $ do divClass "center aligned column" $ do let homeUrl = maybe "." (const "index.html") $ G.getZettel (ZettelCustomID "index") graph elAttr "a" ("href" =: homeUrl <> "title" =: "/") $ fa "fas fa-home" - whenJust editUrl $ \urlPrefix -> + whenJust ((,) <$> mzettel <*> editUrl) $ \(Zettel {..}, urlPrefix) -> divClass "center aligned column" $ do elAttr "a" ("href" =: (urlPrefix <> toText (zettelIDSourceFileName zettelID)) <> "title" =: "Edit this Zettel") $ fa "fas fa-edit" divClass "center aligned column" $ do @@ -220,18 +233,16 @@ renderZettelPanel Config {..} graph z@Zettel {..} = do divClass "center aligned column" $ do elAttr "a" ("href" =: (Rib.routeUrlRel Route_ZIndex) <> "title" =: "All Zettels (z-index)") $ fa "fas fa-tree" - renderBrandFooter False -renderBrandFooter :: DomBuilder t m => Bool -> m () -renderBrandFooter withVersion = +renderBrandFooter :: DomBuilder t m => m () +renderBrandFooter = divClass "ui one column grid footer-version" $ do divClass "center aligned column" $ do el "p" $ do text "Generated by " elAttr "a" ("href" =: "https://neuron.zettel.page") $ text "Neuron" - when withVersion $ do - text " " - el "code" $ text neuronVersion + text " " + el "code" $ text neuronVersion -- | Font awesome element fa :: DomBuilder t m => Text -> m () From bf38065fb78ba12742d6533e1fecffaf8b4b27c1 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 18 May 2020 00:17:13 -0400 Subject: [PATCH 03/14] Revamp connectional panel with "uplink" tree, etc. WIP --- src/app/Main.hs | 31 ++-- src/app/Neuron/Web/View.hs | 166 +++++++++++++++++++-- src/lib/Neuron/Zettelkasten/Zettel/View.hs | 2 +- 3 files changed, 175 insertions(+), 24 deletions(-) diff --git a/src/app/Main.hs b/src/app/Main.hs index 3d7c00066..e99404adc 100644 --- a/src/app/Main.hs +++ b/src/app/Main.hs @@ -55,7 +55,7 @@ renderPage config r val = elAttr "html" ("lang" =: "en") $ do when (Config.mathJaxSupport config) $ elAttr "script" ("id" =: "MathJax-script" <> "src" =: "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" <> "async" =: "") blank el "body" $ do - elAttr "div" ("id" =: "thesite" <> "class" =: "ui text container") $ do + elAttr "div" ("id" =: "thesite" <> "class" =: "ui fluid container") $ do renderRouteBody config r val where googleFonts :: DomBuilder t m => [Text] -> m () @@ -74,16 +74,19 @@ monoFont :: Text monoFont = "DM Mono" mainStyle :: Config -> Css -mainStyle cfg = "div#thesite" ? do - C.fontFamily [bodyFont] [C.serif] - C.paddingTop $ em 1 - C.paddingBottom $ em 1 - "p" ? do - C.lineHeight $ pct 150 - "h1, h2, h3, h4, h5, h6, .ui.header, .headerFont" ? do - C.fontFamily [headerFont] [C.sansSerif] - "img" ? do - C.maxWidth $ pct 100 -- Prevents large images from overflowing beyond zettel borders - "code, pre, tt, .monoFont" ? do - C.fontFamily [monoFont, "SFMono-Regular", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New"] [C.monospace] - style cfg +mainStyle cfg = do + "body" ? do + C.important $ C.backgroundColor "#eee" + "div#thesite" ? do + C.fontFamily [bodyFont] [C.serif] + C.paddingTop $ em 1 + C.paddingBottom $ em 1 + "p" ? do + C.lineHeight $ pct 150 + "h1, h2, h3, h4, h5, h6, .ui.header, .headerFont" ? do + C.fontFamily [headerFont] [C.sansSerif] + "img" ? do + C.maxWidth $ pct 100 -- Prevents large images from overflowing beyond zettel borders + "code, pre, tt, .monoFont" ? do + C.fontFamily [monoFont, "SFMono-Regular", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New"] [C.monospace] + style cfg diff --git a/src/app/Neuron/Web/View.hs b/src/app/Neuron/Web/View.hs index 85294ab00..9b64eeb72 100644 --- a/src/app/Neuron/Web/View.hs +++ b/src/app/Neuron/Web/View.hs @@ -44,9 +44,9 @@ import Neuron.Zettelkasten.Graph (ZettelGraph) import Neuron.Zettelkasten.ID (ZettelID (..), zettelIDSourceFileName, zettelIDText) import Neuron.Zettelkasten.Zettel import qualified Neuron.Zettelkasten.Zettel.View as ZettelView -import Reflex.Dom.Core +import Reflex.Dom.Core hiding ((&)) import Reflex.Dom.Pandoc.Document (PandocBuilder) -import Relude +import Relude hiding ((&)) import qualified Rib import Rib.Extra.OpenGraph import qualified Skylighting.Format.HTML as Skylighting @@ -135,7 +135,7 @@ renderRouteBody config r (g, x) = do elAttr "meta" ("http-equiv" =: "Refresh" <> "content" =: ("0; url=" <> (Rib.routeUrlRel $ Route_Zettel x))) blank renderIndex :: DomBuilder t m => Config -> ZettelGraph -> m () -renderIndex config@Config {..} graph = do +renderIndex config@Config {..} graph = divClass "ui text container" $ do let neuronTheme = Theme.mkTheme theme elClass "h1" "header" $ text "Zettel Index" divClass "z-index" $ do @@ -162,7 +162,7 @@ renderIndex config@Config {..} graph = do n -> "are " <> show n <> " " <> nounPlural renderSearch :: DomBuilder t m => Config -> ZettelGraph -> m () -renderSearch config graph = do +renderSearch config graph = divClass "ui text container" $ do elClass "h1" "header" $ text "Search" divClass "ui fluid icon input search" $ do elAttr "input" ("type" =: "text" <> "id" =: "search-input") blank @@ -187,12 +187,36 @@ renderSearch config graph = do renderZettel :: PandocBuilder t m => Config -> (ZettelGraph, Zettel) -> m () renderZettel config (graph, z@Zettel {..}) = do - divClass "zettel-view" $ do - ZettelView.renderZettelContent z - renderZettelPanel config graph z + let upTree = G.backlinkForest Folgezettel z graph + whenNotNull upTree $ \_ -> do + elAttr "div" ("class" =: "flipped tree" <> "style" =: "transform-origin: 50%") $ do + el "ul" $ do + el "li" $ do + divClass "forest-link" $ el "a" $ text zettelTitle + el "ul" $ do + renderForestNG True Nothing Nothing upTree + divClass "ui text container" $ do + divClass "zettel-view" $ do + ZettelView.renderZettelContent z + let cfBacklinks = G.backlinks OrdinaryConnection z graph + whenNotNull cfBacklinks $ \_ -> divClass "ui attached segment backlinks" $ do + elAttr "div" ("class" =: "ui header" <> title =: "Zettels that link here, but without branching") $ + text "Backlinks" + el "ul" $ do + renderForest True Nothing Nothing $ + fmap (flip Node []) cfBacklinks + renderFooter config graph (Just z) + -- renderZettelPanel config graph z + -- elAttr "div" ("class" =: "tree" <> "style" =: "transform-origin: 50%") $ do + -- el "ul" $ do + -- el "li" $ do + -- divClass "forest-link" $ el "a" $ text zettelTitle + -- el "ul" $ do + -- renderForestNG True (Just 2) Nothing $ G.frontlinkForest Folgezettel z graph + renderBrandFooter -renderZettelPanel :: DomBuilder t m => Config -> ZettelGraph -> Zettel -> m () -renderZettelPanel config@Config {..} graph z@Zettel {..} = do +_renderZettelPanel :: DomBuilder t m => Config -> ZettelGraph -> Zettel -> m () +_renderZettelPanel config@Config {..} graph z@Zettel {..} = do let neuronTheme = Theme.mkTheme theme divClass ("ui inverted " <> Theme.semanticColor neuronTheme <> " top attached connections segment") $ do divClass "ui two column grid" $ do @@ -283,6 +307,39 @@ renderForest isRoot maxLevel mg trees = -- Sort trees so that trees containing the most recent zettel (by ID) come first. sortForest = reverse . sortOn maximum +renderForestNG :: + DomBuilder t m => + Bool -> + Maybe Int -> + -- When given the zettelkasten graph, also show non-parent backlinks. + -- The dfsForest tree is "incomplete" in that it lacks these references. + Maybe ZettelGraph -> + [Tree Zettel] -> + m () +renderForestNG _isRoot maxLevel mg trees = + case maxLevel of + Just 0 -> blank + _ -> do + forM_ (sortForest trees) $ \(Node zettel subtrees) -> + el "li" $ do + let linkDivClass = maybe "forest-link" (const "ui black label forest-link") mg + divClass linkDivClass $ + ZettelView.renderZettelLink def zettel + whenJust mg $ \g -> do + text " " + case G.backlinks Folgezettel zettel g of + conns@(_ : _ : _) -> + -- Has two or more category backlinks + forM_ conns $ \zettel2 -> do + let connTitle = (zettelIDText (zettelID zettel2) <> " " <> zettelTitle zettel2) + elAttr "i" ("class" =: "fas fa-link" <> "title" =: connTitle) blank + _ -> blank + when (length subtrees > 0) $ do + el "ul" $ renderForestNG False ((\n -> n - 1) <$> maxLevel) mg subtrees + where + -- Sort trees so that trees containing the most recent zettel (by ID) come first. + sortForest = reverse . sortOn maximum + style :: Config -> Css style Config {..} = do let neuronTheme = Theme.mkTheme theme @@ -333,3 +390,94 @@ style Config {..} = do C.fontSize $ em 0.7 "[data-tooltip]:after" ? do C.fontSize $ em 0.7 + pureCssTreeDiagram + ".backlinks" ? do + opacity 0.5 + +-- https://codepen.io/philippkuehn/pen/QbrOaN +pureCssTreeDiagram :: Css +pureCssTreeDiagram = do + -- TODO: should only apply for folgezettel + ".zettel-link-container::after" ? do + C.paddingLeft $ em 0.3 + C.content $ stringContent "ᛦ" + let cellBorderWidth = px 2 + flipTree = False + rotateDeg = deg 180 + ".tree.flipped" ? do + C.transform $ C.rotate rotateDeg + ".tree" ? do + C.overflow auto + fontSize $ em 0.9 + when flipTree $ do + C.transform $ C.rotate rotateDeg + -- Clay does not support this; doing it inline in div style. + -- C.transformOrigin $ pct 50 + "ul" ? do + C.position relative + sym2 C.padding (em 1) 0 + C.whiteSpace nowrap + sym2 C.margin (px 0) auto + C.textAlign center + C.after & do + C.content $ stringContent "" + C.display C.displayTable + C.clear both + "li" ? do + C.display C.inlineBlock + C.verticalAlign C.vAlignTop + C.textAlign C.center + C.listStyleType none + C.position relative + C.padding (em 1) (em 0.5) (em 0) (em 0.5) + forM_ [C.before, C.after] $ \sel -> sel & do + C.content $ stringContent "" + C.position absolute + C.top $ px 0 + C.right $ pct 50 + C.borderTop solid cellBorderWidth "#ccc" + C.width $ pct 50 + C.height $ em 1.2 + C.after & do + C.right auto + C.left $ pct 50 + C.borderLeft solid cellBorderWidth "#ccc" + C.onlyChild & do + C.paddingTop $ em 0 + forM_ [C.after, C.before] $ \sel -> sel & do + C.display none + C.firstChild & do + C.before & do + C.borderStyle none + C.borderWidth $ px 0 + C.after & do + C.borderRadius (px 5) 0 0 0 + C.lastChild & do + C.after & do + C.borderStyle none + C.borderWidth $ px 0 + C.before & do + C.borderRight solid cellBorderWidth "#ccc" + C.borderRadius 0 (px 5) 0 0 + "ul ul::before" ? do + C.content $ stringContent "" + C.position absolute + C.top $ px 0 + C.left $ pct 50 + C.borderLeft solid cellBorderWidth "#ccc" + C.width $ px 0 + C.height $ em 1.2 + "li" ? do + "div.forest-link" ? do + border solid cellBorderWidth "#ccc" + sym2 C.padding (em 0.5) (em 0.75) + C.textDecoration none + C.display inlineBlock + sym C.borderRadius (px 5) + C.color "#333" + C.position relative + C.top cellBorderWidth + when flipTree $ do + C.transform $ C.rotate rotateDeg + ".tree.flipped li div.forest-link" ? do + C.transform $ C.rotate rotateDeg diff --git a/src/lib/Neuron/Zettelkasten/Zettel/View.hs b/src/lib/Neuron/Zettelkasten/Zettel/View.hs index 21da68ced..f7f4870fa 100644 --- a/src/lib/Neuron/Zettelkasten/Zettel/View.hs +++ b/src/lib/Neuron/Zettelkasten/Zettel/View.hs @@ -28,7 +28,7 @@ import Relude renderZettelContent :: PandocBuilder t m => Zettel -> m () renderZettelContent Zettel {..} = do - divClass "ui raised segment zettel-content" $ do + divClass "ui raised top attached segment zettel-content" $ do elClass "h1" "header" $ text zettelTitle elPandoc zettelContent renderTags zettelTags From 16aecf5053a699813cd92d9c37cf23daa9743636 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 18 May 2020 00:43:29 -0400 Subject: [PATCH 04/14] Backlinks section should exclude "down" links --- src/app/Neuron/Web/View.hs | 2 +- src/app/Neuron/Zettelkasten/Graph.hs | 6 ++++-- src/lib/Data/Graph/Labelled.hs | 1 + src/lib/Data/Graph/Labelled/Algorithm.hs | 4 ++++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/app/Neuron/Web/View.hs b/src/app/Neuron/Web/View.hs index 9b64eeb72..70a6910f1 100644 --- a/src/app/Neuron/Web/View.hs +++ b/src/app/Neuron/Web/View.hs @@ -201,7 +201,7 @@ renderZettel config (graph, z@Zettel {..}) = do let cfBacklinks = G.backlinks OrdinaryConnection z graph whenNotNull cfBacklinks $ \_ -> divClass "ui attached segment backlinks" $ do elAttr "div" ("class" =: "ui header" <> title =: "Zettels that link here, but without branching") $ - text "Backlinks" + text "More backlinks" el "ul" $ do renderForest True Nothing Nothing $ fmap (flip Node []) cfBacklinks diff --git a/src/app/Neuron/Zettelkasten/Graph.hs b/src/app/Neuron/Zettelkasten/Graph.hs index 4d01d79b3..5ef35f8d4 100644 --- a/src/app/Neuron/Zettelkasten/Graph.hs +++ b/src/app/Neuron/Zettelkasten/Graph.hs @@ -95,8 +95,10 @@ backlinkForest conn z = . G.induceOnEdge (== Just conn) backlinks :: Connection -> Zettel -> ZettelGraph -> [Zettel] -backlinks conn z = - G.preSet z . G.induceOnEdge (== Just conn) +backlinks conn z g = + filter (not . branches) $ G.preSet z $ G.induceOnEdge (== Just conn) g + where + branches bz = G.hasEdge g z bz categoryClusters :: ZettelGraph -> [Forest Zettel] categoryClusters (categoryGraph -> g) = diff --git a/src/lib/Data/Graph/Labelled.hs b/src/lib/Data/Graph/Labelled.hs index 1e8a20da7..3118efbcb 100644 --- a/src/lib/Data/Graph/Labelled.hs +++ b/src/lib/Data/Graph/Labelled.hs @@ -11,6 +11,7 @@ module Data.Graph.Labelled -- * Querying findVertex, getVertices, + hasEdge, -- * Algorithms preSet, diff --git a/src/lib/Data/Graph/Labelled/Algorithm.hs b/src/lib/Data/Graph/Labelled/Algorithm.hs index f814ef5d4..348f486af 100644 --- a/src/lib/Data/Graph/Labelled/Algorithm.hs +++ b/src/lib/Data/Graph/Labelled/Algorithm.hs @@ -28,6 +28,10 @@ getVertices :: LabelledGraph v e -> [v] getVertices (LabelledGraph _ lm) = Map.elems lm +hasEdge :: (Ord (VertexID v), Vertex v) => LabelledGraph v e -> v -> v -> Bool +hasEdge (LabelledGraph g _) x y = + LAM.hasEdge (vertexID x) (vertexID y) g + -- | Return the backlinks to the given vertex preSet :: (Vertex v, Ord (VertexID v)) => v -> LabelledGraph v e -> [v] preSet (vertexID -> zid) g = From 58b65dc79b06311791443627cb24bf74c0a8de09 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 18 May 2020 13:07:57 -0400 Subject: [PATCH 05/14] Scroll past the uplink tree automatically Uses JS to scroll to the zettel title. --- src/app/Neuron/Web/View.hs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/app/Neuron/Web/View.hs b/src/app/Neuron/Web/View.hs index 70a6910f1..dc172068c 100644 --- a/src/app/Neuron/Web/View.hs +++ b/src/app/Neuron/Web/View.hs @@ -189,13 +189,16 @@ renderZettel :: PandocBuilder t m => Config -> (ZettelGraph, Zettel) -> m () renderZettel config (graph, z@Zettel {..}) = do let upTree = G.backlinkForest Folgezettel z graph whenNotNull upTree $ \_ -> do - elAttr "div" ("class" =: "flipped tree" <> "style" =: "transform-origin: 50%") $ do + elAttr "div" ("class" =: "flipped tree" <> "id" =: "zettel-uptree" <> "style" =: "transform-origin: 50%") $ do el "ul" $ do el "li" $ do divClass "forest-link" $ el "a" $ text zettelTitle el "ul" $ do renderForestNG True Nothing Nothing upTree - divClass "ui text container" $ do + elAttr "div" ("class" =: "ui text container" <> "id" =: "zettel-container" <> "style" =: "position: relative") $ do + -- zettel-container-anchor is a trick used by the scrollIntoView JS below + -- cf. https://stackoverflow.com/a/49968820/55246 + elAttr "div" ("id" =: "zettel-container-anchor" <> "style" =: "position: absolute; top: -14px; left: 0") blank divClass "zettel-view" $ do ZettelView.renderZettelContent z let cfBacklinks = G.backlinks OrdinaryConnection z graph @@ -206,6 +209,11 @@ renderZettel config (graph, z@Zettel {..}) = do renderForest True Nothing Nothing $ fmap (flip Node []) cfBacklinks renderFooter config graph (Just z) + -- Because the tree above can be pretty large, we scroll past it + -- automatically when the page loads. + -- TODO: Do this only if we have rendered the tree. + el "script" $ text $ + "document.getElementById(\"zettel-container-anchor\").scrollIntoView({behavior: \"smooth\", block: \"start\"});" -- renderZettelPanel config graph z -- elAttr "div" ("class" =: "tree" <> "style" =: "transform-origin: 50%") $ do -- el "ul" $ do From 131f583321a5a8304133a21b3294bde75f866514 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 18 May 2020 21:45:14 -0400 Subject: [PATCH 06/14] Remove unused code --- src/app/Neuron/Web/View.hs | 37 +++---------------------------------- 1 file changed, 3 insertions(+), 34 deletions(-) diff --git a/src/app/Neuron/Web/View.hs b/src/app/Neuron/Web/View.hs index dc172068c..eeb9da2dc 100644 --- a/src/app/Neuron/Web/View.hs +++ b/src/app/Neuron/Web/View.hs @@ -209,45 +209,14 @@ renderZettel config (graph, z@Zettel {..}) = do renderForest True Nothing Nothing $ fmap (flip Node []) cfBacklinks renderFooter config graph (Just z) + renderBrandFooter -- Because the tree above can be pretty large, we scroll past it -- automatically when the page loads. -- TODO: Do this only if we have rendered the tree. + -- FIXME: This may not scroll sufficiently if the images in the zettel haven't + -- loaded (thus the browser doesn't known the final height yet.) el "script" $ text $ "document.getElementById(\"zettel-container-anchor\").scrollIntoView({behavior: \"smooth\", block: \"start\"});" - -- renderZettelPanel config graph z - -- elAttr "div" ("class" =: "tree" <> "style" =: "transform-origin: 50%") $ do - -- el "ul" $ do - -- el "li" $ do - -- divClass "forest-link" $ el "a" $ text zettelTitle - -- el "ul" $ do - -- renderForestNG True (Just 2) Nothing $ G.frontlinkForest Folgezettel z graph - renderBrandFooter - -_renderZettelPanel :: DomBuilder t m => Config -> ZettelGraph -> Zettel -> m () -_renderZettelPanel config@Config {..} graph z@Zettel {..} = do - let neuronTheme = Theme.mkTheme theme - divClass ("ui inverted " <> Theme.semanticColor neuronTheme <> " top attached connections segment") $ do - divClass "ui two column grid" $ do - divClass "column" $ do - elAttr "div" ("class" =: "ui header" <> title =: "The following zettels branch to this zettel") $ - text "Uplinks" - el "ul" $ do - renderForest True Nothing Nothing $ - G.backlinkForest Folgezettel z graph - let cfBacklinks = G.backlinks OrdinaryConnection z graph - whenNotNull cfBacklinks $ \_ -> do - elAttr "div" ("class" =: "ui header" <> title =: "Zettels that link here, but without branching") $ - text "Backlinks" - el "ul" $ do - renderForest True Nothing Nothing $ - fmap (flip Node []) cfBacklinks - divClass "column" $ do - elAttr "div" ("class" =: "ui header" <> title =: "This zettel branches to the following zettels") $ - text "Downlinks" - el "ul" $ renderForest True (Just 2) Nothing $ - G.frontlinkForest Folgezettel z graph - renderFooter config graph (Just z) - renderBrandFooter renderFooter :: DomBuilder t m => Config -> ZettelGraph -> Maybe Zettel -> m () renderFooter Config {..} graph mzettel = do From 7bcd59c142d004ac30599c9dff8f23586c452b80 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 18 May 2020 22:09:18 -0400 Subject: [PATCH 07/14] Use folgezettel branch icon only in such links --- src/app/Neuron/Web/View.hs | 15 +++++++------ src/app/Neuron/Zettelkasten/Graph.hs | 6 ++++++ src/lib/Data/Graph/Labelled.hs | 1 + src/lib/Data/Graph/Labelled/Algorithm.hs | 6 ++++++ src/lib/Neuron/Zettelkasten/Connection.hs | 15 +++++++++++-- src/lib/Neuron/Zettelkasten/Query/View.hs | 25 +++++++++++++--------- src/lib/Neuron/Zettelkasten/Zettel/View.hs | 10 +++++---- 7 files changed, 55 insertions(+), 23 deletions(-) diff --git a/src/app/Neuron/Web/View.hs b/src/app/Neuron/Web/View.hs index eeb9da2dc..3eaad814b 100644 --- a/src/app/Neuron/Web/View.hs +++ b/src/app/Neuron/Web/View.hs @@ -144,7 +144,7 @@ renderIndex config@Config {..} graph = divClass "ui text container" $ do Left (toList -> cyc) -> divClass "ui orange segment" $ do el "h2" $ text "Cycle detected" forM_ cyc $ \zettel -> - el "li" $ ZettelView.renderZettelLink def zettel + el "li" $ ZettelView.renderZettelLink Nothing def zettel _ -> blank let clusters = G.categoryClusters graph el "p" $ do @@ -194,7 +194,7 @@ renderZettel config (graph, z@Zettel {..}) = do el "li" $ do divClass "forest-link" $ el "a" $ text zettelTitle el "ul" $ do - renderForestNG True Nothing Nothing upTree + renderForestNG (\z2 -> G.getConnection z z2 graph) True Nothing Nothing upTree elAttr "div" ("class" =: "ui text container" <> "id" =: "zettel-container" <> "style" =: "position: relative") $ do -- zettel-container-anchor is a trick used by the scrollIntoView JS below -- cf. https://stackoverflow.com/a/49968820/55246 @@ -268,7 +268,7 @@ renderForest isRoot maxLevel mg trees = divClass (maybe "" (const "ui black label") mg) bool id zettelDiv isRoot $ - ZettelView.renderZettelLink def zettel + ZettelView.renderZettelLink Nothing def zettel whenJust mg $ \g -> do text " " case G.backlinks Folgezettel zettel g of @@ -286,6 +286,7 @@ renderForest isRoot maxLevel mg trees = renderForestNG :: DomBuilder t m => + (Zettel -> Maybe Connection) -> Bool -> Maybe Int -> -- When given the zettelkasten graph, also show non-parent backlinks. @@ -293,7 +294,7 @@ renderForestNG :: Maybe ZettelGraph -> [Tree Zettel] -> m () -renderForestNG _isRoot maxLevel mg trees = +renderForestNG getConn _isRoot maxLevel mg trees = case maxLevel of Just 0 -> blank _ -> do @@ -301,7 +302,7 @@ renderForestNG _isRoot maxLevel mg trees = el "li" $ do let linkDivClass = maybe "forest-link" (const "ui black label forest-link") mg divClass linkDivClass $ - ZettelView.renderZettelLink def zettel + ZettelView.renderZettelLink (getConn zettel) def zettel whenJust mg $ \g -> do text " " case G.backlinks Folgezettel zettel g of @@ -312,7 +313,7 @@ renderForestNG _isRoot maxLevel mg trees = elAttr "i" ("class" =: "fas fa-link" <> "title" =: connTitle) blank _ -> blank when (length subtrees > 0) $ do - el "ul" $ renderForestNG False ((\n -> n - 1) <$> maxLevel) mg subtrees + el "ul" $ renderForestNG getConn False ((\n -> n - 1) <$> maxLevel) mg subtrees where -- Sort trees so that trees containing the most recent zettel (by ID) come first. sortForest = reverse . sortOn maximum @@ -375,7 +376,7 @@ style Config {..} = do pureCssTreeDiagram :: Css pureCssTreeDiagram = do -- TODO: should only apply for folgezettel - ".zettel-link-container::after" ? do + ".zettel-link-container.folgezettel::after" ? do C.paddingLeft $ em 0.3 C.content $ stringContent "ᛦ" let cellBorderWidth = px 2 diff --git a/src/app/Neuron/Zettelkasten/Graph.hs b/src/app/Neuron/Zettelkasten/Graph.hs index 5ef35f8d4..a2f3c0a59 100644 --- a/src/app/Neuron/Zettelkasten/Graph.hs +++ b/src/app/Neuron/Zettelkasten/Graph.hs @@ -17,6 +17,7 @@ module Neuron.Zettelkasten.Graph -- * Graph functions getZettels, getZettel, + getConnection, topSort, frontlinkForest, backlinkForest, @@ -28,6 +29,7 @@ where import Control.Monad.Except (MonadError, liftEither, runExceptT, withExceptT) import Control.Monad.Writer (runWriterT) +import Data.Default import Data.Foldable (maximum) import qualified Data.Graph.Labelled as G import Data.Traversable (for) @@ -123,3 +125,7 @@ getZettels = G.getVertices getZettel :: ZettelID -> ZettelGraph -> Maybe Zettel getZettel = G.findVertex + +-- | If no connection exists, this returns Nothing. +getConnection :: Zettel -> Zettel -> ZettelGraph -> Maybe Connection +getConnection z1 z2 g = fmap (fromMaybe def) $ G.edgeLabel g z1 z2 diff --git a/src/lib/Data/Graph/Labelled.hs b/src/lib/Data/Graph/Labelled.hs index 3118efbcb..61a34dc7d 100644 --- a/src/lib/Data/Graph/Labelled.hs +++ b/src/lib/Data/Graph/Labelled.hs @@ -12,6 +12,7 @@ module Data.Graph.Labelled findVertex, getVertices, hasEdge, + edgeLabel, -- * Algorithms preSet, diff --git a/src/lib/Data/Graph/Labelled/Algorithm.hs b/src/lib/Data/Graph/Labelled/Algorithm.hs index 348f486af..4ef274c6d 100644 --- a/src/lib/Data/Graph/Labelled/Algorithm.hs +++ b/src/lib/Data/Graph/Labelled/Algorithm.hs @@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} @@ -32,6 +33,11 @@ hasEdge :: (Ord (VertexID v), Vertex v) => LabelledGraph v e -> v -> v -> Bool hasEdge (LabelledGraph g _) x y = LAM.hasEdge (vertexID x) (vertexID y) g +edgeLabel :: (Monoid e, Ord (VertexID v), Vertex v) => LabelledGraph v e -> v -> v -> Maybe e +edgeLabel lg@(LabelledGraph g _) x y = do + guard $ hasEdge lg x y + pure $ LAM.edgeLabel (vertexID x) (vertexID y) g + -- | Return the backlinks to the given vertex preSet :: (Vertex v, Ord (VertexID v)) => v -> LabelledGraph v e -> [v] preSet (vertexID -> zid) g = diff --git a/src/lib/Neuron/Zettelkasten/Connection.hs b/src/lib/Neuron/Zettelkasten/Connection.hs index c4a1d6777..56f428d32 100644 --- a/src/lib/Neuron/Zettelkasten/Connection.hs +++ b/src/lib/Neuron/Zettelkasten/Connection.hs @@ -1,11 +1,14 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NoImplicitPrelude #-} module Neuron.Zettelkasten.Connection where import Data.Aeson (ToJSON) -import Relude +import Data.Default +import Relude hiding (show) +import Text.Show -- | Represent the connection between zettels data Connection @@ -14,10 +17,18 @@ data Connection Folgezettel | -- | Any other ordinary connection (eg: "See also") OrdinaryConnection - deriving (Eq, Ord, Show, Enum, Bounded, Generic, ToJSON) + deriving (Eq, Ord, Enum, Bounded, Generic, ToJSON) instance Semigroup Connection where -- A folgezettel link trumps all other kinds in that zettel. Folgezettel <> _ = Folgezettel _ <> Folgezettel = Folgezettel OrdinaryConnection <> OrdinaryConnection = OrdinaryConnection + +instance Default Connection where + def = Folgezettel + +instance Show Connection where + show = \case + Folgezettel -> "folgezettel" + OrdinaryConnection -> "cf" diff --git a/src/lib/Neuron/Zettelkasten/Query/View.hs b/src/lib/Neuron/Zettelkasten/Query/View.hs index 12db9b28c..bd7b1eef6 100644 --- a/src/lib/Neuron/Zettelkasten/Query/View.hs +++ b/src/lib/Neuron/Zettelkasten/Query/View.hs @@ -28,6 +28,7 @@ import Data.Some import Data.TagTree (Tag (..), TagNode (..), TagPattern (..), constructTag, foldTagTree, tagMatchAny, tagTree) import qualified Data.Text as T import Data.Tree +import Neuron.Zettelkasten.Connection import Neuron.Zettelkasten.ID import Neuron.Zettelkasten.Query import Neuron.Zettelkasten.Query.Error (QueryResultError (..)) @@ -39,19 +40,19 @@ import qualified Text.Pandoc.Builder as B -- | Build the Pandoc AST for query results buildQueryView :: MonadError QueryResultError m => DSum Query Identity -> m (Either B.Inline B.Block) buildQueryView = \case - Query_ZettelByID zid _mconn :=> Identity mres -> + Query_ZettelByID zid (fromMaybe def -> conn) :=> Identity mres -> case mres of Nothing -> throwError $ QueryResultError_NoSuchZettel zid Just target -> do - pure $ Left $ buildZettelLink Nothing target - q@(Query_ZettelsByTag pats _mconn view) :=> Identity res -> + pure $ Left $ buildZettelLink (Just conn) Nothing target + q@(Query_ZettelsByTag pats (fromMaybe def -> conn) view) :=> Identity res -> pure $ Right $ B.Div B.nullAttr [ buildQueryName $ Some q, case zettelsViewGroupByTag view of False -> - buildZettelsLinks (zettelsViewLinkView view) res + buildZettelsLinks (Just conn) (zettelsViewLinkView view) res True -> B.Div B.nullAttr $ flip fmap (Map.toList $ groupZettelsByTagsMatching pats res) @@ -64,7 +65,7 @@ buildQueryView = \case [ fontAwesomeIcon "fas fa-tag", B.Str (unTag tag) ], - buildZettelsLinks (zettelsViewLinkView view) zettelGrp + buildZettelsLinks (Just conn) (zettelsViewLinkView view) zettelGrp ] ] q@(Query_Tags _) :=> Identity res -> @@ -84,13 +85,17 @@ buildQueryView = \case mkAttr :: Text -> [(Text, Text)] -> B.Attr mkAttr cls kvs = ("", words cls, kvs) - buildZettelLink (fromMaybe def -> LinkView {..}) Zettel {..} = - let linkTooltip = + -- TODO: This should be consolidated with renderZettelLink in Zettel/View.hs + -- (see module comment in this file) + buildZettelLink :: Maybe Connection -> Maybe LinkView -> Zettel -> B.Inline + buildZettelLink conn (fromMaybe def -> LinkView {..}) Zettel {..} = + let connClass = bool "cf" "folgezettel" $ conn == Just Folgezettel + linkTooltip = if null zettelTags then Nothing else Just $ "Tags: " <> T.intercalate "; " (unTag <$> zettelTags) in B.Span - (mkAttr "zettel-link-container" mempty) + (mkAttr ("zettel-link-container " <> connClass) mempty) $ catMaybes [ if linkViewShowDate then case zettelDay of @@ -101,10 +106,10 @@ buildQueryView = \case Just $ B.Span (mkAttr "zettel-link" $ semanticUITooltipAttrs linkTooltip) $ pure $ B.Link mempty [B.Str zettelTitle] (zettelUrl zettelID, "") ] - buildZettelsLinks view zs = + buildZettelsLinks mconn view zs = B.BulletList $ zs <&> \z -> - [B.Plain $ pure $ buildZettelLink (Just view) z] + [B.Plain $ pure $ buildZettelLink mconn (Just view) z] semanticUITooltipAttrs :: Maybe Text -> [(Text, Text)] semanticUITooltipAttrs = maybe [] $ \s -> [ ("data-tooltip", s), diff --git a/src/lib/Neuron/Zettelkasten/Zettel/View.hs b/src/lib/Neuron/Zettelkasten/Zettel/View.hs index f7f4870fa..7f5d6ec7f 100644 --- a/src/lib/Neuron/Zettelkasten/Zettel/View.hs +++ b/src/lib/Neuron/Zettelkasten/Zettel/View.hs @@ -19,6 +19,7 @@ import qualified Clay as C import Data.TagTree import qualified Data.Text as T import qualified Neuron.Web.Theme as Theme +import Neuron.Zettelkasten.Connection import Neuron.Zettelkasten.Query.Theme (LinkView (..)) import Neuron.Zettelkasten.Query.View (tagUrl, zettelUrl) import Neuron.Zettelkasten.Zettel @@ -52,9 +53,10 @@ renderTags tags = do el "p" blank -- | Render a link to an individual zettel. -renderZettelLink :: DomBuilder t m => Maybe LinkView -> Zettel -> m () -renderZettelLink (fromMaybe def -> LinkView {..}) Zettel {..} = do - let mextra = +renderZettelLink :: DomBuilder t m => Maybe Connection -> Maybe LinkView -> Zettel -> m () +renderZettelLink conn (fromMaybe def -> LinkView {..}) Zettel {..} = do + let connClass = maybe "" show conn + mextra = if linkViewShowDate then case zettelDay of Just day -> @@ -62,7 +64,7 @@ renderZettelLink (fromMaybe def -> LinkView {..}) Zettel {..} = do Nothing -> Nothing else Nothing - elClass "span" "zettel-link-container" $ do + elClass "span" ("zettel-link-container " <> connClass) $ do forM_ mextra $ \extra -> elClass "span" "extra monoFont" $ text extra let linkTooltip = From add59959a8b33e36a5c71ea99e8d83cfc44319aa Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Tue, 19 May 2020 11:10:07 -0400 Subject: [PATCH 08/14] Up ver --- neuron.cabal | 2 +- test/Neuron/VersionSpec.hs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/neuron.cabal b/neuron.cabal index 7e24b5b66..5d60aa639 100644 --- a/neuron.cabal +++ b/neuron.cabal @@ -1,7 +1,7 @@ cabal-version: 2.4 name: neuron -- This version must be in sync with what's in Default.dhall -version: 0.5.0.0 +version: 0.5.1.0 license: AGPL-3.0-only copyright: 2020 Sridhar Ratnakumar maintainer: srid@srid.ca diff --git a/test/Neuron/VersionSpec.hs b/test/Neuron/VersionSpec.hs index 2fa6cc9b4..ecc8895cc 100644 --- a/test/Neuron/VersionSpec.hs +++ b/test/Neuron/VersionSpec.hs @@ -29,9 +29,9 @@ spec = do it "full versions" $ do "0.6.1.2" `isGreater` olderThan "0.5.3" `isGreater` olderThan - "0.5.1.8" `isGreater` olderThan - "0.5.0.0" `isLesserOrEqual` olderThan -- This is current version + "0.5.2.8" `isGreater` olderThan + "0.5.1.0" `isLesserOrEqual` olderThan -- This is current version "0.3.1.0" `isLesserOrEqual` olderThan it "within same major version" $ do - "0.5.1.8" `isGreater` olderThan - "0.5.0.0" `isLesserOrEqual` olderThan -- This is current version + "0.5.2.8" `isGreater` olderThan + "0.5.1.0" `isLesserOrEqual` olderThan -- This is current version From bd0b20dab7225a2ba87d0c6f7de2d5cf1add634c Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Tue, 19 May 2020 11:14:14 -0400 Subject: [PATCH 09/14] Refactor, and make uptree compact --- src/app/Neuron/Web/View.hs | 34 ++++++---------------- src/lib/Neuron/Zettelkasten/Zettel/View.hs | 20 +++++++++++++ 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/app/Neuron/Web/View.hs b/src/app/Neuron/Web/View.hs index 3eaad814b..707d6298f 100644 --- a/src/app/Neuron/Web/View.hs +++ b/src/app/Neuron/Web/View.hs @@ -190,9 +190,8 @@ renderZettel config (graph, z@Zettel {..}) = do let upTree = G.backlinkForest Folgezettel z graph whenNotNull upTree $ \_ -> do elAttr "div" ("class" =: "flipped tree" <> "id" =: "zettel-uptree" <> "style" =: "transform-origin: 50%") $ do - el "ul" $ do + elClass "ul" "root" $ do el "li" $ do - divClass "forest-link" $ el "a" $ text zettelTitle el "ul" $ do renderForestNG (\z2 -> G.getConnection z z2 graph) True Nothing Nothing upTree elAttr "div" ("class" =: "ui text container" <> "id" =: "zettel-container" <> "style" =: "position: relative") $ do @@ -321,23 +320,13 @@ renderForestNG getConn _isRoot maxLevel mg trees = style :: Config -> Css style Config {..} = do let neuronTheme = Theme.mkTheme theme - linkColor = Theme.withRgb neuronTheme C.rgb ".ui.label span.fas" ? do C.marginRight $ em 0.3 - "span.zettel-link-container span.zettel-link a" ? do - C.fontWeight C.bold - C.color linkColor - C.textDecoration C.none - "span.zettel-link-container span.zettel-link a:hover" ? do - C.backgroundColor linkColor - C.color C.white - "span.zettel-link-container span.extra" ? do - C.color C.auto - C.paddingRight $ em 0.3 "div.z-index" ? do C.ul ? do C.listStyleType C.square C.paddingLeft $ em 1.5 + ZettelView.zettelLinkCss neuronTheme "div.zettel-view" ? do -- This list styling applies both to zettel content, and the rest of the -- view (eg: connections pane) @@ -352,11 +341,6 @@ style Config {..} = do C.fontWeight C.bold "a.inactive" ? do C.color "#555" - "div.connections" ? do - "a" ? do - C.important $ color white - "a:hover" ? do - C.opacity 0.5 ".footer" ? do "a" ? do C.color white @@ -366,8 +350,6 @@ style Config {..} = do C.fontWeight C.bold ".footer-version" ? do C.fontSize $ em 0.7 - "[data-tooltip]:after" ? do - C.fontSize $ em 0.7 pureCssTreeDiagram ".backlinks" ? do opacity 0.5 @@ -375,10 +357,6 @@ style Config {..} = do -- https://codepen.io/philippkuehn/pen/QbrOaN pureCssTreeDiagram :: Css pureCssTreeDiagram = do - -- TODO: should only apply for folgezettel - ".zettel-link-container.folgezettel::after" ? do - C.paddingLeft $ em 0.3 - C.content $ stringContent "ᛦ" let cellBorderWidth = px 2 flipTree = False rotateDeg = deg 180 @@ -391,9 +369,13 @@ pureCssTreeDiagram = do C.transform $ C.rotate rotateDeg -- Clay does not support this; doing it inline in div style. -- C.transformOrigin $ pct 50 + "ul.root" ? do + -- Make the tree attach to zettel segment + C.paddingTop $ px 0 + C.marginTop $ px 0 "ul" ? do C.position relative - sym2 C.padding (em 1) 0 + C.padding (em 1) 0 0 0 C.whiteSpace nowrap sym2 C.margin (px 0) auto C.textAlign center @@ -401,6 +383,8 @@ pureCssTreeDiagram = do C.content $ stringContent "" C.display C.displayTable C.clear both + C.lastChild & do + C.paddingBottom $ em 0.1 "li" ? do C.display C.inlineBlock C.verticalAlign C.vAlignTop diff --git a/src/lib/Neuron/Zettelkasten/Zettel/View.hs b/src/lib/Neuron/Zettelkasten/Zettel/View.hs index 7f5d6ec7f..c28a1abd7 100644 --- a/src/lib/Neuron/Zettelkasten/Zettel/View.hs +++ b/src/lib/Neuron/Zettelkasten/Zettel/View.hs @@ -10,6 +10,7 @@ module Neuron.Zettelkasten.Zettel.View ( renderZettelContent, renderZettelLink, zettelCss, + zettelLinkCss, ) where @@ -83,6 +84,25 @@ renderZettelLink conn (fromMaybe def -> LinkView {..}) Zettel {..} = do <> "data-position" =: "right center" ) +zettelLinkCss :: Theme.Theme -> Css +zettelLinkCss neuronTheme = do + let linkColor = Theme.withRgb neuronTheme C.rgb + "span.zettel-link-container span.zettel-link a" ? do + C.fontWeight C.bold + C.color linkColor + C.textDecoration C.none + "span.zettel-link-container span.zettel-link a:hover" ? do + C.backgroundColor linkColor + C.color C.white + "span.zettel-link-container span.extra" ? do + C.color C.auto + C.paddingRight $ em 0.3 + "span.zettel-link-container.folgezettel::after" ? do + C.paddingLeft $ em 0.3 + C.content $ C.stringContent "ᛦ" + "[data-tooltip]:after" ? do + C.fontSize $ em 0.7 + zettelCss :: Theme.Theme -> Css zettelCss neuronTheme = do let linkColor = Theme.withRgb neuronTheme C.rgb From 44e4a6f72c070b06d1c6d24a514830f2797eecbd Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Tue, 19 May 2020 11:37:35 -0400 Subject: [PATCH 10/14] Remove redirecting examples --- guide/2013101.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/guide/2013101.md b/guide/2013101.md index dc9b3222b..e4bc31a82 100644 --- a/guide/2013101.md +++ b/guide/2013101.md @@ -9,9 +9,6 @@ Here is a list of public Zettelkastens that are managed by neuron: - [haskell.zettel.page](https://haskell.zettel.page) ([source](https://github.com/srid/haskell-zettelkasten)): A public Zettekasten for the [Haskell](https://www.haskell.org/) community. - [www.srid.ca](https://www.srid.ca/) ([source](https://github.com/srid/srid.ca)): Personal homepage of Srid; a demonstration of using Neuron for creating your personal website. -- [emacs.zettel.page](https://emacs.zettel.page) - ([source](https://github.com/srid/emacs.zettel.page)): A public Zettekasten - for Emacs (still in its infancy) - [rib.srid.ca](https://rib.srid.ca/) ([source](https://github.com/srid/rib/tree/master/guide)): Rib project website. If you are hosting your own Zettelkasten publicly and would like to include it in this list, edit this page (using the link below) to open a pull request. From a645286058b61780c316f3fddd70a5661fddfb8d Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Tue, 19 May 2020 11:37:44 -0400 Subject: [PATCH 11/14] Compatify and de-emphasize the tree even more --- src/app/Neuron/Web/View.hs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/app/Neuron/Web/View.hs b/src/app/Neuron/Web/View.hs index 707d6298f..5d7b7e634 100644 --- a/src/app/Neuron/Web/View.hs +++ b/src/app/Neuron/Web/View.hs @@ -351,6 +351,8 @@ style Config {..} = do ".footer-version" ? do C.fontSize $ em 0.7 pureCssTreeDiagram + ".backlinks:hover" ? do + opacity 1 ".backlinks" ? do opacity 0.5 @@ -362,8 +364,11 @@ pureCssTreeDiagram = do rotateDeg = deg 180 ".tree.flipped" ? do C.transform $ C.rotate rotateDeg + ".tree:hover" ? do + C.opacity 1 ".tree" ? do C.overflow auto + C.opacity 0.5 fontSize $ em 0.9 when flipTree $ do C.transform $ C.rotate rotateDeg @@ -432,7 +437,7 @@ pureCssTreeDiagram = do "li" ? do "div.forest-link" ? do border solid cellBorderWidth "#ccc" - sym2 C.padding (em 0.5) (em 0.75) + sym2 C.padding (em 0.2) (em 0.3) C.textDecoration none C.display inlineBlock sym C.borderRadius (px 5) From 9da340eb301cfce7b31543eb51843bf107c9fd4a Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Tue, 19 May 2020 11:46:07 -0400 Subject: [PATCH 12/14] Deemphasize css mod, and simplify backlinks render --- src/app/Neuron/Web/View.hs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/app/Neuron/Web/View.hs b/src/app/Neuron/Web/View.hs index 5d7b7e634..dd91cf233 100644 --- a/src/app/Neuron/Web/View.hs +++ b/src/app/Neuron/Web/View.hs @@ -189,7 +189,7 @@ renderZettel :: PandocBuilder t m => Config -> (ZettelGraph, Zettel) -> m () renderZettel config (graph, z@Zettel {..}) = do let upTree = G.backlinkForest Folgezettel z graph whenNotNull upTree $ \_ -> do - elAttr "div" ("class" =: "flipped tree" <> "id" =: "zettel-uptree" <> "style" =: "transform-origin: 50%") $ do + elAttr "div" ("class" =: "flipped tree deemphasized" <> "id" =: "zettel-uptree" <> "style" =: "transform-origin: 50%") $ do elClass "ul" "root" $ do el "li" $ do el "ul" $ do @@ -201,12 +201,12 @@ renderZettel config (graph, z@Zettel {..}) = do divClass "zettel-view" $ do ZettelView.renderZettelContent z let cfBacklinks = G.backlinks OrdinaryConnection z graph - whenNotNull cfBacklinks $ \_ -> divClass "ui attached segment backlinks" $ do + whenNotNull cfBacklinks $ \_ -> divClass "ui attached segment deemphasized" $ do elAttr "div" ("class" =: "ui header" <> title =: "Zettels that link here, but without branching") $ text "More backlinks" el "ul" $ do - renderForest True Nothing Nothing $ - fmap (flip Node []) cfBacklinks + forM_ cfBacklinks $ \zl -> + el "li" $ ZettelView.renderZettelLink Nothing def zl renderFooter config graph (Just z) renderBrandFooter -- Because the tree above can be pretty large, we scroll past it @@ -351,10 +351,13 @@ style Config {..} = do ".footer-version" ? do C.fontSize $ em 0.7 pureCssTreeDiagram - ".backlinks:hover" ? do + ".deemphasized" ? do + fontSize $ em 0.85 + ".deemphasized:hover" ? do opacity 1 - ".backlinks" ? do + ".deemphasized:not(:hover)" ? do opacity 0.5 + "a" ? important (color gray) -- https://codepen.io/philippkuehn/pen/QbrOaN pureCssTreeDiagram :: Css @@ -364,12 +367,8 @@ pureCssTreeDiagram = do rotateDeg = deg 180 ".tree.flipped" ? do C.transform $ C.rotate rotateDeg - ".tree:hover" ? do - C.opacity 1 ".tree" ? do C.overflow auto - C.opacity 0.5 - fontSize $ em 0.9 when flipTree $ do C.transform $ C.rotate rotateDeg -- Clay does not support this; doing it inline in div style. From 4939b733c828f87e4c0f7c0c62b23905889dac67 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Tue, 19 May 2020 11:51:55 -0400 Subject: [PATCH 13/14] Refactor --- src/app/Neuron/Web/View.hs | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/src/app/Neuron/Web/View.hs b/src/app/Neuron/Web/View.hs index dd91cf233..acbb445d7 100644 --- a/src/app/Neuron/Web/View.hs +++ b/src/app/Neuron/Web/View.hs @@ -193,7 +193,7 @@ renderZettel config (graph, z@Zettel {..}) = do elClass "ul" "root" $ do el "li" $ do el "ul" $ do - renderForestNG (\z2 -> G.getConnection z z2 graph) True Nothing Nothing upTree + renderUplinkForest (\z2 -> G.getConnection z z2 graph) upTree elAttr "div" ("class" =: "ui text container" <> "id" =: "zettel-container" <> "style" =: "position: relative") $ do -- zettel-container-anchor is a trick used by the scrollIntoView JS below -- cf. https://stackoverflow.com/a/49968820/55246 @@ -248,6 +248,7 @@ renderBrandFooter = fa :: DomBuilder t m => Text -> m () fa k = elClass "i" k blank +-- | Used in z-index page renderForest :: DomBuilder t m => Bool -> @@ -283,36 +284,18 @@ renderForest isRoot maxLevel mg trees = -- Sort trees so that trees containing the most recent zettel (by ID) come first. sortForest = reverse . sortOn maximum -renderForestNG :: +renderUplinkForest :: DomBuilder t m => (Zettel -> Maybe Connection) -> - Bool -> - Maybe Int -> - -- When given the zettelkasten graph, also show non-parent backlinks. - -- The dfsForest tree is "incomplete" in that it lacks these references. - Maybe ZettelGraph -> [Tree Zettel] -> m () -renderForestNG getConn _isRoot maxLevel mg trees = - case maxLevel of - Just 0 -> blank - _ -> do - forM_ (sortForest trees) $ \(Node zettel subtrees) -> - el "li" $ do - let linkDivClass = maybe "forest-link" (const "ui black label forest-link") mg - divClass linkDivClass $ - ZettelView.renderZettelLink (getConn zettel) def zettel - whenJust mg $ \g -> do - text " " - case G.backlinks Folgezettel zettel g of - conns@(_ : _ : _) -> - -- Has two or more category backlinks - forM_ conns $ \zettel2 -> do - let connTitle = (zettelIDText (zettelID zettel2) <> " " <> zettelTitle zettel2) - elAttr "i" ("class" =: "fas fa-link" <> "title" =: connTitle) blank - _ -> blank - when (length subtrees > 0) $ do - el "ul" $ renderForestNG getConn False ((\n -> n - 1) <$> maxLevel) mg subtrees +renderUplinkForest getConn trees = do + forM_ (sortForest trees) $ \(Node zettel subtrees) -> + el "li" $ do + divClass "forest-link" $ + ZettelView.renderZettelLink (getConn zettel) def zettel + when (length subtrees > 0) $ do + el "ul" $ renderUplinkForest getConn subtrees where -- Sort trees so that trees containing the most recent zettel (by ID) come first. sortForest = reverse . sortOn maximum From a42dbc76bef473ee9b23fac142c6f9381deb5bcd Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Tue, 19 May 2020 12:07:47 -0400 Subject: [PATCH 14/14] Docs --- CHANGELOG.md | 1 + guide/2011503.md | 9 ++++----- guide/2017401.md | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8255fdb1e..c87f55b8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ reST, etc.) - Switch to GHC 8.6 (for reflex-dom) - Raw HTML support (#191) +- Introduce new "uplink tree" view (#195) - Bug fixes - Fix 'neuron new' generating invalid Markdown when title contains special characters (#163) diff --git a/guide/2011503.md b/guide/2011503.md index f8e89134f..fbc9f150b 100644 --- a/guide/2011503.md +++ b/guide/2011503.md @@ -2,13 +2,12 @@ title: Graph view --- -A zettelkasten is a [directed graph](https://en.wikipedia.org/wiki/Directed_graph), and <2017401> is a subset of this graph established using special links. +A zettelkasten is a [directed graph](https://en.wikipedia.org/wiki/Directed_graph), and <2017401> is a subset of this graph established by having zettels branch off to other zettels. ## z-index -The z-index page (at `/z-index.html`; also linked in the footer) displays your Zettelkasten graph. It detects clusters in the graph, and renders each of them as a forest; see <2012301>. +The z-index page (at `/z-index.html`; also linked in the footer) displays your Zettelkasten graph. Neuron detects if there are any cycles in your Zettelkasten graph (use `cf` to resolve cycles). Then, it detects all <2012301> in the graph, and displays the <2017401> for each cluster. -## Connections - -Each zettel has a "connection" pane at the bottom. It shows both the children and the parent connections to other zettels (as defined by <2017401> connections), as well the backlinks not part of the tree. +## Uplinks and Backlinks +Uplinks are a kind of backlinks. Specifically an uplink tree of a zettel is the subset of the category tree which branch off to the zettel. Uplink tree is displayed above the zettel; other backlinks are displayed below. diff --git a/guide/2017401.md b/guide/2017401.md index b54f64615..2b8f7082b 100644 --- a/guide/2017401.md +++ b/guide/2017401.md @@ -2,9 +2,9 @@ title: Category Tree --- -Neuron allows you to organically build a category tree out of your Zettelkasten graph. This is achieved by <2011504?cf> without the `cf` flag. First, Neuron detects if there are any cycles in your Zettelkasten graph (use `cf` to resolve cycles). Then, it detects all clusters in the graph, and displays a tree view (aka. 'category tree') for each cluster. +Neuron allows you to organically build a hierarchy out of your Zettelkasten over time. This is achieved by <2011504?cf> *without* the `cf` flag. When a zettel links to another, it "branches of" to that zettel ... unless `cf` is used (in which case it is not a branch off). -The category tree is displayed in the z-index, as well as the connections panel of each zettel. +**Uplink trees** are a kind of category tree that display all zettels that branch off to a particular zettel. The uplink tree of a zettel is displayed at the top of each zettel page. ## See also