Browse Source

Merge branch 'master' into encryption

Tienson Qin 4 years ago
parent
commit
3f774a9a4f
39 changed files with 760 additions and 514 deletions
  1. 1 1
      deps.edn
  2. 1 2
      package.json
  3. 158 45
      resources/css/common.css
  4. 1 1
      shadow-cljs.edn
  5. 1 1
      src/electron/electron/core.cljs
  6. 1 1
      src/main/api.cljs
  7. 34 30
      src/main/frontend/components/block.cljs
  8. 2 2
      src/main/frontend/components/block.css
  9. 4 5
      src/main/frontend/components/editor.cljs
  10. 2 2
      src/main/frontend/components/header.cljs
  11. 1 1
      src/main/frontend/components/header.css
  12. 7 12
      src/main/frontend/components/repo.cljs
  13. 18 5
      src/main/frontend/components/search.cljs
  14. 1 2
      src/main/frontend/components/search.css
  15. 9 5
      src/main/frontend/components/sidebar.cljs
  16. 11 1
      src/main/frontend/components/theme.cljs
  17. 11 13
      src/main/frontend/components/theme.css
  18. 75 13
      src/main/frontend/db/model.cljs
  19. 29 26
      src/main/frontend/db/query_custom.cljs
  20. 6 4
      src/main/frontend/db/query_dsl.cljs
  21. 3 0
      src/main/frontend/db_schema.cljs
  22. 85 37
      src/main/frontend/format/block.cljs
  23. 1 1
      src/main/frontend/fs.cljs
  24. 158 174
      src/main/frontend/handler/editor.cljs
  25. 4 2
      src/main/frontend/handler/extract.cljs
  26. 3 3
      src/main/frontend/handler/file.cljs
  27. 1 3
      src/main/frontend/handler/image.cljs
  28. 2 2
      src/main/frontend/handler/page.cljs
  29. 12 0
      src/main/frontend/handler/repo.cljs
  30. 10 5
      src/main/frontend/handler/route.cljs
  31. 4 6
      src/main/frontend/handler/search.cljs
  32. 8 0
      src/main/frontend/keyboards.cljs
  33. 41 52
      src/main/frontend/search.cljs
  34. 34 0
      src/main/frontend/template.cljs
  35. 0 23
      src/main/frontend/ui.css
  36. 0 10
      src/main/frontend/utils.js
  37. 1 1
      src/main/frontend/version.cljs
  38. 16 16
      src/test/frontend/db/query_dsl_test.cljs
  39. 4 7
      yarn.lock

+ 1 - 1
deps.edn

@@ -8,7 +8,7 @@
   ;; FIXME: doesn't work on my archlinux laptop (tienson)
   ;; The required namespace "datascript.core" is not available, it was required by "frontend/db.cljs".
   datascript/datascript       {:git/url "https://github.com/tiensonqin/datascript",
-                               :sha "7c2822565d9a114c7d8604c335af89de4640e2e5"}
+                               :sha "efde8d389e6703b6f60ca3538f484a579b0d6de0"}
   ;; datascript                  {:mvn/version "1.0.1"}
   datascript-transit/datascript-transit
   {:mvn/version "0.3.0"

+ 1 - 2
package.json

@@ -63,9 +63,8 @@
         "diff": "5.0.0",
         "diff-match-patch": "^1.0.5",
         "electron": "^11.2.0",
-        "flexsearch": "git+https://github.com/logseq/flexsearch",
         "fs": "^0.0.1-security",
-        "fuzzysort": "git+https://github.com/getstation/fuzzysort#a66f5813825d2415b606cc69129070c4eb612ae2",
+        "fuse.js": "^6.4.6",
         "gulp-cached": "^1.1.1",
         "ignore": "^5.1.8",
         "jszip": "^3.5.0",

+ 158 - 45
resources/css/common.css

@@ -81,45 +81,45 @@ html[data-theme=dark] {
 .white-theme,
 html[data-theme=light] {
   --ls-primary-background-color: white;
-  --ls-secondary-background-color: #dee9f2;
-  --ls-tertiary-background-color: #f0f8ff;
-  --ls-quaternary-background-color: #e1f0fe;
+  --ls-secondary-background-color: #f7f6f4;
+  --ls-tertiary-background-color: #f1eee8;
+  --ls-quaternary-background-color: #e8e5de;
   --ls-table-tr-even-background-color: #f4f5f7;
   --ls-active-primary-color: rgb(4, 85, 145);
   --ls-active-secondary-color: #003761;
-  --ls-block-properties-background-color: var(--ls-tertiary-background-color);
+  --ls-block-properties-background-color: #f7f6f4;
   --ls-block-ref-link-text-color: #D8E1E8;
   --ls-search-background-color: var(--ls-primary-background-color);
   --ls-border-color: #ccc;
   --ls-secondary-border-color: #e2e2e2;
-  --ls-guideline-color: var(--ls-border-color);
+  --ls-guideline-color: rgba(46, 27, 5, 0.08);
   --ls-menu-hover-color: var(--ls-a-chosen-bg);
-  --ls-primary-text-color: #24292e;
+  --ls-primary-text-color: #433F38;
   --ls-secondary-text-color: #161e2e;
-  --ls-title-text-color: #222;
-  --ls-link-text-color: var(--ls-active-primary-color);
-  --ls-link-text-hover-color: var(--ls-active-secondary-color);
-  --ls-link-ref-text-color: var(--ls-link-text-color);
-  --ls-link-ref-text-hover-color: var(--ls-link-text-hover-color);
-  --ls-tag-text-color: var(--ls-link-text-color);
-  --ls-tag-text-hover-color: var(--ls-link-text-hover-color);
+  --ls-title-text-color: var(--ls-primary-text-color);
+  --ls-link-text-color: #106BA3;
+  --ls-link-text-hover-color: #5d9cd6;
+  --ls-link-ref-text-color: #106BA3;
+  --ls-link-ref-text-hover-color: #5d9cd6;
+  --ls-tag-text-color: var(--ls-link-ref-text-color);
+  --ls-tag-text-hover-color: var(--ls-link-ref-text-hover-color);
   --ls-slide-background-color: #fff;
-  --ls-block-bullet-border-color: var(--ls-border-color);
-  --ls-block-bullet-color: #394b59;
+  --ls-block-bullet-border-color: #dedede;
+  --ls-block-bullet-color: rgba(67, 63, 56, 0.25);
   --ls-block-highlight-color: #c0e6fd;
   --ls-selection-background-color: #e4f2ff;
-  --ls-page-checkbox-color: var(--ls-active-primary-color);
-  --ls-page-checkbox-border-color: #8c8c8c;
+  --ls-page-checkbox-color: #9dbbd8;
+  --ls-page-checkbox-border-color: var(--ls-page-checkbox-color);
   --ls-page-blockquote-color: var(--ls-primary-text-color);
-  --ls-page-blockquote-bg-color: var(--ls-secondary-background-color);
-  --ls-page-blockquote-border-color: var(--ls-active-primary-color);
-  --ls-page-inline-code-bg-color: var(--ls-secondary-background-color);
+  --ls-page-blockquote-bg-color: #fbfaf8;
+  --ls-page-blockquote-border-color: #799bbc;
+  --ls-page-inline-code-bg-color: #f7f6f4;
   --ls-page-inline-code-color: var(--ls-primary-text-color);
   --ls-scrollbar-foreground-color: rgba(0, 0, 0, 0.1);
   --ls-scrollbar-background-color: rgba(0, 0, 0, 0.05);
   --ls-scrollbar-thumb-hover-color: rgba(0, 0, 0, 0.2);
   --ls-head-text-color: var(--ls-link-text-color);
-  --ls-icon-color: #6b7280;
+  --ls-icon-color: #c1bdb7;
   --ls-search-icon-color: var(--ls-icon-color);
   --ls-a-chosen-bg: #f4f5f7;
   --ls-right-sidebar-code-bg-color: var(--ls-secondary-background-color);
@@ -445,6 +445,10 @@ li p:last-child,
   opacity: 0.6;
 }
 
+.opacity-30 {
+    opacity: 0.3;
+}
+
 .opacity-70 {
   opacity: 0.7;
 }
@@ -542,22 +546,6 @@ li p:last-child,
   overflow-y: auto;
 }
 
-.marker-switch {
-  font-size: 85%;
-  margin-right: 6px;
-  margin-left: 2px;
-  border-radius: 3px;
-  font-weight: 500;
-  display: inline-block;
-  text-align: center;
-  width: 16px;
-  height: 18px;
-  opacity: 0.5;
-  padding: 0 2px 0 2px;
-  border: 1px solid;
-  line-height: 1.3;
-}
-
 .heading-bg {
   border-radius: 50%;
   width: 12px;
@@ -571,6 +559,7 @@ h1.title {
   margin-bottom: 1.5rem;
   color: var(--ls-title-text-color, #222);
   font-size: var(--ls-page-title-size, 36px);
+  font-weight: 700;
 }
 
 .block-highlight,
@@ -636,10 +625,6 @@ a.login:hover {
   color: var(--ls-link-text-hover-color, #000);
 }
 
-a.marker-switch:hover {
-  opacity: 1;
-}
-
 a.tooltip-priority {
   display: contents;
   position: absolute;
@@ -672,13 +657,16 @@ img.small {
 }
 
 a.tag {
-  opacity: var(--ls-tag-text-opacity, 0.6);
-  color: var(--ls-tag-text-color, #045591);
+    font-size: 13px;
+    text-align: center;
+    text-decoration: none;
+    display: inline-block;
+    cursor: pointer;
+    opacity: 0.8;
 }
 
 a.tag:hover {
-  opacity: var(--ls-tag-text-hover-opacity, 0.8);
-  color: var(--ls-tag-text-hover-color, #045591);
+    opacity: 1;
 }
 
 svg.note {
@@ -713,3 +701,128 @@ hr {
     resize: both;
     overflow: hidden;
 }
+
+/* ideas from https://github.com/PiotrSss/logseq-bujo-theme/blob/main/main.css */
+
+/***************************************************************
+***************************** TOP ******************************
+***************************************************************/
+
+.cp__header-logo, .fade-link {
+    opacity: .3;
+    transition: .3s;
+}
+
+a.fade-link:hover {
+    opacity: 1;
+}
+
+/* import (arrows) icon */
+
+#head .refresh svg {
+    height: 20px;
+}
+
+#head {
+    background: none;
+}
+
+/* < > buttons */
+
+a[title="Go Back"],
+a[title="Go Forward"] {
+    border-radius: 3px;
+    opacity: 1;
+    transition: .3s;
+}
+
+/* search-field */
+
+#search-wrapper {
+    opacity: 0;
+    transition: .3s;
+    padding-right: 12px;
+}
+
+#search-wrapper:hover,
+#search-wrapper:focus-within {
+    opacity: 1;
+}
+
+#search>.inner {
+    max-width: 100%;
+    border-radius: 4px;
+}
+
+#search_field:focus {
+    background: var(--ls-search-background-color);
+}
+
+/* text mark/highlight */
+
+mark {
+    padding: 2px 4px;
+    border-radius: 3px;
+    font-size: 14px;
+}
+
+/* page reference */
+
+.page-reference {
+    border-radius: 3px;
+    padding: 2px 0px;
+    transition: .3s;
+}
+
+.page-reference .bracket {
+    opacity: .3;
+}
+
+/* block references */
+
+.block-ref {
+    padding: 2px 5px;
+    border-radius: 3px;
+    font-style: italic;
+}
+
+.block-ref .block-ref {
+    padding: 6px 5px;
+    border: none;
+}
+
+/* inline code */
+:not(pre)>code {
+    border-radius: 3px;
+    font-size: .9em;
+    font-family: MonoLisa, "Fira Code", Monaco, Menlo, Consolas, "COURIER NEW", monospace;
+    padding: 3px 5px !important;
+}
+
+a {
+    transition: .3s;
+}
+
+/* search-field */
+
+.dark-theme #search_field {
+    background: linear-gradient(to right, #021c23 0%, #021b21 200px, #002b36 100%);
+    transition: background .3s;
+}
+
+.dark-theme #search_field:focus {
+    box-shadow: 0px 0px 20px 0px rgba(18, 18, 18, .8);
+}
+
+.page-reference:hover {
+    background: var(--ls-secondary-background-color);
+}
+
+.references-blocks .page-reference:hover {
+    background: var(--ls-tertiary-background-color);
+}
+
+#head .fade-link {
+    font-weight: 600;
+    font-size: 13px;
+}

+ 1 - 1
shadow-cljs.edn

@@ -19,7 +19,7 @@
    :release {:asset-path "https://asset.logseq.com/static/js"}
    :compiler-options {:infer-externs :auto
                       :output-feature-set :es-next
-                      :source-map true
+                      ;; :source-map true
                       :externs ["datascript/externs.js"
                                 "externs.js"]
                       :warnings {:fn-deprecated false}}

+ 1 - 1
src/electron/electron/core.cljs

@@ -14,7 +14,7 @@
 (defonce *teardown-fn (volatile! nil))
 
 ;; Handle creating/removing shortcuts on Windows when installing/uninstalling.
-;(when (js/require "electron-squirrel-startup") (.quit app))
+(when (js/require "electron-squirrel-startup") (.quit app))
 
 (defn create-main-window
   "create main app window"

+ 1 - 1
src/main/api.cljs

@@ -10,7 +10,7 @@
   (when-let [repo (state/get-current-repo)]
     (when-let [conn (db/get-conn repo)]
       (when-let [result (query-dsl/query repo query-string)]
-        @result))))
+        (clj->js @result)))))
 
 (defn ^:export datascript_query
   [query & inputs]

+ 34 - 30
src/main/frontend/components/block.cljs

@@ -47,7 +47,8 @@
             [reitit.frontend.easy :as rfe]
             [frontend.commands :as commands]
             [lambdaisland.glogi :as log]
-            [frontend.context.i18n :as i18n]))
+            [frontend.context.i18n :as i18n]
+            [frontend.template :as template]))
 
 ;; TODO: remove rum/with-context because it'll make reactive queries not working
 
@@ -393,7 +394,7 @@
      (when (and (or show-brackets? nested-link?)
                 (not html-export?)
                 (not contents-page?))
-       [:span.text-gray-500 "[["])
+       [:span.text-gray-500.bracket "[["])
      (if (string/ends-with? s ".excalidraw")
        [:a.page-ref
         {:on-click (fn [e]
@@ -409,7 +410,7 @@
      (when (and (or show-brackets? nested-link?)
                 (not html-export?)
                 (not contents-page?))
-       [:span.text-gray-500 "]]"])]))
+       [:span.text-gray-500.bracket "]]"])]))
 
 (defn- latex-environment-content
   [name option content]
@@ -841,12 +842,12 @@
                                 (block/macro-subs macro-content arguments)
                                 macro-content)
                 macro-content (when macro-content
-                                (editor-handler/resolve-dynamic-template! macro-content))]
+                                (template/resolve-dynamic-template! macro-content))]
             (render-macro config name arguments macro-content format))
 
           (when-let [macro-txt (macro->text name arguments)]
             (let [macro-txt (when macro-txt
-                              (editor-handler/resolve-dynamic-template! macro-txt))
+                              (template/resolve-dynamic-template! macro-txt))
                   format (get-in config [:block :block/format] :markdown)]
               (render-macro config name arguments macro-txt format))))))
 
@@ -1506,18 +1507,18 @@
                          (text/build-data-value refs))]
     [:div.ls-block.flex.flex-col.rounded-sm
      (cond->
-         {:id block-id
-          :data-refs data-refs
-          :data-refs-self data-refs-self
-          :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?)}
+      {:id block-id
+       :data-refs data-refs
+       :data-refs-self data-refs-self
+       :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))
 
@@ -1748,9 +1749,11 @@
             (and (seq result)
                  (or only-blocks? blocks-grouped-by-page?))
             (->hiccup result (cond-> (assoc config
-                                            ;; :editor-box editor/box
                                             :custom-query? true
-                                            :group-by-page? blocks-grouped-by-page?)
+                                            ;; :breadcrumb-show? true
+                                            :group-by-page? blocks-grouped-by-page?
+                                            ;; :ref? true
+)
                                children?
                                (assoc :ref? true))
                       {:style {:margin-top "0.25rem"
@@ -2044,17 +2047,18 @@
      (assoc :class "doc-mode"))
    (if (:group-by-page? config)
      [:div.flex.flex-col
-      (for [[page blocks] blocks]
-        (let [alias? (:page/alias? page)
-              page (db/entity (:db/id page))]
-          [:div.my-2 (cond-> {:key (str "page-" (:db/id page))}
-                       (:ref? config)
-                       (assoc :class "color-level px-7 py-2 rounded"))
-           (ui/foldable
-            [:div
-             (page-cp config page)
-             (when alias? [:span.text-sm.font-medium.opacity-50 " Alias"])]
-            (blocks-container blocks config))]))]
+      (let [blocks (sort-by (comp :page/journal-day first) > blocks)]
+        (for [[page blocks] blocks]
+          (let [alias? (:page/alias? page)
+                page (db/entity (:db/id page))]
+            [:div.my-2 (cond-> {:key (str "page-" (:db/id page))}
+                         (:ref? config)
+                         (assoc :class "color-level px-7 py-2 rounded"))
+             (ui/foldable
+              [:div
+               (page-cp config page)
+               (when alias? [:span.text-sm.font-medium.opacity-50 " Alias"])]
+              (blocks-container blocks config))])))]
      (blocks-container blocks config))])
 
 (comment

+ 2 - 2
src/main/frontend/components/block.css

@@ -94,7 +94,7 @@
 }
 
 .block-children {
-  border-left: 2px solid;
+  border-left: 1px solid;
   border-left-color: var(--ls-guideline-color, #ddd);
 
   padding-top: 2px;
@@ -118,7 +118,6 @@
 }
 
 .block-ref {
-  color: var(--ls-link-text-color);
   padding-bottom: 2px;
   border-bottom: 0.5px solid;
   border-bottom-color: var(--ls-block-ref-link-text-color);
@@ -176,6 +175,7 @@
 
   &:hover {
     color: var(--ls-link-text-hover-color);
+    opacity: 1;
   }
 }
 

+ 4 - 5
src/main/frontend/components/editor.cljs

@@ -32,6 +32,7 @@
             [medley.core :as medley]
             [cljs-drag-n-drop.core :as dnd]
             [frontend.text :as text]
+            [frontend.template :as template]
             ["/frontend/utils" :as utils]))
 
 (rum/defc commands < rum/reactive
@@ -149,10 +150,8 @@
              @editor-handler/*selected-text
              (when (> (count edit-content) current-pos)
                (subs edit-content pos current-pos)))
-          _ (p/let [matched-blocks (when-not (string/blank? q)
-                                     (editor-handler/get-matched-blocks q (:block/uuid edit-block)))]
-              (state/set-search-result! matched-blocks))
-          matched-blocks (state/sub :search/result)]
+          matched-blocks (when-not (string/blank? q)
+                           (editor-handler/get-matched-blocks q (:block/uuid edit-block)))]
       (when input
         (let [chosen-handler (fn [chosen _click?]
                                (state/set-editor-show-block-search! false)
@@ -228,7 +227,7 @@
                                        content (if (string/includes? (string/trim edit-content) "\n")
                                                  content
                                                  (text/remove-level-spaces content format))
-                                       content (editor-handler/resolve-dynamic-template! content)]
+                                       content (template/resolve-dynamic-template! content)]
                                    (state/set-editor-show-template-search! false)
                                    (editor-handler/insert-command! id
                                                                    content

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

@@ -40,8 +40,8 @@
 
       (ui/dropdown-with-links
        (fn [{:keys [toggle-fn]}]
-         [:a {:on-click toggle-fn}
-          [:span.ml-1.text-sm (t :login)]])
+         [:a.fade-link {:on-click toggle-fn}
+          [:span.ml-1 (t :login)]])
        (let [list [{:title (t :login-google)
                     :url (str config/website "/login/google")}
                    {:title (t :login-github)

+ 1 - 1
src/main/frontend/components/header.css

@@ -46,7 +46,7 @@
 
 .cp__header-logo,
 .cp__right-menu-button {
-  opacity: 0.7;
+  opacity: 0.3;
 }
 
 .cp__header-logo {

+ 7 - 12
src/main/frontend/components/repo.cljs

@@ -71,14 +71,8 @@
                                            "Sync with the local directory"
                                            "Clone again and re-index the db")
                                   :on-click (fn []
-                                              (if local?
-                                                (nfs-handler/rebuild-index! url
-                                                                            repo-handler/create-today-journal!)
-                                                (repo-handler/rebuild-index! url))
-                                              (js/setTimeout
-                                               (fn []
-                                                 (route-handler/redirect! {:to :home}))
-                                               500))}
+                                              (repo-handler/re-index! nfs-handler/rebuild-index!)
+                                              )}
                  "Re-index"]
                 [:a.control.ml-4 {:title "Clone again and re-index the db"
                                   :on-click (fn []
@@ -101,7 +95,7 @@
       (when-not (= repo config/local-repo)
         (if (and nfs-repo? (nfs-handler/supported?))
           (let [syncing? (state/sub :graph/syncing?)]
-            [:div.ml-2.mr-1.opacity-70.hover:opacity-100 {:class (if syncing? "loader" "initial")}
+            [:div.ml-2.mr-2.opacity-30.refresh.hover:opacity-100 {:class (if syncing? "loader" "initial")}
              [:a
               {:on-click #(nfs-handler/refresh! repo
                                                 repo-handler/create-today-journal!)
@@ -206,7 +200,7 @@
           (> (count repos) 1)
           (ui/dropdown-with-links
            (fn [{:keys [toggle-fn]}]
-             [:a#repo-switch {:on-click toggle-fn}
+             [:a#repo-switch.fade-link {:on-click toggle-fn}
               (let [repo-name (get-repo-name current-repo)
                     repo-name (if (util/electron?)
                                 (last (string/split repo-name #"/"))
@@ -235,8 +229,9 @@
             (if (config/local-db? current-repo)
               (if (util/electron?)
                 (last (string/split repo-name #"/"))
-                repo-name)
-              [:a
+                [:span.fade-link
+                 repo-name])
+              [:a.fade-link
                {:href current-repo
                 :target "_blank"}
                repo-name]))

+ 18 - 5
src/main/frontend/components/search.cljs

@@ -124,16 +124,24 @@
   (when-let [input (gdom/getElement "search_field")]
     (.blur input)))
 
+(defonce search-timeout (atom nil))
+
 (rum/defc search-auto-complete
-  [{:keys [pages files blocks]} search-q]
+  [{:keys [pages files blocks] :as result} search-q]
   (rum/with-context [[t] i18n/*tongue-context*]
-    (let [new-page [{:type :new-page}]
-          new-file (when-let [ext (util/get-file-ext search-q)]
+    (let [new-file (when-let [ext (util/get-file-ext search-q)]
                      (when (contains? config/mldoc-support-formats (keyword (string/lower-case ext)))
                        [{:type :new-file}]))
           pages (map (fn [page] {:type :page :data page}) pages)
           files (map (fn [file] {:type :file :data file}) files)
           blocks (map (fn [block] {:type :block :data block}) blocks)
+          new-page (if (or
+                        (and (seq pages)
+                             (= (string/lower-case search-q)
+                                (string/lower-case (:data (first pages)))))
+                        (nil? result))
+                     []
+                     [{:type :new-page}])
           result (if config/publishing?
                    (concat pages files blocks)
                    (concat new-page pages new-file files blocks))]
@@ -238,7 +246,7 @@
        [:div.inner
         [:label.sr-only {:for "search_field"} (t :search)]
         [:div#search-wrapper.relative.w-full.text-gray-400.focus-within:text-gray-600
-         [:div.absolute.inset-y-0.flex.items-center.pointer-events-none.left-0
+         [:div.absolute.inset-y-0.flex.items-center.pointer-events-none {:style {:left 6}}
           [:svg.h-5.w-5
            {:view-box "0 0 20 20", :fill "currentColor"}
            [:path
@@ -253,12 +261,17 @@
            :auto-complete (if (util/chrome?) "chrome-off" "off") ; off not working here
            :default-value ""
            :on-change (fn [e]
+                        (when @search-timeout
+                          (js/clearTimeout @search-timeout))
                         (let [value (util/evalue e)]
                           (if (string/blank? value)
                             (search-handler/clear-search!)
                             (do
                               (state/set-q! value)
-                              (search-handler/search value)))))}]
+                              (reset! search-timeout
+                                      (js/setTimeout
+                                       #(search-handler/search value)
+                                       500))))))}]
          (when-not (string/blank? search-q)
            (ui/css-transition
             {:class-names "fade"

+ 1 - 2
src/main/frontend/components/search.css

@@ -1,7 +1,6 @@
 #search {
   > .inner {
     width: 100%;
-    max-width: 280px;
   }
 }
 
@@ -16,4 +15,4 @@
 #search_field {
   background-color: var(--ls-search-background-color, #fff);
   color: var(--ls-secondary-text-color, #161e2e);
-}
+}

+ 9 - 5
src/main/frontend/components/sidebar.cljs

@@ -55,16 +55,16 @@
         left-sidebar? (state/sub :ui/left-sidebar-open?)]
     (when left-sidebar?
       [:nav.flex-1.left-sidebar-inner
-       (nav-item "Journals" "/"
+       (nav-item "Journals" "#/"
                  "M3 12l9-9 9 9M5 10v10a1 1 0 001 1h3a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1h3a1 1 0 001-1V10M9 21h6"
                  (active? :home)
                  close-modal-fn)
-       (nav-item "All Pages" "/all-pages"
+       (nav-item "All Pages" "#/all-pages"
                  "M6 2h9a1 1 0 0 1 .7.3l4 4a1 1 0 0 1 .3.7v13a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4c0-1.1.9-2 2-2zm9 2.41V7h2.59L15 4.41zM18 9h-3a2 2 0 0 1-2-2V4H6v16h12V9zm-2 7a1 1 0 0 1-1 1H9a1 1 0 0 1 0-2h6a1 1 0 0 1 1 1zm0-4a1 1 0 0 1-1 1H9a1 1 0 0 1 0-2h6a1 1 0 0 1 1 1zm-5-4a1 1 0 0 1-1 1H9a1 1 0 1 1 0-2h1a1 1 0 0 1 1 1z"
                  (active? :all-pages)
                  close-modal-fn)
        (when-not config/publishing?
-         (nav-item "All Files" "/all-files"
+         (nav-item "All Files" "#/all-files"
                    "M3 7V17C3 18.1046 3.89543 19 5 19H19C20.1046 19 21 18.1046 21 17V9C21 7.89543 20.1046 7 19 7H13L11 5H5C3.89543 5 3 5.89543 3 7Z"
                    (active? :all-files)
                    close-modal-fn))
@@ -297,6 +297,7 @@
                    (state/set-left-sidebar-open! false))
         me (state/sub :me)
         current-repo (state/sub :git/current-repo)
+        granted? (state/sub [:nfs/user-granted? (state/get-current-repo)])
         theme (state/sub :ui/theme)
         white? (= "white" (state/sub :ui/theme))
         sidebar-open? (state/sub :ui/sidebar-open?)
@@ -310,8 +311,11 @@
         default-home (get-default-home-if-valid)]
     (rum/with-context [[t] i18n/*tongue-context*]
       (theme/container
-       {:theme theme
-        :on-click editor-handler/unhighlight-block!}
+       {:theme         theme
+        :route         route-match
+        :nfs-granted?  granted?
+        :db-restoring? db-restoring?
+        :on-click      editor-handler/unhighlight-block!}
 
        [:div.theme-inner
         (sidebar-mobile-sidebar

+ 11 - 1
src/main/frontend/components/theme.cljs

@@ -1,11 +1,12 @@
 (ns frontend.components.theme
   (:require [rum.core :as rum]
             [frontend.util :as util]
+            [frontend.handler.route :as route-handler]
             [frontend.version :refer [version]]
             [frontend.components.svg :as svg]))
 
 (rum/defc container
-  [{:keys [theme on-click] :as props} child]
+  [{:keys [route theme on-click nfs-granted? db-restoring?] :as props} child]
   (rum/use-effect!
    #(let [doc js/document.documentElement
           cls (.-classList doc)]
@@ -14,6 +15,15 @@
         (.add cls "dark")
         (.remove cls "dark")))
    [theme])
+
+  (rum/use-effect!
+   #(let [db-restored? (false? db-restoring?)]
+      (if db-restoring?
+        (util/set-title! "Loading")
+        (when (or nfs-granted? db-restored?)
+          (route-handler/update-page-title! route))))
+   [nfs-granted? db-restoring? route])
+
   [:div
    {:class    (str theme "-theme")
     :on-click on-click}

+ 11 - 13
src/main/frontend/components/theme.css

@@ -41,23 +41,23 @@ html {
   }
 }
 
+.form-checkbox {
+    color: var(--ls-page-checkbox-color, #6093a0);
+    background-color: var(--ls-page-checkbox-color, #6093a0);
+    border-color: var(--ls-page-checkbox-border-color, #6093a0);
+    border: none;
+}
+
+.form-checkbox:hover {
+    transform: scale(1.1);
+}
+
 html[data-theme=dark] {
   background-color: var(--ls-primary-background-color);
 
-  input {
-    color: var(--ls-secondary-text-color);
-  }
-
   input.form-input {
     background: none;
   }
-
-  .form-checkbox {
-    color: var(--ls-page-checkbox-color, #6093a0);
-    background-color: var(--ls-page-checkbox-color, #6093a0);
-    border-color: var(--ls-page-checkbox-border-color, #6093a0);
-    border: none;
-  }
 }
 
 html[data-theme=light] {
@@ -203,5 +203,3 @@ html.is-electron {
     }
   }
 }
-
-

+ 75 - 13
src/main/frontend/db/model.cljs

@@ -12,7 +12,6 @@
             [clojure.set :as set]
             [frontend.utf8 :as utf8]
             [frontend.config :as config]
-            [frontend.format.block :as block]
             [cljs.reader :as reader]
             [cljs-time.core :as t]
             [cljs-time.coerce :as tc]
@@ -22,6 +21,31 @@
 ;; TODO: extract to specific models and move data transform logic to the
 ;; correponding handlers.
 
+(def rules
+  '[[(parent ?p ?c)
+     [?p :block/children ?c]]
+    [(parent ?p ?c)
+     [?t :block/children ?c]
+     (parent ?p ?t)]
+
+    ;; from https://stackoverflow.com/questions/43784258/find-entities-whose-ref-to-many-attribute-contains-all-elements-of-input
+    ;; Quote:
+    ;; You're tackling the general problem of 'dynamic conjunction' in Datomic's Datalog.
+    ;; Write a dynamic Datalog query which uses 2 negations and 1 disjunction or a recursive rule
+    ;; Datalog has no direct way of expressing dynamic conjunction (logical AND / 'for all ...' / set intersection).
+    ;; However, you can achieve it in pure Datalog by combining one disjunction
+    ;; (logical OR / 'exists ...' / set union) and two negations, i.e
+    ;; (For all ?g in ?Gs p(?e,?g)) <=> NOT(Exists ?g in ?Gs, such that NOT(p(?e, ?g)))
+
+    ;; [(matches-all ?e ?a ?vs)
+    ;;  [(first ?vs) ?v0]
+    ;;  [?e ?a ?v0]
+    ;;  (not-join [?e ?vs]
+    ;;            [(identity ?vs) [?v ...]]
+    ;;            (not-join [?e ?v]
+    ;;                      [?e ?a ?v]))]
+])
+
 (defn transact-files-db!
   ([tx-data]
    (db-utils/transact! (state/get-current-repo) tx-data))
@@ -315,6 +339,16 @@
      (->> (db-utils/pull-many repo '[:page/name] ids)
           (map :page/name)))))
 
+(defn get-page-ids-by-names
+  ([names]
+   (get-page-ids-by-names (state/get-current-repo) names))
+  ([repo names]
+   (when repo
+     (let [lookup-refs (map (fn [name]
+                              [:page/name (string/lower-case name)]) names)]
+       (->> (db-utils/pull-many repo '[:db/id] lookup-refs)
+            (mapv :db/id))))))
+
 (defn get-page-alias-names
   [repo page-name]
   (let [alias-ids (page-alias-set repo page-name)]
@@ -352,7 +386,7 @@
 (defn sort-blocks
   [blocks]
   (let [pages-ids (map (comp :db/id :block/page) blocks)
-        pages (db-utils/pull-many '[:db/id :page/name :page/original-name] pages-ids)
+        pages (db-utils/pull-many '[:db/id :page/name :page/original-name :page/journal-day] pages-ids)
         pages-map (reduce (fn [acc p] (assoc acc (:db/id p) p)) {} pages)
         blocks (map
                 (fn [block]
@@ -475,7 +509,8 @@
 (defn get-block-parent
   [repo block-id]
   (when-let [conn (conn/get-conn repo)]
-    (d/entity conn [:block/children [:block/uuid block-id]])))
+    (when-let [block (d/entity conn [:block/uuid block-id])]
+      (d/entity conn [:block/children [:block/uuid block-id]]))))
 
 ;; non recursive query
 (defn get-block-parents
@@ -569,17 +604,12 @@
   (when-let [conn (conn/get-conn repo)]
     (let [eid (:db/id (db-utils/entity repo [:block/uuid block-uuid]))]
       (->> (d/q
-            '[:find ?e1
-              :in $ ?e2 %
-              :where (parent ?e2 ?e1)]
+            '[:find ?c
+              :in $ ?p %
+              :where (parent ?p ?c)]
             conn
             eid
-             ;; recursive rules
-            '[[(parent ?e2 ?e1)
-               [?e2 :block/children ?e1]]
-              [(parent ?e2 ?e1)
-               [?t :block/children ?e1]
-               (parent ?e2 ?t)]])
+            rules)
            (apply concat)))))
 
 (defn get-block-immediate-children
@@ -702,13 +732,19 @@
     (db-utils/entity [:block/uuid (uuid page-name)])
     (db-utils/entity [:page/name page-name])))
 
+(defn- heading-block?
+  [block]
+  (and
+   (vector? block)
+   (= "Heading" (first block))))
+
 (defn get-page-name
   [file ast]
   ;; headline
   (let [ast (map first ast)]
     (if (string/includes? file "pages/contents.")
       "Contents"
-      (let [first-block (last (first (filter block/heading-block? ast)))
+      (let [first-block (last (first (filter heading-block? ast)))
             property-name (when (and (= "Properties" (ffirst ast))
                                      (not (string/blank? (:title (last (first ast))))))
                             (:title (last (first ast))))
@@ -1206,3 +1242,29 @@
        [tx-data]
        {:key [:file/content path]
         :files-db? true}))))
+
+(comment
+  (def page-names ["foo" "bar"])
+
+  (def page-ids (set (get-page-ids-by-names page-names)))
+
+  (d/q '[:find [(pull ?b [*]) ...]
+         :in $ % ?refs
+         :where
+         [?b :block/ref-pages ?p]
+         ;; Filter other blocks
+         [(contains? ?refs ?p)]
+         (or-join [?b ?refs]
+                  (matches-all ?b :block/ref-pages ?refs)
+                  (and
+                   (parent ?p ?b)
+                   ;; FIXME: not working
+                   ;; (matches-all (union ?p ?b) :block/ref-pages ?refs)
+                   [?p :block/ref-pages ?p-ref]
+                   [?b :block/ref-pages ?b-ref]
+                   [(not= ?p-ref ?b-ref)]
+                   [(contains? ?refs ?p-ref)]
+                   [(contains? ?refs ?b-ref)]))]
+       (conn/get-conn)
+       rules
+       page-ids))

+ 29 - 26
src/main/frontend/db/query_custom.cljs

@@ -45,32 +45,35 @@
 
 (defn custom-query-result-transform
   [query-result remove-blocks q]
-  (let [repo (state/get-current-repo)
-        result (db-utils/seq-flatten query-result)
-        block? (:block/uuid (first result))]
-    (let [result (if block?
-                   (let [result (if (seq remove-blocks)
-                                  (let [remove-blocks (set remove-blocks)]
-                                    (remove (fn [h]
-                                              (contains? remove-blocks (:block/uuid h)))
-                                            result))
-                                  result)]
-                     (some->> result
-                              (db-utils/with-repo repo)
-                              (model/with-block-refs-count repo)
-                              (model/sort-blocks)))
-                   result)]
-      (if-let [result-transform (:result-transform q)]
-        (if-let [f (sci/eval-string (pr-str result-transform))]
-          (try
-            (sci/call-fn f result)
-            (catch js/Error e
-              (log/error :sci/call-error e)
-              result))
-          result)
-        (if block?
-          (db-utils/group-by-page result)
-          result)))))
+  (try
+    (let [repo (state/get-current-repo)
+         result (db-utils/seq-flatten query-result)
+         block? (:block/uuid (first result))]
+     (let [result (if block?
+                    (let [result (if (seq remove-blocks)
+                                   (let [remove-blocks (set remove-blocks)]
+                                     (remove (fn [h]
+                                               (contains? remove-blocks (:block/uuid h)))
+                                             result))
+                                   result)]
+                      (some->> result
+                               (db-utils/with-repo repo)
+                               (model/with-block-refs-count repo)
+                               (model/sort-blocks)))
+                    result)]
+       (if-let [result-transform (:result-transform q)]
+         (if-let [f (sci/eval-string (pr-str result-transform))]
+           (try
+             (sci/call-fn f result)
+             (catch js/Error e
+               (log/error :sci/call-error e)
+               result))
+           result)
+         (if block?
+           (db-utils/group-by-page result)
+           result))))
+    (catch js/Error e
+      (log/error :query/failed e))))
 
 (defn- resolve-query
   [query]

+ 6 - 4
src/main/frontend/db/query_dsl.cljs

@@ -14,7 +14,8 @@
             [medley.core :as medley]
             [clojure.walk :as walk]
             [clojure.core]
-            [clojure.set :as set]))
+            [clojure.set :as set]
+            [frontend.template :as template]))
 
 ;; Query fields:
 
@@ -148,7 +149,7 @@
                            (string/lower-case))]
          (when (and (not (string/blank? page-name))
                     (some? (db-utils/entity repo [:page/name page-name])))
-           [['?b :block/ref-pages [:page/name page-name]]]))
+           [['?b :block/path-ref-pages [:page/name page-name]]]))
 
        (contains? #{'and 'or 'not} fe)
        (let [clauses (->> (map (fn [form]
@@ -377,8 +378,9 @@
 
 (defn query
   [repo query-string]
-  (when query-string
-    (let [{:keys [query sort-by blocks?]} (parse repo query-string)]
+  (when (string? query-string)
+    (let [query-string (template/resolve-dynamic-template! query-string)
+          {:keys [query sort-by blocks?]} (parse repo query-string)]
       (when query
         (let [query (query-wrapper query blocks?)]
           (query-custom/react-query repo

+ 3 - 0
src/main/frontend/db_schema.cljs

@@ -62,6 +62,9 @@
    ;; referenced pages
    :block/ref-pages {:db/valueType   :db.type/ref
                      :db/cardinality :db.cardinality/many}
+   ;; referenced pages inherited from the parents
+   :block/path-ref-pages {:db/valueType   :db.type/ref
+                          :db/cardinality :db.cardinality/many}
 
    ;; Referenced pages
    ;; Notice: it's only for org mode, :tag1:tag2:

+ 85 - 37
src/main/frontend/format/block.cljs

@@ -9,7 +9,9 @@
             [datascript.core :as d]
             [frontend.date :as date]
             [frontend.text :as text]
-            [medley.core :as medley]))
+            [medley.core :as medley]
+            [frontend.state :as state]
+            [frontend.db :as db]))
 
 (defn heading-block?
   [block]
@@ -34,10 +36,12 @@
                    (= typ "Search")
                    ;; FIXME: alert error
                    (not (contains? #{\# \* \/ \[} (first (second (:url (second block))))))
-                   (let [page (second (:url (second block)))]
-                     (when (and (not (util/starts-with? page "http"))
-                                (not (util/starts-with? page "file"))
-                                (not (string/ends-with? page ".html")))
+                   (let [page (second (:url (second block)))
+                         ext (some-> (util/get-file-ext page) keyword)]
+                     (when (and (not (util/starts-with? page "http:"))
+                                (not (util/starts-with? page "https:"))
+                                (not (util/starts-with? page "file:"))
+                                (not (contains? (config/supported-formats) ext)))
                        page)))
 
                   (and
@@ -251,9 +255,9 @@
      (concat title body))
     (let [ref-blocks (remove string/blank? @ref-blocks)]
       (assoc block :ref-blocks (map
-                                 (fn [id]
-                                   [:block/uuid (medley/uuid id)])
-                                 ref-blocks)))))
+                                (fn [id]
+                                  [:block/uuid (medley/uuid id)])
+                                ref-blocks)))))
 
 (defn update-src-pos-meta!
   [{:keys [body] :as block}]
@@ -286,6 +290,43 @@
          (block-keywordize (util/remove-nils block)))
        blocks))
 
+(defn with-path-refs
+  [blocks]
+  (loop [blocks blocks
+         acc []
+         parents []]
+    (if (empty? blocks)
+      acc
+      (let [block (first blocks)
+            cur-level (:block/level block)
+            level-diff (- cur-level
+                          (get (last parents) :block/level 0))
+            [path-refs parents]
+            (cond
+              (zero? level-diff)            ; sibling
+              (let [path-refs (mapcat :block/ref-pages (drop-last parents))
+                    parents (conj (vec (butlast parents)) block)]
+                [path-refs parents])
+
+              (> level-diff 0)              ; child
+              (let [path-refs (mapcat :block/ref-pages parents)]
+                [path-refs (conj parents block)])
+
+              (< level-diff 0)              ; new parent
+              (let [parents (take-while (fn [p] (< (:block/level p) cur-level)) parents)
+                    path-refs (mapcat :block/ref-pages parents)]
+                [path-refs (conj parents block)]))]
+        (recur (rest blocks)
+               (conj acc (assoc block :block/path-ref-pages
+                                (->> path-refs
+                                     (concat (:block/ref-pages block))
+                                     (remove string/blank?)
+                                     (map string/lower-case)
+                                     (distinct)
+                                     (map (fn [p]
+                                            {:page/name p})))))
+               parents)))))
+
 (defn extract-blocks
   [blocks last-pos encoded-content]
   (let [blocks
@@ -361,29 +402,30 @@
             (-> (reverse headings)
                 safe-blocks)))]
     (let [first-block (first blocks)
-          first-block-start-pos (get-in first-block [:block/meta :start-pos])]
-      (if (and
-           (not (string/blank? encoded-content))
-           (or (empty? blocks)
-               (> first-block-start-pos 1)))
-        (cons
-         (merge
-          (let [content (utf8/substring encoded-content 0 first-block-start-pos)
-                uuid (d/squuid)]
-            (->
-             {:uuid uuid
-              :content content
-              :anchor (str uuid)
-              :level 2
-              :meta {:start-pos 0
-                     :end-pos (or first-block-start-pos
-                                  (utf8/length encoded-content))}
-              :body (take-while (fn [block] (not (heading-block? block))) blocks)
-              :pre-block? true}
-             (block-keywordize)))
-          (select-keys first-block [:block/file :block/format :block/page]))
-         blocks)
-        blocks))))
+          first-block-start-pos (get-in first-block [:block/meta :start-pos])
+          blocks (if (and
+                      (not (string/blank? encoded-content))
+                      (or (empty? blocks)
+                          (> first-block-start-pos 1)))
+                   (cons
+                    (merge
+                     (let [content (utf8/substring encoded-content 0 first-block-start-pos)
+                           uuid (d/squuid)]
+                       (->
+                        {:uuid uuid
+                         :content content
+                         :anchor (str uuid)
+                         :level 2
+                         :meta {:start-pos 0
+                                :end-pos (or first-block-start-pos
+                                             (utf8/length encoded-content))}
+                         :body (take-while (fn [block] (not (heading-block? block))) blocks)
+                         :pre-block? true}
+                        (block-keywordize)))
+                     (select-keys first-block [:block/file :block/format :block/page]))
+                    blocks)
+                   blocks)]
+      (with-path-refs blocks))))
 
 (defn- page-with-journal
   [original-page-name]
@@ -408,10 +450,18 @@
            content-length (utf8/length encoded-content)
            blocks (extract-blocks ast content-length encoded-content)
            ref-pages-atom (atom [])
+           parent-ref-pages (->> (db/get-block-parent (state/get-current-repo) uuid)
+                                 :block/path-ref-pages
+                                 (map :db/id))
            blocks (doall
                    (map-indexed
                     (fn [idx {:block/keys [ref-pages ref-blocks meta] :as block}]
-                      (let [block (merge
+                      (let [path-ref-pages (->> ref-pages
+                                                (remove string/blank?)
+                                                (map string/lower-case)
+                                                (map (fn [p] [:page/name p]))
+                                                (concat parent-ref-pages))
+                            block (merge
                                    block
                                    {:block/meta meta
                                     :block/marker (get block :block/marker "nil")
@@ -421,12 +471,10 @@
                                     :block/page page
                                     :block/content (utf8/substring encoded-content
                                                                    (:start-pos meta)
-                                                                   (:end-pos meta))}
+                                                                   (:end-pos meta))
+                                    :block/path-ref-pages path-ref-pages}
                                    ;; Preserve the original block id
-                                   (when (and (zero? idx)
-                                              ;; not custom-id
-                                              (not (get-in block [:block/properties "custom_id"]))
-                                              (not (get-in block [:block/properties "id"])))
+                                   (when (zero? idx)
                                      {:block/uuid uuid})
                                    (when (seq ref-pages)
                                      {:block/ref-pages

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

@@ -82,7 +82,7 @@
          options (if (= fs bfs-record)
                    {:encoding "utf8"}
                    {})]
-     (read-file dir path {})))
+     (read-file dir path options)))
   ([dir path options]
    (p/chain (protocol/read-file (get-fs dir) dir path options)
             encrypt/decrypt)))

+ 158 - 174
src/main/frontend/handler/editor.cljs

@@ -473,152 +473,164 @@
                                             (set (keys text-properties))
                                             text/hidden-properties)
                             (set/union (set remove-properties)))
-         properties (medley/remove-keys (fn [k] (contains? remove-properties k)) properties)
-         value (block-text-with-time block format value properties)
-         content-changed? (not= (text/remove-timestamp-property! (string/trim content))
-                                (text/remove-timestamp-property! (string/trim value)))]
-     (cond
-       content-changed?
-       (let [file (db/entity repo (:db/id file))]
-         (cond
-           ;; Page was referenced but no related file
-           ;; TODO: replace with handler.page/create!
-           (and page (not file))
-           (let [format (name format)
-                 title (string/capitalize (:page/name page))
-                 journal-page? (date/valid-journal-title? title)
-                 path (str
-                       (if journal-page?
-                         config/default-journals-directory
-                         (config/get-pages-directory))
-                       "/"
-                       (if journal-page?
-                         (date/journal-title->default title)
-                         (-> (:page/name page)
-                             (util/page-name-sanity))) "."
-                       (if (= format "markdown") "md" format))
-                 file-path (str "/" path)
-                 dir (config/get-repo-dir repo)]
-             (p/let [exists? (fs/file-exists? dir file-path)]
-               (if exists?
-                 (notification/show!
-                  [:p.content
-                   (util/format "File %s already exists!" file-path)]
-                  :error)
-                 ;; create the file
-                 (let [value (block-text-with-time nil format value)
-                       content (str (util/default-content-with-title format
-                                                                     (or (:page/original-name page)
-                                                                         (:page/name page)))
-                                    value)]
-                   (p/let [_ (fs/create-if-not-exists repo dir file-path content)
-                           _ (git-handler/git-add repo path)]
-                     (file-handler/reset-file! repo path content)
-                     (ui-handler/re-render-root!)
-
-                     ;; Continue to edit the last block
-                     (let [blocks (db/get-page-blocks repo (:page/name page))
-                           last-block (last blocks)]
-                       (edit-last-block-for-new-page! last-block :max)))))))
-
-           (and file page)
-           (let [file (db/entity repo (:db/id file))
-                 file-path (:file/path file)
-                 format (format/get-format file-path)
-                 file-content (db/get-file repo file-path)
-                 value (get-block-new-value block file-content value)
-                 value (if rebuild-content?
-                         (rebuild-block-content value format)
-                         value)
-                 block (assoc block :block/content value)
-                 {:keys [blocks pages start-pos end-pos]} (if pre-block?
-                                                            (let [new-end-pos (utf8/length (utf8/encode value))]
-                                                              {:blocks [(assoc-in block [:block/meta :end-pos] new-end-pos)]
-                                                               :pages []
-                                                               :start-pos 0
-                                                               :end-pos new-end-pos})
-                                                            (block/parse-block block format))
-                 block-retracted-attrs (when-not pre-block?
-                                         ;; TODO: should we retract the whole block instead?
-                                         (when-let [id (:db/id block)]
-                                           [[:db/retract id :block/properties]
-                                            [:db/retract id :block/priority]
-                                            [:db/retract id :block/deadline]
-                                            [:db/retract id :block/deadline-ast]
-                                            [:db/retract id :block/scheduled]
-                                            [:db/retract id :block/scheduled-ast]
-                                            [:db/retract id :block/marker]
-                                            [:db/retract id :block/repeated?]]))
-                 [after-blocks block-children-content new-end-pos] (rebuild-after-blocks-indent-outdent repo file block (:end-pos (:block/meta block)) end-pos indent-left?)
-                 retract-refs (compute-retract-refs (:db/id e) (first blocks) ref-pages ref-blocks)
-                 page-id (:db/id page)
-                 page-properties (when pre-block?
-                                   (if (seq new-properties)
-                                     [[:db/retract page-id :page/properties]
+         properties (medley/remove-keys (fn [k] (contains? remove-properties k)) properties)]
+     (let [id (get properties "id")]
+       (cond
+         (and (string? id)
+              (util/uuid-string? id)
+              (not= uuid (cljs.core/uuid id))
+              (db/entity [:block/uuid (cljs.core/uuid id)]))
+         (notification/show!
+          [:p.content
+           (util/format "Block with the id % already exists!" id)]
+          :error)
+
+         :else
+         (let [value (block-text-with-time block format value properties)
+               content-changed? (not= (text/remove-timestamp-property! (string/trim content))
+                                      (text/remove-timestamp-property! (string/trim value)))]
+           (cond
+             content-changed?
+             (let [file (db/entity repo (:db/id file))]
+               (cond
+                 ;; Page was referenced but no related file
+                 ;; TODO: replace with handler.page/create!
+                 (and page (not file))
+                 (let [format (name format)
+                       title (string/capitalize (:page/name page))
+                       journal-page? (date/valid-journal-title? title)
+                       path (str
+                             (if journal-page?
+                               config/default-journals-directory
+                               (config/get-pages-directory))
+                             "/"
+                             (if journal-page?
+                               (date/journal-title->default title)
+                               (-> (:page/name page)
+                                   (util/page-name-sanity))) "."
+                             (if (= format "markdown") "md" format))
+                       file-path (str "/" path)
+                       dir (config/get-repo-dir repo)]
+                   (p/let [exists? (fs/file-exists? dir file-path)]
+                     (if exists?
+                       (notification/show!
+                        [:p.content
+                         (util/format "File %s already exists!" file-path)]
+                        :error)
+                       ;; create the file
+                       (let [value (block-text-with-time nil format value)
+                             content (str (util/default-content-with-title format
+                                                                           (or (:page/original-name page)
+                                                                               (:page/name page)))
+                                          value)]
+                         (p/let [_ (fs/create-if-not-exists repo dir file-path content)
+                                 _ (git-handler/git-add repo path)]
+                           (file-handler/reset-file! repo path content)
+                           (ui-handler/re-render-root!)
+
+                           ;; Continue to edit the last block
+                           (let [blocks (db/get-page-blocks repo (:page/name page))
+                                 last-block (last blocks)]
+                             (edit-last-block-for-new-page! last-block :max)))))))
+
+                 (and file page)
+                 (let [file (db/entity repo (:db/id file))
+                       file-path (:file/path file)
+                       format (format/get-format file-path)
+                       file-content (db/get-file repo file-path)
+                       value (get-block-new-value block file-content value)
+                       value (if rebuild-content?
+                               (rebuild-block-content value format)
+                               value)
+                       block (assoc block :block/content value)
+                       {:keys [blocks pages start-pos end-pos]} (if pre-block?
+                                                                  (let [new-end-pos (utf8/length (utf8/encode value))]
+                                                                    {:blocks [(assoc-in block [:block/meta :end-pos] new-end-pos)]
+                                                                     :pages []
+                                                                     :start-pos 0
+                                                                     :end-pos new-end-pos})
+                                                                  (block/parse-block block format))
+                       block-retracted-attrs (when-not pre-block?
+                                               ;; TODO: should we retract the whole block instead?
+                                               (when-let [id (:db/id block)]
+                                                 [[:db/retract id :block/properties]
+                                                  [:db/retract id :block/priority]
+                                                  [:db/retract id :block/deadline]
+                                                  [:db/retract id :block/deadline-ast]
+                                                  [:db/retract id :block/scheduled]
+                                                  [:db/retract id :block/scheduled-ast]
+                                                  [:db/retract id :block/marker]
+                                                  [:db/retract id :block/repeated?]]))
+                       [after-blocks block-children-content new-end-pos] (rebuild-after-blocks-indent-outdent repo file block (:end-pos (:block/meta block)) end-pos indent-left?)
+                       retract-refs (compute-retract-refs (:db/id e) (first blocks) ref-pages ref-blocks)
+                       page-id (:db/id page)
+                       page-properties (when pre-block?
+                                         (if (seq new-properties)
+                                           [[:db/retract page-id :page/properties]
+                                            {:db/id page-id
+                                             :page/properties new-properties}]
+                                           [[:db/retract page-id :page/properties]]))
+                       pages (if (seq page-tags)
+                               (concat pages page-tags)
+                               pages)
+                       pages (remove
+                              (fn [page]
+                                (string/blank? (:page/name page)))
+                              pages)
+                       page-tags (when (and pre-block? (seq page-tags))
+                                   (if (seq page-tags)
+                                     [[:db/retract page-id :page/tags]
                                       {:db/id page-id
-                                       :page/properties new-properties}]
-                                     [[:db/retract page-id :page/properties]]))
-                 pages (if (seq page-tags)
-                         (concat pages page-tags)
-                         pages)
-                 pages (remove
-                        (fn [page]
-                          (string/blank? (:page/name page)))
-                        pages)
-                 page-tags (when (and pre-block? (seq page-tags))
-                             (if (seq page-tags)
-                               [[:db/retract page-id :page/tags]
-                                {:db/id page-id
-                                 :page/tags page-tags}]
-                               [[:db/retract page-id :page/tags]]))
-                 page-alias (when (and pre-block? (seq page-alias))
-                              (if (seq page-alias)
-                                [[:db/retract page-id :page/alias]
-                                 {:db/id page-id
-                                  :page/alias page-alias}]
-                                [[:db/retract page-id :page/alias]]))]
-             (profile
-              "Save block: "
-              (repo-handler/transact-react-and-alter-file!
-               repo
-               (concat
-                pages
-                block-retracted-attrs
-                (mapv (fn [b] {:block/uuid (:block/uuid b)}) blocks)
-                blocks
-                retract-refs
-                page-properties
-                page-tags
-                page-alias
-                after-blocks)
-               {:key :block/change
-                :data (map (fn [block] (assoc block :block/page page)) blocks)}
-               (let [new-content (new-file-content-indent-outdent block file-content value block-children-content new-end-pos indent-left?)]
-                 [[file-path new-content]])
-               (when chan {:chan chan
-                           :chan-callback chan-callback})))
-
-             ;; fix editing template with multiple headings
-             (when (> (count blocks) 1)
-               (let [new-value (-> (text/remove-level-spaces (:block/content (first blocks)) (:block/format (first blocks)))
-                                   (string/trim-newline))
-                     edit-input-id (state/get-edit-input-id)]
-                 (when edit-input-id
-                   (state/set-edit-content! edit-input-id new-value))))
-
-             (when (or (seq retract-refs) pre-block?)
-               (ui-handler/re-render-root!))
-
-             (repo-handler/push-if-auto-enabled! repo))
-
-           :else
-           nil))
-
-       (seq (state/get-changed-files))
-       (repo-handler/push-if-auto-enabled! repo)
-
-       :else
-       nil))))
+                                       :page/tags page-tags}]
+                                     [[:db/retract page-id :page/tags]]))
+                       page-alias (when (and pre-block? (seq page-alias))
+                                    (if (seq page-alias)
+                                      [[:db/retract page-id :page/alias]
+                                       {:db/id page-id
+                                        :page/alias page-alias}]
+                                      [[:db/retract page-id :page/alias]]))]
+                   (profile
+                    "Save block: "
+                    (repo-handler/transact-react-and-alter-file!
+                     repo
+                     (concat
+                      pages
+                      block-retracted-attrs
+                      (mapv (fn [b] {:block/uuid (:block/uuid b)}) blocks)
+                      blocks
+                      retract-refs
+                      page-properties
+                      page-tags
+                      page-alias
+                      after-blocks)
+                     {:key :block/change
+                      :data (map (fn [block] (assoc block :block/page page)) blocks)}
+                     (let [new-content (new-file-content-indent-outdent block file-content value block-children-content new-end-pos indent-left?)]
+                       [[file-path new-content]])
+                     (when chan {:chan chan
+                                 :chan-callback chan-callback})))
+
+                   ;; fix editing template with multiple headings
+                   (when (> (count blocks) 1)
+                     (let [new-value (-> (text/remove-level-spaces (:block/content (first blocks)) (:block/format (first blocks)))
+                                         (string/trim-newline))
+                           edit-input-id (state/get-edit-input-id)]
+                       (when edit-input-id
+                         (state/set-edit-content! edit-input-id new-value))))
+
+                   (when (or (seq retract-refs) pre-block?)
+                     (ui-handler/re-render-root!))
+
+                   (repo-handler/push-if-auto-enabled! repo))
+
+                 :else
+                 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? pre-block?] :as block}
@@ -1764,7 +1776,7 @@
                                                       99)
                                 (map (comp str :block/uuid))))
         current-and-parents (set/union #{(str (:block/uuid current-block))} block-parents)]
-    (p/let [result (search/block-search q 10)]
+    (let [result (search/block-search q 10)]
       (remove
        (fn [h]
          (contains? current-and-parents (:block/uuid h)))
@@ -2245,31 +2257,3 @@
         value (:block/content block)
         new-value (string/replace value full_text new-full-text)]
     (save-block-aux! block new-value (:block/format block) {})))
-
-(defn variable-rules
-  []
-  {"today" (util/format "[[%s]]" (date/today))
-   "yesterday" (util/format "[[%s]]" (date/yesterday))
-   "tomorrow" (util/format "[[%s]]" (date/tomorrow))
-   "time" (date/get-current-time)
-   "current page" (util/format "[[%s]]"
-                               (or (state/get-current-page)
-                                   (date/today)))})
-
-;; TODO: programmable
-;; context information, date, current page
-(defn resolve-dynamic-template!
-  [content]
-  (string/replace content #"<%([^%].*?)%>"
-                  (fn [[_ match]]
-                    (let [match (string/trim match)]
-                      (cond
-                       (string/blank? match)
-                       ""
-                       (get (variable-rules) (string/lower-case match))
-                       (get (variable-rules) (string/lower-case match))
-                       :else
-                       (if-let [nld (date/nld-parse match)]
-                         (let [date (tc/to-local-date-time nld)]
-                           (util/format "[[%s]]" (date/journal-name date)))
-                         match))))))

+ 4 - 2
src/main/frontend/handler/extract.cljs

@@ -36,7 +36,8 @@
                    (fn [[page blocks]]
                      (if page
                        (map (fn [block]
-                              (let [block-ref-pages (seq (:block/ref-pages block))]
+                              (let [block-ref-pages (seq (:block/ref-pages block))
+                                    block-path-ref-pages (seq (:block/path-ref-pages block))]
                                 (when block-ref-pages
                                   (swap! ref-pages set/union (set block-ref-pages)))
                                 (-> block
@@ -48,7 +49,8 @@
                                            :block/ref-pages (mapv
                                                              (fn [page]
                                                                (block/page-with-journal page))
-                                                             block-ref-pages)))))
+                                                             block-ref-pages)
+                                           :block/path-ref-pages block-path-ref-pages))))
                             blocks)))
                    (remove nil? pages)))
           pages (doall

+ 3 - 3
src/main/frontend/handler/file.cljs

@@ -192,7 +192,7 @@
          (history/add-history! repo [[path original-content content]])))
      (fn [error]
        (println "Write file failed, path: " path ", content: " content)
-       (js/console.error error)))))
+       (log/error :write/failed error)))))
 
 (defn set-file-content!
   [repo path new-content]
@@ -283,7 +283,7 @@
   [repo file]
   (when-not (string/blank? file)
     (->
-     (p/let [_ (git/remove-file repo file)
+     (p/let [_ (or (config/local-db? repo) (git/remove-file repo file))
              result (fs/unlink! (config/get-repo-path repo file) nil)]
        (when-let [file (db/entity repo [:file/path file])]
          (common-handler/check-changed-files-status)
@@ -296,7 +296,7 @@
            (when (seq tx-data)
              (db/transact! repo tx-data)))))
      (p/catch (fn [err]
-                (prn "error: " err))))))
+                (js/console.error "error: " err))))))
 
 (defn re-index!
   [file]

+ 1 - 3
src/main/frontend/handler/image.cljs

@@ -36,9 +36,7 @@
                        (subs path 1)
                        path)]
             (util/p-handle
-             (fs/read-file (config/get-repo-dir (state/get-current-repo))
-                           path
-                           {})
+             (fs/read-file (config/get-repo-dir (state/get-current-repo)) path {})
              (fn [blob]
                (let [blob (js/Blob. (array blob) (clj->js {:type "image"}))
                      img-url (image/create-object-url blob)]

+ 2 - 2
src/main/frontend/handler/page.cljs

@@ -278,12 +278,12 @@
               (db/transact! tx-data)
               ;; remove file
               (->
-               (p/let [_ (git/remove-file repo file-path)
+               (p/let [_ (or (config/local-db? repo) (git/remove-file repo file-path))
                        _ (fs/unlink! (config/get-repo-path repo file-path) nil)]
                  (common-handler/check-changed-files-status)
                  (repo-handler/push-if-auto-enabled! repo))
                (p/catch (fn [err]
-                          (prn "error: " err))))))
+                          (js/console.error "error: " err))))))
 
           (db/transact! [[:db.fn/retractEntity [:page/name page-name]]])
 

+ 12 - 0
src/main/frontend/handler/repo.cljs

@@ -686,6 +686,18 @@
         (p/catch (fn [error]
                    (prn "Delete repo failed, error: " error))))))
 
+(defn re-index!
+  [nfs-rebuild-index!]
+  (when-let [repo (state/get-current-repo)]
+    (let [local? (config/local-db? repo)]
+      (if local?
+        (nfs-rebuild-index! repo create-today-journal!)
+        (rebuild-index! repo))
+      (js/setTimeout
+       (fn []
+         (route-handler/redirect! {:to :home}))
+       500))))
+
 (defn git-commit-and-push!
   [commit-message]
   (when-let [repo (state/get-current-repo)]

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

@@ -58,7 +58,8 @@
         (let [page (util/url-decode name)
               page (db/pull [:page/name (string/lower-case page)])]
           (or (:page/original-name page)
-              (:page/name page)))))
+              (:page/name page)
+              "Logseq"))))
     :tag
     (str "#" (util/url-decode (:name path-params)))
     :diff
@@ -71,14 +72,18 @@
     "Import data into Logseq"
     "Logseq"))
 
+(defn update-page-title!
+  [route]
+  (let [{:keys [data path-params]} route
+        title (get-title (:name data) path-params)]
+    (util/set-title! title)))
+
 (defn set-route-match!
   [route]
   (let [route route]
     (swap! state/state assoc :route-match route)
-    (let [{:keys [data path-params]} route
-          title (get-title (:name data) path-params)]
-      (util/set-title! title)
-      (util/scroll-to-top))))
+    (update-page-title! route)
+    (util/scroll-to-top)))
 
 (defn jump-to-anchor!
   [anchor-text]

+ 4 - 6
src/main/frontend/handler/search.cljs

@@ -8,12 +8,10 @@
 
 (defn search
   [q]
-  ;; TODO: separate rendering for blocks
-  (p/let [blocks-result (search/block-search q 10)]
-    (swap! state/state assoc :search/result
-           {:pages (search/page-search q)
-            :files (search/file-search q)
-            :blocks blocks-result})))
+  (swap! state/state assoc :search/result
+         {:pages (search/page-search q)
+          :files (search/file-search q)
+          :blocks (search/block-search q 10)}))
 
 (defn clear-search!
   []

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

@@ -5,6 +5,8 @@
             [frontend.handler.route :as route-handler]
             [frontend.handler.search :as search-handler]
             [frontend.handler.config :as config-handler]
+            [frontend.handler.repo :as repo-handler]
+            [frontend.handler.web.nfs :as nfs-handler]
             [frontend.state :as state]
             [frontend.search :as search]
             [frontend.util :as util]
@@ -30,6 +32,8 @@
 
 (def shortcut state/get-shortcut)
 
+(def re-index! #(repo-handler/re-index! nfs-handler/rebuild-index!))
+
 (defonce chords
   {
    ;; non-editing mode
@@ -50,6 +54,7 @@
 
    (or (shortcut :editor/undo) "mod+z") [history-handler/undo! true]
    (or (shortcut :editor/redo) "mod+y") [history-handler/redo! true]
+   (or (shortcut :editor/redo) "mod+shift+z") [history-handler/redo! true]
    (or (shortcut :go/search) "mod+u") [route-handler/go-to-search! true]
    (or (shortcut :go/journals) (if util/mac? "mod+j" "alt+j")) [route-handler/go-to-journals! true]
    (or (shortcut :editor/zoom-in)
@@ -75,6 +80,9 @@
    (or (shortcut :editor/prev) "up") (fn [state e] (editor-handler/open-block! false))
 
    (or (shortcut :search/re-index) "mod+c mod+s") [search-handler/rebuild-indices! true]
+
+   (or (shortcut :graph/re-index) "mod+c mod+r") [re-index! true]
+
    (or (shortcut :ui/toggle-brackets) "mod+c mod+b") [config-handler/toggle-ui-show-brackets! true]})
 
 (defonce bind! (gobj/get mousetrap "bind"))

+ 41 - 52
src/main/frontend/search.cljs

@@ -11,24 +11,14 @@
             [frontend.text :as text]
             [cljs-bean.core :as bean]
             [goog.object :as gobj]
-            ["fuzzysort" :as fuzzy]
-            ["flexsearch" :as flexsearch]
+            ["fuse.js" :as fuse]
             [medley.core :as medley]
             [promesa.core :as p]
             ["/frontend/utils" :as utils]))
 
-(def fuzzy-go (gobj/get fuzzy "go"))
-(defonce prepare (gobj/get fuzzy "prepare"))
-(defonce highlight (gobj/get fuzzy "highlight"))
-
 (defn go
-  [q indice-type indice opts]
-  (case indice-type
-    :page
-    (fuzzy-go q indice opts)
-
-    :block
-    (.search indice q opts)))
+  [q indice opts]
+  (.search indice q opts))
 
 (defn block->index
   [{:block/keys [uuid content format] :as block}]
@@ -45,15 +35,10 @@
                       (map block->index)
                       (remove nil?)
                       (bean/->js))
-          indice (flexsearch.
-                  (clj->js
-                   {:encode "icase"
-                    :tokenize utils/searchTokenize
-                    :doc {:id "id"
-                          :field ["content"]}
-                    :async true}))]
-      (p/let [result (.add indice blocks)]
-        (swap! indices assoc-in [repo :blocks] indice))
+          indice (fuse. blocks
+                        (clj->js {:keys ["uuid" "content"]
+                                  }))]
+      (swap! indices assoc-in [repo :blocks] indice)
       indice)))
 
 (defn make-pages-indice!
@@ -62,9 +47,12 @@
     (let [pages (->> (db/get-pages (state/get-current-repo))
                      (remove string/blank?)
                      (map (fn [p] {:name p}))
-                     (bean/->js))]
-      (swap! indices assoc-in [repo :pages] pages)
-      pages)))
+                     (bean/->js))
+          indice (fuse. pages
+                        (clj->js {:keys ["name"]
+                                  :threshold 0.4}))]
+      (swap! indices assoc-in [repo :pages] indice)
+      indice)))
 
 ;; TODO: persist indices to indexeddb, it'll be better if the future db
 ;; can has the direct fuzzy search support.
@@ -161,14 +149,15 @@
          (when-not (string/blank? q)
            (let [indice (or (get-in @indices [repo :blocks])
                             (make-blocks-indice!))]
-             (p/let [result (go q :block indice (clj->js {:limit limit}))
-                     result (bean/->clj result)]
+             (let [result (go q indice (clj->js {:limit limit}))
+                   result (bean/->clj result)]
                (->>
                 (map
-                 (fn [{:keys [content uuid] :as block}]
-                   {:block/uuid uuid
-                    :block/content content
-                    :block/page (:block/page (db/entity [:block/uuid (medley/uuid (str uuid))]))})
+                 (fn [{:keys [item] :as block}]
+                   (let [{:keys [content uuid]} item]
+                     {:block/uuid uuid
+                      :block/content content
+                      :block/page (:block/page (db/entity [:block/uuid (medley/uuid (str uuid))]))}))
                  result)
                 (remove nil?))))))))))
 
@@ -182,15 +171,12 @@
        (when-not (string/blank? q)
          (let [indice (or (get-in @indices [repo :pages])
                           (make-pages-indice!))
-               result (->> (go q :page indice (clj->js {:limit limit
-                                                        :key "name"
-                                                        :allowTypo false
-                                                        :threshold -10000}))
+               result (->> (go q indice (clj->js {:limit limit}))
                            (bean/->clj))]
            ;; TODO: add indexes for highlights
            (->> (map
-                 (fn [{:keys [obj]}]
-                   (:name obj))
+                  (fn [{:keys [item]}]
+                    (:name item))
                  result)
                 (remove nil?))))))))
 
@@ -233,19 +219,20 @@
                 pages-to-add (->> (filter (fn [page]
                                             (contains? pages-to-add-set (:db/id page))) pages-result)
                                   (map (fn [p] {:name (or (:page/original-name p)
-                                                          (:page/name p))}))
-                                  (set))
+                                                          (:page/name p))})))
                 pages-to-remove-set (->> (remove :added pages)
-                                         (map :v)
-                                         (set))]
+                                         (map :v))]
             (swap! search-db/indices update-in [repo :pages]
-                   (fn [pages]
-                     (let [pages (or pages (array))
-                           pages (.filter pages (fn [page]
-                                                  (when-let [page-name (gobj/get page "name")]
-                                                    (not (contains? pages-to-remove-set
-                                                                    (string/lower-case page-name))))))]
-                       (.concat pages (bean/->js pages-to-add)))))))
+                   (fn [indice]
+                     (when indice
+                       (doseq [page-name pages-to-remove-set]
+                         (.remove indice
+                                  (fn [page]
+                                    (= page-name (gobj/get page "name")))))
+                       (when (seq pages-to-add)
+                         (doseq [page pages-to-add]
+                           (.add indice (bean/->js page)))))
+                     indice))))
         (when (seq blocks)
           (let [blocks-result (db/pull-many '[:db/id :block/uuid :block/format :block/content] (set (map :e blocks)))
                 blocks-to-add-set (->> (filter :added blocks)
@@ -254,8 +241,7 @@
                 blocks-to-add (->> (filter (fn [block]
                                              (contains? blocks-to-add-set (:db/id block)))
                                            blocks-result)
-                                   (map block->index)
-                                   (set))
+                                   (map block->index))
                 blocks-to-remove-set (->> (remove :added blocks)
                                           (map :e)
                                           (set))]
@@ -263,7 +249,10 @@
                    (fn [indice]
                      (when indice
                        (doseq [block-id blocks-to-remove-set]
-                         (.remove indice #js {:id block-id}))
+                         (.remove indice
+                                  (fn [block]
+                                    (= block-id (gobj/get block "id")))))
                        (when (seq blocks-to-add)
-                         (.add indice (bean/->js blocks-to-add))))
+                         (doseq [block blocks-to-add]
+                           (.add indice (bean/->js block)))))
                      indice))))))))

+ 34 - 0
src/main/frontend/template.cljs

@@ -0,0 +1,34 @@
+(ns frontend.template
+  (:require [frontend.util :as util :refer-macros [profile]]
+            [frontend.date :as date]
+            [clojure.string :as string]
+            [cljs-time.coerce :as tc]
+            [frontend.state :as state]))
+
+(defn- variable-rules
+  []
+  {"today" (util/format "[[%s]]" (date/today))
+   "yesterday" (util/format "[[%s]]" (date/yesterday))
+   "tomorrow" (util/format "[[%s]]" (date/tomorrow))
+   "time" (date/get-current-time)
+   "current page" (util/format "[[%s]]"
+                               (or (state/get-current-page)
+                                   (date/today)))})
+
+;; TODO: programmable
+;; context information, date, current page
+(defn resolve-dynamic-template!
+  [content]
+  (string/replace content #"<%([^%].*?)%>"
+                  (fn [[_ match]]
+                    (let [match (string/trim match)]
+                      (cond
+                       (string/blank? match)
+                       ""
+                       (get (variable-rules) (string/lower-case match))
+                       (get (variable-rules) (string/lower-case match))
+                       :else
+                       (if-let [nld (date/nld-parse match)]
+                         (let [date (tc/to-local-date-time nld)]
+                           (util/format "[[%s]]" (date/journal-name date)))
+                         match))))))

+ 0 - 23
src/main/frontend/ui.css

@@ -82,29 +82,6 @@
   border-left: 4px solid transparent;
 }
 
-.form-checkbox {
-  color: var(--ls-page-checkbox-color, #137cbd);
-  background-color: transparent;
-  border-radius: 0;
-  border: 2px solid;
-  border-color: var(--ls-page-checkbox-border-color);
-  appearance: none
-}
-
-.form-checkbox {
-  &:checked {
-    border: none;
-
-    &:focus {
-      box-shadow: none;
-    }
-  }
-
-  &:not(:checked):focus {
-    box-shadow: none;
-  }
-}
-
 .form-select {
   background-color: var(--ls-primary-background-color, transparent);
   background-repeat: no-repeat;

+ 0 - 10
src/main/frontend/utils.js

@@ -204,13 +204,3 @@ export const win32 = path => {
   // UNC paths are always absolute
   return Boolean(result[2] || isUnc);
 };
-
-export const searchTokenize = str => {
-  let ascii_words = str.split(/\W+/);
-  let non_ascii_str = str.replace(/[\x00-\x7F]/g, '');
-  if (non_ascii_str == '') {
-    return ascii_words;
-  } else {
-    return ascii_words.concat(non_ascii_str.split('')).filter(e => !!e);
-  }
-};

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

@@ -1,3 +1,3 @@
 (ns frontend.version)
 
-(defonce version "0.0.8")
+(defonce version "0.0.9")

+ 16 - 16
src/test/frontend/db/query_dsl_test.cljs

@@ -136,11 +136,11 @@ parent: child page 2
   (testing "Single page query"
     (are [x y] (= (q-count x) y)
       "[[page 1]]"
-      {:query '[[?b :block/ref-pages [:page/name "page 1"]]]
+      {:query '[[?b :block/path-ref-pages [:page/name "page 1"]]]
        :count 6}
 
       "[[page 2]]"
-      {:query '[[?b :block/ref-pages [:page/name "page 2"]]]
+      {:query '[[?b :block/path-ref-pages [:page/name "page 2"]]]
        :count 4}))
 
   (testing "Block properties query"
@@ -327,29 +327,29 @@ parent: child page 2
   (testing "AND queries"
     (are [x y] (= (q-count x) y)
       "(and [[tag1]] [[page 2]])"
-      {:query '([?b :block/ref-pages [:page/name "tag1"]]
-                [?b :block/ref-pages [:page/name "page 2"]])
+      {:query '([?b :block/path-ref-pages [:page/name "tag1"]]
+                [?b :block/path-ref-pages [:page/name "page 2"]])
        :count 1})
 
     (are [x y] (= (q-count x) y)
       "(and [[tag1]] [[page 2]])"
-      {:query '([?b :block/ref-pages [:page/name "tag1"]]
-                [?b :block/ref-pages [:page/name "page 2"]])
+      {:query '([?b :block/path-ref-pages [:page/name "tag1"]]
+                [?b :block/path-ref-pages [:page/name "page 2"]])
        :count 1}))
 
   (testing "OR queries"
     (are [x y] (= (q-count x) y)
       "(or [[tag1]] [[page 2]])"
       {:query '(or
-                (and [?b :block/ref-pages [:page/name "tag1"]])
-                (and [?b :block/ref-pages [:page/name "page 2"]]))
+                (and [?b :block/path-ref-pages [:page/name "tag1"]])
+                (and [?b :block/path-ref-pages [:page/name "page 2"]]))
        :count 4}))
 
   (testing "NOT queries"
     (are [x y] (= (q-count x) y)
       "(not [[page 1]])"
       {:query '([?b :block/uuid]
-                (not [?b :block/ref-pages [:page/name "page 1"]]))
+                (not [?b :block/path-ref-pages [:page/name "page 1"]]))
        :count 8}))
 
   (testing "Between query"
@@ -375,15 +375,15 @@ parent: child page 2
       {:query '([?b :block/uuid]
                 [?b :block/marker ?marker]
                 [(contains? #{"DONE"} ?marker)]
-                (not [?b :block/ref-pages [:page/name "page 1"]]))
+                (not [?b :block/path-ref-pages [:page/name "page 1"]]))
        :count 0})
 
     (are [x y] (= (q-count x) y)
       "(and (todo now later) (or [[page 1]] [[page 2]]))"
       {:query '([?b :block/marker ?marker]
                 [(contains? #{"NOW" "LATER"} ?marker)]
-                (or (and [?b :block/ref-pages [:page/name "page 1"]])
-                    (and [?b :block/ref-pages [:page/name "page 2"]])))
+                (or (and [?b :block/path-ref-pages [:page/name "page 1"]])
+                    (and [?b :block/path-ref-pages [:page/name "page 2"]])))
        :count 3})
 
     (are [x y] (= (q-count x) y)
@@ -393,8 +393,8 @@ parent: child page 2
                  [?b :block/marker ?marker]
                  [(contains? #{"NOW" "LATER"} ?marker)]
                  (or
-                  (and [?b :block/ref-pages [:page/name "page 1"]])
-                  (and [?b :block/ref-pages [:page/name "page 2"]]))))
+                  (and [?b :block/path-ref-pages [:page/name "page 1"]])
+                  (and [?b :block/path-ref-pages [:page/name "page 2"]]))))
        :count 11})
 
     ;; FIXME: not working
@@ -412,8 +412,8 @@ parent: child page 2
                 [?b :block/marker ?marker]
                 [(contains? #{"NOW" "LATER" "DONE"} ?marker)]
                 (or
-                 (and [?b :block/ref-pages [:page/name "page 1"]])
-                 (and (not [?b :block/ref-pages [:page/name "page 1"]]))))
+                 (and [?b :block/path-ref-pages [:page/name "page 1"]])
+                 (and (not [?b :block/path-ref-pages [:page/name "page 1"]]))))
        :count 5}))
 
   (testing "sort-by (created_at defaults to desc)"

+ 4 - 7
yarn.lock

@@ -2310,10 +2310,6 @@ flatted@^3.1.0:
   resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469"
   integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==
 
-"flexsearch@git+https://github.com/logseq/flexsearch":
-  version "0.6.32"
-  resolved "git+https://github.com/logseq/flexsearch#0a04c518ef0a9b3c76e18da893642835f98d5616"
-
 flush-write-stream@^1.0.2:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
@@ -2396,9 +2392,10 @@ function-bind@^1.1.1:
   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
   integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
 
-"fuzzysort@git+https://github.com/getstation/fuzzysort#a66f5813825d2415b606cc69129070c4eb612ae2":
-  version "1.1.4"
-  resolved "git+https://github.com/getstation/fuzzysort#a66f5813825d2415b606cc69129070c4eb612ae2"
+fuse.js@^6.4.6:
+  version "6.4.6"
+  resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-6.4.6.tgz#62f216c110e5aa22486aff20be7896d19a059b79"
+  integrity sha512-/gYxR/0VpXmWSfZOIPS3rWwU8SHgsRTwWuXhyb2O6s7aRuVtHtxCkR33bNYu3wyLyNx/Wpv0vU7FZy8Vj53VNw==
 
 gensync@^1.0.0-beta.1:
   version "1.0.0-beta.2"