فهرست منبع

Merge branch 'master' into fix-IOS-bottom-fixed-position

Charlie 5 سال پیش
والد
کامیت
43740a8e58
67فایلهای تغییر یافته به همراه1691 افزوده شده و 2712 حذف شده
  1. 36 0
      LICENSE.md
  2. 32 24
      resources/css/common.css
  3. 1 1
      resources/index.html
  4. 0 0
      resources/js/lightning-fs.min.js
  5. 0 0
      resources/js/magic_portal.js
  6. 0 303
      resources/js/worker.js
  7. 0 1
      src/main/frontend/commands.cljs
  8. 1126 4
      src/main/frontend/components/block.cljs
  9. 0 4
      src/main/frontend/components/commit.cljs
  10. 1 3
      src/main/frontend/components/content.cljs
  11. 0 1
      src/main/frontend/components/datetime.cljs
  12. 6 8
      src/main/frontend/components/draw.cljs
  13. 2 9
      src/main/frontend/components/editor.cljs
  14. 2 2
      src/main/frontend/components/external.cljs
  15. 0 9
      src/main/frontend/components/file.cljs
  16. 0 1816
      src/main/frontend/components/hiccup.cljs
  17. 6 10
      src/main/frontend/components/journal.cljs
  18. 4 4
      src/main/frontend/components/onboarding.cljs
  19. 16 20
      src/main/frontend/components/page.cljs
  20. 1 7
      src/main/frontend/components/project.cljs
  21. 23 27
      src/main/frontend/components/reference.cljs
  22. 112 3
      src/main/frontend/components/repo.cljs
  23. 18 21
      src/main/frontend/components/right_sidebar.cljs
  24. 0 3
      src/main/frontend/components/search.cljs
  25. 7 15
      src/main/frontend/components/sidebar.cljs
  26. 0 101
      src/main/frontend/components/widgets.cljs
  27. 0 1
      src/main/frontend/core.cljs
  28. 0 1
      src/main/frontend/db.cljs
  29. 1 2
      src/main/frontend/db_mixins.cljs
  30. 0 2
      src/main/frontend/dicts.cljs
  31. 0 2
      src/main/frontend/extensions/code.cljs
  32. 7 2
      src/main/frontend/extensions/code.css
  33. 1 6
      src/main/frontend/extensions/sci.cljs
  34. 0 1
      src/main/frontend/extensions/slide.cljs
  35. 4 3
      src/main/frontend/external/roam.cljs
  36. 0 1
      src/main/frontend/format/adoc.cljs
  37. 0 2
      src/main/frontend/format/block.cljs
  38. 0 3
      src/main/frontend/format/mldoc.cljs
  39. 1 3
      src/main/frontend/fs.cljs
  40. 1 4
      src/main/frontend/git.cljs
  41. 1 3
      src/main/frontend/graph.cljs
  42. 0 1
      src/main/frontend/handler.cljs
  43. 19 0
      src/main/frontend/handler/common.cljs
  44. 4 5
      src/main/frontend/handler/dnd.cljs
  45. 0 6
      src/main/frontend/handler/draw.cljs
  46. 32 27
      src/main/frontend/handler/editor.cljs
  47. 0 2
      src/main/frontend/handler/expand.cljs
  48. 41 32
      src/main/frontend/handler/external.cljs
  49. 48 28
      src/main/frontend/handler/file.cljs
  50. 11 15
      src/main/frontend/handler/git.cljs
  51. 0 1
      src/main/frontend/handler/history.cljs
  52. 0 1
      src/main/frontend/handler/migration.cljs
  53. 10 5
      src/main/frontend/handler/page.cljs
  54. 93 106
      src/main/frontend/handler/repo.cljs
  55. 1 2
      src/main/frontend/history.cljs
  56. 0 1
      src/main/frontend/image.cljs
  57. 1 2
      src/main/frontend/keyboard.cljs
  58. 0 1
      src/main/frontend/keyboards.cljs
  59. 0 1
      src/main/frontend/page.cljs
  60. 0 1
      src/main/frontend/publishing.cljs
  61. 1 2
      src/main/frontend/publishing/html.cljs
  62. 0 1
      src/main/frontend/routes.cljs
  63. 0 1
      src/main/frontend/search.cljs
  64. 1 2
      src/main/frontend/security.cljs
  65. 10 27
      src/main/frontend/state.cljs
  66. 9 9
      src/main/frontend/tools/html_export.cljs
  67. 1 1
      src/main/frontend/version.cljs

+ 36 - 0
LICENSE.md

@@ -659,3 +659,39 @@ You should also get your employer (if you work as a programmer) or school,
 if any, to sign a "copyright disclaimer" for the program, if necessary.
 For more information on this, and how to apply and follow the GNU AGPL, see
 <https://www.gnu.org/licenses/>.
+
+Additional permission under GNU GPL version 3 section 7
+
+If you modify this Program, or any covered work, by linking or combining it with any of the below libraries (or a modified version of that library), containing parts covered by the terms of any of the below libraries, the licensors of this Program grant you additional permission to convey the resulting work.
+
+* cider/cider-nrepl
+* clojure-complete
+* com.cemerick/friend
+* compojure
+* environ
+* hiccup
+* medley
+* org.clojure/clojure
+* org.clojure/clojurescript
+* org.clojure/core.async
+* org.clojure/core.cache
+* org.clojure/core.incubator
+* org.clojure/core.logic
+* org.clojure/core.match
+* org.clojure/core.memoize
+* org.clojure/data.csv
+* org.clojure/data.json
+* org.clojure/data.priority-map
+* org.clojure/java.classpath
+* org.clojure/java.jdbc
+* org.clojure/math.numeric-tower
+* org.clojure/tools.analyzer
+* org.clojure/tools.analyzer.jvm
+* org.clojure/tools.logging
+* org.clojure/tools.macro
+* org.clojure/tools.namespace
+* org.clojure/tools.nrepl
+* org.clojure/tools.reader
+* org.tcrawley/dynapath
+* refactor-nrepl
+* slingshot

+ 32 - 24
resources/css/common.css

@@ -9,7 +9,7 @@
 .dark-theme {
   --ls-primary-background-color: #002b36;
   --ls-secondary-background-color: #073642;
-  --ls-tertiary-background-color: var(--ls-secondary-background-color);
+  --ls-tertiary-background-color: #0f4552;
   --ls-block-properties-background-color: #02222a;
   --ls-search-background-color: var(--ls-primary-background-color);
   --ls-border-color: #0e5263;
@@ -32,7 +32,7 @@
   --ls-page-checkbox-color: #6093a0;
   --ls-page-checkbox-border-color: var(--ls-primary-background-color);
   --ls-page-blockquote-color: var(--ls-primary-text-color);
-  --ls-page-blockquote-bg-color: var(--ls-tertiary-background-color);
+  --ls-page-blockquote-bg-color: var(--ls-secondary-background-color);
   --ls-page-blockquote-border-color: var(--ls-secondary-text-color);
   --ls-page-inline-code-color: var(--ls-primary-text-color);
   --ls-page-inline-code-bg-color: #01222a;
@@ -510,7 +510,7 @@ a.nav-item:hover, a.star-page:hover {
 
 .bg-base-2 {
   background-color: #f0f8ff;
-  background-color: var(--ls-tertiary-background-color);
+  background-color: var(--ls-secondary-background-color);
 }
 
 a.menu-link:hover, button.pull:hover, button.menu:focus {
@@ -548,7 +548,7 @@ button.menu {
 .form-checkbox {
   color: #137cbd;
   color: var(--ls-page-checkbox-color);
-  background-color: none;
+  background-color: transparent;
   background-color: var(--ls-page-checkbox-color);
   border: 1px solid;
   border-color: #808080;
@@ -570,7 +570,7 @@ input {
 /* } */
 
 .form-select {
-  background-color: none;
+  background-color: transparent;
   background-color: var(--ls-primary-background-color);
   background-repeat: no-repeat;
   background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='currentColor'%3e%3cpath d='M7 7l3-3 3 3m0 6l-3 3-3-3' stroke='%239fa6b2' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3e%3c/svg%3e");
@@ -643,10 +643,6 @@ hr {
 }
 
 a.button {
-  -webkit-appearance: button;
-  -moz-appearance: button;
-  appearance: button;
-
   text-decoration: none;
   color: #FFF;
   display: inline;
@@ -885,19 +881,19 @@ iframe {
   padding: 0;
   overflow: hidden;
 
-.embed-responsive-item,
-iframe,
-embed,
-object,
-video {
-  position: absolute;
-  top: 0;
-  left: 0;
-  bottom: 0;
-  height: 100%;
-  width: 100%;
-  border: 0;
-}
+  .embed-responsive-item,
+  iframe,
+  embed,
+  object,
+  video {
+    position: absolute;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    height: 100%;
+    width: 100%;
+    border: 0;
+  }
 
 }
 
@@ -1409,8 +1405,9 @@ a.tag:hover {
   border-color: var(--ls-page-checkbox-border-color);
 }
 
-.dark-theme #right-sidebar .bg-base-2 {
-  background-color: #0f4552;
+#right-sidebar .bg-base-2,
+#right-sidebar blockquote {
+  background-color: var(--ls-tertiary-background-color);
 }
 
 .white-theme a.right-sidebar-button {
@@ -1450,3 +1447,14 @@ a.tag:hover {
 }
 
 /* endregion */
+
+/* Hide scrollbar for IE, Edge and Firefox */
+.hide-scrollbar {
+  -ms-overflow-style: none; /* IE and Edge */
+  scrollbar-width: none; /* Firefox */
+}
+
+/* Hide scrollbar for Chrome, Safari and Opera */
+.hide-scrollbar::-webkit-scrollbar {
+  display: none;
+}

+ 1 - 1
resources/index.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html><head><meta charset="utf-8"><meta content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no" name="viewport"><meta content="Agp2znmEoRKqxMhzbNL2R3UOCNcagP7+fu0KSM+09O21u7EHdJgqhTrslpfyFC/dSt6jvpaDzNiFf2769fLHMAUAAABoeyJvcmlnaW4iOiJodHRwczovL2xvZ3NlcS5jb206NDQzIiwiZmVhdHVyZSI6Ik5hdGl2ZUZpbGVTeXN0ZW0yIiwiZXhwaXJ5IjoxNTk3Mjg5MzY5LCJpc1N1YmRvbWFpbiI6dHJ1ZX0=" http-equiv="origin-trial"><link href="https://asset.logseq.com/static/style.css" rel="stylesheet" type="text/css"><link href="https://asset.logseq.com/static/img/logo.png" rel="shortcut icon" type="image/png"><link href="https://asset.logseq.com/static/img/logo.png" rel="shortcut icon" sizes="192x192"><link href="https://asset.logseq.com/static/img/logo.png" rel="apple-touch-icon"><meta content="summary" name="twitter:card"><meta content="A local-first notes app which uses Git to store and sync your knowledge." name="twitter:description"><meta content="@logseq" name="twitter:site"><meta content="A local-first notes app." name="twitter:title"><meta content="https://asset.logseq.com/static/img/logo.png" name="twitter:image:src"><meta content="A local-first notes app." name="twitter:image:alt"><meta content="A local-first notes app." property="og:title"><meta content="site" property="og:type"><meta content="https://logseq.com" property="og:url"><meta content="https://asset.logseq.com/static/img/logo.png" property="og:image"><meta content="A local-first notes app which uses Git to store and sync your knowledge." property="og:description"><title>Logseq: A local-first notes app</title><meta content="logseq" property="og:site_name"><meta description="A local-first notes app which uses Git to store and sync your knowledge."><script crossorigin="anonymous" defer onload="if (window.location.host != &apos;localhost:3000&apos;) {
           Sentry.init({dsn: &apos;https://[email protected]/5311485&apos;});
-};" src="https://asset.logseq.com/static/js/sentry.min.js"></script></head><body><div id="root"></div><script>window.user={"name":"tiensonqin","email":"[email protected]","avatar":"https://avatars3.githubusercontent.com/u/479169?v=4","repos":[{"id":"bc80efff-1420-4eb7-9e07-9506b8d9bbe0","url":"https://github.com/tiensonqin/notes"}],"preferred_format":"org","encrypt_object_key":"snRsaP8r9VG6KsXxu0IfDA"};</script><script src="https://asset.logseq.com/static/js/mldoc.min.js"></script><script src="https://asset.logseq.com/static/js/magic_portal.js"></script><script>let worker = new Worker("/static/js/worker.js");
+};" src="https://asset.logseq.com/static/js/sentry.min.js"></script></head><body><div id="root"></div><script>window.user={"name":"tiensonqin","email":"[email protected]","avatar":"https://avatars3.githubusercontent.com/u/479169?v=4","repos":[{"id":"bc80efff-1420-4eb7-9e07-9506b8d9bbe0","url":"https://github.com/tiensonqin/notes"}],"preferred_format":"org","encrypt_object_key":"snRsaP8r9VG6KsXxu0IfDA"};</script><script src="https://asset.logseq.com/static/js/mldoc.min.js"></script><script src="/js/magic_portal.js"></script><script>let worker = new Worker("/js/worker.js");
 const portal = new MagicPortal(worker);
 ;(async () => {
   const git = await portal.get('git');

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
resources/js/lightning-fs.min.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
resources/js/magic_portal.js


+ 0 - 303
resources/js/worker.js

@@ -1,303 +0,0 @@
-importScripts(
-  // Batched optimization
-  "/static/js/lightning-fs.min.js?v=0.0.2.3",
-  "https://cdn.jsdelivr.net/npm/[email protected]/index.umd.min.js",
-  "https://cdn.jsdelivr.net/npm/[email protected]/http/web/index.umd.js",
-  // Fixed a bug
-  "/static/js/magic_portal.js"
-);
-
-const detect = () => {
-  if (typeof window !== 'undefined' && !self.skipWaiting) {
-    return 'window'
-  } else if (typeof self !== 'undefined' && !self.skipWaiting) {
-    return 'Worker'
-  } else if (typeof self !== 'undefined' && self.skipWaiting) {
-    return 'ServiceWorker'
-  }
-};
-
-function basicAuth (username, token) {
-  return "Basic " + btoa("tiensonqin:" + token);
-}
-
-const fsName = 'logseq';
-const createFS = () => new LightningFS(fsName);
-let fs = createFS();
-let pfs = fs.promises;
-
-if (detect() === 'Worker') {
-  const portal = new MagicPortal(self);
-  portal.set('git', git);
-  portal.set('fs', fs);
-  portal.set('pfs', pfs);
-  portal.set('gitHttp', GitHttp);
-  portal.set('workerThread', {
-    setConfig: function (dir, path, value) {
-      return git.setConfig ({
-        fs,
-        dir,
-        path,
-        value
-      });
-    },
-    clone: function (dir, url, corsProxy, depth, branch, username, token) {
-      return git.clone ({
-        fs,
-        dir,
-        http: GitHttp,
-        url,
-        corsProxy,
-        ref: branch,
-        singleBranch: true,
-        depth,
-        headers: {
-          "Authorization": basicAuth(username, token)
-        }
-      });
-    },
-    fetch: function (dir, url, corsProxy, depth, branch, username, token) {
-      return git.fetch ({
-        fs,
-        dir,
-        http: GitHttp,
-        url,
-        corsProxy,
-        ref: branch,
-        singleBranch: true,
-        depth,
-        headers: {
-          "Authorization": basicAuth(username, token)
-        }
-      });
-    },
-    pull: function (dir, corsProxy, branch, username, token) {
-      return git.pull ({
-        fs,
-        dir,
-        http: GitHttp,
-        corsProxy,
-        ref: branch,
-        singleBranch: true,
-        // fast: true,
-        headers: {
-          "Authorization": basicAuth(username, token)
-        }
-      });
-    },
-    push: function (dir, corsProxy, branch, force, username, token) {
-      return git.push ({
-        fs,
-        dir,
-        http: GitHttp,
-        ref: branch,
-        corsProxy,
-        remote: "origin",
-        force,
-        headers: {
-          "Authorization": basicAuth(username, token)
-        }
-      });
-    },
-    merge: function (dir, branch) {
-      return git.merge ({
-        fs,
-        dir,
-        ours: branch,
-        theirs: "remotes/origin/" + branch,
-        // fastForwardOnly: true
-      });
-    },
-    checkout: function (dir, branch) {
-      return git.checkout ({
-        fs,
-        dir,
-        ref: branch,
-      });
-    },
-    log: function (dir, branch, depth) {
-      return git.log ({
-        fs,
-        dir,
-        ref: branch,
-        depth,
-        singleBranch: true
-      })
-    },
-    add: function (dir, file) {
-      return git.add ({
-        fs,
-        dir,
-        filepath: file
-      });
-    },
-    remove: function (dir, file) {
-      return git.remove ({
-        fs,
-        dir,
-        filepath: file
-      });
-    },
-    commit: function (dir, message, name, email, parent) {
-      if (parent) {
-        return git.commit ({
-          fs,
-          dir,
-          message,
-          author: {name: name,
-                   email: email},
-          parent: parent
-        });
-      } else {
-        return git.commit ({
-          fs,
-          dir,
-          message,
-          author: {name: name,
-                   email: email}
-        });
-      }
-    },
-    readCommit: function (dir, oid) {
-      return git.readCommit ({
-        fs,
-        dir,
-        oid
-      });
-    },
-    readBlob: function (dir, oid, path) {
-      return git.readBlob ({
-        fs,
-        dir,
-        oid,
-        path
-      });
-    },
-    writeRef: function (dir, branch, oid) {
-      return git.writeRef ({
-        fs,
-        dir,
-        ref: "refs/heads/" + branch,
-        value: oid,
-        force: true
-      });
-    },
-    resolveRef: function (dir, ref) {
-      return git.resolveRef ({
-        fs,
-        dir,
-        ref
-      });
-    },
-    listFiles: function (dir, branch) {
-      return git.listFiles ({
-        fs,
-        dir,
-        ref: branch
-      });
-    },
-    rimraf: async function (path) {
-      // try {
-      //   // First assume path is itself a file
-      //   await pfs.unlink(path)
-      //   // if that worked we're done
-      //   return
-      // } catch (err) {
-      //   // Otherwise, path must be a directory
-      //   if (err.code !== 'EISDIR') throw err
-      // }
-      // Knowing path is a directory,
-      // first, assume everything inside path is a file.
-      let files = await pfs.readdir(path);
-      for (let file of files) {
-        let child = path + '/' + file
-        try {
-          await pfs.unlink(child)
-        } catch (err) {
-          if (err.code !== 'EISDIR') throw err
-        }
-      }
-      // Assume what's left are directories and recurse.
-      let dirs = await pfs.readdir(path)
-      for (let dir of dirs) {
-        let child = path + '/' + dir
-        await rimraf(child, pfs)
-      }
-      // Finally, delete the empty directory
-      await pfs.rmdir(path)
-    },
-    getFileStateChanges: async function (commitHash1, commitHash2, dir) {
-      return git.walk({
-        fs,
-        dir,
-        trees: [git.TREE({ ref: commitHash1 }), git.TREE({ ref: commitHash2 })],
-        map: async function(filepath, [A, B]) {
-          var type = 'equal';
-          if (A === null) {
-            type = "add";
-          }
-
-          if (B === null) {
-            type = "remove";
-          }
-
-          // ignore directories
-          if (filepath === '.') {
-            return
-          }
-          if ((A !== null && (await A.type()) === 'tree')
-              ||
-              (B !== null && (await B.type()) === 'tree')) {
-            return
-          }
-
-          // generate ids
-          const Aoid = A !== null && await A.oid();
-          const Boid = B !== null && await B.oid();
-
-          if (type === "equal") {
-            // determine modification type
-            if (Aoid !== Boid) {
-              type = 'modify'
-            }
-            if (Aoid === undefined) {
-              type = 'add'
-            }
-            if (Boid === undefined) {
-              type = 'remove'
-            }
-          }
-
-          if (Aoid === undefined && Boid === undefined) {
-            console.log('Something weird happened:')
-            console.log(A)
-            console.log(B)
-          }
-
-          return {
-            path: `/${filepath}`,
-            type: type,
-          }
-        },
-      })
-    },
-    statusMatrix: async function (dir) {
-      await git.statusMatrix({ fs, dir });
-    },
-    getChangedFiles: async function (dir) {
-      try {
-        const FILE = 0, HEAD = 1, WORKDIR = 2;
-
-        let filenames = (await git.statusMatrix({ fs, dir }))
-            .filter(row => row[HEAD] !== row[WORKDIR])
-            .map(row => row[FILE]);
-
-        return filenames;
-      } catch (err) {
-        console.error(err);
-        return [];
-      }
-    }
-  });
-  // self.addEventListener("message", ({ data }) => console.log(data));
-}

+ 0 - 1
src/main/frontend/commands.cljs

@@ -1,7 +1,6 @@
 (ns frontend.commands
   (:require [frontend.util :as util]
             [frontend.date :as date]
-            [frontend.text :as text]
             [frontend.state :as state]
             [frontend.search :as search]
             [clojure.string :as string]

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1126 - 4
src/main/frontend/components/block.cljs


+ 0 - 4
src/main/frontend/components/commit.cljs

@@ -1,12 +1,8 @@
 (ns frontend.components.commit
   (:require [rum.core :as rum]
             [frontend.util :as util :refer-macros [profile]]
-            [frontend.handler.git :as git-handler]
             [frontend.handler.repo :as repo-handler]
             [frontend.state :as state]
-            [clojure.string :as string]
-            [frontend.db :as db]
-            [frontend.ui :as ui]
             [frontend.mixins :as mixins]
             [goog.dom :as gdom]
             [goog.object :as gobj]))

+ 1 - 3
src/main/frontend/components/content.cljs

@@ -18,10 +18,8 @@
             [cljs.pprint :as pprint]
             [frontend.handler.notification :as notification]
             [frontend.components.editor :as editor]
-            [frontend.components.svg :as svg]
             [frontend.context.i18n :as i18n]
-            [frontend.text :as text]
-            [frontend.security :as security]))
+            [frontend.text :as text]))
 
 (defn- set-format-js-loading!
   [format value]

+ 0 - 1
src/main/frontend/components/datetime.cljs

@@ -4,7 +4,6 @@
             [frontend.components.svg :as svg]
             [frontend.date :as date]
             [frontend.state :as state]
-            [frontend.mixins :as mixins]
             [frontend.handler.repeated :as repeated]
             [frontend.handler.editor :as editor-handler]
             [cljs-time.core :as t]

+ 6 - 8
src/main/frontend/components/draw.cljs

@@ -3,14 +3,12 @@
             [goog.object :as gobj]
             [frontend.rum :as r]
             [frontend.util :as util :refer-macros [profile]]
-            [frontend.fs :as fs]
             [frontend.mixins :as mixins]
             [frontend.storage :as storage]
             [frontend.components.svg :as svg]
             [cljs-bean.core :as bean]
             [dommy.core :as d]
             [clojure.string :as string]
-            [frontend.date :as date]
             [frontend.handler.notification :as notification]
             [frontend.handler.draw :as draw :refer
              [*files
@@ -28,7 +26,7 @@
             [frontend.config :as config]
             [frontend.state :as state]
             [frontend.search :as search]
-            [frontend.components.widgets :as widgets]
+            [frontend.components.repo :as repo]
             [promesa.core :as p]
             [reitit.frontend.easy :as rfe]))
 
@@ -139,7 +137,7 @@
          "Please specify a title first!"
          :error)
         ;; TODO: focus the title input
-        )
+)
 
       (= title @*saving-title)
       nil
@@ -437,10 +435,10 @@
      (when current-repo
        [:div.absolute.top-4.right-4.hidden.md:block
         [:div.flex.flex-row.items-center
-         (widgets/sync-status current-repo)
-         (widgets/repos true
-                        (fn [repo]
-                          (reset! *current-file (get-last-file repo))))]])]))
+         (repo/sync-status current-repo)
+         (repo/repos-dropdown true
+                              (fn [repo]
+                                (reset! *current-file (get-last-file repo))))]])]))
 
 (rum/defcs draw-2 < rum/reactive
   {:init (fn [state]

+ 2 - 9
src/main/frontend/components/editor.cljs

@@ -3,20 +3,16 @@
             [frontend.components.svg :as svg]
             [frontend.config :as config]
             [frontend.handler.editor :as editor-handler :refer [get-state]]
-            [frontend.handler.image :as image-handler]
             [frontend.util :as util :refer-macros [profile]]
             [frontend.handler.file :as file]
             [frontend.handler.page :as page-handler]
             [frontend.components.datetime :as datetime-comp]
             [promesa.core :as p]
-            [frontend.date :as date]
             [frontend.state :as state]
             [frontend.mixins :as mixins]
-            [frontend.image :as image]
             [frontend.ui :as ui]
             [frontend.db :as db]
             [frontend.config :as config]
-            [frontend.utf8 :as utf8]
             [dommy.core :as d]
             [goog.object :as gobj]
             [goog.dom :as gdom]
@@ -29,12 +25,8 @@
                      *angle-bracket-caret-pos
                      *matched-block-commands
                      *show-block-commands]]
-            [frontend.format.block :as block]
             [medley.core :as medley]
-            [cljs-time.core :as t]
-            [cljs-time.coerce :as tc]
             [cljs-drag-n-drop.core :as dnd]
-            [frontend.search :as search]
             [frontend.text :as text]
             ["/frontend/utils" :as utils]))
 
@@ -640,7 +632,8 @@
                                         (not (editor-handler/in-auto-complete? (gdom/getElement id)))))
                                 (state/clear-edit!))))))
                       :node (gdom/getElement id)
-                      :visibilitychange? true))
+                      ;; :visibilitychange? true
+                      ))
                    100)
 
                   (when-let [element (gdom/getElement id)]

+ 2 - 2
src/main/frontend/components/external.cljs

@@ -27,8 +27,8 @@
                             (set! (.-onload reader)
                                   (fn [e]
                                     (let [text (.. e -target -result)]
-                                      (external-handler/import-from-roam-json! text)
-                                      (reset! *importing? false))))
+                                      (external-handler/import-from-roam-json! text
+                                                                               #(reset! *importing? false)))))
                             (.readAsText reader file)))
                         (notification/show! "Please choose a JSON file."
                                             :error))))}]

+ 0 - 9
src/main/frontend/components/file.cljs

@@ -2,24 +2,15 @@
   (:require [rum.core :as rum]
             [frontend.util :as util]
             [frontend.handler.project :as project]
-            [frontend.handler.ui :as ui-handler]
-            [frontend.handler.image :as image-handler]
-            [frontend.handler.file :as file]
             [frontend.handler.export :as export-handler]
-            [frontend.handler.page :as page-handler]
             [frontend.config :as config]
             [frontend.state :as state]
             [clojure.string :as string]
             [frontend.db :as db]
-            [frontend.components.hiccup :as hiccup]
-            [frontend.ui :as ui]
             [frontend.format :as format]
-            [frontend.format.mldoc :as mldoc]
             [frontend.components.content :as content]
             [frontend.components.lazy-editor :as lazy-editor]
             [frontend.config :as config]
-            [frontend.utf8 :as utf8]
-            [goog.dom :as gdom]
             [goog.object :as gobj]
             [frontend.date :as date]
             [cljs-time.coerce :as tc]

+ 0 - 1816
src/main/frontend/components/hiccup.cljs

@@ -1,1816 +0,0 @@
-(ns frontend.components.hiccup
-  (:refer-clojure :exclude [range])
-  (:require [frontend.config :as config]
-            [cljs.core.match :refer-macros [match]]
-            [clojure.string :as string]
-            [frontend.util :as util]
-            [rum.core :as rum]
-            [frontend.state :as state]
-            [frontend.db :as db]
-            [dommy.core :as d]
-            [datascript.core :as dc]
-            [goog.dom :as gdom]
-            [frontend.handler.expand :as expand]
-            [frontend.components.svg :as svg]
-            [frontend.components.draw :as draw]
-            [frontend.components.block :as block-comp]
-            [frontend.components.datetime :as datetime-comp]
-            [frontend.ui :as ui]
-            [frontend.components.widgets :as widgets]
-            [frontend.handler.ui :as ui-handler]
-            [frontend.handler.image :as image-handler]
-            [frontend.handler.editor :as editor-handler]
-            [frontend.handler.dnd :as dnd]
-            [frontend.handler.repeated :as repeated]
-            [goog.object :as gobj]
-            [medley.core :as medley]
-            [cljs.reader :as reader]
-            [frontend.util :as util :refer-macros [profile]]
-            [frontend.mixins :as mixins]
-            [frontend.db-mixins :as db-mixins]
-            [frontend.extensions.latex :as latex]
-            [frontend.components.lazy-editor :as lazy-editor]
-            [frontend.extensions.highlight :as highlight]
-            [frontend.extensions.sci :as sci]
-            ["/frontend/utils" :as utils]
-            [frontend.format.block :as block]
-            [clojure.walk :as walk]
-            [cljs-bean.core :as bean]
-            [frontend.handler.image :as image-handler]
-            [frontend.format.mldoc :as mldoc]
-            [frontend.text :as text]
-            [frontend.utf8 :as utf8]
-            [frontend.date :as date]
-            [frontend.security :as security]
-            [reitit.frontend.easy :as rfe]
-            [frontend.commands :as commands]))
-
-(defn safe-read-string
-  [s]
-  (try
-    (reader/read-string s)
-    (catch js/Error e
-      (println "read-string error:")
-      (js/console.error e)
-      [:div.warning {:title "read-string failed"}
-       s])))
-
-;; local state
-(defonce *block-children
-  (atom {}))
-
-(defonce *dragging?
-  (atom false))
-(defonce *dragging-block
-  (atom nil))
-(defonce *move-to-top?
-  (atom false))
-
-;; TODO: Improve blocks grouped by pages
-(defonce max-blocks-per-page 500)
-(defonce virtual-list-scroll-step 450)
-(defonce virtual-list-previous 50)
-
-(defonce container-ids (atom {}))
-(defonce container-idx (atom 0))
-
-;; TODO:
-;; add `key`
-
-(defn- remove-nils
-  [col]
-  (remove nil? col))
-
-(defn anchor-link
-  [s]
-  (.anchorLink js/window.Mldoc s))
-
-(defn vec-cat
-  [& args]
-  (->> (apply concat args)
-       remove-nils
-       vec))
-
-(defn ->elem
-  ([elem items]
-   (->elem elem nil items))
-  ([elem attrs items]
-   (let [elem (keyword elem)]
-     (if attrs
-       (vec
-        (cons elem
-              (cons attrs
-                    (seq items))))
-       (vec
-        (cons elem
-              (seq items)))))))
-
-(defn- join-lines
-  [l]
-  (string/trim (apply str l)))
-
-(defn- string-of-url
-  [url]
-  (match url
-    (:or ["File" s] ["Search" s])
-    s
-    ["Complex" m]
-    (let [{:keys [link protocol]} m]
-      (if (= protocol "file")
-        link
-        (str protocol ":" link)))))
-
-(defn- get-file-absolute-path
-  [config path]
-  (let [path (string/replace path "file:" "")
-        block-id (:block/uuid config)
-        current-file (and block-id
-                          (:file/path (:page/file (:block/page (db/entity [:block/uuid block-id])))))]
-    (when current-file
-      (let [parts (string/split current-file #"/")
-            parts-2 (string/split path #"/")
-            current-dir (string/join "/" (drop-last 1 parts))]
-        (cond
-          (util/starts-with? path "/")
-          path
-
-          (and (not (util/starts-with? path ".."))
-               (not (util/starts-with? path ".")))
-          (str current-dir "/" path)
-
-          :else
-          (let [parts (loop [acc []
-                             parts (reverse parts)
-                             col (reverse parts-2)]
-                        (if (empty? col)
-                          acc
-                          (let [[part parts] (case (first col)
-                                               ".."
-                                               [(first parts) (rest parts)]
-                                               "."
-                                               ["" parts]
-                                               [(first col) (rest parts)])]
-                            (recur (conj acc part)
-                                   parts
-                                   (rest col)))))
-                parts (remove #(string/blank? %) parts)]
-            (string/join "/" (reverse parts))))))))
-
-;; TODO: safe encoding asciis
-;; TODO: image link to another link
-(defn image-link [config url href label]
-  (let [href (if (util/starts-with? href "http")
-               href
-               (get-file-absolute-path config href))]
-    [:img.rounded-sm.shadow-xl.mb-2.mt-2
-     {:class "object-contain object-center"
-      :loading "lazy"
-      :style {:max-height "24rem"}
-      ;; :on-error (fn [])
-      :src href
-      :title (second (first label))}]))
-
-(defn repetition-to-string
-  [[[kind] [duration] n]]
-  (let [kind (case kind
-               "Dotted" "."
-               "Plus" "+"
-               "DoublePlus" "++")]
-    (str kind n (string/lower-case (str (first duration))))))
-
-(defn timestamp-to-string
-  [{:keys [active date time repetition wday active]}]
-  (let [{:keys [year month day]} date
-        {:keys [hour min]} time
-        [open close] (if active ["<" ">"] ["[" "]"])
-        repetition (if repetition
-                     (str " " (repetition-to-string repetition))
-                     "")
-        hour (if hour (util/zero-pad hour))
-        min  (if min (util/zero-pad min))
-        time (cond
-               (and hour min)
-               (util/format " %s:%s" hour min)
-               hour
-               (util/format " %s" hour)
-               :else
-               "")]
-    (util/format "%s%s-%s-%s %s%s%s%s"
-                 open
-                 (str year)
-                 (util/zero-pad month)
-                 (util/zero-pad day)
-                 wday
-                 time
-                 repetition
-                 close)))
-
-(defn timestamp [{:keys [active date time repetition wday] :as t} kind]
-  (let [prefix (case kind
-                 "Scheduled"
-                 [:i {:class "fa fa-calendar"
-                      :style {:margin-right 3.5}}]
-                 "Deadline"
-                 [:i {:class "fa fa-calendar-times-o"
-                      :style {:margin-right 3.5}}]
-                 "Date"
-                 nil
-                 "Closed"
-                 nil
-                 "Started"
-                 [:i {:class "fa fa-clock-o"
-                      :style {:margin-right 3.5}}]
-                 "Start"
-                 "From: "
-                 "Stop"
-                 "To: "
-                 nil)]
-    (let [class (if (= kind "Closed")
-                  "line-through")]
-      [:span.timestamp (cond-> {:active (str active)}
-                         class
-                         (assoc :class class))
-       prefix
-       (timestamp-to-string t)])))
-
-(defn range [{:keys [start stop]} stopped?]
-  [:div {:class "timestamp-range"
-         :stopped stopped?}
-   (timestamp start "Start")
-   (timestamp stop "Stop")])
-
-(declare map-inline)
-(declare block-cp)
-
-(declare page-reference)
-
-(defn page-cp
-  [{:keys [html-export? label children] :as config} page]
-  (when-let [page-name (:page/name page)]
-    (let [original-page-name (get page :page/original-name page-name)
-          original-page-name (if (date/valid-journal-title? original-page-name)
-                               (string/capitalize original-page-name)
-                               original-page-name)
-          page (string/lower-case page-name)
-          href (if html-export?
-                 (util/encode-str page)
-                 (rfe/href :page {:name page}))]
-      [:a.page-ref
-       {:href href
-        :on-click (fn [e]
-                    (util/stop e)
-                    (when (gobj/get e "shiftKey")
-                      (when-let [page-entity (db/entity [:page/name page])]
-                        (state/sidebar-add-block!
-                         (state/get-current-repo)
-                         (:db/id page-entity)
-                         :page
-                         {:page page-entity}))))}
-       (if (seq children)
-         (for [child children]
-           (if (= (first child) "Label")
-             [:span (last child)]
-             (let [{:keys [content children]} (last child)
-                   page-name (subs content 2 (- (count content) 2))]
-               (page-reference html-export? page-name (assoc config :children children) nil))))
-         (if (and label
-                  (string? label)
-                  (not (string/blank? label))) ; alias
-           label
-           original-page-name))])))
-
-(defn page-reference
-  [html-export? s config label]
-  [:span.page-reference
-   (when (and (not html-export?)
-              (not (= (:id config) "contents"))
-              (not (= (:id config) "Contents")))
-     [:span.text-gray-500 "[["])
-   (if (string/ends-with? s ".excalidraw")
-     [:a.page-ref
-      {:href (rfe/href :draw nil {:file (string/replace s (str config/default-draw-directory "/") "")})
-       :on-click (fn [e] (util/stop e))}
-      [:span
-       (svg/excalidraw-logo)
-       (string/capitalize (draw/get-file-title s))]]
-     (page-cp (assoc config
-                     :label (mldoc/plain->text label)) {:page/name s}))
-   (when (and (not html-export?)
-              (not (= (:id config) "contents"))
-              (not (= (:id config) "Contents")))
-     [:span.text-gray-500 "]]"])])
-
-(defn- latex-environment-content
-  [name option content]
-  (if (= (string/lower-case name) "equation")
-    content
-    (util/format "\\begin%s\n%s\\end{%s}"
-                 (str "{" name "}" option)
-                 content
-                 name)))
-
-(declare blocks-container)
-
-(rum/defc block-embed < rum/reactive db-mixins/query
-  [config id]
-  (let [blocks (db/get-block-and-children (state/get-current-repo) id)]
-    [:div.embed-block.bg-base-2 {:style {:z-index 2}}
-     [:code "Embed block:"]
-     [:div.px-2
-      (blocks-container blocks (assoc config :embed? true))]]))
-
-(rum/defc page-embed < rum/reactive db-mixins/query
-  [config page-name]
-  (let [page-name (string/lower-case page-name)
-        page-original-name (:page/original-name (db/entity [:page/name page-name]))
-        blocks (db/get-page-blocks (state/get-current-repo) page-name)]
-    [:div.embed-page.py-2.my-2.px-3.bg-base-2
-     [:p
-      [:code.mr-2 "Embed page:"]
-      (page-cp config {:page/name page-name})]
-     (blocks-container blocks (assoc config :embed? true))]))
-
-(defn- get-label-text
-  [label]
-  (and (= 1 (count label))
-       (let [label (first label)]
-         (string? (last label))
-         (last label))))
-
-(defn- get-page
-  [label]
-  (when-let [label-text (get-label-text label)]
-    (db/entity [:page/name (string/lower-case label-text)])))
-
-(defn- macro->text
-  [name arguments]
-  (if (and (seq arguments)
-           (not= arguments ["null"]))
-    (util/format "{{{%s %s}}}" name (string/join ", " arguments))
-    (util/format "{{{%s}}}" name)))
-
-(defn inline
-  [{:keys [html-export?] :as config} item]
-  (match item
-    ["Plain" s]
-    s
-    ["Spaces" s]
-    s
-    ["Superscript" l]
-    (->elem :sup (map-inline config l))
-    ["Subscript" l]
-    (->elem :sub (map-inline config l))
-    ["Tag" s]
-    (if (and s (util/tag-valid? s))
-      [:a.tag.mr-1 {:href (rfe/href :page {:name s})
-                    :on-click (fn [e]
-                                (util/stop e)
-                                (let [repo (state/get-current-repo)
-                                      page (db/pull repo '[*] [:page/name (string/lower-case (util/url-decode s))])]
-                                  (when (gobj/get e "shiftKey")
-                                    (state/sidebar-add-block!
-                                     repo
-                                     (:db/id page)
-                                     :page
-                                     {:page page}))))}
-       (str "#" s)]
-      [:span.warning.mr-1 {:title "Invalid tag, tags only accept alphanumeric characters, \"-\", \"_\", \"@\" and \"%\"."}
-       (str "#" s)])
-    ["Emphasis" [[kind] data]]
-    (let [elem (case kind
-                 "Bold" :b
-                 "Italic" :i
-                 "Underline" :ins
-                 "Strike_through" :del
-                 "Highlight" :mark)]
-      (->elem elem (map-inline config data)))
-    ["Entity" e]
-    [:span {:dangerouslySetInnerHTML
-            {:__html (:html e)}}]
-
-    ["Latex_Fragment" ["Displayed" s]]
-    (if html-export?
-      (latex/html-export s false true)
-      (latex/latex (str (dc/squuid)) s false true))
-
-    ["Latex_Fragment" ["Inline" s]]
-    (if html-export?
-      (latex/html-export s false true)
-      (latex/latex (str (dc/squuid)) s false false))
-
-    ["Target" s]
-    [:a {:id s} s]
-
-    ["Radio_Target" s]
-    [:a {:id s} s]
-
-    ["Email" address]
-    (let [{:keys [local_part domain]} address
-          address (str local_part "@" domain)]
-      [:a {:href (str "mainto:" address)}
-       address])
-
-    ["Block_reference" id]
-    ;; FIXME: alert when self block reference
-    (when-not (string/blank? id)
-      (let [block (and (util/uuid-string? id)
-                       (db/pull-block (uuid id)))]
-        (if block
-          [:span
-           [:span.text-gray-500 "(("]
-           [:a {:href (rfe/href :page {:name id})
-                :on-click (fn [e]
-                            (util/stop e)
-                            (when (gobj/get e "shiftKey")
-                              (state/sidebar-add-block!
-                               (state/get-current-repo)
-                               (:db/id block)
-                               :block-ref
-                               {:block block})))}
-            (->elem
-             :span.block-ref
-             (map-inline config (:block/title block)))]
-           [:span.text-gray-500 "))"]]
-          [:span.warning.mr-1 {:title "Block ref invalid"}
-           (util/format "((%s))" id)])))
-
-    ["Nested_link" link]
-    (let [{:keys [content children]} link]
-      [:span.page-reference
-       (when (and (not html-export?)
-                  (not (= (:id config) "contents")))
-         [:span.text-gray-500 "[["])
-       (let [page-name (subs content 2 (- (count content) 2))]
-         (page-cp (assoc config :children children) {:page/name page-name}))
-       (when (and (not html-export?)
-                  (not (= (:id config) "contents")))
-         [:span.text-gray-500 "]]"])])
-
-    ["Link" link]
-    (let [{:keys [url label title]} link
-          img-formats (set (map name (config/img-formats)))]
-      (match url
-        ["Search" s]
-        (cond
-          ;; image
-          (some (fn [fmt] (re-find (re-pattern (str "(?i)\\." fmt)) s)) img-formats)
-          (image-link config url s label)
-
-          (= \# (first s))
-          (->elem :a {:href (str "#" (anchor-link (subs s 1)))} (map-inline config label))
-          ;; FIXME: same headline, see more https://orgmode.org/manual/Internal-Links.html
-          (and (= \* (first s))
-               (not= \* (last s)))
-          (->elem :a {:href (str "#" (anchor-link (subs s 1)))} (map-inline config label))
-          (re-find #"^https://" s)
-          (->elem :a {:href s}
-                  (map-inline config label))
-
-          :else
-          (page-reference html-export? s config label))
-
-        :else
-        (let [href (string-of-url url)
-              protocol (or
-                        (and (= "Complex" (first url))
-                             (:protocol (second url)))
-                        (and (= "File" (first url))
-                             "file"))]
-          (cond
-            (= protocol "file")
-            (if (some (fn [fmt] (re-find (re-pattern (str "(?i)\\." fmt)) href)) img-formats)
-              (image-link config url href label)
-              (let [label-text (get-label-text label)
-                    page (if (string/blank? label-text)
-                           {:page/name (db/get-file-page (string/replace href "file:" ""))}
-                           (get-page label))]
-                (if (and page
-                         (when-let [ext (util/get-file-ext href)]
-                           (config/mldoc-support? ext)))
-                  [:span.page-reference
-                   [:span.text-gray-500 "[["]
-                   (page-cp config page)
-                   [:span.text-gray-500 "]]"]]
-
-                  (->elem
-                   :a
-                   (cond->
-                       {:href href}
-                     title
-                     (assoc :title title))
-                   (map-inline config label)))))
-
-            ;; image
-            (some (fn [fmt] (re-find (re-pattern (str "(?i)\\." fmt)) href)) img-formats)
-            (image-link config url href label)
-
-            :else
-            (->elem
-             :a
-             (cond->
-                 {:href href
-                  :target "_blank"}
-               title
-               (assoc :title title))
-             (map-inline config label))))))
-
-    ["Verbatim" s]
-    [:code s]
-
-    ["Code" s]
-    [:code s]
-
-    ["Inline_Source_Block" x]
-    [:code (:code x)]
-
-    ["Export_Snippet" "html" s]
-    (when (not html-export?)
-      [:span {:dangerouslySetInnerHTML
-              {:__html s}}])
-
-    ;; String to hiccup
-    ["Inline_Hiccup" s]
-    (ui/catch-error
-     [:div.warning {:title "Invalid hiccup"} s]
-     (-> (safe-read-string s)
-         (security/remove-javascript-links-in-href)))
-
-    ["Break_Line"]
-    [:br]
-    ["Hard_Break_Line"]
-    [:br]
-
-    ["Timestamp" ["Scheduled" t]]
-    (timestamp t "Scheduled")
-    ["Timestamp" ["Deadline" t]]
-    (timestamp t "Deadline")
-    ["Timestamp" ["Date" t]]
-    (timestamp t "Date")
-    ["Timestamp" ["Closed" t]]
-    (timestamp t "Closed")
-    ["Timestamp" ["Range" t]]
-    (range t false)
-    ["Timestamp" ["Clock" ["Stopped" t]]]
-    (range t true)
-    ["Timestamp" ["Clock" ["Started" t]]]
-    (timestamp t "Started")
-
-    ["Cookie" ["Percent" n]]
-    [:span {:class "cookie-percent"}
-     (util/format "[d%%]" n)]
-
-    ["Cookie" ["Absolute" current total]]
-    [:span {:class "cookie-absolute"}
-     (util/format "[%d/%d]" current total)]
-
-    ["Footnote_Reference" options]
-    (let [{:keys [id name]} options
-          encode-name (util/url-encode name)]
-      [:sup.fn
-       [:a {:id (str "fnr." encode-name)
-            :class "footref"
-            :href (str "#fn." encode-name)}
-        name]])
-
-    ["Macro" options]
-    (let [{:keys [name arguments]} options
-          arguments (if (and
-                         (>= (count arguments) 2)
-                         (and (string/starts-with? (first arguments) "[[")
-                              (string/ends-with? (last arguments) "]]"))) ; page reference
-                      (let [title (string/join ", " arguments)]
-                        [title])
-                      arguments)]
-      (cond
-        (= name "embed")
-        (let [a (first arguments)]
-          (cond
-            (and (string/starts-with? a "[[")
-                 (string/ends-with? a "]]"))
-            (let [page-name (-> (string/replace a "[[" "")
-                                (string/replace "]]" "")
-                                string/trim)]
-              (when-not (string/blank? page-name)
-                (page-embed config page-name)))
-
-            (and (string/starts-with? a "((")
-                 (string/ends-with? a "))"))
-            (when-let [s (-> (string/replace a "((" "")
-                             (string/replace "))" "")
-                             string/trim)]
-              (when-let [id (and s
-                                 (let [s (string/trim s)]
-                                   (and (util/uuid-string? s)
-                                        (uuid s))))]
-                (block-embed config id)))
-
-            :else                       ;TODO: maybe collections?
-            nil))
-
-        :else
-        (if-let [block-uuid (:block/uuid config)]
-          (let [macro-content (or
-                               (-> (db/entity [:block/uuid block-uuid])
-                                   (:block/page)
-                                   (:db/id)
-                                   (db/entity)
-                                   :page/properties
-                                   :macros
-                                   (get name))
-                               (get-in (state/get-config) [:macros name])
-                               (get-in (state/get-config) [:macros (keyword name)]))]
-            [:span
-             (if (and (seq arguments) macro-content)
-               (block/macro-subs macro-content arguments)
-               (or
-                macro-content
-                [:span.warning {:title (str "Unsupported macro name: " name)}
-                 (macro->text name arguments)]))])
-
-          [:span
-           (macro->text name arguments)])))
-
-    :else
-    ""))
-
-(declare blocks-cp)
-
-(rum/defc block-child
-  [block]
-  block)
-
-(defonce *control-show? (atom {}))
-
-(rum/defcs block-control < rum/reactive
-  {:will-mount (fn [state]
-                 (let [block (nth (:rum/args state) 1)
-                       collapsed? (:block/collapsed? block)]
-                   (state/set-collapsed-state! (:block/uuid block)
-                                               collapsed?))
-                 state)}
-  [state config block uuid block-id level start-level body children dummy?]
-  (let [has-child? (and
-                    (not (:pre-block? block))
-                    (or (seq children)
-                        (seq body)))
-        collapsed? (state/sub [:ui/collapsed-blocks uuid])
-        collapsed? (and has-child? collapsed?)
-        control-show (util/react (rum/cursor *control-show? block-id))
-        dark? (= "dark" (state/sub :ui/theme))
-        heading? (= (get (:block/properties block) "heading") "true")]
-    [:div.mr-2.flex.flex-row.items-center
-     {:style {:height 24
-              :margin-top (if (and heading? (<= level 6))
-                            (case level
-                              1
-                              32
-                              2
-                              22
-                              18)
-                            0)
-              :float "left"}}
-
-     [:a.block-control.opacity-50.hover:opacity-100
-      {:id (str "control-" uuid)
-       :style {:width 14
-               :height 16
-               :margin-right 2}
-       :on-click (fn [e]
-                   (util/stop e)
-                   (if collapsed?
-                     (expand/expand! block)
-                     (expand/collapse! block))
-
-                   (state/set-collapsed-state! uuid (not collapsed?)))}
-      (cond
-        (and control-show collapsed?)
-        (svg/caret-right)
-
-        (and control-show has-child?)
-        (svg/caret-down)
-
-        :else
-        [:span ""])]
-     [:a (if (not dummy?)
-           {:href (rfe/href :page {:name uuid})
-            :on-click (fn [e]
-                        (util/stop e)
-                        (when (gobj/get e "shiftKey")
-                          (state/sidebar-add-block!
-                           (state/get-current-repo)
-                           (:db/id block)
-                           :block
-                           block)))})
-      [:span.bullet-container.cursor
-       {:id (str "dot-" uuid)
-        :draggable true
-        :on-drag-start (fn [event]
-                         (editor-handler/highlight-block! uuid)
-                         (.setData (gobj/get event "dataTransfer")
-                                   "block-uuid"
-                                   uuid)
-                         (.setData (gobj/get event "dataTransfer")
-                                   "block-dom-id"
-                                   block-id)
-                         (state/clear-selection!)
-                         (reset! *dragging? true)
-                         (reset! *dragging-block block))
-        :blockid (str uuid)
-        :class (str (when collapsed? "bullet-closed")
-                    " "
-                    (when (and (:document/mode? config)
-                               (not collapsed?))
-                      "hide-inner-bullet"))}
-       [:span.bullet {:blockid (str uuid)
-                      :class (if heading? "bullet-heading" "")}]]]]))
-
-(defn- build-id
-  [config ref? sidebar? embed?]
-  (let [k (pr-str config)
-        n (or
-           (get @container-ids k)
-           (let [n' (swap! container-idx inc)]
-             (swap! container-ids assoc k n')
-             n'))]
-    (str n "-")))
-
-(rum/defc dnd-separator
-  [block margin-left bottom top? nested?]
-  (let [id (str (:block/uuid block)
-                (cond nested?
-                      "-nested"
-                      top?
-                      "-top"
-                      :else
-                      nil))]
-    [:div.dnd-separator
-     {:id id
-      :style (merge
-              {:position "absolute"
-               :left margin-left
-               :width "100%"
-               :z-index 3}
-              (if top?
-                {:top 0}
-                {:bottom 0}))}]))
-
-(declare block-container)
-(defn block-checkbox
-  [block class]
-  (let [marker (:block/marker block)
-        [class checked?] (cond
-                           (nil? marker)
-                           nil
-                           (contains? #{"NOW" "LATER" "DOING" "IN-PROGRESS" "TODO" "WAIT" "WAITING"} marker)
-                           [class false]
-                           (= "DONE" marker)
-                           [(str class " checked") true])]
-    (when class
-      (ui/checkbox {:class class
-                    :style {:margin-top -2
-                            :margin-right 5}
-                    :checked checked?
-                    :on-change (fn [_e]
-                                 ;; FIXME: Log timestamp
-                                 (if checked?
-                                   (editor-handler/uncheck block)
-                                   (editor-handler/check block)))}))))
-
-(defn list-checkbox
-  [checked?]
-  (ui/checkbox {:style {:margin-right 6
-                        :margin-top -1}
-                :checked checked?}))
-
-(defn marker-switch
-  [{:block/keys [pre-block? marker] :as block}]
-  (when (contains? #{"NOW" "LATER" "TODO" "DOING"} marker)
-    (let [set-marker-fn (fn [marker]
-                          (fn [e]
-                            (util/stop e)
-                            (editor-handler/set-marker block marker)))]
-      (case marker
-        "NOW"
-        [:a.marker-switch
-         {:title "Change from NOW to LATER"
-          :on-click (set-marker-fn "LATER")}
-         [:span "N"]]
-        "LATER"
-        [:a.marker-switch
-         {:title "Change from LATER to NOW"
-          :on-click (set-marker-fn "NOW")}
-         "L"]
-
-        "TODO"
-        [:a.marker-switch
-         {:title "Change from TODO to DOING"
-          :on-click (set-marker-fn "DOING")}
-         "T"]
-        "DOING"
-        [:a.marker-switch
-         {:title "Change from DOING to TODO"
-          :on-click (set-marker-fn "TODO")}
-         "D"]
-        nil))))
-
-(defn marker-cp
-  [{:block/keys [pre-block? marker] :as block}]
-  (when-not pre-block?
-    (if (contains? #{"IN-PROGRESS" "WAIT" "WAITING"} marker)
-      [:span {:class (str "task-status " (string/lower-case marker))
-              :style {:margin-right 3.5}}
-       (string/upper-case marker)])))
-
-(defn priority-cp
-  [{:block/keys [pre-block? priority] :as block}]
-
-  (when (and (not pre-block?) priority)
-    (ui/tooltip
-     [:ul
-      (for [p (remove #(= priority %) ["A" "B" "C"])]
-        [:a.mr-2.text-base.tooltip-priority {:priority p
-                                             :on-click (fn [] (editor-handler/set-priority block p))}])]
-     [:a.opacity-50.hover:opacity-100
-      {:class "priority"
-       :href (rfe/href :page {:name priority})
-       :style {:margin-right 3.5}}
-      (util/format "[#%s]" (str priority))])))
-
-(defn block-tags-cp
-  [{:block/keys [pre-block? tags] :as block}]
-  (when (and (not pre-block?)
-             (seq tags))
-    (->elem
-     :span
-     {:class "block-tags"}
-     (mapv (fn [{:keys [db/id tag/name]}]
-             (if (util/tag-valid? name)
-               [:a.tag.mx-1 {:key (str "tag-" id)
-                             :href (rfe/href :page {:name name})}
-                (str "#" name)]
-               [:span.warning.mx-1 {:title "Invalid tag, tags only accept alphanumeric characters, \"-\", \"_\", \"@\" and \"%\"."}
-                (str "#" name)]))
-           tags))))
-
-(defn build-block-part
-  [{:keys [slide?] :as config} {:block/keys [uuid title tags marker level priority anchor meta format content pre-block? dummy? block-refs-count page properties]
-                                :as t}]
-  (let [config (assoc config :block t)
-        slide? (boolean (:slide? config))
-        html-export? (:html-export? config)
-        checkbox (when (and (not pre-block?)
-                            (not html-export?))
-                   (block-checkbox t (str "mr-1 cursor")))
-        marker-switch (when (and (not pre-block?)
-                                 (not html-export?))
-                        (marker-switch t))
-        marker-cp (marker-cp t)
-        priority (priority-cp t)
-        tags (block-tags-cp t)
-        contents? (= (:id config) "contents")
-        heading? (= (get properties "heading") "true")
-        bg-color (get properties "background_color")]
-    (when level
-      (let [element (if (and (<= level 6) heading?)
-                      (keyword (str "h" level))
-                      :div)]
-        (->elem
-         element
-         (merge
-          {:id anchor}
-          (when (and marker
-                     (not (string/blank? marker))
-                     (not= "nil" marker))
-            {:class (str (string/lower-case marker)
-                         "flex flex-row items-center")})
-          (when bg-color
-            {:style {:background-color bg-color
-                     :padding-left 6
-                     :padding-right 6
-                     :color "#FFFFFF"}}))
-         (remove-nils
-          (concat
-           [(when-not slide? checkbox)
-            (when-not slide? marker-switch)
-            marker-cp
-            priority]
-           (cond
-             dummy?
-             [[:span.opacity-50 "Click here to start writing"]]
-
-             ;; empty item
-             (and contents? (or
-                             (empty? title)
-                             (= title [["Plain" "[[]]"]])))
-             [[:span.opacity-50 "Click here to add a page, e.g. [[favorite-page]]"]]
-
-             :else
-             (map-inline config title))
-           [tags])))))))
-
-(defn dnd-same-block?
-  [uuid]
-  (= (:block/uuid @*dragging-block) uuid))
-
-(defn show-dnd-separator
-  [element-id]
-  (when-let [element (gdom/getElement element-id)]
-    (when (d/has-class? element "dnd-separator")
-      (d/remove-class! element "dnd-separator")
-      (d/add-class! element "dnd-separator-cur"))))
-
-(defn hide-dnd-separator
-  [element-id]
-  (when-let [element (gdom/getElement element-id)]
-    (when (d/has-class? element "dnd-separator-cur")
-      (d/remove-class! element "dnd-separator-cur")
-      (d/add-class! element "dnd-separator"))))
-
-(defn- get-data-transfer-attr
-  [event attr]
-  (.getData (gobj/get event "dataTransfer") attr))
-
-(defn- pre-block-cp
-  [config content format]
-  (let [ast (mldoc/->edn content (mldoc/default-config format))
-        ast (map first ast)]
-    [:div.pre-block.bg-base-2.p-2
-     (blocks-cp (assoc config :block/format format) ast)]))
-
-(defn property-value
-  [format v]
-  (let [inline-list (mldoc/inline->edn v (mldoc/default-config format))]
-    [:div.inline (map-inline {} inline-list)]))
-
-(rum/defc properties-cp
-  [block]
-  (let [properties (apply dissoc (:block/properties block) text/hidden-properties)]
-    (when (seq properties)
-      [:div.blocks__properties.text-sm.opacity-80.my-1.p-2
-       (for [[k v] properties]
-         [:div.my-1
-          [:b k]
-          [:span.mr-1 ":"]
-          (property-value (:block/format block) v)])])))
-
-(rum/defcs timestamp-cp < rum/reactive
-  (rum/local false ::show?)
-  (rum/local {} ::pos)
-  {:will-unmount (fn [state]
-                   (when-let [show? (::show? state)]
-                     (reset! show? false))
-                   state)}
-  [state block typ ast]
-  (let [show? (get state ::show?)]
-    [:div.flex.flex-col
-     [:div.text-sm.mt-1.flex.flex-row
-      [:div.opacity-50.font-medium {:style {:width 95}}
-       (str typ ": ")]
-      [:a.opacity-80.hover:opacity-100
-       {:on-click (fn []
-                    (if @show?
-                      (do
-                        (reset! show? false)
-                        (reset! commands/*current-command nil)
-                        (state/set-editor-show-date-picker false)
-                        (state/set-timestamp-block! nil))
-                      (do
-                        (reset! show? true)
-                        (reset! commands/*current-command typ)
-                        (state/set-editor-show-date-picker true)
-                        (state/set-timestamp-block! {:block block
-                                                     :typ typ
-                                                     :show? show?}))))}
-       (repeated/timestamp->text ast)]]
-     (when (true? @show?)
-       (let [ts (repeated/timestamp->map ast)]
-         [:div.my-4
-          (datetime-comp/date-picker nil nil ts)]))]))
-
-(rum/defc block-content < rum/reactive
-  [config {:block/keys [uuid title level body meta content marker dummy? page format repo children pre-block? properties collapsed? idx block-refs-count scheduled scheduled-ast deadline deadline-ast repeated?] :as block} edit-input-id block-id slide?]
-  (let [dragging? (rum/react *dragging?)
-        attrs {:blockid (str uuid)
-               ;; FIXME: Click to copy a selection instead of click first and then copy
-               ;; It seems that `util/caret-range` can't get the correct range
-               :on-click (fn [e]
-                           (let [target (gobj/get e "target")]
-                             (when-not (or (util/link? target)
-                                           (util/input? target)
-                                           (util/details-or-summary? target)
-                                           (and (util/sup? target)
-                                                (d/has-class? target "fn")))
-                               (editor-handler/clear-selection! nil)
-                               (editor-handler/unhighlight-block!)
-                               (let [cursor-range (util/caret-range (gdom/getElement block-id))
-                                     properties-hidden? (text/properties-hidden? properties)
-                                     content (text/remove-level-spaces content format)
-                                     content (if properties-hidden? (text/remove-properties! content) content)]
-                                 (state/set-editing!
-                                  edit-input-id
-                                  content
-                                  block
-                                  cursor-range))
-                               (util/stop e))))
-               :on-drag-over (fn [event]
-                               (util/stop event)
-                               (when-not (dnd-same-block? uuid)
-                                 (show-dnd-separator (str uuid "-nested"))))
-               :on-drag-leave (fn [event]
-                                (hide-dnd-separator (str uuid))
-                                (hide-dnd-separator (str uuid "-nested"))
-                                (hide-dnd-separator (str uuid "-top")))
-               :on-drop (fn [event]
-                          (util/stop event)
-                          (when-not (dnd-same-block? uuid)
-                            (let [from-dom-id (get-data-transfer-attr event "block-dom-id")]
-                              (dnd/move-block @*dragging-block
-                                              block
-                                              from-dom-id
-                                              false
-                                              true)))
-                          (reset! *dragging? false)
-                          (reset! *dragging-block nil)
-                          (editor-handler/unhighlight-block!))}]
-    [:div.flex.overflow-x-auto.overflow-y-hidden.relative
-     [:div.flex-1.flex-col.relative.block-content
-      (cond-> {:id (str "block-content-" uuid)
-               :style {:cursor "text"
-                       :min-height 24}}
-        (not slide?)
-        (merge attrs))
-
-      (if pre-block?
-        (pre-block-cp config (string/trim content) format)
-        (build-block-part config block))
-
-      (when (and dragging? (not slide?))
-        (dnd-separator block 0 -4 false true))
-
-      (when (and deadline deadline-ast)
-        (timestamp-cp block "DEADLINE" deadline-ast))
-
-      (when (and scheduled scheduled-ast)
-        (timestamp-cp block "SCHEDULED" scheduled-ast))
-
-      (when (and (seq properties)
-                 (let [hidden? (text/properties-hidden? properties)]
-                   (not hidden?)))
-        (properties-cp block))
-
-      (when (and (not pre-block?) (seq body))
-        [:div.block-body {:style {:display (if collapsed? "none" "")}}
-         ;; TODO: consistent id instead of the idx (since it could be changed later)
-         (let [body (block/trim-break-lines! (:block/body block))]
-           (for [[idx child] (medley/indexed body)]
-             (when-let [block (block-cp config child)]
-               (rum/with-key (block-child block)
-                 (str uuid "-" idx)))))])]
-     (when (and block-refs-count (> block-refs-count 0))
-       [:div
-        [:a.block.py-0.px-2.rounded.bg-base-2.opacity-50.hover:opacity-100
-         {:title "Open block references"
-          :style {:margin-top -1}
-          :on-click (fn []
-                      (state/sidebar-add-block!
-                       (state/get-current-repo)
-                       (:db/id block)
-                       :block-ref
-                       {:block block}))}
-         block-refs-count]])
-
-     (when (and (= marker "DONE")
-                (state/enable-timetracking?))
-       (let [start-time (or
-                         (get properties "now")
-                         (get properties "doing")
-                         (get properties "in-progress")
-                         (get properties "later")
-                         (get properties "todo"))
-             finish-time (get properties "done")]
-         (when (and start-time finish-time (> finish-time start-time))
-           [:div.text-sm.absolute.time-spent {:style {:top 0
-                                                      :right 0
-                                                      :padding-left 2
-                                                      :z-index 4}
-                                              :title (str (date/int->local-time start-time) " ~ " (date/int->local-time finish-time))}
-            [:span.opacity-70
-             (utils/timeConversion (- finish-time start-time))]])))]))
-
-(rum/defc block-content-or-editor < rum/reactive
-  [config {:block/keys [uuid title level body meta content dummy? page format repo children pre-block? collapsed? idx] :as block} edit-input-id block-id slide?]
-  (let [edit? (state/sub [:editor/editing? edit-input-id])
-        editor-box (get config :editor-box)]
-    (if (and edit? editor-box)
-      [:div.editor-wrapper {:id (str "editor-" edit-input-id)}
-       (editor-box {:block block
-                    :block-id uuid
-                    :block-parent-id block-id
-                    :format format
-                    :dummy? dummy?
-                    :on-hide (fn [value event]
-                               (when (= event :esc)
-                                 (editor-handler/highlight-block! uuid)))}
-                   edit-input-id
-                   config)]
-      (block-content config block edit-input-id block-id slide?))))
-
-(rum/defc dnd-separator-wrapper < rum/reactive
-  [block slide? top?]
-  (let [dragging? (rum/react *dragging?)]
-    (cond
-      (and dragging? (not slide?))
-      (dnd-separator block 20 0 top? false)
-
-      :else
-      nil)))
-
-(defn non-dragging?
-  [e]
-  (and (= (gobj/get e "buttons") 1)
-       (not (d/has-class? (gobj/get e "target") "bullet-container"))
-       (not (d/has-class? (gobj/get e "target") "bullet"))
-       (not @*dragging?)))
-
-(rum/defc block-container < rum/static
-  {:did-mount (fn [state]
-                (let [block (nth (:rum/args state) 1)
-                      collapsed? (:block/collapsed? block)]
-                  (when collapsed?
-                    (expand/collapse! block))
-                  state))}
-  [config {:block/keys [uuid title level body meta content dummy? page format repo children collapsed? pre-block? idx properties] :as block}]
-  (let [ref? (boolean (:ref? config))
-        ref-child? (:ref-child? config)
-        sidebar? (boolean (:sidebar? config))
-        slide? (boolean (:slide? config))
-        doc-mode? (:document/mode? config)
-        embed? (:embed? config)
-        unique-dom-id (build-id (dissoc config :block/uuid) ref? sidebar? embed?)
-        edit-input-id (str "edit-block-" unique-dom-id uuid)
-        block-id (str "ls-block-" unique-dom-id uuid)
-        has-child? (boolean
-                    (and
-                     (not pre-block?)
-                     (or (seq children)
-                         (seq body))))
-        start-level (or (:start-level config) 1)
-        attrs {:on-drag-over (fn [event]
-                               (util/stop event)
-                               (when-not (dnd-same-block? uuid)
-                                 (if (zero? idx)
-                                   (let [element-top (gobj/get (utils/getOffsetRect (gdom/getElement block-id)) "top")
-                                         cursor-top (gobj/get event "clientY")]
-                                     (if (<= (js/Math.abs (- cursor-top element-top)) 16)
-                                       ;; top
-                                       (do
-                                         (hide-dnd-separator (str uuid))
-                                         (show-dnd-separator (str uuid "-top"))
-                                         (reset! *move-to-top? true))
-                                       (do
-                                         (hide-dnd-separator (str uuid "-top"))
-                                         (show-dnd-separator (str uuid)))))
-                                   (show-dnd-separator (str uuid)))))
-               :on-drag-leave (fn [event]
-                                (hide-dnd-separator (str uuid))
-                                (hide-dnd-separator (str uuid "-nested"))
-                                (hide-dnd-separator (str uuid "-top"))
-                                (reset! *move-to-top? false))
-               :on-drop (fn [event]
-                          (when-not (dnd-same-block? uuid)
-                            (let [from-dom-id (get-data-transfer-attr event "block-dom-id")]
-                              (dnd/move-block @*dragging-block
-                                              block
-                                              from-dom-id
-                                              @*move-to-top?
-                                              false)))
-                          (reset! *dragging? false)
-                          (reset! *dragging-block nil)
-                          (editor-handler/unhighlight-block!))
-               :on-mouse-move (fn [e]
-                                (when (non-dragging? e)
-                                  (state/into-selection-mode!)))
-               :on-mouse-down (fn [e]
-                                (when (and
-                                       (not (state/get-selection-start-block))
-                                       (= (gobj/get e "buttons") 1))
-                                  (when block-id (state/set-selection-start-block! block-id))))
-               :on-mouse-over (fn [e]
-                                (util/stop e)
-                                (when has-child?
-                                  (swap! *control-show? assoc block-id true))
-                                (when-let [parent (gdom/getElement block-id)]
-                                  (let [node (.querySelector parent ".bullet-container")
-                                        closed? (d/has-class? node "bullet-closed")]
-                                    (if closed?
-                                      (state/collapse-block! uuid)
-                                      (state/expand-block! uuid))
-                                    (when doc-mode?
-                                      (d/remove-class! node "hide-inner-bullet"))))
-                                (when (and
-                                       (state/in-selection-mode?)
-                                       (non-dragging? e))
-                                  (util/stop e)
-                                  (editor-handler/highlight-selection-area! block-id)))
-               :on-mouse-out (fn [e]
-                               (util/stop e)
-                               (when has-child?
-                                 (swap! *control-show?
-                                        assoc block-id false))
-                               (when doc-mode?
-                                 (when-let [parent (gdom/getElement block-id)]
-                                   (when-let [node (.querySelector parent ".bullet-container")]
-                                     (d/add-class! node "hide-inner-bullet")))))}]
-    [:div.ls-block.flex.flex-col.pt-1
-     (cond->
-         {:id block-id
-          :style {:position "relative"}
-          :class (str uuid
-                      (when dummy? " dummy")
-                      (when (and collapsed? has-child?) " collapsed")
-                      (when pre-block? " pre-block"))
-          :blockid (str uuid)
-          :repo repo
-          :level level
-          :haschild (str has-child?)}
-       (not slide?)
-       (merge attrs))
-
-     (when (and ref? (not ref-child?))
-       (when-let [comp (block-comp/block-parents repo uuid format false)]
-         [:div.my-2.opacity-50.ml-4 comp]))
-
-     (dnd-separator-wrapper block slide? (zero? idx))
-
-     [:div.flex-1.flex-row
-      (when (not slide?)
-        (block-control config block uuid block-id level start-level body children dummy?))
-
-      (block-content-or-editor config block edit-input-id block-id slide?)]
-
-     (when (seq children)
-       [:div.block-children {:style {:margin-left (if doc-mode? 12 22)
-                                     :display (if collapsed? "none" "")}}
-        (for [child children]
-          (when (map? child)
-            (let [child (dissoc child :block/meta)]
-              (rum/with-key (block-container (assoc config :block/uuid (:block/uuid child)) child)
-                (:block/uuid child)))))])
-
-     (when (and ref? (not ref-child?))
-       (let [children (-> (db/get-block-children-unsafe repo uuid)
-                          db/sort-by-pos)]
-         (when (seq children)
-           [:div.ref-children.ml-12
-            (blocks-container children (assoc config
-                                              :ref-child? true
-                                              :ref? true))])))
-
-     (dnd-separator-wrapper block slide? false)]))
-
-(defn divide-lists
-  [[f & l]]
-  (loop [l l
-         ordered? (:ordered f)
-         result [[f]]]
-    (if (seq l)
-      (let [cur (first l)
-            cur-ordered? (:ordered cur)]
-        (if (= ordered? cur-ordered?)
-          (recur
-           (rest l)
-           cur-ordered?
-           (update result (dec (count result)) conj cur))
-          (recur
-           (rest l)
-           cur-ordered?
-           (conj result [cur]))))
-      result)))
-
-(defn list-element
-  [l]
-  (match l
-    [l1 & tl]
-    (let [{:keys [ordered name]} l1]
-      (cond
-        (seq name)
-        :dl
-        ordered
-        :ol
-        :else
-        :ul))
-
-    :else
-    :ul))
-
-(defn list-item
-  [config {:keys [name content checkbox items number] :as l}]
-  (let [content (when-not (empty? content)
-                  (match content
-                    [["Paragraph" i] & rest]
-                    (vec-cat
-                     (map-inline config i)
-                     (blocks-cp config rest))
-                    :else
-                    (blocks-cp config content)))
-        checked? (some? checkbox)
-        items (if (seq items)
-                (->elem
-                 (list-element items)
-                 (for [item items]
-                   (list-item config item))))]
-    (cond
-      (seq name)
-      [:dl {:checked checked?}
-       [:dt (map-inline config name)]
-       (->elem :dd
-               (vec-cat content [items]))]
-
-      :else
-      (if (nil? checkbox)
-        (->elem
-         :li
-         {:checked checked?}
-         (vec-cat
-          [(->elem
-            :p
-            content)]
-          [items]))
-        (->elem
-         :li
-         {:checked checked?}
-         (vec-cat
-          [(->elem
-            :p
-            (list-checkbox checkbox)
-            content)]
-          [items]))))))
-
-(defn table
-  [config {:keys [header groups col_groups]}]
-  (let [tr (fn [elm cols]
-             (->elem
-              :tr
-              (mapv (fn [col]
-                      (->elem
-                       elm
-                       {:scope "col"
-                        :class "org-left"}
-                       (map-inline config col)))
-                    cols)))
-        tb-col-groups (try
-                        (mapv (fn [number]
-                                (let [col-elem [:col {:class "org-left"}]]
-                                  (->elem
-                                   :colgroup
-                                   (repeat number col-elem))))
-                              col_groups)
-                        (catch js/Error e
-                          []))
-        head (if header
-               [:thead (tr :th header)])
-        groups (mapv (fn [group]
-                       (->elem
-                        :tbody
-                        (mapv #(tr :td %) group)))
-                     groups)]
-    [:div.table-wrapper {:style {:max-width (min 700
-                                                 (gobj/get js/window "innerWidth"))}}
-     (->elem
-      :table
-      {:class "table-auto"
-       :border 2
-       :cell-spacing 0
-       :cell-padding 6
-       :rules "groups"
-       :frame "hsides"}
-      (vec-cat
-       tb-col-groups
-       (cons head groups)))]))
-
-(defn map-inline
-  [config col]
-  (map #(inline config %) col))
-
-(declare ->hiccup)
-
-(defn built-in-custom-query?
-  [title]
-  (contains? #{"🔨 NOW" "📅 NEXT"}
-             title))
-
-(rum/defcs custom-query < rum/reactive
-  {:will-mount (fn [state]
-                 (let [[config query] (:rum/args state)]
-                   (let [query-atom (db/custom-query query)]
-                     (assoc state :query-atom query-atom))))
-   :did-mount (fn [state]
-                (when-let [query (last (:rum/args state))]
-                  (state/add-custom-query-component! query (:rum/react-component state)))
-                state)
-   :will-unmount (fn [state]
-                   (when-let [query (last (:rum/args state))]
-                     (state/remove-custom-query-component! query)
-                     (db/remove-custom-query! (state/get-current-repo) query))
-                   state)}
-  [state config {:keys [title query inputs view collapsed? children?] :as q}]
-  (let [query-atom (:query-atom state)]
-    (let [current-block-uuid (or (:block/uuid (:block config))
-                                 (:block/uuid config))
-          ;; exclude the current one, otherwise it'll loop forever
-          remove-blocks (if current-block-uuid [current-block-uuid] nil)
-          query-result (and query-atom (rum/react query-atom))
-          result (if query-result
-                   (db/custom-query-result-transform query-result remove-blocks q))
-          view-f (sci/eval-string (pr-str view))
-          only-blocks? (:block/uuid (first result))
-          blocks-grouped-by-page? (and (seq result)
-                                       (coll? (first result))
-                                       (:page/name (ffirst result))
-                                       (:block/uuid (first (second (first result))))
-                                       true)
-          built-in? (built-in-custom-query? title)]
-      [:div.custom-query.mt-2 (get config :attr {})
-       (when-not (and built-in? (empty? result))
-         (ui/foldable
-          [:div.opacity-70
-           title]
-          (cond
-            (and (seq result) view-f)
-            (let [result (sci/call-fn view-f result)]
-              (util/hiccup-keywordize result))
-
-            (and (seq result)
-                 (or only-blocks? blocks-grouped-by-page?))
-            (->hiccup result (cond-> (assoc config
-                                            :custom-query? true
-                                            :group-by-page? blocks-grouped-by-page?)
-                               children?
-                               (assoc :ref? true))
-                      {:style {:margin-top "0.25rem"
-                               :margin-left "0.25rem"}})
-
-            (seq result)                     ;TODO: table
-            [:pre
-             (for [record result]
-               (if (map? record)
-                 (str (util/pp-str record) "\n")
-                 record))]
-
-            :else
-            [:div.text-sm.mt-2.ml-2.font-medium.opacity-50 "Empty"])
-          collapsed?))])))
-
-(defn admonition
-  [config type options result]
-  (when-let [icon (case (string/lower-case (name type))
-                    "note" svg/note
-                    "tip" svg/tip
-                    "important" svg/important
-                    "caution" svg/caution
-                    "warning" svg/warning
-                    nil)]
-    [:div.flex.flex-row.admonitionblock.align-items {:class type}
-     [:div.pr-4.admonition-icon.flex.flex-col.justify-center
-      {:title (string/upper-case type)} (icon)]
-     [:div.ml-4.text-lg
-      (blocks-cp config result)]]))
-
-(defn block-cp
-  [{:keys [html-export?] :as config} item]
-  (try
-    (match item
-      ["Properties" m]
-      [:div.properties
-       (let [format (:block/format config)]
-         (for [[k v] m]
-           (when (and (not (and (= k :macros) (empty? v))) ; empty macros
-                      (not (= k :title)))
-             [:div.property
-              [:span.font-medium.mr-1 (string/upper-case (str (name k) ": "))]
-              (if (coll? v)
-                (for [item v]
-                  (if (= k :tags)
-                    (if (string/includes? item "[[")
-                      (property-value format item)
-                      (let [tag (-> item
-                                    (string/replace "[" "")
-                                    (string/replace "]" "")
-                                    (string/replace "#" ""))]
-                        [:a.tag.mr-1 {:href (rfe/href :page {:name tag})}
-                         tag]))
-                    (property-value format v)))
-                (property-value format v))])))]
-
-      ["Paragraph" l]
-      ;; TODO: speedup
-      (if (re-find #"\"Export_Snippet\" \"embed\"" (str l))
-        (->elem :div (map-inline config l))
-        (->elem :p (map-inline config l)))
-
-      ["Horizontal_Rule"]
-      (when-not (:slide? config)
-        [:hr])
-      ["Heading" h]
-      (block-container config h)
-      ["List" l]
-      (let [lists (divide-lists l)]
-        (if (= 1 (count lists))
-          (let [l (first lists)]
-            (->elem
-             (list-element l)
-             (map #(list-item config %) l)))
-          [:div.list-group
-           (for [l lists]
-             (->elem
-              (list-element l)
-              (map #(list-item config %) l)))]))
-      ["Table" t]
-      (table config t)
-      ["Math" s]
-      (if html-export?
-        (latex/html-export s true true)
-        (latex/latex (str (dc/squuid)) s true true))
-      ["Example" l]
-      [:pre.pre-wrap-white-space
-       (join-lines l)]
-      ["Src" options]
-      (let [{:keys [language options lines pos_meta]} options
-            attr (if language
-                   {:data-lang language})
-            code (join-lines lines)]
-        (cond
-          html-export?
-          (highlight/html-export attr code)
-
-          :else
-          (let [language (if (contains? #{"edn" "clj" "cljc" "cljs" "clojure"} language) "text/x-clojure" language)]
-            [:div
-             (lazy-editor/editor config (str (dc/squuid)) attr code pos_meta)
-             (when (and (= language "clojure") (contains? (set options) ":results"))
-               (sci/eval-result code))])))
-      ["Quote" l]
-      (->elem
-       :blockquote
-       (blocks-cp config l))
-      ["Raw_Html" content]
-      (when (not html-export?)
-        [:div.raw_html {:dangerouslySetInnerHTML
-                        {:__html content}}])
-      ["Export" "html" options content]
-      (when (not html-export?)
-        [:div.export_html {:dangerouslySetInnerHTML
-                           {:__html content}}])
-      ["Hiccup" content]
-      (ui/catch-error
-       [:div.warning {:title "Invalid hiccup"}
-        content]
-       (-> (safe-read-string content)
-           (security/remove-javascript-links-in-href)))
-
-      ["Export" "latex" options content]
-      (if html-export?
-        (latex/html-export content true false)
-        (latex/latex (str (dc/squuid)) content true false))
-
-      ["Custom" "query" _options result content]
-      (try
-        (let [query (reader/read-string content)]
-          (custom-query config query))
-        (catch js/Error e
-          (println "read-string error:")
-          (js/console.error e)
-          [:div.warning {:title "Invalid query"}
-           content]))
-
-      ["Custom" "note" options result content]
-      (admonition config "note" options result)
-
-      ["Custom" "tip" options result content]
-      (admonition config "tip" options result)
-
-      ["Custom" "important" options result content]
-      (admonition config "important" options result)
-
-      ["Custom" "caution" options result content]
-      (admonition config "caution" options result)
-
-      ["Custom" "warning" options result content]
-      (admonition config "warning" options result)
-
-      ["Custom" name options l content]
-      (->elem
-       :div
-       {:class name}
-       (blocks-cp config l))
-
-      ["Latex_Fragment" l]
-      [:p.latex-fragment
-       (inline config ["Latex_Fragment" l])]
-
-      ["Latex_Environment" name option content]
-      (let [content (latex-environment-content name option content)]
-        (if html-export?
-          (latex/html-export content true true)
-          (latex/latex (str (dc/squuid)) content true true)))
-
-      ["Displayed_Math" content]
-      (if html-export?
-        (latex/html-export content true true)
-        (latex/latex (str (dc/squuid)) content true true))
-
-      ["Footnote_Definition" name definition]
-      (let [id (util/url-encode name)]
-        [:div.footdef
-         [:div.footpara
-          (conj
-           (block-cp config ["Paragraph" definition])
-           [:a.ml-1 {:id (str "fn." id)
-                     :style {:font-size 14}
-                     :class "footnum"
-                     :href (str "#fnr." id)}
-            [:sup.fn (str name "↩︎")]])]])
-
-      :else
-      "")
-    (catch js/Error e
-      (println "Convert to html failed, error: " e)
-      "")))
-
-(defn blocks-cp
-  [config col]
-  (map #(block-cp config %) col))
-
-(rum/defcs build-blocks < rum/reactive
-  {:init (fn [state]
-           (let [blocks (first (:rum/args state))
-                 segment-data (if (> (count blocks) max-blocks-per-page)
-                                (take max-blocks-per-page blocks)
-                                blocks)]
-             (assoc state
-                    ::segment (atom segment-data)
-                    ::idx (atom 0))))
-   :did-update (fn [state]
-                 (let [blocks (first (:rum/args state))
-                       segment (get state ::segment)
-                       idx (get state ::idx)]
-                   (when (and
-                          (seq @segment)
-                          (> (count blocks) max-blocks-per-page))
-                     (reset! segment (->> blocks
-                                          (drop @idx)
-                                          (take max-blocks-per-page))))
-                   state))}
-  [state blocks config]
-  (let [segment (get state ::segment)
-        idx (get state ::idx)
-        custom-query? (:custom-query? config)
-        ref? (:ref? config)]
-    (let [blocks-cp (fn [blocks segment?]
-                      (let [first-block (first blocks)
-                            blocks' (if (and (:block/pre-block? first-block)
-                                             (db/pre-block-with-only-title? (:block/repo first-block) (:block/uuid first-block)))
-                                      (rest blocks)
-                                      blocks)
-                            first-id (:block/uuid (first blocks'))]
-                        (for [item blocks']
-                          (let [item (-> (if (:block/dummy? item)
-                                           item
-                                           (dissoc item :block/meta)))
-                                item (if (= first-id (:block/uuid item))
-                                       (assoc item :block/idx 0)
-                                       item)
-                                config (assoc config :block/uuid (:block/uuid item))]
-                            (rum/with-key
-                              (block-container config item)
-                              (:block/uuid item))))))
-          blocks->vec-tree #(if (or custom-query? ref?) % (db/blocks->vec-tree %))]
-      (if (> (count blocks) max-blocks-per-page)
-        (ui/infinite-list
-         (blocks-cp (blocks->vec-tree (rum/react segment)) true)
-         {:on-load (fn []
-                     (when (= (count @segment) max-blocks-per-page)
-                       (if (zero? @idx)
-                         (reset! idx (dec max-blocks-per-page))
-                         (swap! idx + virtual-list-scroll-step))
-                       (let [tail (take-last virtual-list-previous @segment)
-                             new-segment (->> blocks
-                                              (drop (inc @idx))
-                                              (take virtual-list-scroll-step))]
-                         (reset! segment
-                                 (->> (concat tail new-segment)
-                                      (remove nil?))))))
-          :on-top-reached (fn []
-                            (when (> @idx 0)
-                              (if (> @idx (dec max-blocks-per-page))
-                                (swap! idx - max-blocks-per-page)
-                                (reset! idx 0))
-                              (if (zero? @idx)
-                                (reset! segment
-                                        (take max-blocks-per-page blocks))
-                                (let [tail (take virtual-list-previous @segment)
-                                      prev (->> blocks
-                                                (drop (inc @idx))
-                                                (take virtual-list-scroll-step))]
-                                  (reset! segment
-                                          (->> (concat prev tail)
-                                               (remove nil?)))
-                                  (util/scroll-to 100)))))})
-        (blocks-cp (blocks->vec-tree blocks) false)))))
-
-(defn build-slide-sections
-  ([blocks config]
-   (build-slide-sections blocks config nil))
-  ([blocks config build-block-fn]
-   (when (seq blocks)
-     (let [blocks (map #(dissoc % :block/children) blocks)
-           first-block-level (:block/level (first blocks))
-           sections (reduce
-                     (fn [acc block]
-                       (let [block (dissoc block :block/meta)
-                             level (:block/level block)
-                             block-cp (if build-block-fn
-                                        (build-block-fn config block)
-                                        (rum/with-key
-                                          (block-container config block)
-                                          (str "slide-" (:block/uuid block))))]
-                         (if (= first-block-level level)
-                           ;; new slide
-                           (conj acc [[block block-cp]])
-                           (update acc (dec (count acc))
-                                   (fn [sections]
-                                     (conj sections [block block-cp]))))))
-                     []
-                     blocks)]
-       sections))))
-
-(rum/defc add-button < rum/reactive
-  [config ref? custom-query? blocks]
-  (let [editing (state/sub [:editor/editing?])]
-    (when-not (or ref? custom-query?
-                  (:block/dummy? (last blocks))
-                  (second (first editing)))
-      (when-let [last-block (last blocks)]
-        [:a.add-button-link {:on-click (fn []
-                                         (editor-handler/insert-new-block-without-save-previous! config last-block))}
-         svg/plus-circle]))))
-
-(rum/defc blocks-container < rum/static
-  [blocks config]
-  (let [blocks (map #(dissoc % :block/children) blocks)
-        sidebar? (:sidebar? config)
-        ref? (:ref? config)
-        custom-query? (:custom-query? config)]
-    (when (seq blocks)
-      [:div.blocks-container.flex-1
-       {:style {:margin-left (cond
-                               sidebar?
-                               0
-                               :else
-                               -18)}}
-       (build-blocks blocks config)
-       ;; (add-button config ref? custom-query? blocks)
-       ])))
-
-;; headers to hiccup
-(rum/defc ->hiccup < rum/reactive
-  [blocks config option]
-  (let [document-mode? (state/sub [:document/mode?])
-        config (assoc config
-                      :document/mode? document-mode?)]
-    [:div.content
-     (cond-> option
-       document-mode?
-       (assoc :class "doc-mode"))
-     (if (:group-by-page? config)
-       [:div.flex.flex-col
-        (for [[page blocks] blocks]
-          (let [page (db/entity (:db/id page))]
-            [:div.my-2 (cond-> {:key (str "page-" (:db/id page))}
-                         (:ref? config)
-                         (assoc :class "bg-base-2 px-7 py-2 rounded"))
-             (ui/foldable
-              (page-cp config page)
-              (blocks-container blocks config))]))]
-       (blocks-container blocks config))]))
-
-(comment
-  ;; timestamps
-  ;; [2020-02-10 Mon 13:22]
-  ;; repetition
-  (def r1 "<2005-10-01 Sat +1m>")
-  ;; TODO: mldoc add supports
-  (def r2 "<2005-10-01 Sat +1m -3d>")
-
-  (def l
-    "1. First item
-hello world
-2. Second item
-nice
-3. Third item")
-
-  (def t
-    "| Name  | Phone | Age |
-|-------+-------+-----|
-| Peter |  1234 |  17 |
-| Anna  |  4321 |  25 |"))

+ 6 - 10
src/main/frontend/components/journal.cljs

@@ -4,22 +4,18 @@
             [frontend.date :as date]
             [frontend.db-mixins :as db-mixins]
             [frontend.handler.notification :as notification]
-            [frontend.handler.repo :as repo-handler]
             [frontend.handler.page :as page-handler]
             [frontend.handler.editor :as editor-handler]
-            [frontend.handler.ui :as ui-handler]
             [frontend.db :as db]
             [frontend.state :as state]
             [clojure.string :as string]
             [frontend.ui :as ui]
-            [frontend.format :as format]
             [frontend.components.content :as content]
-            [frontend.components.hiccup :as hiccup]
+            [frontend.components.block :as block]
             [frontend.components.editor :as editor]
             [frontend.components.reference :as reference]
             [frontend.components.page :as page]
             [frontend.components.onboarding :as onboarding]
-            [frontend.utf8 :as utf8]
             [goog.object :as gobj]
             [clojure.string :as string]))
 
@@ -47,11 +43,11 @@
   (let [start-level (or (:block/level (first blocks)) 1)]
     (content/content
      encoded-page-name
-     {:hiccup (hiccup/->hiccup blocks
-                               {:id encoded-page-name
-                                :start-level 2
-                                :editor-box editor/box}
-                               {})})))
+     {:hiccup (block/->hiccup blocks
+                              {:id encoded-page-name
+                               :start-level 2
+                               :editor-box editor/box}
+                              {})})))
 
 (rum/defc blocks-cp < rum/reactive db-mixins/query
   {}

+ 4 - 4
src/main/frontend/components/onboarding.cljs

@@ -311,12 +311,12 @@
           [:td.text-right
            [:a {:href "https://www.example.com"}
             "label"]]]
-         [:tr [:td "![image](https://logseq.com/static/img/logo.png)"]
+         [:tr [:td "![image](https://asset.logseq.com/static/img/logo.png)"]
           [:td.text-right
            [:img {:style {:float "right"
                           :width 64
                           :height 64}
-                  :src "https://logseq.com/static/img/logo.png"
+                  :src "https://asset.logseq.com/static/img/logo.png"
                   :alt "image"}]]]]]]
 
       [:li
@@ -342,10 +342,10 @@
           [:td.text-right
            [:a {:href "https://www.example.com"}
             "label"]]]
-         [:tr [:td "[[https://logseq.com/static/img/logo.png][image]]"]
+         [:tr [:td "[[https://asset.logseq.com/static/img/logo.png][image]]"]
           [:td.text-right
            [:img {:style {:float "right"
                           :width 64
                           :height 64}
-                  :src "https://logseq.com/static/img/logo.png"
+                  :src "https://asset.logseq.com/static/img/logo.png"
                   :alt "image"}]]]]]]]]))

+ 16 - 20
src/main/frontend/components/page.cljs

@@ -1,6 +1,5 @@
 (ns frontend.components.page
   (:require [rum.core :as rum]
-            [medley.core :as medley]
             [frontend.util :as util :refer-macros [profile]]
             [frontend.handler.file :as file]
             [frontend.handler.page :as page-handler]
@@ -8,19 +7,16 @@
             [frontend.handler.route :as route-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.editor :as editor-handler]
-            [frontend.handler.export :as export-handler]
             [frontend.state :as state]
             [clojure.string :as string]
             [frontend.db :as db]
             [dommy.core :as d]
-            [frontend.components.hiccup :as hiccup]
-            [frontend.components.editor :as editor]
             [frontend.components.block :as block]
+            [frontend.components.editor :as editor]
             [frontend.components.reference :as reference]
             [frontend.components.svg :as svg]
             [frontend.extensions.graph-2d :as graph-2d]
             [frontend.ui :as ui]
-            [frontend.format :as format]
             [frontend.components.content :as content]
             [frontend.components.project :as project]
             [frontend.config :as config]
@@ -73,7 +69,7 @@
                        :sidebar? sidebar?
                        :block? block?
                        :editor-box editor/box}
-        hiccup (hiccup/->hiccup page-blocks hiccup-config {})]
+        hiccup (block/->hiccup page-blocks hiccup-config {})]
     (rum/with-key
       (content/content encoded-page-name
                        {:hiccup hiccup
@@ -108,9 +104,9 @@
         [:div#today-queries.mt-10
          (for [{:keys [title] :as query} queries]
            (rum/with-key
-             (hiccup/custom-query {:start-level 2
-                                   :attr {:class "mt-10"}
-                                   :editor-box editor/box} query)
+             (block/custom-query {:start-level 2
+                                  :attr {:class "mt-10"}
+                                  :editor-box editor/box} query)
              (str repo "-custom-query-" (:query query))))]))))
 
 (defn- delete-page!
@@ -340,7 +336,7 @@
                   (if (and (string/includes? page-original-name "[[")
                            (string/includes? page-original-name "]]"))
                     (let [ast (mldoc/->edn page-original-name (mldoc/default-config format))]
-                      (hiccup/block-cp {} (ffirst ast)))
+                      (block/markup-element-cp {} (ffirst ast)))
                     page-original-name)
                   (or
                    page-name
@@ -454,16 +450,16 @@
                (let [encoded-page (util/encode-str page)]
                  [:tr {:key encoded-page}
                   [:td [:a {:on-click (fn [e]
-                                                      (util/stop e)
-                                                      (let [repo (state/get-current-repo)
-                                                            page (db/pull repo '[*] [:page/name (string/lower-case page)])]
-                                                        (when (gobj/get e "shiftKey")
-                                                          (state/sidebar-add-block!
-                                                           repo
-                                                           (:db/id page)
-                                                           :page
-                                                           {:page page}))))
-                                          :href (rfe/href :page {:name encoded-page})}
+                                        (util/stop e)
+                                        (let [repo (state/get-current-repo)
+                                              page (db/pull repo '[*] [:page/name (string/lower-case page)])]
+                                          (when (gobj/get e "shiftKey")
+                                            (state/sidebar-add-block!
+                                             repo
+                                             (:db/id page)
+                                             :page
+                                             {:page page}))))
+                            :href (rfe/href :page {:name encoded-page})}
                         page]]
                   [:td [:span.text-gray-500.text-sm
                         (if (zero? modified-at)

+ 1 - 7
src/main/frontend/components/project.cljs

@@ -1,13 +1,7 @@
 (ns frontend.components.project
   (:require [rum.core :as rum]
             [frontend.util :as util :refer-macros [profile]]
-            [frontend.handler.project :as project-handler]
-            [frontend.state :as state]
-            [clojure.string :as string]
-            [frontend.ui :as ui]
-            [frontend.mixins :as mixins]
-            [goog.dom :as gdom]
-            [goog.object :as gobj]))
+            [frontend.handler.project :as project-handler]))
 
 (rum/defcs add-project <
   (rum/local "" ::project)

+ 23 - 27
src/main/frontend/components/reference.cljs

@@ -4,17 +4,12 @@
             [frontend.state :as state]
             [clojure.string :as string]
             [frontend.db :as db]
-            [frontend.components.hiccup :as hiccup]
+            [frontend.components.block :as block]
             [frontend.ui :as ui]
-            [frontend.format :as format]
             [frontend.components.content :as content]
-            [frontend.config :as config]
             [frontend.date :as date]
-            [frontend.components.svg :as svg]
             [frontend.components.editor :as editor]
-            [frontend.handler.page :as page-handler]
             [frontend.db-mixins :as db-mixins]
-            [clojure.set :as set]
             [clojure.string :as string]))
 
 (rum/defc references < rum/reactive db-mixins/query
@@ -48,13 +43,13 @@
              [:h2.font-bold.opacity-50 (let []
                                          "SCHEDULED AND DEADLINE")]
              [:div.references-blocks.mb-6
-              (let [ref-hiccup (hiccup/->hiccup scheduled-or-deadlines
-                                                {:id (str encoded-page-name "-agenda")
-                                                 :start-level 2
-                                                 :ref? true
-                                                 :group-by-page? true
-                                                 :editor-box editor/box}
-                                                {})]
+              (let [ref-hiccup (block/->hiccup scheduled-or-deadlines
+                                               {:id (str encoded-page-name "-agenda")
+                                                :start-level 2
+                                                :ref? true
+                                                :group-by-page? true
+                                                :editor-box editor/box}
+                                               {})]
                 (content/content encoded-page-name
                                  {:hiccup ref-hiccup}))]))
 
@@ -62,13 +57,14 @@
            [:h2.font-bold.opacity-50 (let []
                                        (str n-ref " Linked References"))]
            [:div.references-blocks
-            (let [ref-hiccup (hiccup/->hiccup ref-blocks
-                                              {:id encoded-page-name
-                                               :start-level 2
-                                               :ref? true
-                                               :group-by-page? true
-                                               :editor-box editor/box}
-                                              {})]
+            (let [ref-hiccup (block/->hiccup ref-blocks
+                                             {:id encoded-page-name
+                                              :start-level 2
+                                              :ref? true
+                                              :breadcrumb-show? true
+                                              :group-by-page? true
+                                              :editor-box editor/box}
+                                             {})]
               (content/content encoded-page-name
                                {:hiccup ref-hiccup}))])]]))))
 
@@ -78,13 +74,13 @@
         encoded-page-name (util/url-encode page-name)]
     (reset! n-ref (count ref-blocks))
     [:div.references-blocks
-     (let [ref-hiccup (hiccup/->hiccup ref-blocks
-                                       {:id (str encoded-page-name "-unlinked-")
-                                        :start-level 2
-                                        :ref? true
-                                        :group-by-page? true
-                                        :editor-box editor/box}
-                                       {})]
+     (let [ref-hiccup (block/->hiccup ref-blocks
+                                      {:id (str encoded-page-name "-unlinked-")
+                                       :start-level 2
+                                       :ref? true
+                                       :group-by-page? true
+                                       :editor-box editor/box}
+                                      {})]
        (content/content encoded-page-name
                         {:hiccup ref-hiccup}))]))
 

+ 112 - 3
src/main/frontend/components/repo.cljs

@@ -5,10 +5,16 @@
             [frontend.state :as state]
             [frontend.db :as db]
             [frontend.handler.repo :as repo-handler]
+            [frontend.handler.common :as common-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.export :as export-handler]
             [frontend.util :as util]
-            [reitit.frontend.easy :as rfe]))
+            [frontend.config :as config]
+            [reitit.frontend.easy :as rfe]
+            [frontend.version :as version]
+            [frontend.components.commit :as commit]
+            [frontend.context.i18n :as i18n]
+            [clojure.string :as string]))
 
 (rum/defc add-repo
   []
@@ -25,8 +31,8 @@
        [:div.pl-1.content
         [:div.flex.my-4 {:key "add-button"}
          (ui/button
-          "Add another repo"
-          :href (rfe/href :repo-add))]
+           "Add another repo"
+           :href (rfe/href :repo-add))]
 
         (for [{:keys [id url] :as repo} repos]
           [:div.flex.justify-between.mb-1 {:key id}
@@ -52,3 +58,106 @@
 
        [:a#download-as-json.hidden]]
       (widgets/add-repo))))
+
+(rum/defc sync-status < rum/reactive
+  {:did-mount (fn [state]
+                (js/setTimeout common-handler/check-changed-files-status 1000)
+                state)}
+  []
+  (let [repo (state/get-current-repo)]
+    (when-not (= repo config/local-repo)
+      (let [changed-files (state/sub [:repo/changed-files repo])
+            should-push? (seq changed-files)
+            git-status (state/sub [:git/status repo])
+            pushing? (= :pushing git-status)
+            pulling? (= :pulling git-status)
+            last-pulled-at (db/sub-key-value repo :git/last-pulled-at)]
+        [:div.flex-row.flex.items-center
+         (when pushing?
+           [:span.lds-dual-ring.mt-1])
+         (ui/dropdown
+          (fn [{:keys [toggle-fn]}]
+            [:div.cursor.w-2.h-2.sync-status.mr-2
+             {:class (if (or should-push? pushing?) "bg-orange-400" "bg-green-600")
+              :style {:border-radius "50%"
+                      :margin-top 2}
+              :on-mouse-over
+              (fn [e]
+                (toggle-fn)
+                (js/setTimeout common-handler/check-changed-files-status 0))}])
+          (fn [{:keys [toggle-fn]}]
+            (rum/with-context [[t] i18n/*tongue-context*]
+      [:div.p-2.rounded-md.shadow-xs.bg-base-3.flex.flex-col.sync-content
+       {:on-mouse-leave toggle-fn}
+       [:div
+        [:div
+         (if (and should-push? (seq changed-files))
+           [:div.changes
+            [:ul
+             (for [file changed-files]
+               [:li {:key (str "sync-" file)}
+                [:div.flex.flex-row.justify-between.align-items
+                 [:a {:href (rfe/href :file {:path file})}
+                  file]
+                 [:a.ml-4.text-sm.mt-1
+                  {:on-click (fn [e]
+                               (export-handler/download-file! file))}
+                  [:span (t :download)]]]])]]
+           [:p (t :git/local-changes-synced)])]
+        ;; [:a.text-sm.font-bold {:href "/diff"} "Check diff"]
+        [:div.flex.flex-row.justify-between.align-items.mt-2
+         (ui/button (t :git/push)
+           :on-click (fn [] (state/set-modal! commit/add-commit-message)))
+         (if pushing?
+           [:span.lds-dual-ring.mt-1])]]
+       [:hr]
+       [:div
+        (when-not (string/blank? last-pulled-at)
+          [:p {:style {:font-size 12}} (t :git/last-pull)
+           (str ": " last-pulled-at)])
+        [:div.flex.flex-row.justify-between.align-items
+         (ui/button (t :git/pull)
+           :on-click (fn [] (repo-handler/pull-current-repo)))
+         (if pulling?
+           [:span.lds-dual-ring.mt-1])]
+        [:p.pt-2.text-sm.opacity-50
+         (t :git/version) (str " " version/version)]]])))]))))
+
+(rum/defc repos-dropdown < rum/reactive
+  [head? on-click]
+  (let [current-repo (state/sub :git/current-repo)
+        logged? (state/logged?)
+        local-repo? (= current-repo config/local-repo)
+        get-repo-name (fn [repo]
+                          (if head?
+                            (db/get-repo-path repo)
+                            (util/take-at-most (db/get-repo-name repo) 20)))]
+    (when logged?
+      (if current-repo
+        (let [repos (state/sub [:me :repos])]
+          (if (> (count repos) 1)
+            (ui/dropdown-with-links
+             (fn [{:keys [toggle-fn]}]
+               [:a#repo-switch {:on-click toggle-fn}
+                [:span (get-repo-name current-repo)]
+                [:span.dropdown-caret.ml-1 {:style {:border-top-color "#6b7280"}}]])
+             (mapv
+              (fn [{:keys [id url]}]
+                {:title (get-repo-name url)
+                 :options {:on-click (fn []
+                                       (state/set-current-repo! url)
+                                       (when-not (= :draw (state/get-current-route))
+                                         (route-handler/redirect-to-home!))
+                                       (when on-click
+                                         (on-click url)))}})
+              (remove (fn [repo]
+                        (= current-repo (:url repo)))
+                      repos))
+             {:modal-class (util/hiccup->class
+                            "origin-top-right.absolute.left-0.mt-2.w-48.rounded-md.shadow-lg ")})
+            (if local-repo?
+              [:span (get-repo-name current-repo)]
+              [:a
+               {:href current-repo
+                :target "_blank"}
+               (get-repo-name current-repo)])))))))

+ 18 - 21
src/main/frontend/components/right_sidebar.cljs

@@ -3,12 +3,10 @@
             [frontend.ui :as ui]
             [frontend.components.svg :as svg]
             [frontend.components.page :as page]
-            [frontend.components.hiccup :as hiccup]
             [frontend.components.block :as block]
             [frontend.extensions.graph-2d :as graph-2d]
             [frontend.components.onboarding :as onboarding]
             [frontend.handler.route :as route-handler]
-            [frontend.handler.editor :as editor-handler]
             [frontend.handler.page :as page-handler]
             [frontend.state :as state]
             [frontend.db :as db]
@@ -150,10 +148,10 @@
           blocks (if journal?
                    (rest blocks)
                    blocks)
-          sections (hiccup/build-slide-sections blocks {:id "slide-reveal-js"
-                                                        :start-level 2
-                                                        :slide? true
-                                                        :sidebar? true})]
+          sections (block/build-slide-sections blocks {:id "slide-reveal-js"
+                                                       :start-level 2
+                                                       :slide? true
+                                                       :sidebar? true})]
       [[:a {:href (str "/page/" (util/url-encode page-name))}
         (util/capitalize-all page-name)]
        [:div.ml-2.slide.mt-2
@@ -228,22 +226,21 @@
                                                     "1 0 40%"
                                                     "0 0 0px")}}
        (if sidebar-open?
-         [:div {:style {:flex "1 1 auto"
+         [:div.hide-scrollbar {:style {:flex "1 1 auto"
                         :padding 12
                         :height "100%"
-                        :overflow-y "scroll"
+                        :overflow-y "auto"
                         :overflow-x "hidden"
-                        :box-sizing "content-box"
-                        :margin-right -17}}
+                        :box-sizing "content-box"}}
           [:div.flex.flex-row.mb-2 {:key "right-sidebar-settings"}
            [:div.mr-4.text-sm
             [:a.right-sidebar-button {:on-click (fn [e]
-                             (state/sidebar-add-block! repo "contents" :contents nil))}
+                                                  (state/sidebar-add-block! repo "contents" :contents nil))}
              (t :right-side-bar/contents)]]
 
            [:div.mr-4.text-sm
             [:a.right-sidebar-button {:on-click (fn [_e]
-                             (state/sidebar-add-block! repo "recent" :recent nil))}
+                                                  (state/sidebar-add-block! repo "recent" :recent nil))}
              (t :right-side-bar/recent)]]
 
            (when config/publishing?
@@ -253,25 +250,25 @@
 
            [:div.mr-4.text-sm
             [:a.right-sidebar-button {:on-click (fn []
-                             (when-let [page (get-current-page)]
-                               (state/sidebar-add-block!
-                                repo
-                                (str "page-graph-" page)
-                                :page-graph
-                                page)))}
+                                                  (when-let [page (get-current-page)]
+                                                    (state/sidebar-add-block!
+                                                     repo
+                                                     (str "page-graph-" page)
+                                                     :page-graph
+                                                     page)))}
              (t :right-side-bar/page)]]
 
            [:div.mr-4.text-sm
             (let [theme (if dark? "white" "dark")]
               [:a.right-sidebar-button {:title (t :right-side-bar/switch-theme theme)
-                   :on-click (fn []
-                               (state/set-theme! theme))}
+                                        :on-click (fn []
+                                                    (state/set-theme! theme))}
                (t :right-side-bar/theme (t (keyword theme)))])]
 
            (when-not config/publishing?
              [:div.mr-4.text-sm
               [:a.right-sidebar-button {:on-click (fn [_e]
-                               (state/sidebar-add-block! repo "help" :help nil))}
+                                                    (state/sidebar-add-block! repo "help" :help nil))}
                (t :right-side-bar/help)]])]
 
           (for [[idx [repo db-id block-type block-data]] (medley/indexed blocks)]

+ 0 - 3
src/main/frontend/components/search.cljs

@@ -2,7 +2,6 @@
   (:require [rum.core :as rum]
             [frontend.util :as util]
             [frontend.handler.route :as route]
-            [frontend.handler.editor :as editor-handler]
             [frontend.handler.page :as page-handler]
             [frontend.db :as db]
             [frontend.handler.search :as search-handler]
@@ -12,8 +11,6 @@
             [frontend.config :as config]
             [frontend.search :as search]
             [clojure.string :as string]
-            [goog.crypt.base64 :as b64]
-            [goog.object :as gobj]
             [goog.dom :as gdom]
             [frontend.context.i18n :as i18n]))
 

+ 7 - 15
src/main/frontend/components/sidebar.cljs

@@ -10,18 +10,14 @@
             [frontend.components.page :as page]
             [frontend.components.settings :as settings]
             [frontend.components.svg :as svg]
-            [frontend.components.project :as project]
+            [frontend.components.repo :as repo]
             [frontend.components.commit :as commit]
             [frontend.components.right-sidebar :as right-sidebar]
             [frontend.storage :as storage]
-            [goog.crypt.base64 :as b64]
             [frontend.util :as util]
             [frontend.state :as state]
-            [frontend.handler.notification :as notification]
-            [frontend.handler.ui :as ui-handler]
             [frontend.handler.user :as user-handler]
             [frontend.handler.editor :as editor-handler]
-            [frontend.handler.repo :as repo-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.export :as export]
             [frontend.config :as config]
@@ -236,13 +232,9 @@
                        (not (gobj/get e "altKey"))
                        (not (gobj/get e "metaKey")))
               (when-let [repo-url (state/get-current-repo)]
-                (if (and
-                     (not (state/get-edit-input-id))
-                     (seq (state/get-changed-files repo-url)))
-                  (do
-                    (util/stop e)
-                    (state/set-modal! commit/add-commit-message))
-                  (notification/show! "No changed files yet!" :warning)))))}
+                (when-not (state/get-edit-input-id)
+                  (util/stop e)
+                  (state/set-modal! commit/add-commit-message)))))}
       (fn [e key-code]
         nil))))
   {:did-mount (fn [state]
@@ -295,7 +287,7 @@
                  :stroke-linejoin "round"
                  :stroke-linecap "round"}]]]])
           [:div.flex-shrink-0.flex.items-center.px-4.h-16 {:style {:background-color "#002b36"}}
-           (widgets/repos false)]
+           (repo/repos-dropdown false)]
           [:div.flex-1.h-0.overflow-y-auto
            (sidebar-nav route-match close-fn)]]]
         [:div.flex.flex-col.w-0.flex-1.overflow-hidden
@@ -326,10 +318,10 @@
                             (storage/remove :git/current-repo))}
                (t :login-github)])
 
-            (widgets/sync-status)
+            (repo/sync-status)
 
             [:div.repos.hidden.md:block
-             (widgets/repos true)]
+             (repo/repos-dropdown true)]
 
             (when-let [project (and current-repo (state/get-current-project))]
               [:a.opacity-70.hover:opacity-100.ml-4

+ 0 - 101
src/main/frontend/components/widgets.cljs

@@ -2,7 +2,6 @@
   (:require [rum.core :as rum]
             [frontend.util :as util]
             [frontend.handler.user :as user-handler]
-            [frontend.handler.git :as git-handler]
             [frontend.handler.repo :as repo-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.export :as export-handler]
@@ -13,9 +12,7 @@
             [frontend.ui :as ui]
             [frontend.db :as db]
             [frontend.version :as version]
-            [frontend.components.svg :as svg]
             [frontend.components.commit :as commit]
-            [clojure.set :as set]
             [frontend.context.i18n :as i18n]
             [reitit.frontend.easy :as rfe]))
 
@@ -39,104 +36,6 @@
        :on-click
        #(user-handler/set-preferred-format! :org))]]))
 
-(rum/defc sync-status < rum/reactive
-  []
-  (let [repo (state/get-current-repo)]
-    (when-not (= repo config/local-repo)
-      (let [git-status (state/sub [:git/status repo])
-            pulling? (= :pulling git-status)
-            pushing? (= :pushing git-status)
-            last-pulled-at (db/sub-key-value repo :git/last-pulled-at)
-            changed-files (state/sub [:repo/changed-files repo])
-            should-push? (seq changed-files)]
-        (rum/with-context [[t] i18n/*tongue-context*]
-          [:div.flex-row.flex.items-center
-           (when pushing?
-             [:span.lds-dual-ring.mt-1])
-           (ui/dropdown
-            (fn [{:keys [toggle-fn]}]
-              [:div.cursor.w-2.h-2.sync-status.mr-2
-               {:class (if (or should-push? pushing?) "bg-orange-400" "bg-green-600")
-                :style {:border-radius "50%"
-                        :margin-top 2}
-                :on-mouse-over
-                (fn [e]
-                  (toggle-fn)
-                  (js/setTimeout repo-handler/check-changed-files-status 0))}])
-            (fn [{:keys [toggle-fn]}]
-              [:div.p-2.rounded-md.shadow-xs.bg-base-3.flex.flex-col.sync-content
-               {:on-mouse-leave toggle-fn}
-               (if (and should-push? (seq changed-files))
-                 [:div
-                  [:div.changes
-                   [:ul
-                    (for [file changed-files]
-                      [:li {:key (str "sync-" file)}
-                       [:div.flex.flex-row.justify-between.align-items
-                        [:a {:href (rfe/href :file {:path file})}
-                         file]
-                        [:a.ml-4.text-sm.mt-1
-                         {:on-click (fn [e]
-                                      (export-handler/download-file! file))}
-                         [:span (t :download)]]]])]]
-                  ;; [:a.text-sm.font-bold {:href "/diff"} "Check diff"]
-                  [:div.flex.flex-row.justify-between.align-items.mt-2
-                   (ui/button (t :git/push)
-                              :on-click (fn [] (state/set-modal! commit/add-commit-message)))
-                   (if pushing?
-                     [:span.lds-dual-ring.mt-1])]]
-                 [:p (t :git/local-changes-synced)])
-               [:hr]
-               [:div
-                [:p {:style {:font-size 12}} (t :git/last-pull)
-                 last-pulled-at]
-                [:div.flex.flex-row.justify-between.align-items
-                 (ui/button (t :git/pull)
-                            :on-click (fn [] (repo-handler/pull-current-repo)))
-                 (if pulling?
-                   [:span.lds-dual-ring.mt-1])]
-                [:p.pt-2.text-sm.opacity-50
-                 (t :git/version) (str " " version/version)]]]))])))))
-
-(rum/defc repos < rum/reactive
-  [head? on-click]
-  (let [current-repo (state/sub :git/current-repo)
-        logged? (state/logged?)
-        local-repo? (= current-repo config/local-repo)
-        get-repo-name-f (fn [repo]
-                          (if head?
-                            (db/get-repo-path repo)
-                            (util/take-at-most (db/get-repo-name repo) 20)))]
-    (when logged?
-      (if current-repo
-        (let [repos (state/sub [:me :repos])]
-          (if (> (count repos) 1)
-            (ui/dropdown-with-links
-             (fn [{:keys [toggle-fn]}]
-               [:a#repo-switch {:on-click toggle-fn}
-                [:span (get-repo-name-f current-repo)]
-                [:span.dropdown-caret.ml-1 {:style {:border-top-color "#6b7280"}}]])
-             (mapv
-              (fn [{:keys [id url]}]
-                {:title (get-repo-name-f url)
-                 :options {:on-click (fn []
-                                       (state/set-current-repo! url)
-                                       (when-not (= :draw (state/get-current-route))
-                                         (route-handler/redirect-to-home!))
-                                       (when on-click
-                                         (on-click url)))}})
-              (remove (fn [repo]
-                        (= current-repo (:url repo)))
-                      repos))
-             {:modal-class (util/hiccup->class
-                            "origin-top-right.absolute.left-0.mt-2.w-48.rounded-md.shadow-lg ")})
-            (if local-repo?
-              [:span (get-repo-name-f current-repo)]
-              [:a
-               {:href current-repo
-                :target "_blank"}
-               (get-repo-name-f current-repo)])))))))
-
 (rum/defcs add-repo <
   (rum/local "" ::repo)
   (rum/local "" ::branch)

+ 0 - 1
src/main/frontend/core.cljs

@@ -4,7 +4,6 @@
             [frontend.handler.route :as route]
             [frontend.page :as page]
             [frontend.routes :as routes]
-            [frontend.util :as util]
             [reitit.frontend :as rf]
             [reitit.frontend.easy :as rfe]))
 

+ 0 - 1
src/main/frontend/db.cljs

@@ -13,7 +13,6 @@
             [frontend.utf8 :as utf8]
             [cljs-bean.core :as bean]
             [frontend.config :as config]
-            [rum.core :as rum]
             [goog.object :as gobj]
             ["localforage" :as localforage]
             [promesa.core :as p]

+ 1 - 2
src/main/frontend/db_mixins.cljs

@@ -1,6 +1,5 @@
 (ns frontend.db-mixins
-  (:require [frontend.db :as db]
-            [rum.core :as rum]))
+  (:require [frontend.db :as db]))
 
 (def query
   {:wrap-render

+ 0 - 2
src/main/frontend/dicts.cljs

@@ -1,7 +1,5 @@
 (ns frontend.dicts
   (:require [tongue.core :as tongue]
-            [frontend.state :as state]
-            [clojure.string :as string]
             [frontend.config :as config]))
 
 

+ 0 - 2
src/main/frontend/extensions/code.cljs

@@ -1,8 +1,6 @@
 (ns frontend.extensions.code
   (:require [rum.core :as rum]
-            [frontend.config :as config]
             [frontend.util :as util]
-            [frontend.mixins :as mixins]
             [goog.dom :as gdom]
             [goog.object :as gobj]
             [frontend.db :as db]

+ 7 - 2
src/main/frontend/extensions/code.css

@@ -1,9 +1,14 @@
 .extensions__code {
-    @apply relative
+    @apply relative;
+    z-index: 0;
 }
 
 .extensions__code-lang {
     @apply absolute top-0 right-0 p-1 text-sm text-gray-500;
-    z-index: 2;
+    z-index: 1;
     background: white;
+}
+
+.extensions__code > .CodeMirror {
+    z-index: 0;
 }

+ 1 - 6
src/main/frontend/extensions/sci.cljs

@@ -1,10 +1,5 @@
 (ns frontend.extensions.sci
-  (:require [rum.core :as rum]
-            [frontend.config :as config]
-            [frontend.ui :as ui]
-            [goog.object :as gobj]
-            [cljs-bean.core :as bean]
-            [sci.core :as sci]))
+  (:require [sci.core :as sci]))
 
 (defn eval-string
   [s]

+ 0 - 1
src/main/frontend/extensions/slide.cljs

@@ -2,7 +2,6 @@
   (:require [rum.core :as rum]
             [medley.core :as medley]
             [cljs-bean.core :as bean]
-            [promesa.core :as p]
             [frontend.loader :as loader]
             [frontend.ui :as ui]
             [frontend.config :as config]))

+ 4 - 3
src/main/frontend/external/roam.cljs

@@ -21,7 +21,7 @@
 ;; TODO: 5. Roam attributes -> properties
 ;; TODO: 6. hiccup
 
-(defonce uid-pattern #"\(\(([a-zA-Z0-9_\\-]{10})\)\)")
+(defonce uid-pattern #"\(\(([a-zA-Z0-9_\\-]{6,24})\)\)")
 (defonce macro-pattern #"\{\{([^{}]+)\}\}")
 
 (defn uid-transform
@@ -55,7 +55,9 @@
                     (map last)
                     (distinct)
                     (set))]
-      (reset! all-refed-uids uids))))
+      (reset! all-refed-uids uids)
+      (doseq [uid uids]
+        (swap! uid->uuid assoc uid (medley/random-uuid))))))
 
 (defn transform
   [text]
@@ -65,7 +67,6 @@
       (uid-transform)
       (macro-transform)))
 
-;; #"(([a-zA-Z0-9_\\-]{9}))"
 (declare children->text)
 (defn child->text
   [{:keys [uid string children] :as child} level]

+ 0 - 1
src/main/frontend/format/adoc.cljs

@@ -1,6 +1,5 @@
 (ns frontend.format.adoc
   (:require [frontend.format.protocol :as protocol]
-            [frontend.config :as config]
             [frontend.loader :as loader]))
 
 (defn loaded? []

+ 0 - 2
src/main/frontend/format/block.cljs

@@ -7,9 +7,7 @@
             [medley.core :as medley]
             [frontend.config :as config]
             [datascript.core :as d]
-            [clojure.set :as set]
             [frontend.date :as date]
-            [frontend.format.mldoc :as mldoc]
             [medley.core :as medley]))
 
 (defn heading-block?

+ 0 - 3
src/main/frontend/format/mldoc.cljs

@@ -1,11 +1,8 @@
 (ns frontend.format.mldoc
   (:require [frontend.format.protocol :as protocol]
             [frontend.util :as util]
-            [frontend.config :as config]
             [clojure.string :as string]
-            [frontend.loader :as loader]
             [cljs-bean.core :as bean]
-            [medley.core :as medley]
             [cljs.core.match :refer-macros [match]]))
 
 (defn default-config

+ 1 - 3
src/main/frontend/fs.cljs

@@ -1,7 +1,5 @@
 (ns frontend.fs
-  (:require [frontend.util :as util]
-            [promesa.core :as p]
-            [clojure.string :as string]))
+  (:require [frontend.util :as util]))
 
 (defn mkdir
   [dir]

+ 1 - 4
src/main/frontend/git.cljs

@@ -5,10 +5,7 @@
             [clojure.string :as string]
             [clojure.set :as set]
             [frontend.state :as state]
-            [goog.object :as gobj]
-            [cljs-bean.core :as bean]
-            [cljs-time.coerce :as tc]
-            [cljs-time.core :as t]))
+            [cljs-bean.core :as bean]))
 
 ;; only support Github now
 (defn get-username

+ 1 - 3
src/main/frontend/graph.cljs

@@ -1,7 +1,5 @@
 (ns frontend.graph
-  (:require [frontend.handler.ui :as ui-handler]
-            [frontend.handler.route :as route-handler]
-            [frontend.util :as util]
+  (:require [frontend.handler.route :as route-handler]
             [clojure.string :as string]
             [cljs-bean.core :as bean]
             [goog.object :as gobj]

+ 0 - 1
src/main/frontend/handler.cljs

@@ -10,7 +10,6 @@
             [frontend.handler.notification :as notification]
             [frontend.handler.migration :as migration-handler]
             [frontend.handler.repo :as repo-handler]
-            [frontend.handler.route :as route-handler]
             [frontend.handler.file :as file-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.ui :as ui]))

+ 19 - 0
src/main/frontend/handler/common.cljs

@@ -0,0 +1,19 @@
+(ns frontend.handler.common
+  (:require [goog.object :as gobj]
+            [frontend.state :as state]
+            [cljs-bean.core :as bean]
+            [promesa.core :as p]
+            [frontend.util :as util]))
+
+(defn check-changed-files-status
+  []
+  (when-let [repo (state/get-current-repo)]
+    (when (and
+           (gobj/get js/window "workerThread")
+           (gobj/get js/window.workerThread "getChangedFiles"))
+      (->
+       (p/let [files (js/window.workerThread.getChangedFiles (util/get-repo-dir repo))]
+         (let [files (bean/->clj files)]
+           (state/set-changed-files! repo files)))
+       (p/catch (fn [error]
+                  (js/console.dir error)))))))

+ 4 - 5
src/main/frontend/handler/dnd.cljs

@@ -511,8 +511,7 @@
           :else
           (move-block-in-different-repos target-block-repo to-block-repo target-block to-block top-block bottom-block nested? top? target-child? direction target-content target-file original-top-block-start-pos block-changes))
 
-        ;; (when (state/git-auto-push?)
-        ;;   (doseq [repo (->> #{target-block-repo to-block-repo}
-        ;;                     (remove nil?))]
-        ;;     (repo-handler/push repo nil)))
-        ))))
+        (when (state/git-auto-push?)
+          (doseq [repo (->> #{target-block-repo to-block-repo}
+                            (remove nil?))]
+            (repo-handler/push repo nil)))))))

+ 0 - 6
src/main/frontend/handler/draw.cljs

@@ -5,18 +5,12 @@
             [promesa.core :as p]
             [frontend.state :as state]
             [frontend.db :as db]
-            [frontend.git :as git]
-            [frontend.github :as github]
             [frontend.handler.file :as file-handler]
             [frontend.handler.git :as git-handler]
-            [cljs-bean.core :as bean]
             [frontend.date :as date]
             [frontend.config :as config]
-            [frontend.format :as format]
-            [frontend.format.protocol :as protocol]
             [frontend.storage :as storage]
             [clojure.string :as string]
-            [cljs-time.local :as tl]
             [cljs-time.core :as t]
             [cljs-time.coerce :as tc]))
 

+ 32 - 27
src/main/frontend/handler/editor.cljs

@@ -10,7 +10,6 @@
             [frontend.format :as format]
             [frontend.format.block :as block]
             [frontend.image :as image]
-            [cljs-time.local :as tl]
             [cljs-time.core :as t]
             [cljs-time.coerce :as tc]
             [frontend.db :as db]
@@ -423,7 +422,8 @@
 
                  :else
                  value)]
-     (when (not= (string/trim content) (string/trim value)) ; block content changed
+     (cond
+       (not= (string/trim content) (string/trim value)) ; block content changed
        (let [file (db/entity repo (:db/id file))]
          (cond
            ;; Page was referenced but no related file
@@ -545,12 +545,16 @@
              (when (or (seq retract-refs) pre-block?)
                (ui-handler/re-render-root!))
 
-             ;; (when (state/git-auto-push?)
-             ;;     (repo-handler/push repo nil))
-             )
+             (repo-handler/push-if-auto-enabled! repo))
 
            :else
-           nil))))))
+           nil))
+
+       (seq (state/get-changed-files))
+       (repo-handler/push-if-auto-enabled! repo)
+
+       :else
+       nil))))
 
 (defn insert-new-block-aux!
   [{:block/keys [uuid content meta file dummy? level repo page format properties collapsed?] :as block}
@@ -566,22 +570,22 @@
         repo (or repo (state/get-current-repo))
         ;; block-has-children? (seq children) ; not working for now
         block-has-children? (db/block-has-children? repo block)
-        v1 (subs value 0 pos)
-        v2 (string/triml (subs value pos))
-        v1 (string/trim (if with-level? v1 (block/with-levels v1 format block)))
-        v2-level (cond
-                   new-level
-                   new-level
-                   (or block-self? block-has-children?)
-                   (inc level)
-                   :else
-                   level)
-        v2 (if (and v2
-                    (re-find (re-pattern (util/format "^[%s]+\\s+" (config/get-block-pattern format))) v2))
-             v2
-             (rebuild-block-content
-              (str (config/default-empty-block format v2-level) " " v2)
-              format))
+        fst-block-text (subs value 0 pos)
+        snd-block-text (string/triml (subs value pos))
+        fst-block-text (string/trim (if with-level? fst-block-text (block/with-levels fst-block-text format block)))
+        snd-block-text-level (cond
+                               new-level
+                               new-level
+                               (or block-self? block-has-children?)
+                               (inc level)
+                               :else
+                               level)
+        snd-block-text (if (and snd-block-text
+                                (re-find (re-pattern (util/format "^[%s]+\\s+" (config/get-block-pattern format))) snd-block-text))
+                         snd-block-text
+                         (rebuild-block-content
+                          (str (config/default-empty-block format snd-block-text-level) " " snd-block-text)
+                          format))
         block (with-block-meta repo block)
         original-id (:block/uuid block)
         format (:block/format block)
@@ -589,7 +593,7 @@
         file (db/entity repo (:db/id file))
         insert-block (fn [block file-path file-content]
                        (let [value (if create-new-block?
-                                     (str v1 "\n" v2)
+                                     (str fst-block-text "\n" snd-block-text)
                                      value)
                              value (text/re-construct-block-properties block value properties)
                              value (rebuild-block-content value format)
@@ -652,7 +656,7 @@
                            (when ok-handler
                              (let [first-block (first blocks)
                                    last-block (last blocks)]
-                               (ok-handler [first-block last-block v2]))))))]
+                               (ok-handler [first-block last-block snd-block-text]))))))]
     (cond
       (and (not file) page)
       ;; TODO: replace with handler.page/create!
@@ -686,7 +690,7 @@
                                 (str content
                                      (text/remove-level-spaces value (keyword format))
                                      "\n"
-                                     v2))
+                                     snd-block-text))
                 (git-handler/git-add repo path)
                 (ui-handler/re-render-root!)
 
@@ -1008,7 +1012,8 @@
         (when top-block?
           (route-handler/redirect! {:to :page
                                     :path-params {:name (:page/name page)}})
-          (ui-handler/re-render-root!))))))
+          (ui-handler/re-render-root!))
+        (repo-handler/push-if-auto-enabled! repo)))))
 
 (defn remove-block-property!
   [block-id key]
@@ -1065,7 +1070,7 @@
                                             (if (string/starts-with? (string/lower-case line) key)
                                               new-line
                                               line))
-                                       (rest lines))]
+                                          (rest lines))]
                             (->> (cons (first lines) body)
                                  (string/join "\n")))
 

+ 0 - 2
src/main/frontend/handler/expand.cljs

@@ -3,8 +3,6 @@
             [goog.dom :as gdom]
             [goog.object :as gobj]
             [frontend.util :as util]
-            [clojure.string :as string]
-            [medley.core :as medley]
             [frontend.state :as state]
             [frontend.db :as db]))
 

+ 41 - 32
src/main/frontend/handler/external.cljs

@@ -1,7 +1,7 @@
 (ns frontend.handler.external
   (:require [frontend.external :as external]
             [frontend.handler.file :as file-handler]
-            [frontend.handler.notification :as notification]
+            [frontend.handler.common :as common-handler]
             [frontend.state :as state]
             [frontend.date :as date]
             [frontend.config :as config]
@@ -9,37 +9,46 @@
             [frontend.db :as db]))
 
 (defn index-files!
-  [repo files error-files]
-  (doseq [file files]
-    (let [title (:title file)
-          journal? (date/valid-journal-title? title)]
-      (try
-        (when-let [text (:text file)]
-          (let [path (str (if journal?
-                            config/default-journals-directory
-                            config/default-pages-directory)
-                          "/"
-                          (if journal?
-                            (date/journal-title->default title)
-                            (string/replace title "/" "-"))
-                          ".md")]
-            (file-handler/alter-file repo path text {})
-            (when journal?
-              (let [page-name (string/lower-case title)]
-                (db/transact! repo
-                              [{:page/name page-name
-                                :page/journal? true
-                                :page/journal-day (date/journal-title->int title)}])))))
-        (catch js/Error e
-          (swap! error-files conj file))))))
+  [repo files git-add-cb]
+  (let [titles (->> files
+                    (map :title)
+                    (map :text)
+                    (remove nil?))
+        files (map (fn [file]
+                     (let [title (:title file)
+                           journal? (date/valid-journal-title? title)]
+                       (when-let [text (:text file)]
+                         (let [path (str (if journal?
+                                           config/default-journals-directory
+                                           config/default-pages-directory)
+                                         "/"
+                                         (if journal?
+                                           (date/journal-title->default title)
+                                           (string/replace title "/" "-"))
+                                         ".md")]
+                           [path text]))))
+                   files)]
+    ;; TODO: git add is quite slow
+    (file-handler/alter-files repo files {:add-history? false
+                                          :update-status? false
+                                          :reset? true
+                                          :git-add-cb git-add-cb})
+    (let [journal-pages-tx (let [titles (filter date/valid-journal-title? titles)]
+                             (map
+                              (fn [title]
+                                (let [page-name (string/lower-case title)]
+                                  {:page/name page-name
+                                   :page/journal? true
+                                   :page/journal-day (date/journal-title->int title)}))
+                              titles))]
+      (when (seq journal-pages-tx)
+        (db/transact! repo journal-pages-tx)))))
 
-;; TODO: compute the dependencies
-;; TODO: Should it merge the roam daily notes with the month journals
 (defn import-from-roam-json!
-  [data]
+  [data finished-ok-handler]
   (when-let [repo (state/get-current-repo)]
-    (let [files (external/to-markdown-files :roam data {})
-          error-files (atom #{})]
-      (index-files! repo files error-files)
-      (when (seq @error-files)
-        (index-files! repo @error-files (atom nil))))))
+    (let [files (external/to-markdown-files :roam data {})]
+      (index-files! repo files
+                    (fn []
+                      (common-handler/check-changed-files-status)
+                      (finished-ok-handler))))))

+ 48 - 28
src/main/frontend/handler/file.cljs

@@ -6,15 +6,12 @@
             [frontend.state :as state]
             [frontend.db :as db]
             [frontend.git :as git]
+            [frontend.handler.common :as common-handler]
             [frontend.handler.git :as git-handler]
             [frontend.handler.ui :as ui-handler]
-            [datascript.core :as d]
-            [frontend.github :as github]
             [cljs-bean.core :as bean]
-            [frontend.date :as date]
             [frontend.config :as config]
             [frontend.format :as format]
-            [frontend.format.protocol :as protocol]
             [clojure.string :as string]
             [frontend.history :as history]
             [frontend.handler.project :as project-handler]))
@@ -107,10 +104,11 @@
                    (js/console.dir error))))))
 
 (defn alter-file
-  [repo path content {:keys [reset? re-render-root? add-history?]
+  [repo path content {:keys [reset? re-render-root? add-history? update-status?]
                       :or {reset? true
                            re-render-root? false
-                           add-history? true}}]
+                           add-history? true
+                           update-status? false}}]
   (let [original-content (db/get-file-no-sub repo path)]
     (if reset?
       (db/reset-file! repo path content)
@@ -118,7 +116,7 @@
     (util/p-handle
      (fs/write-file (util/get-repo-dir repo) path content)
      (fn [_]
-       (git-handler/git-add repo path)
+       (git-handler/git-add repo path update-status?)
        (when (= path (str config/app-name "/" config/config-file))
          (restore-config! repo true))
        (when (= path (str config/app-name "/" config/custom-css-file))
@@ -131,26 +129,48 @@
        (js/console.error error)))))
 
 (defn alter-files
-  [repo files]
-  (let [files-tx (mapv (fn [[path content]]
-                         (let [original-content (db/get-file-no-sub repo path)]
-                           [path original-content content])) files)]
-    (-> (p/all
-         (doall
-          (map
-           (fn [[path content]]
-             (db/set-file-content! repo path content)
-             (util/p-handle
-              (fs/write-file (util/get-repo-dir repo) path content)
-              (fn [_]
-                (git-handler/git-add repo path))
-              (fn [error]
-                (println "Write file failed, path: " path ", content: " content)
-                (js/console.error error))))
-           files)))
-        (p/then (fn [_result]
-                  (ui-handler/re-render-file!)
-                  (history/add-history! repo files-tx))))))
+  ([repo files]
+   (alter-files repo files {}))
+  ([repo files {:keys [add-history? update-status? git-add-cb reset?]
+                :or {add-history? true
+                     update-status? true
+                     reset? false}}]
+   (let [files-tx (mapv (fn [[path content]]
+                          (let [original-content (db/get-file-no-sub repo path)]
+                            [path original-content content])) files)
+         write-file-f (fn [[path content]]
+                        (if reset?
+                          (db/reset-file! repo path content)
+                          (db/set-file-content! repo path content))
+                        (util/p-handle
+                         (fs/write-file (util/get-repo-dir repo) path content)
+                         (fn [_])
+                         (fn [error]
+                           (println "Write file failed, path: " path ", content: " content)
+                           (js/console.error error))))
+         git-add-f (fn [_result]
+                     (let [add-helper
+                           (fn []
+                             (doall
+                              (map
+                               (fn [[path content]]
+                                 (git-handler/git-add repo path update-status?))
+                               files)))]
+                       (-> (p/all (add-helper))
+                           (p/then (fn [_]
+                                     (when git-add-cb
+                                       (git-add-cb))))
+                           (p/catch (fn [error]
+                                      (println "Git add failed:")
+                                      (js/console.error error)))))
+                     (ui-handler/re-render-file!)
+                     (when add-history?
+                       (history/add-history! repo files-tx)))]
+     (-> (p/all (doall (map write-file-f files)))
+         (p/then git-add-f)
+         (p/catch (fn [error]
+                    (println "Alter files failed:")
+                    (js/console.error error)))))))
 
 (defn remove-file!
   [repo file]
@@ -161,8 +181,8 @@
                                     "/"
                                     file)
                                nil)]
-       (state/git-add! repo (str "- " file))
        (when-let [file (db/entity repo [:file/path file])]
+         (common-handler/check-changed-files-status)
          (let [file-id (:db/id file)
                page-id (db/get-file-page-id (:file/path file))
                tx-data (map

+ 11 - 15
src/main/frontend/handler/git.cljs

@@ -1,24 +1,16 @@
 (ns frontend.handler.git
   (:refer-clojure :exclude [clone load-file])
   (:require [frontend.util :as util :refer-macros [profile]]
-            [frontend.fs :as fs]
             [promesa.core :as p]
             [frontend.state :as state]
             [frontend.db :as db]
             [frontend.git :as git]
-            [frontend.github :as github]
-            [cljs-bean.core :as bean]
             [frontend.date :as date]
-            [frontend.config :as config]
-            [frontend.format :as format]
-            [frontend.format.protocol :as protocol]
             [goog.object :as gobj]
             [frontend.handler.notification :as notification]
             [frontend.handler.route :as route-handler]
-            [clojure.string :as string]
-            [cljs-time.local :as tl]
-            [cljs-time.core :as t]
-            [cljs-time.coerce :as tc]))
+            [frontend.handler.common :as common-handler]
+            [cljs-time.local :as tl]))
 
 (defn- set-latest-commit!
   [repo-url hash]
@@ -43,10 +35,15 @@
   (db/set-key-value repo-url :git/error (if value (str value))))
 
 (defn git-add
-  [repo-url file]
-  (p/let [result (git/add repo-url file)]
-    (state/git-add! repo-url file)))
-
+  ([repo-url file]
+   (git-add repo-url file true))
+  ([repo-url file update-status?]
+   (-> (p/let [result (git/add repo-url file)]
+         (when update-status?
+           (common-handler/check-changed-files-status)))
+       (p/catch (fn [error]
+                  (println "git add '" file "' failed: " error)
+                  (js/console.error error))))))
 (defn get-latest-commit
   ([repo-url handler]
    (get-latest-commit repo-url handler 1))
@@ -86,7 +83,6 @@
                                     (state/get-github-token repo)
                                     true)]
         (reset! pushing? false)
-        (state/clear-changed-files! repo)
         (notification/clear! nil)
         (route-handler/redirect! {:to :home})))))
 

+ 0 - 1
src/main/frontend/handler/history.cljs

@@ -1,7 +1,6 @@
 (ns frontend.handler.history
   (:require [frontend.state :as state]
             [frontend.history :as history]
-            [frontend.handler.ui :as ui-handler]
             [frontend.handler.file :as file]))
 
 (defn- default-undo

+ 0 - 1
src/main/frontend/handler/migration.cljs

@@ -4,7 +4,6 @@
             [frontend.ui :as ui]
             [promesa.core :as p]
             [frontend.util :as util]
-            [frontend.git :as git]
             [clojure.string :as str]
             [frontend.date :as date]
             [frontend.config :as config]

+ 10 - 5
src/main/frontend/handler/page.cljs

@@ -6,8 +6,10 @@
             [frontend.util :as util :refer-macros [profile]]
             [frontend.tools.html-export :as html-export]
             [frontend.config :as config]
+            [frontend.handler.common :as common-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.file :as file-handler]
+            [frontend.handler.repo :as repo-handler]
             [frontend.handler.git :as git-handler]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.project :as project-handler]
@@ -250,11 +252,12 @@
               ;; remove file
               (->
                (p/let [_ (git/remove-file repo file-path)
-                       _result (fs/unlink (str (util/get-repo-dir repo)
-                                               "/"
-                                               file-path)
-                                          nil)]
-                 (state/git-add! repo (str "- " file-path)))
+                       _ (fs/unlink (str (util/get-repo-dir repo)
+                                         "/"
+                                         file-path)
+                                    nil)]
+                 (common-handler/check-changed-files-status)
+                 (repo-handler/push-if-auto-enabled! repo))
                (p/catch (fn [err]
                           (prn "error: " err))))))
 
@@ -300,6 +303,8 @@
 
         (notification/show! "Page renamed successfully!" :success)
 
+        (repo-handler/push-if-auto-enabled! repo)
+
         (ui-handler/re-render-root!)))))
 
 (defn rename-when-alter-title-propertiy!

+ 93 - 106
src/main/frontend/handler/repo.cljs

@@ -3,28 +3,22 @@
   (:require [frontend.util :as util :refer-macros [profile]]
             [frontend.fs :as fs]
             [promesa.core :as p]
-            [datascript.core :as d]
             [frontend.state :as state]
             [frontend.db :as db]
             [frontend.git :as git]
-            [frontend.github :as github]
             [cljs-bean.core :as bean]
             [frontend.date :as date]
             [frontend.config :as config]
             [frontend.format :as format]
-            [frontend.format.protocol :as protocol]
             [goog.object :as gobj]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.git :as git-handler]
             [frontend.handler.file :as file-handler]
             [frontend.handler.migration :as migration-handler]
-            [frontend.handler.project :as project-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.route :as route-handler]
-            [frontend.handler.user :as user-handler]
+            [frontend.handler.common :as common-handler]
             [frontend.ui :as ui]
-            [cljs-time.local :as tl]
-            [cljs-time.core :as t]
             [cljs.reader :as reader]
             [clojure.string :as string]
             [frontend.dicts :as dicts]
@@ -103,18 +97,6 @@
    :error
    false))
 
-(defn show-diff-error!
-  [_repo-url]
-  (notification/show!
-   [:p
-    [:span.text-gray-700.font-bold.mr-2
-     "Please resolve the diffs if any."]
-    (ui/button
-     "Go to diff"
-     :href "/diff")]
-   :error
-   false))
-
 (defn get-new-token
   [repo]
   (when-let [installation-id (-> (filter
@@ -317,88 +299,90 @@
          (db/get-conn repo-url true)
          (db/cloned? repo-url)
          token)
-    (let [status (db/get-key-value repo-url :git/status)]
-      (when (or
-             force-pull?
-             (and
-              ;; (not= status :push-failed)
-              (not= status :pushing)
-              (empty? (state/get-changed-files repo-url))
-              (not (state/get-edit-input-id))
-              (not (state/in-draw-mode?))))
-        (git-handler/set-git-status! repo-url :pulling)
-        (let [latest-commit (db/get-key-value repo-url :git/latest-commit)]
-          (->
-           (p/let [result (git/fetch repo-url token)]
-             (let [{:keys [fetchHead]} (bean/->clj result)]
-               (when fetchHead
-                 (git-handler/set-remote-latest-commit! repo-url fetchHead))
-               (-> (git/merge repo-url)
-                   (p/then (fn [result]
-                             (-> (git/checkout repo-url)
-                                 (p/then (fn [result]
-                                           (git-handler/set-git-status! repo-url nil)
-                                           (git-handler/set-git-last-pulled-at! repo-url)
-                                           (when (and latest-commit fetchHead
-                                                      (not= latest-commit fetchHead))
-                                             (p/let [diffs (git/get-diffs repo-url latest-commit fetchHead)]
-                                               (when (seq diffs)
-                                                 (load-db-and-journals! repo-url diffs false)
-                                                 (git-handler/set-latest-commit! repo-url fetchHead)
-                                                 (when (seq (state/get-changed-files repo-url))
-                                                   ;; FIXME: no need to create a new commit
-                                                   (push repo-url {:diff-push? true})))))))
-                                 (p/catch (fn [error]
-                                            (git-handler/set-git-status! repo-url :checkout-failed)
-                                            (git-handler/set-git-error! repo-url error))))))
-                   (p/catch (fn [error]
-                              (git-handler/set-git-status! repo-url :merge-failed)
-                              (git-handler/set-git-error! repo-url error)
-                              (notification/show!
-                               [:p.content
-                                "Failed to merge, please "
-                                [:span.text-gray-700.font-bold
-                                 "resolve any diffs first."]]
-                               :error)
-                              (route-handler/redirect! {:to :diff}))))))
-           (p/catch (fn [error]
-                      (println "Pull error:" (str error))
-                      (js/console.error error)
-                      ;; token might be expired, request new token
-
-                      (cond
-                        (and (or (string/includes? (str error) "401")
-                                 (string/includes? (str error) "404"))
-                             (not fallback?))
-                        (request-app-tokens!
-                         (fn []
-                           (pull repo-url (state/get-github-token repo-url) {:fallback? true}))
-                         nil)
-
-                        (or (string/includes? (str error) "401")
-                            (string/includes? (str error) "404"))
-                        (show-install-error! repo-url (util/format "Failed to fetch %s." repo-url))
-
-                        :else
-                        nil)))))))))
-
-(defn check-changed-files-status
-  [f]
-  (when (gobj/get js/window.workerThread "getChangedFiles")
-    (->
-     (p/let [files (js/window.workerThread.getChangedFiles (util/get-repo-dir (state/get-current-repo)))]
-       (let [files (bean/->clj files)]
-         (when (empty? files)
-           ;; FIXME: getChangedFiles not return right result
-           (state/reset-changed-files! files))))
-     (p/catch (fn [error]
-                (js/console.dir error))))))
+    (p/let [files (js/window.workerThread.getChangedFiles (util/get-repo-dir repo-url))]
+      (when (empty? files)
+        (let [status (db/get-key-value repo-url :git/status)]
+          (when (or
+                 force-pull?
+                 (and
+                  ;; (not= status :push-failed)
+                  (not= status :pushing)
+                  (not (state/get-edit-input-id))
+                  (not (state/in-draw-mode?))))
+            (git-handler/set-git-status! repo-url :pulling)
+            (let [latest-commit (db/get-key-value repo-url :git/latest-commit)]
+              (->
+               (p/let [result (git/fetch repo-url token)]
+                 (let [{:keys [fetchHead]} (bean/->clj result)]
+                   (when fetchHead
+                     (git-handler/set-remote-latest-commit! repo-url fetchHead))
+                   (-> (git/merge repo-url)
+                       (p/then (fn [result]
+                                 (-> (git/checkout repo-url)
+                                     (p/then (fn [result]
+                                               (git-handler/set-git-status! repo-url nil)
+                                               (git-handler/set-git-last-pulled-at! repo-url)
+                                               (when (and latest-commit fetchHead
+                                                          (not= latest-commit fetchHead))
+                                                 (p/let [diffs (git/get-diffs repo-url latest-commit fetchHead)]
+                                                   (when (seq diffs)
+                                                     (load-db-and-journals! repo-url diffs false)
+                                                     (git-handler/set-latest-commit! repo-url fetchHead))))))
+                                     (p/catch (fn [error]
+                                                (git-handler/set-git-status! repo-url :checkout-failed)
+                                                (git-handler/set-git-error! repo-url error))))
+                                 (state/set-changed-files! repo-url nil)))
+                       (p/catch (fn [error]
+                                  (println "Git pull error:")
+                                  (js/console.error error)
+                                  (git-handler/set-git-status! repo-url :merge-failed)
+                                  (git-handler/set-git-error! repo-url error)
+                                  (git-handler/get-latest-commit
+                                   repo-url
+                                   (fn [commit]
+                                     (let [local-oid (gobj/get commit "oid")
+                                           remote-oid (db/get-key-value repo-url
+                                                                        :git/remote-latest-commit)]
+                                       (p/let [result (git/get-local-diffs repo-url local-oid remote-oid)]
+                                         (if (seq result)
+                                           (do
+                                             (notification/show!
+                                              [:p.content
+                                               "Failed to merge, please "
+                                               [:span.text-gray-700.font-bold
+                                                "resolve any diffs first."]]
+                                              :error)
+                                             (route-handler/redirect! {:to :diff}))
+                                           (push repo-url {:commit-push? true
+                                                           :force? true
+                                                           :commit-message "Merge push without diffed files"})))))))))))
+               (p/catch (fn [error]
+                          (println "Pull error:" (str error))
+                          (js/console.error error)
+                          ;; token might be expired, request new token
+
+                          (cond
+                            (and (or (string/includes? (str error) "401")
+                                     (string/includes? (str error) "404"))
+                                 (not fallback?))
+                            (request-app-tokens!
+                             (fn []
+                               (pull repo-url (state/get-github-token repo-url) {:fallback? true}))
+                             nil)
+
+                            (or (string/includes? (str error) "401")
+                                (string/includes? (str error) "404"))
+                            (show-install-error! repo-url (util/format "Failed to fetch %s." repo-url))
+
+                            :else
+                            nil)))))))))))
 
 (defn push
-  [repo-url {:keys [commit-message fallback? diff-push? force?]
+  [repo-url {:keys [commit-message fallback? diff-push? commit-push? force?]
              :or {commit-message "Logseq auto save"
                   fallback? false
                   diff-push? false
+                  commit-push? false
                   force? false}}]
   (let [status (db/get-key-value repo-url :git/status)]
     (when (and
@@ -406,10 +390,8 @@
            (not (state/get-edit-input-id)))
       (-> (p/let [files (js/window.workerThread.getChangedFiles (util/get-repo-dir (state/get-current-repo)))]
             (when (or
-                   force?
-                   (and
-                    (seq (state/get-changed-files repo-url))
-                    (seq files))
+                   commit-push?
+                   (seq files)
                    fallback?
                    diff-push?)
               ;; auto commit if there are any un-committed changes
@@ -421,13 +403,15 @@
                   (git-handler/set-git-status! repo-url :pushing)
                   (when-let [token (state/get-github-token repo-url)]
                     (util/p-handle
-                     (git/push repo-url token)
+                     (git/push repo-url token force?)
                      (fn [result]
                        (git-handler/set-git-status! repo-url nil)
                        (git-handler/set-git-error! repo-url nil)
-                       (state/clear-changed-files! repo-url))
+                       (state/set-changed-files! repo-url nil))
                      (fn [error]
+                       (println "Git push error: ")
                        (js/console.error error)
+                       (common-handler/check-changed-files-status)
                        (let [permission? (or (string/includes? (str error) "401")
                                              (string/includes? (str error) "404"))]
                          (cond
@@ -450,6 +434,11 @@
                      (println "Git push error: ")
                      (js/console.dir error)))))))
 
+(defn push-if-auto-enabled!
+  [repo]
+  (when (state/git-auto-push?)
+    (push repo nil)))
+
 (defn pull-current-repo
   []
   (when-let [repo (state/get-current-repo)]
@@ -519,8 +508,7 @@
                  (db/remove-db! url)
                  (db/remove-files-db! url)
                  (fs/rmdir (util/get-repo-dir url))
-                 (state/delete-repo! repo)
-                 (state/clear-changed-files! repo))
+                 (state/delete-repo! repo))
                (fn [error]
                  (prn "Delete repo failed, error: " error))))
 
@@ -617,7 +605,6 @@
   [{:keys [id url] :as repo}]
   (db/remove-conn! url)
   (db/clear-query-state!)
-  (state/clear-changed-files! url)
   (-> (p/let [_ (db/remove-db! url)
               _ (db/remove-files-db! url)]
         (fs/rmdir (util/get-repo-dir url)))
@@ -631,7 +618,7 @@
   (when-let [repo (state/get-current-repo)]
     (push repo {:commit-message commit-message
                 :fallback? false
-                :force? true})))
+                :commit-push? true})))
 
 (defn read-repair-journals!
   [repo-url]

+ 1 - 2
src/main/frontend/history.cljs

@@ -1,5 +1,4 @@
-(ns frontend.history
-  (:require [frontend.db :as db]))
+(ns frontend.history)
 
 ;; Undo && Redo that works with files
 ;; TODO:

+ 0 - 1
src/main/frontend/image.cljs

@@ -1,6 +1,5 @@
 (ns frontend.image
   (:require [goog.object :as gobj]
-            [frontend.blob :as blob]
             ["/frontend/exif" :as exif]
             [frontend.util :as util]
             [frontend.date :as date]

+ 1 - 2
src/main/frontend/keyboard.cljs

@@ -1,7 +1,6 @@
 (ns frontend.keyboard
   (:require [goog.events :as events]
-            [goog.ui.KeyboardShortcutHandler.EventType :as EventType]
-            [goog.events.KeyCodes :as KeyCodes])
+            [goog.ui.KeyboardShortcutHandler.EventType :as EventType])
   (:import  [goog.ui KeyboardShortcutHandler]))
 
 ;; Copy from https://github.com/tonsky/rum/blob/gh-pages/doc/useful-mixins.md#keyboard-shortcut

+ 0 - 1
src/main/frontend/keyboards.cljs

@@ -4,7 +4,6 @@
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.route :as route-handler]
             [frontend.state :as state]
-            [goog.events.KeyCodes :as codes]
             [frontend.util :as util]
             [medley.core :as medley]
             ["mousetrap" :as mousetrap]

+ 0 - 1
src/main/frontend/page.cljs

@@ -2,7 +2,6 @@
   (:require [rum.core :as rum]
             [frontend.state :as state]
             [frontend.components.sidebar :as sidebar]
-            [frontend.ui :as ui]
             [frontend.context.i18n :as i18n]))
 
 (rum/defc route-view

+ 0 - 1
src/main/frontend/publishing.cljs

@@ -7,7 +7,6 @@
             [frontend.handler.route :as route]
             [frontend.page :as page]
             [frontend.routes :as routes]
-            [frontend.util :as util]
             [reitit.frontend :as rf]
             [reitit.frontend.easy :as rfe]
             [cljs.reader :as reader]))

+ 1 - 2
src/main/frontend/publishing/html.cljs

@@ -1,7 +1,6 @@
 (ns frontend.publishing.html
   (:require-macros [hiccups.core])
-  (:require [frontend.config :as config]
-            [frontend.state :as state]
+  (:require [frontend.state :as state]
             [hiccups.runtime]))
 
 (defn publishing-html

+ 0 - 1
src/main/frontend/routes.cljs

@@ -1,7 +1,6 @@
 (ns frontend.routes
   (:require [frontend.components.home :as home]
             [frontend.components.repo :as repo]
-            [frontend.components.sidebar :as sidebar]
             [frontend.components.file :as file]
             [frontend.components.page :as page]
             [frontend.components.diff :as diff]

+ 0 - 1
src/main/frontend/search.cljs

@@ -2,7 +2,6 @@
   (:require [frontend.db :as db]
             [frontend.config :as config]
             [frontend.state :as state]
-            [medley.core :as medley]
             [frontend.util :as util]
             [cljs-bean.core :as bean]
             [clojure.string :as string]

+ 1 - 2
src/main/frontend/security.cljs

@@ -1,6 +1,5 @@
 (ns frontend.security
-  (:require [clojure.walk :as walk]
-            [clojure.string :as string]))
+  (:require [clojure.walk :as walk]))
 
 ;; To prevent from cross-site scripting vulnerability, we should add security checks for both hiccup and raw html.
 ;; Hiccup: [:a {:href "javascript:alert('hei')"} "click me"]

+ 10 - 27
src/main/frontend/state.cljs

@@ -7,9 +7,7 @@
             [goog.object :as gobj]
             [goog.dom :as gdom]
             [dommy.core :as dom]
-            [cljs-time.core :as t]
-            [cljs-time.coerce :as tc]
-            [clojure.core.async :as async]))
+            [cljs.core.async :as async]))
 
 (defonce state
   (atom
@@ -23,9 +21,7 @@
     :repo/loading-files? nil
     :repo/importing-to-db? nil
     :repo/sync-status {}
-    :repo/changed-files (or
-                         (storage/get "git-changed-files")
-                         {})
+    :repo/changed-files nil
     :indexeddb/support? true
     ;; TODO: save in local storage so that if :changed? is true when user
     ;; reloads the browser, the app should re-index the repo (another way
@@ -718,27 +714,6 @@
   [value]
   (set-state! :indexeddb/support? value))
 
-(defn git-add!
-  [repo file]
-  (update-state! [:repo/changed-files repo]
-                 (fn [files] (distinct (conj files file))))
-  (storage/set "git-changed-files" (:repo/changed-files @state)))
-
-(defn reset-changed-files!
-  [files]
-  (when-let [repo (get-current-repo)]
-    (swap! state assoc-in [:repo/changed-files repo] files)))
-
-(defn clear-changed-files!
-  [repo]
-  (set-state! [:repo/changed-files repo] nil)
-  (set-state! [:git/status repo] nil)
-  (storage/set "git-changed-files" (:repo/changed-files @state)))
-
-(defn get-changed-files
-  [repo]
-  (get-in @state [:repo/changed-files repo]))
-
 (defn set-modal!
   [modal-panel-content]
   (swap! state assoc
@@ -838,3 +813,11 @@
 (defn git-auto-push?
   []
   (true? (:git-auto-push (get-config (get-current-repo)))))
+
+(defn set-changed-files!
+  [repo changed-files]
+  (set-state! [:repo/changed-files repo] changed-files))
+
+(defn get-changed-files
+  []
+  (get-in @state [:repo/changed-files (get-current-repo)]))

+ 9 - 9
src/main/frontend/tools/html_export.cljs

@@ -1,7 +1,7 @@
 (ns frontend.tools.html-export
   (:require-macros [hiccups.core :as hiccups :refer [html]])
   (:require [frontend.db :as db]
-            [frontend.components.hiccup :as hiccup]
+            [frontend.components.block :as block]
             [frontend.extensions.slide :as slide]
             [hiccups.runtime :as hiccupsrt]
             [clojure.walk :as walk]
@@ -18,13 +18,13 @@
 (defn- build-block
   [config block]
   (let [body (:block/body block)
-        block (hiccup/build-block-part config block)]
+        block (block/build-block-part config block)]
     [:div.block
      block
      (when (seq body)
        (for [child body]
          (do
-           (hiccup/block-cp config child))))]))
+           (block/markup-element-cp config child))))]))
 
 (defn export-page
   [page-name blocks show-notification!]
@@ -36,12 +36,12 @@
     (if (seq blocks)
       (let [config {:html-export? true :slide? slide?}
             hiccup (if slide?
-                     (let [sections (hiccup/build-slide-sections blocks
-                                                                 (merge
-                                                                  config
-                                                                  {:id "slide"
-                                                                   :start-level 2})
-                                                                 build-block)]
+                     (let [sections (block/build-slide-sections blocks
+                                                                (merge
+                                                                 config
+                                                                 {:id "slide"
+                                                                  :start-level 2})
+                                                                build-block)]
                        (slide/slide-content false "" sections))
                      [:div.page
                       (for [block blocks]

+ 1 - 1
src/main/frontend/version.cljs

@@ -1,3 +1,3 @@
 (ns frontend.version)
 
-(defonce version "0.0.4.3-2")
+(defonce version "0.0.4.3-5")

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است