瀏覽代碼

Merge branch 'master' into encryption

Tienson Qin 4 年之前
父節點
當前提交
678afea969
共有 46 個文件被更改,包括 609 次插入371 次删除
  1. 1 1
      Dockerfile
  2. 1 1
      README.md
  3. 3 0
      externs.js
  4. 4 2
      package.json
  5. 4 2
      src/electron/electron/core.cljs
  6. 2 2
      src/electron/electron/handler.cljs
  7. 33 20
      src/main/frontend/components/block.cljs
  8. 22 2
      src/main/frontend/components/content.cljs
  9. 41 24
      src/main/frontend/components/editor.cljs
  10. 3 2
      src/main/frontend/components/header.cljs
  11. 8 5
      src/main/frontend/components/header.css
  12. 1 4
      src/main/frontend/components/journal.cljs
  13. 0 5
      src/main/frontend/components/page.cljs
  14. 36 1
      src/main/frontend/components/search.cljs
  15. 32 1
      src/main/frontend/components/settings.cljs
  16. 0 1
      src/main/frontend/components/sidebar.cljs
  17. 7 1
      src/main/frontend/date.cljs
  18. 6 2
      src/main/frontend/db.cljs
  19. 3 0
      src/main/frontend/db/conn.cljs
  20. 9 0
      src/main/frontend/db/default.cljs
  21. 2 9
      src/main/frontend/db/model.cljs
  22. 10 4
      src/main/frontend/dicts.cljs
  23. 11 1
      src/main/frontend/external/roam.cljc
  24. 3 3
      src/main/frontend/format/block.cljs
  25. 26 19
      src/main/frontend/fs.cljs
  26. 2 3
      src/main/frontend/fs/bfs.cljs
  27. 5 2
      src/main/frontend/fs/nfs.cljs
  28. 1 1
      src/main/frontend/fs/node.cljs
  29. 1 1
      src/main/frontend/fs/protocol.cljs
  30. 5 3
      src/main/frontend/handler/block.cljs
  31. 83 42
      src/main/frontend/handler/editor.cljs
  32. 1 1
      src/main/frontend/handler/extract.cljs
  33. 2 4
      src/main/frontend/handler/file.cljs
  34. 2 1
      src/main/frontend/handler/image.cljs
  35. 8 5
      src/main/frontend/handler/search.cljs
  36. 23 10
      src/main/frontend/handler/user.cljs
  37. 23 18
      src/main/frontend/history.cljs
  38. 59 75
      src/main/frontend/keyboards.cljs
  39. 0 27
      src/main/frontend/mixins.cljs
  40. 52 40
      src/main/frontend/search.cljs
  41. 23 0
      src/main/frontend/state.cljs
  42. 5 5
      src/main/frontend/ui.cljs
  43. 12 12
      src/main/frontend/util.cljc
  44. 11 1
      src/main/frontend/utils.js
  45. 1 1
      src/main/frontend/version.cljs
  46. 22 7
      yarn.lock

+ 1 - 1
Dockerfile

@@ -1,4 +1,4 @@
-FROM clojure:openjdk-11-tools-deps
+FROM clojure:openjdk-11-tools-deps-1.10.0.442
 
 RUN curl -sL https://deb.nodesource.com/setup_15.x | bash - && \
     apt-get install -y nodejs

+ 1 - 1
README.md

@@ -16,7 +16,7 @@ Use it to organize your todo list, to write your journals, or to record your uni
 ## Why Logseq?
 
 [Logseq](https://logseq.com) is a platform for knowledge sharing and management. It focuses on privacy, longevity, and user control.
-Notice: the backend code will not be open-sourced for security reasons and other potential risks.
+Notice: the backend code will be open-sourced as soon as we’re sure that the backend service meets the security standards.
 
 The server will never store or analyze your private notes. Your data are plain text files and we currently support both Markdown and Emacs Org mode (more to be added soon).
 

+ 3 - 0
externs.js

@@ -17,6 +17,9 @@ dummy.getRangeAt = function() {};
 dummy.getElementsByClassName = function() {};
 dummy.containsNode = function() {};
 dummy.select = function() {};
+dummy.search = function() {};
+dummy.add = function() {};
+dummy.remove = function() {};
 dummy.closest = function () {};
 dummy.setAttribute = function() {};
 dummy.getAttribute = function() {};

+ 4 - 2
package.json

@@ -58,16 +58,18 @@
     "dependencies": {
         "@kanru/rage-wasm": "^0.2.1",
         "chokidar": "^3.5.1",
+        "chrono-node": "^2.2.1",
         "codemirror": "^5.58.1",
         "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": "^1.1.4",
+        "fuzzysort": "git+https://github.com/getstation/fuzzysort#a66f5813825d2415b606cc69129070c4eb612ae2",
         "gulp-cached": "^1.1.1",
         "ignore": "^5.1.8",
         "jszip": "^3.5.0",
-        "mldoc": "^0.3.8",
+        "mldoc": "^0.4.0",
         "mousetrap": "^1.6.5",
         "path": "^0.12.7",
         "react": "^17.0.1",

+ 4 - 2
src/electron/electron/core.cljs

@@ -77,8 +77,10 @@
 
     (.on web-contents  "new-window"
          (fn [e url]
-           (.. logger (info "new-window" url))
-           (open url)
+           (let [url (if (string/starts-with? url "file:")
+                       (js/decodeURIComponent url) url)]
+             (.. logger (info "new-window" url))
+             (open url))
            (.preventDefault e)))
 
     (doto win

+ 2 - 2
src/electron/electron/handler.cljs

@@ -65,11 +65,11 @@
     (vec (cons {:path (fix-win-path! path)} result))))
 
 ;; TODO: Is it going to be slow if it's a huge directory
-(defmethod handle :openDir [window _messages]
+(defmethod handle :openDir [^js window _messages]
   (let [result (.showOpenDialogSync dialog (bean/->js
                                             {:properties ["openDirectory"]}))
         path (first result)]
-    (.. window -webContents
+    (.. ^js window -webContents
         (send "open-dir-confirmed"
               (bean/->js {:opened? true})))
     (get-files path)))

+ 33 - 20
src/main/frontend/components/block.cljs

@@ -181,7 +181,7 @@
                              (reset! *resizing-image? true))
                            (reset! size value))
           :onMouseUp (fn []
-                       (when @size
+                       (when (and @size @*resizing-image?)
                          (when-let [block-id (:block/uuid config)]
                            (let [size (bean/->clj @size)]
                              (editor-handler/resize-image! block-id metadata full_text size))))
@@ -241,7 +241,8 @@
                    nil
                    (safe-read-string metadata false))
         title (second (first label))]
-    (if (config/local-asset? href)
+    (if (and (config/local-asset? href)
+             (config/local-db? (state/get-current-repo)))
       (asset-link config title href label metadata full_text)
       (let [href (if (util/starts-with? href "http")
                    href
@@ -380,7 +381,7 @@
 (rum/defc asset-reference
   [title path]
   (let [repo-path (config/get-repo-dir (state/get-current-repo))
-        full-path (str repo-path (string/replace path "../" "/"))]
+        full-path (.. util/node-path (join repo-path (config/get-local-asset-absolute-path path)))]
     [:a.asset-ref {:target "_blank" :href full-path} (or title path)]))
 
 (rum/defc page-reference < rum/reactive
@@ -838,11 +839,15 @@
                                (get (state/get-macros) (keyword name)))
                 macro-content (if (and (seq arguments) macro-content)
                                 (block/macro-subs macro-content arguments)
-                                macro-content)]
+                                macro-content)
+                macro-content (when macro-content
+                                (editor-handler/resolve-dynamic-template! macro-content))]
             (render-macro config name arguments macro-content format))
 
           (when-let [macro-txt (macro->text name arguments)]
-            (let [format (get-in config [:block :block/format] :markdown)]
+            (let [macro-txt (when macro-txt
+                              (editor-handler/resolve-dynamic-template! macro-txt))
+                  format (get-in config [:block :block/format] :markdown)]
               (render-macro config name arguments macro-txt format))))))
 
     :else
@@ -1100,7 +1105,8 @@
             {:style {:background-color bg-color
                      :padding-left 6
                      :padding-right 6
-                     :color "#FFFFFF"}}))
+                     :color "#FFFFFF"}
+             :class "with-bg-color"}))
          (remove-nils
           (concat
            [(when-not slide? checkbox)
@@ -1489,22 +1495,29 @@
                                (when doc-mode?
                                  (when-let [parent (gdom/getElement block-id)]
                                    (when-let [node (.querySelector parent ".bullet-container")]
-                                     (d/add-class! node "hide-inner-bullet")))))}]
+                                     (d/add-class! node "hide-inner-bullet")))))}
+        data-refs (let [refs (model/get-page-names-by-ids
+                              (->> (map :db/id refs-with-children)
+                                   (remove nil?)))]
+                    (text/build-data-value refs))
+        data-refs-self (let [refs  (model/get-page-names-by-ids
+                                    (->> (map :db/id (:block/ref-pages block))
+                                         (remove nil?)))]
+                         (text/build-data-value refs))]
     [:div.ls-block.flex.flex-col.rounded-sm
      (cond->
-      {:id block-id
-       :data-refs (let [refs (model/get-page-names-by-ids
-                              (map :db/id refs-with-children))]
-                    (text/build-data-value refs))
-       :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))
 

+ 22 - 2
src/main/frontend/components/content.cljs

@@ -72,12 +72,28 @@
    "#264c9b"
    "#793e3e"])
 
-(rum/defcs block-template <
+(defonce *including-parent? (atom nil))
+
+(rum/defc template-checkbox
+  [including-parent?]
+  [:div.flex.flex-row
+   [:span.text-medium.mr-2 "Including the parent block in the template?"]
+   (ui/toggle including-parent?
+              #(swap! *including-parent? not))])
+
+(rum/defcs block-template < rum/reactive
   (rum/local false ::edit?)
   (rum/local "" ::input)
   [state block-id]
   (let [edit? (get state ::edit?)
-        input (get state ::input)]
+        input (get state ::input)
+        including-parent? (rum/react *including-parent?)
+        block-id (if (string? block-id) (uuid block-id) block-id)
+        block (db/entity [:block/uuid block-id])
+        has-children? (seq (:block/children block))]
+    (when (and (nil? including-parent?) has-children?)
+      (reset! *including-parent? true))
+
     (if @edit?
       (do
         (state/clear-edit!)
@@ -87,6 +103,8 @@
           {:auto-focus true
            :on-change (fn [e]
                         (reset! input (util/evalue e)))}]
+         (when has-children?
+           (template-checkbox including-parent?))
          (ui/button "Submit"
                     :on-click (fn []
                                 (let [title (string/trim @input)]
@@ -97,6 +115,8 @@
                                        :error)
                                       (do
                                         (editor-handler/set-block-property! block-id "template" title)
+                                        (when-not including-parent?
+                                          (editor-handler/set-block-property! block-id "including-parent" false))
                                         (state/hide-custom-context-menu!)))))))])
       (ui/menu-link
        {:key "Make template"

+ 41 - 24
src/main/frontend/components/editor.cljs

@@ -132,23 +132,29 @@
             :empty-div [:div.text-gray-500.pl-4.pr-4 "Search for a page"]
             :class     "black"}))))))
 
-(rum/defc block-search < rum/reactive
-  {:will-unmount (fn [state] (reset! editor-handler/*selected-text nil) state)}
-  [id format]
+(rum/defcs block-search < rum/reactive
+  {:will-unmount (fn [state]
+                   (reset! editor-handler/*selected-text nil)
+                   (state/clear-search-result!)
+                   state)}
+  [state id format]
   (when (state/sub :editor/show-block-search?)
     (let [pos (:editor/last-saved-cursor @state/state)
-          input (gdom/getElement id)]
+          input (gdom/getElement id)
+          [id format] (:rum/args state)
+          current-pos (:pos (util/get-caret-pos input))
+          edit-content (state/sub [:editor/content id])
+          edit-block (state/get-edit-block)
+          q (or
+             @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)]
       (when input
-        (let [current-pos (:pos (util/get-caret-pos input))
-              edit-content (state/sub [:editor/content id])
-              edit-block (state/get-edit-block)
-              q (or
-                 @editor-handler/*selected-text
-                 (when (> (count edit-content) current-pos)
-                   (subs edit-content pos current-pos)))
-              matched-blocks (when-not (string/blank? q)
-                               (editor-handler/get-matched-blocks q (:block/uuid edit-block)))
-              chosen-handler (fn [chosen _click?]
+        (let [chosen-handler (fn [chosen _click?]
                                (state/set-editor-show-block-search! false)
                                (let [uuid-string (str (:block/uuid chosen))]
 
@@ -196,23 +202,33 @@
               chosen-handler (fn [[template db-id] _click?]
                                (if-let [block (db/entity db-id)]
                                  (let [new-level (:block/level edit-block)
+                                       properties (:block/properties block)
+                                       block-uuid (:block/uuid block)
+                                       including-parent? (not= (get properties "including-parent") "false")
                                        template-parent-level (:block/level block)
                                        pattern (config/get-block-pattern format)
                                        content
                                        (block-handler/get-block-full-content
                                         (state/get-current-repo)
                                         (:block/uuid block)
-                                        (fn [{:block/keys [level content properties] :as block}]
-                                          (let [new-level (+ new-level (- level template-parent-level))
-                                                properties' (dissoc (into {} properties) "id" "custom_id" "template")]
-                                            (-> content
-                                                (string/replace-first (apply str (repeat level pattern))
-                                                                      (apply str (repeat new-level pattern)))
-                                                text/remove-properties!
-                                                (text/rejoin-properties properties')))))
+                                        (fn [{:block/keys [uuid level content properties] :as block}]
+                                          (let [parent? (= uuid block-uuid)
+                                                ignore-parent? (and parent? (not including-parent?))]
+                                            (if ignore-parent?
+                                              ""
+                                              (let [new-level (+ new-level
+                                                                 (- level template-parent-level
+                                                                    (if (not including-parent?) 1 0)))
+                                                    properties' (dissoc (into {} properties) "id" "custom_id" "template")]
+                                                (-> content
+                                                   (string/replace-first (apply str (repeat level pattern))
+                                                                         (apply str (repeat new-level pattern)))
+                                                   text/remove-properties!
+                                                   (text/rejoin-properties properties')))))))
                                        content (if (string/includes? (string/trim edit-content) "\n")
                                                  content
-                                                 (text/remove-level-spaces content format))]
+                                                 (text/remove-level-spaces content format))
+                                       content (editor-handler/resolve-dynamic-template! content)]
                                    (state/set-editor-show-template-search! false)
                                    (editor-handler/insert-command! id
                                                                    content
@@ -250,7 +266,7 @@
     {:on-click #(commands/simple-insert! parent-id "\n" {})}
     svg/multi-line-input]
    [:button.bottom-action
-    {:on-click #(commands/insert-before! parent-id "TODO " {})} 
+    {:on-click #(commands/insert-before! parent-id "TODO " {})}
     svg/checkbox]
    [:button.font-extrabold.bottom-action.-mt-1
     {:on-click #(commands/simple-insert!
@@ -712,6 +728,7 @@
      (when config/mobile? (mobile-bar state id))
      (ui/ls-textarea
       {:id                id
+       :class             "mousetrap"
        :cacheMeasurements true
        :default-value     (or content "")
        :minRows           (if (state/enable-grammarly?) 2 1)

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

@@ -77,8 +77,9 @@
         {:on-click toggle-fn}
         (svg/horizontal-dots nil)])
      (->>
-      [{:title (t :help/toggle-right-sidebar)
-        :options {:on-click state/toggle-sidebar-open?!}}
+      [(when-not (util/mobile?)
+         {:title (t :help/toggle-right-sidebar)
+          :options {:on-click state/toggle-sidebar-open?!}})
 
        (when current-repo
          {:title (t :graph)

+ 8 - 5
src/main/frontend/components/header.css

@@ -47,7 +47,10 @@
 .cp__header-logo,
 .cp__right-menu-button {
   opacity: 0.7;
-  display: none;
+}
+
+.cp__header-logo {
+    display: none;
 }
 
 .cp__header-logo:hover,
@@ -64,6 +67,10 @@
   @apply ml-3;
 }
 
+.cp__right-menu-button {
+    display: block;
+}
+
 @screen sm {
   .cp__header {
     @apply shadow-none;
@@ -77,8 +84,4 @@
     display: flex;
     align-items: center;
   }
-
-  .cp__right-menu-button {
-    display: block;
-  }
 }

+ 1 - 4
src/main/frontend/components/journal.cljs

@@ -111,10 +111,7 @@
 
      (when intro? (onboarding/intro))]))
 
-(rum/defc journals <
-  {:did-mount (fn [state]
-                (editor-handler/open-last-block! true)
-                state)}
+(rum/defc journals
   [latest-journals]
   [:div#journals
    (ui/infinite-list

+ 0 - 5
src/main/frontend/components/page.cljs

@@ -224,15 +224,10 @@
              [:a {:href (rfe/href :page {:name name})}
               original-name]])] false)]])))
 
-(defonce last-route (atom :home))
 ;; A page is just a logical block
 (rum/defcs page < rum/reactive
   {:did-mount (fn [state]
                 (ui-handler/scroll-and-highlight! state)
-                ;; only when route changed
-                (when (not= @last-route (state/get-current-route))
-                  (editor-handler/open-last-block! false))
-                (reset! last-route (state/get-current-route))
                 state)
    :did-update (fn [state]
                  (ui-handler/scroll-and-highlight! state)

+ 36 - 1
src/main/frontend/components/search.cljs

@@ -31,6 +31,41 @@
   (let [switch (reductions not= true (map pred? coll (rest coll)))]
     (map (partial map first) (partition-by second (map list coll switch)))))
 
+(defn highlight-exact-query
+  [content q]
+  (let [q-words (string/split q #" ")
+        lc-content (string/lower-case content)
+        lc-q (string/lower-case q)]
+    (if (or (string/includes? lc-content lc-q)
+            (not (re-find #" " q)))
+      (let [i (string/index-of lc-content lc-q)
+            [before after] [(subs content 0 i) (subs content (+ i (count q)))]]
+        [:p
+         (when-not (string/blank? before)
+           [:span before])
+         [:mark (subs content i (+ i (count q)))]
+         (when-not (string/blank? after)
+           [:span after])])
+      (let [elements (loop [words q-words
+                            content content
+                            result []]
+                       (if (and (seq words) content)
+                         (let [word (first words)
+                               lc-word (string/lower-case word)
+                               lc-content (string/lower-case content)]
+                           (if-let [i (string/index-of lc-content lc-word)]
+                             (recur (rest words)
+                                    (subs content (+ i (count word)))
+                                    (vec
+                                     (concat result
+                                             [[:span (subs content 0 i)]
+                                              [:mark (subs content i (+ i (count word)))]])))
+                             (recur nil
+                                    content
+                                    result)))
+                         (conj result [:span content])))]
+        [:p elements]))))
+
 (rum/defc highlight-fuzzy
   [content indexes]
   (let [n (count content)
@@ -182,7 +217,7 @@
                                          (:page/name page))]
                             [:div.flex-1
                              [:div.text-sm.font-medium (str "-> " page)]
-                             (highlight-fuzzy content indexes)])
+                             (highlight-exact-query content search-q)])
 
                           nil))})])))
 

+ 32 - 1
src/main/frontend/components/settings.cljs

@@ -111,6 +111,25 @@
                (util/stop e))}
             svg/external-link " release channel"]])])]))
 
+(rum/defc delete-account-confirm
+  [close-fn]
+  (rum/with-context [[t] i18n/*tongue-context*]
+    [:div
+     (ui/admonition
+      :important
+      [:p.text-gray-700 (t :user/delete-account-notice)])
+     [:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
+      [:span.flex.w-full.rounded-md.shadow-sm.sm:ml-3.sm:w-auto
+       [:button.inline-flex.justify-center.w-full.rounded-md.border.border-transparent.px-4.py-2.bg-indigo-600.text-base.leading-6.font-medium.text-white.shadow-sm.hover:bg-indigo-500.focus:outline-none.focus:border-indigo-700.focus:shadow-outline-indigo.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
+        {:type "button"
+         :on-click user-handler/delete-account!}
+        (t :user/delete-account)]]
+      [:span.mt-3.flex.w-full.rounded-md.shadow-sm.sm:mt-0.sm:w-auto
+       [:button.inline-flex.justify-center.w-full.rounded-md.border.border-gray-300.px-4.py-2.bg-white.text-base.leading-6.font-medium.text-gray-700.shadow-sm.hover:text-gray-500.focus:outline-none.focus:border-blue-300.focus:shadow-outline-blue.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
+        {:type "button"
+         :on-click close-fn}
+        "Cancel"]]]]))
+
 (rum/defcs settings < rum/reactive
   []
   (let [preferred-format (state/get-preferred-format)
@@ -329,4 +348,16 @@
                        :on-click #(state/set-developer-mode! (not developer-mode?)))]]]
 
          [:br]
-         (t :settings-page/developer-mode-desc)]]])))
+         (t :settings-page/developer-mode-desc)
+
+         [:hr]
+
+         (when logged?
+           [:div.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start.sm:pt-5
+            [:label.block.text-sm.font-medium.leading-5.sm:mt-px.sm:pt-2.opacity-70.text-red-600
+             {:for "delete account"}
+             (t :user/delete-account)]
+            [:div.mt-1.sm:mt-0.sm:col-span-2
+             [:div.max-w-lg.rounded-md.shadow-sm.sm:max-w-xs
+              (ui/button (t :user/delete-your-account)
+                :on-click #(state/set-modal! delete-account-confirm))]]])]]])))

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

@@ -290,7 +290,6 @@
   {:did-mount (fn [state]
                 (keyboards/bind-shortcuts!)
                 state)}
-  (mixins/keyboards-mixin keyboards/keyboards)
   [state route-match main-content]
   (let [{:keys [open? close-fn open-fn]} state
         close-fn (fn []

+ 7 - 1
src/main/frontend/date.cljs

@@ -7,7 +7,13 @@
             [cljs-bean.core :as bean]
             [frontend.util :as util]
             [clojure.string :as string]
-            [goog.object :as gobj]))
+            [goog.object :as gobj]
+            ["chrono-node" :as chrono]))
+
+(defn nld-parse
+  [s]
+  (when (string? s)
+    ((gobj/get chrono "parseDate") s)))
 
 (defn format
   [date]

+ 6 - 2
src/main/frontend/db.cljs

@@ -10,6 +10,7 @@
             [frontend.state :as state]
             [promesa.core :as p]
             [frontend.db-schema :as db-schema]
+            [frontend.db.default :as default-db]
             [clojure.core.async :as async]
             [frontend.idb :as idb]))
 
@@ -147,7 +148,8 @@
          (p/let [stored (idb/get-item db-name)
                  _ (when stored
                      (let [stored-db (string->db stored)
-                           attached-db (d/db-with stored-db [(me-tx stored-db me)])]
+                           attached-db (d/db-with stored-db
+                                                  [(me-tx stored-db me)])]
                        (conn/reset-conn! db-conn attached-db)))
                  db-name (datascript-db repo)
                  db-conn (d/create-conn db-schema/schema)
@@ -156,7 +158,9 @@
                  stored (idb/get-item db-name)
                  _ (if stored
                      (let [stored-db (string->db stored)
-                           attached-db (d/db-with stored-db [(me-tx stored-db me)])]
+                           attached-db (d/db-with stored-db (concat
+                                                             [(me-tx stored-db me)]
+                                                             default-db/built-in-pages))]
                        (conn/reset-conn! db-conn attached-db))
                      (when logged?
                        (d/transact! db-conn [(me-tx (d/db db-conn) me)])))]

+ 3 - 0
src/main/frontend/db/conn.cljs

@@ -2,6 +2,7 @@
   "Contains db connections."
   (:require [clojure.string :as string]
             [frontend.db-schema :as db-schema]
+            [frontend.db.default :as default-db]
             [frontend.util :as util]
             [frontend.state :as state]
             [frontend.config :as config]
@@ -85,6 +86,8 @@
      (when me
        (d/transact! db-conn [(me-tx (d/db db-conn) me)]))
 
+     (d/transact! db-conn default-db/built-in-pages)
+
      (when listen-handler (listen-handler repo)))))
 
 (defn destroy-all!

+ 9 - 0
src/main/frontend/db/default.cljs

@@ -0,0 +1,9 @@
+(ns frontend.db.default
+  (:require [clojure.string :as string]))
+
+(def built-in-pages
+  (mapv (fn [p]
+          {:page/name (string/lower-case p)
+           :page/original-name p
+           :page/journal? false})
+        #{"NOW" "LATER" "DOING" "DONE" "IN-PROGRESS" "TODO" "WAIT" "WAITING" "A" "B" "C"}))

+ 2 - 9
src/main/frontend/db/model.cljs

@@ -717,11 +717,7 @@
                                     (string? title)
                                     title))
             file-name (when-let [file-name (last (string/split file #"/"))]
-                        (when-let [file-name (first (util/split-last "." file-name))]
-                          (-> file-name
-                              (string/replace "-" " ")
-                              (string/replace "_" " ")
-                              (util/capitalize-all))))]
+                        (first (util/split-last "." file-name)))]
         (or property-name
             (if (= (state/page-name-order) "file")
               (or file-name first-block-name)
@@ -1204,10 +1200,7 @@
   [repo path content]
   (when (and repo path)
     (let [tx-data {:file/path path
-                   :file/content content}
-          tx-data (if (config/local-db? repo)
-                    (dissoc tx-data :file/last-modified-at)
-                    tx-data)]
+                   :file/content content}]
       (react/transact-react!
        repo
        [tx-data]

+ 10 - 4
src/main/frontend/dicts.cljs

@@ -343,8 +343,11 @@ title: How to take dummy notes?
         :dark "Dark"
         :remove-background "Remove background"
         :open "Open"
-        :open-a-directory "Open a local directory"}
-        
+        :open-a-directory "Open a local directory"
+        :user/delete-account "Delete account"
+        :user/delete-your-account "Delete your account"
+        :user/delete-account-notice "All your published pages on logseq.com will be deleted."}
+
    :de {:help/about "Über Logseq"
         :help/bug "Fehlerbericht"
         :help/feature "Feature-Anfrage"
@@ -573,7 +576,7 @@ title: How to take dummy notes?
         :remove-background "Hintergrund entfernen"
         :open "Öffnen"
         :open-a-directory "Öffne ein lokales Verzeichnis"}
-        
+
    :fr {:help/about "A propos de Logseq"
         :help/bug "Signaler une anomalie"
         :help/feature "Demander une fonctionnalité"
@@ -1057,7 +1060,10 @@ title: How to take dummy notes?
            :dark "暗黑"
            :remove-background "去除背景"
            :open "打开"
-           :open-a-directory "打开本地文件夹"}
+           :open-a-directory "打开本地文件夹"
+           :user/delete-account "删除帐号"
+           :user/delete-your-account "删除你的帐号"
+           :user/delete-account-notice "你在 logseq.com 发布的页面(假如有的话)也会被删除。"}
 
    :zh-Hant {:on-boarding/title "你好,歡迎使用 Logseq!"
              :on-boarding/sharing "分享"

+ 11 - 1
src/main/frontend/external/roam.cljc

@@ -43,6 +43,15 @@
                                              (util/format "{{%s %s}}" name arg))
                                            original)))))
 
+(defn- fenced-code-transform
+  [text]
+  (string/replace text
+                  #"```([a-z]*\n[\s\S]*?\n*)```"
+                  (fn [[_ match]]
+                    (str "```"
+                         (str match "\n")
+                         "```"))))
+
 (defn load-all-refed-uids!
   [data]
   (let [full-text (atom "")]
@@ -66,7 +75,8 @@
       (string/replace "{{[[TODO]]}}" "TODO")
       (string/replace "{{[[DONE]]}}" "DONE")
       (uid-transform)
-      (macro-transform)))
+      (macro-transform)
+      (fenced-code-transform)))
 
 (declare children->text)
 (defn child->text

+ 3 - 3
src/main/frontend/format/block.cljs

@@ -251,9 +251,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}]

+ 26 - 19
src/main/frontend/fs.cljs

@@ -57,28 +57,35 @@
   [dir]
   (protocol/rmdir! (get-fs dir) dir))
 
-(defn read-file
-  [dir path]
-  (p/chain (protocol/read-file (get-fs dir) dir path)
-           encrypt/decrypt))
-
 (defn write-file!
   [repo dir path content opts]
   (when content
-    (p/let [metadata-or-css? (or (string/ends-with? path config/metadata-file)
-                                 (string/ends-with? path config/custom-css-file))
-            content (if metadata-or-css? content (encrypt/encrypt content))]
-      (->
-       (do
-         (protocol/write-file! (get-fs dir) repo dir path content opts)
-         (db/set-file-last-modified-at! repo (config/get-file-path repo path) (js/Date.)))
-       (p/catch (fn [error]
-                  (log/error :file/write-failed? {:dir dir
-                                                  :path path
-                                                  :error error})
-                  ;; Disable this temporarily
-                  ;; (js/alert "Current file can't be saved! Please copy its content to your local file system and click the refresh button.")
-))))))
+    (let [fs-record (get-fs dir)]
+      (p/let [metadata-or-css? (or (string/ends-with? path config/metadata-file)
+                                  (string/ends-with? path config/custom-css-file))
+             content (if metadata-or-css? content (encrypt/encrypt content))]
+       (->
+        (p/let [_ (protocol/write-file! (get-fs dir) repo dir path content opts)]
+          (when-not (= fs-record nfs-record)
+           (db/set-file-last-modified-at! repo (config/get-file-path repo path) (js/Date.))))
+        (p/catch (fn [error]
+                   (log/error :file/write-failed? {:dir dir
+                                                   :path path
+                                                   :error error})
+                   ;; Disable this temporarily
+                   ;; (js/alert "Current file can't be saved! Please copy its content to your local file system and click the refresh button.")
+                   )))))))
+
+(defn read-file
+  ([dir path]
+   (let [fs (get-fs dir)
+         options (if (= fs bfs-record)
+                   {:encoding "utf8"}
+                   {})]
+     (read-file dir path {})))
+  ([dir path options]
+   (p/chain (protocol/read-file (get-fs dir) dir path options)
+            encrypt/decrypt)))
 
 (defn rename!
   [repo old-path new-path]

+ 2 - 3
src/main/frontend/fs/bfs.cljs

@@ -22,9 +22,8 @@
           (p/rejected "Unlinking a directory is not allowed")))))
   (rmdir! [this dir]
     (js/window.workerThread.rimraf dir))
-  (read-file [this dir path]
-    (let [option (clj->js {:encoding "utf8"})]
-      (js/window.pfs.readFile (str dir "/" path) option)))
+  (read-file [this dir path options]
+    (js/window.pfs.readFile (str dir "/" path) (clj->js options)))
   (write-file! [this repo dir path content opts]
     (when-not (util/electron?)
       (js/window.pfs.writeFile (str dir "/" path) content)))

+ 5 - 2
src/main/frontend/fs/nfs.cljs

@@ -97,14 +97,15 @@
   (rmdir! [this dir]
     nil)
 
-  (read-file [this dir path]
+  (read-file [this dir path options]
     (let [handle-path (str "handle" dir "/" path)]
       (p/let [handle (idb/get-item handle-path)
               local-file (and handle (.getFile handle))]
         (and local-file (.text local-file)))))
 
   (write-file! [this repo dir path content opts]
-    (let [{:keys [old-content last-modified-at]} opts
+    (let [{:keys [old-content]} opts
+          last-modified-at (db/get-file-last-modified-at repo path)
           parts (string/split path "/")
           basename (last parts)
           sub-dir (->> (butlast parts)
@@ -136,7 +137,9 @@
                                        ;; temporally fix
                                        (and path (string/ends-with? path ".excalidraw"))) new?
                      (or
+                      ;; Writing not finished
                       (> pending-writes 0)
+                      ;; not changed by other editors
                       not-changed?
                       new-created?))
               (do

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

@@ -30,7 +30,7 @@
     (ipc/ipc "unlink" path))
   (rmdir! [this dir]
     nil)
-  (read-file [this dir path]
+  (read-file [this dir path _options]
     (let [path (concat-path dir path)]
       (ipc/ipc "readFile" path)))
   (write-file! [this repo dir path content _opts]

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

@@ -5,7 +5,7 @@
   (readdir [this dir])
   (unlink! [this path opts])
   (rmdir! [this dir])
-  (read-file [this dir path])
+  (read-file [this dir path opts])
   (write-file! [this repo dir path content opts])
   (rename! [this repo old-path new-path])
   (stat [this dir path])

+ 5 - 3
src/main/frontend/handler/block.cljs

@@ -31,7 +31,8 @@
               cur-level (:block/level item)
               bottom-level (:block/level (first children))
               pre-block? (:block/pre-block? item)
-              item (assoc item :block/refs-with-children (get-all-refs item))]
+              item (assoc item :block/refs-with-children (->> (get-all-refs item)
+                                                              (remove nil?)))]
           (cond
             (empty? children)
             (recur others (list item))
@@ -46,8 +47,9 @@
             (let [[children other-children] (split-with (fn [h]
                                                           (> (:block/level h) cur-level))
                                                         children)
-                  refs-with-children (-> (mapcat get-all-refs (cons item children))
-                                         distinct)
+                  refs-with-children (->> (mapcat get-all-refs (cons item children))
+                                          (remove nil?)
+                                          distinct)
                   children (cons
                             (assoc item
                                    :block/children children

+ 83 - 42
src/main/frontend/handler/editor.cljs

@@ -294,8 +294,8 @@
                                 (reset! last-child-end-pos old-end-pos)))
 
                             (cond->
-                             {:block/uuid uuid
-                              :block/meta new-meta}
+                                {:block/uuid uuid
+                                 :block/meta new-meta}
                               (and (some? indent-left?) (not @next-leq-level?))
                               (assoc :block/level (if indent-left? (dec level) (inc level)))
                               (and new-content (not @next-leq-level?))
@@ -448,10 +448,10 @@
                                       :page/original-name tag}) tags))
          page-alias (when-let [alias (:alias new-properties)]
                       (map
-                       (fn [alias]
-                         {:page/original-name alias
-                          :page/name (string/lower-case alias)})
-                       (remove #{(:page/name page)} alias)))
+                        (fn [alias]
+                          {:page/original-name alias
+                           :page/name (string/lower-case alias)})
+                        (remove #{(:page/name page)} alias)))
 
          permalink-changed? (when (and pre-block? (:permalink old-properties))
                               (not= (:permalink old-properties)
@@ -508,8 +508,8 @@
                  ;; 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)))
+                                                                     (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)]
@@ -981,7 +981,7 @@
   [{:block/keys [uuid marker content meta file dummy? properties] :as block} new-marker]
   (let [new-content (string/replace-first content marker new-marker)]
     (save-block-if-changed! block new-content
-                            (with-marker-time block marker))))
+                            {:custom-properties (with-marker-time block new-marker)})))
 
 (defn set-priority
   [{:block/keys [uuid marker priority content meta file dummy?] :as block} new-priority]
@@ -1168,7 +1168,7 @@
                                                (if (string/starts-with? (string/lower-case line) key)
                                                  new-line
                                                  line))
-                                             lines)
+                                          lines)
                               new-lines (if (not= lines new-lines)
                                           new-lines
                                           (cons (first new-lines) ;; title
@@ -1437,7 +1437,9 @@
    (save-current-block-when-idle! {}))
   ([{:keys [check-idle? chan chan-callback]
      :or {check-idle? true}}]
-   (when (nil? (state/get-editor-op))
+   (when (and (nil? (state/get-editor-op))
+              ;; non English input method
+              (not (state/editor-in-composition?)))
      (when-let [repo (state/get-current-repo)]
        (when (and (if check-idle? (state/input-idle? repo) true)
                   (not (state/get-editor-show-page-search?))
@@ -1559,18 +1561,18 @@
             ext (if ext (subs ext (string/last-index-of ext ".")) "")
             filename (str (gen-filename index file) ext)
             filename (str path "/" filename)]
-        ;(js/console.debug "Write asset #" dir filename file)
+                                        ;(js/console.debug "Write asset #" dir filename file)
         (if (util/electron?)
           (let [from (.-path file)]
             (p/then (js/window.apis.copyFileToAssets dir filename from)
-                    #(p/resolved [filename (if (string? %) (js/File. #js[] %) file)])))
+                    #(p/resolved [filename (if (string? %) (js/File. #js[] %) file) (.join util/node-path dir filename)])))
           (p/then (fs/write-file! repo dir filename (.stream file) nil)
                   #(p/resolved [filename file]))))))))
 
 (defonce *assets-url-cache (atom {}))
 
 (defn make-asset-url
-  [path]                                                    ;; path start with "/assets" or compatible for "../assets"
+  [path] ;; path start with "/assets" or compatible for "../assets"
   (let [repo-dir (config/get-repo-dir (state/get-current-repo))
         path (string/replace path "../" "/")]
     (if (util/electron?)
@@ -1620,11 +1622,11 @@
       (-> (save-assets! block repo (js->clj files))
           (p/then
            (fn [res]
-             (when-let [[url file] (and (seq res) (first res))]
-               (let [image? (util/ext-of-image? url)]
+             (when-let [[asset-file-name file full-file-path] (and (seq res) (first res))]
+               (let [image? (util/ext-of-image? asset-file-name)]
                  (insert-command!
                   id
-                  (get-asset-file-link format (resolve-relative-path url)
+                  (get-asset-file-link format (resolve-relative-path (or full-file-path asset-file-name))
                                        (if file (.-name file) (if image? "image" "asset"))
                                        image?)
                   format
@@ -1674,7 +1676,7 @@
    ;; "_" "_"
    ;; ":" ":"                              ; TODO: only properties editing and org mode tag
    ;; "^" "^"
-})
+   })
 
 (def reversed-autopair-map
   (zipmap (vals autopair-map)
@@ -1762,10 +1764,11 @@
                                                       99)
                                 (map (comp str :block/uuid))))
         current-and-parents (set/union #{(str (:block/uuid current-block))} block-parents)]
-    (remove
-     (fn [h]
-       (contains? current-and-parents (:block/uuid h)))
-     (search/search q 10))))
+    (p/let [result (search/block-search q 10)]
+      (remove
+       (fn [h]
+         (contains? current-and-parents (:block/uuid h)))
+       result))))
 
 (defn get-matched-templates
   [q]
@@ -1980,7 +1983,7 @@
                                 true)))
 
 (defn cycle-collapse!
-  [_state e]
+  [e]
   (when (and
          ;; not input, t
          (nil? (state/get-edit-input-id))
@@ -1991,7 +1994,7 @@
 
 (defn on-tab
   [direction]
-  (fn [state e]
+  (fn [e]
     (when-let [repo (state/get-current-repo)]
       (let [blocks (seq (state/get-selection-blocks))]
         (cond
@@ -2027,7 +2030,7 @@
                                                                 :end-pos end-pos}))]
                                  (reset! last-start-pos end-pos)
                                  block))
-                             blocks))
+                          blocks))
                 file-id (:db/id (:block/file block))
                 file (db/entity file-id)
                 page (:block/page block)
@@ -2053,7 +2056,7 @@
           nil
 
           :else
-          (cycle-collapse! state e))))))
+          (cycle-collapse! e))))))
 
 (defn bulk-make-todos
   [state e]
@@ -2094,7 +2097,7 @@
                                                               :end-pos end-pos}))]
                                (reset! last-start-pos end-pos)
                                block))
-                           blocks))
+                        blocks))
               file-id (:db/id (:block/file block))
               file (db/entity file-id)
               page (:block/page block)
@@ -2112,7 +2115,7 @@
             {:key :block/change
              :data (map (fn [block] (assoc block :block/page page)) blocks)}
             [[file-path new-content]])))
-        (cycle-collapse! state e)))))
+        (cycle-collapse! e)))))
 
 (defn- get-link
   [format link label]
@@ -2164,26 +2167,36 @@
   [block-id value]
   (set-block-property! block-id "heading" value))
 
-;; Should preserve the cursor too.
-(defn open-last-block!
-  [journal?]
-  (let [edit-id (state/get-edit-input-id)
-        last-pos (state/get-edit-pos)
-        block-id (when edit-id (subs edit-id (- (count edit-id) 36)))]
-    (let [last-edit-block (first (array-seq (js/document.getElementsByClassName block-id)))
-          first-block (first (array-seq (js/document.getElementsByClassName "ls-block")))
-          node (or last-edit-block
-                   (and (not journal?) first-block))]
+(defn open-block!
+  [first?]
+  (when-not (state/editing?)
+    (let [edit-id (state/get-last-edit-input-id)
+          block-id (when edit-id (subs edit-id (- (count edit-id) 36)))
+          last-edit-block (first (array-seq (js/document.getElementsByClassName block-id)))
+          nodes (array-seq (js/document.getElementsByClassName "ls-block"))
+          first-node (first nodes)
+          node (cond
+                 last-edit-block
+                 last-edit-block
+                 first?
+                 first-node
+                 :else
+                 (when-let [blocks-container (util/rec-get-blocks-container first-node)]
+                   (let [nodes (dom/by-class blocks-container "ls-block")]
+                     (last nodes))))]
       (when node
+        (state/clear-selection!)
+        (unhighlight-block!)
         (let [block-id (and node (d/attr node "blockid"))
               edit-block-id (string/replace (gobj/get node "id") "ls-block" "edit-block")
               block-id (medley/uuid block-id)]
-          (when-let [block (db/entity [:block/uuid block-id])]
+          (when-let [block (or (db/entity [:block/uuid block-id])
+                               {:block/uuid block-id})]
             (edit-block! block
-                         (or (and last-edit-block last-pos)
-                             :max)
+                         :max
                          (:block/format block)
-                         edit-block-id)))))))
+                         edit-block-id))))
+      false)))
 
 (defn get-search-q
   []
@@ -2232,3 +2245,31 @@
         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))))))

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

@@ -157,7 +157,7 @@
                       (remove empty?))]
       (when (seq result)
         (let [[pages block-ids blocks] (apply map concat result)
-              block-ids-set (set block-ids)
+              block-ids-set (set (map (fn [{:block/keys [uuid]}] [:block/uuid uuid]) block-ids))
               ;; To prevent "unique constraint" on datascript
               pages-index (map #(select-keys % [:page/name]) pages)
               blocks (map (fn [b]

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

@@ -180,8 +180,7 @@
         (reset-file! repo path content))
       (db/set-file-content! repo path content))
     (util/p-handle
-     (fs/write-file! repo (config/get-repo-dir repo) path content {:old-content original-content
-                                                                   :last-modified-at (db/get-file-last-modified-at repo path)})
+     (fs/write-file! repo (config/get-repo-dir repo) path content {:old-content original-content})
      (fn [_]
        (git-handler/git-add repo path update-status?)
        (when (= path (config/get-config-path repo))
@@ -245,8 +244,7 @@
                        (let [original-content (get file->content path)]
                          (-> (p/let [_ (nfs/check-directory-permission! repo)]
                                (fs/write-file! repo (config/get-repo-dir repo) path content
-                                               {:old-content original-content
-                                                :last-modified-at (db/get-file-last-modified-at repo path)}))
+                                               {:old-content original-content}))
                              (p/catch (fn [error]
                                         (log/error :write-file/failed {:path path
                                                                        :content content

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

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

+ 8 - 5
src/main/frontend/handler/search.cljs

@@ -3,14 +3,17 @@
             [frontend.state :as state]
             [goog.dom :as gdom]
             [frontend.search :as search]
-            [frontend.handler.notification :as notification-handler]))
+            [frontend.handler.notification :as notification-handler]
+            [promesa.core :as p]))
 
 (defn search
   [q]
-  (swap! state/state assoc :search/result
-         {:pages (search/page-search q)
-          :files (search/file-search q)
-          :blocks (search/search q 10)}))
+  ;; 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})))
 
 (defn clear-search!
   []

+ 23 - 10
src/main/frontend/handler/user.cljs

@@ -8,7 +8,8 @@
             [promesa.core :as p]
             [goog.object :as gobj]
             [frontend.handler.notification :as notification]
-            [frontend.handler.config :as config-handler])
+            [frontend.handler.config :as config-handler]
+            [lambdaisland.glogi :as log])
   (:import [goog.format EmailAddress]))
 
 (defn email? [v]
@@ -63,12 +64,24 @@
                  (fn [_e])))))
 
 (defn sign-out!
-  [_e]
-  (when (js/confirm "Your local notes will be completely removed after signing out. Continue?")
-    (->
-     (idb/clear-local-storage-and-idb!)
-     (p/catch (fn [e]
-                (println "sign out error: ")
-                (js/console.dir e)))
-     (p/finally (fn []
-                  (set! (.-href js/window.location) "/logout"))))))
+  ([]
+   (sign-out! true))
+  ([confirm?]
+   (when (or (not confirm?)
+             (js/confirm "Your local notes will be completely removed after signing out. Continue?"))
+     (->
+      (idb/clear-local-storage-and-idb!)
+      (p/catch (fn [e]
+                 (println "sign out error: ")
+                 (js/console.dir e)))
+      (p/finally (fn []
+                   (set! (.-href js/window.location) "/logout")))))))
+
+(defn delete-account!
+  []
+  (p/let [_ (idb/clear-local-storage-and-idb!)]
+    (util/delete (str config/api "account")
+                 (fn []
+                   (sign-out! false))
+                 (fn [error]
+                   (log/error :user/delete-account-failed error)))))

+ 23 - 18
src/main/frontend/history.cljs

@@ -7,7 +7,8 @@
             [goog.dom :as gdom]
             [goog.object :as gobj]
             [clojure.string :as string]
-            [frontend.util :as util]))
+            [frontend.util :as util]
+            [frontend.handler.ui :as ui-handler]))
 
 ;; Undo && Redo that works with files
 
@@ -21,7 +22,7 @@
 ;; repo -> idx
 (defonce history-idx (atom {}))
 
-(defonce history-limit 500)
+(defonce history-limit 100)
 
 ;; tx [[file1-path patches] [file2-path patches]]
 (defn add-history!
@@ -30,10 +31,9 @@
                 (remove (fn [[_ old new]] (= old new)))
                 (map (fn [[file old new]]
                        (when (and old new)
-                         (let [diffs (diff/diffs new old)
-                              patches (diff/get-patches new old diffs)]
-                           [file patches {:old old
-                                          :new new}]))))
+                         (let [diffs (diff/diffs new old)]
+                           [file {:old old
+                                  :new new}]))))
                 (remove nil?))]
     (when (seq tx)
       (let [last-edit-block (get @state/state :editor/last-edit-block)
@@ -74,12 +74,11 @@
             tx (get-in @history [repo idx'])
             {:keys [data]} tx
             _ (reset! *undoing? true)
-            promises (for [[path patches] data]
-                       (let [current-content (db/get-file-no-sub path)
-                             original-content (diff/apply-patches! current-content patches)]
-                         (alter-file repo path original-content
+            promises (for [[path {:keys [old]}] data]
+                       (let [current-content (db/get-file-no-sub path)]
+                         (alter-file repo path old
                                      {:add-history? false
-                                      :re-render-root? true})))]
+                                      :re-render-root? false})))]
         (-> (p/all promises)
             (p/then (fn []
                       (db/clear-query-state!)
@@ -88,7 +87,8 @@
                       ;; restore cursor
                       (when (> idx' 0)
                         (let [prev-tx (get-in @history [repo (dec idx')])]
-                          (when restore-cursor (restore-cursor prev-tx)))))))))))
+                          (when restore-cursor (restore-cursor prev-tx))))
+                      (ui-handler/re-render-root!))))))))
 
 (defonce *redoing? (atom false))
 (defn redo!
@@ -98,17 +98,22 @@
     (when (and (> (count txs) idx) (false? @*redoing?))
       (let [tx (get-in @history [repo idx])
             _ (reset! *redoing? true)
-            promises (for [[path patches] (:data tx)]
-                       (let [current-content (db/get-file-no-sub path)
-                             reversed-patches (utils/reversePatch patches)
-                             content (diff/apply-patches! current-content reversed-patches)]
-                         (alter-file repo path content
+            promises (for [[path {:keys [new]}] (:data tx)]
+                       (let [current-content (db/get-file-no-sub path)]
+                         (alter-file repo path new
                                      {:add-history? false
-                                      :re-render-root? true})))]
+                                      :re-render-root? false})))]
         (-> (p/all promises)
             (p/then (fn []
                       (db/clear-query-state!)
                       (swap! history-idx assoc repo (inc idx))
                       (reset! *redoing? false)
+                      (ui-handler/re-render-root!)
                       ;; restore cursor
                       (when restore-cursor (restore-cursor tx)))))))))
+
+(comment
+  (prn
+   {:history-idx (get @history-idx (frontend.state/get-current-repo))
+    :history (get @history (frontend.state/get-current-repo))})
+  )

+ 59 - 75
src/main/frontend/keyboards.cljs

@@ -12,73 +12,9 @@
             ["mousetrap" :as mousetrap]
             [goog.object :as gobj]))
 
-;; KeyCodes.QUESTION_MARK
-
 ;; Credits to roamresearch
 
-;; Triggers
-;; Slash Autocomplete /
-;; Block Insert Autocomplete <
-;; Page reference Autocomplete [[]]
-;; Block Reference Autocomplete (())
-
-;; Key Commands (working with lists)
-;; Indent Block Tab
-;; Unindent Block Shift-Tab
-;; Create New Block Enter
-;; New Line in Block Shift-Enter
-;; Undo Ctrl-z
-;; Redo Ctrl-y
-;; Zoom In Alt-Right
-;; Zoom out Alt-left
-;; Follow link under cursor Ctrl-o
-;; Open link in Sidebar Ctrl-shift-o
-;; Select Block Above Shift-Up
-;; Select Block Below Shift-Down
-;; Select All Blocks Ctrl-Shift-a
-
-;; General
-;; Full Text Search
-;; Open Link in Sidebar
-;; Context Menu
-;; Jump to Journals
-
-;; Formatting
-;; Bold Ctrl-b
-;; Italics Ctrl-i
-;; Html Link Ctrl-k
-;; Highlight Ctrl-h
-
-;; Markdown
-;; Block
-
-(def keyboards
-  (->>
-   {"tab" (editor-handler/on-tab :right)
-    "shift+tab" (editor-handler/on-tab :left)
-    "ctrl+z" history-handler/undo!
-    "ctrl+y" history-handler/redo!
-    "ctrl+u" route-handler/go-to-search!
-    "alt+j" route-handler/go-to-journals!
-    (or (state/get-shortcut :editor/zoom-in)
-        (if util/mac? "alt+." "alt+right")) editor-handler/zoom-in!
-    (or (state/get-shortcut :editor/zoom-out)
-        (if util/mac? "alt+," "alt+left")) editor-handler/zoom-out!
-    "ctrl+enter" editor-handler/cycle-todo!
-    "ctrl+down" editor-handler/expand!
-    "ctrl+up" editor-handler/collapse!
-    "ctrl+o" editor-handler/follow-link-under-cursor!
-    "ctrl+shift+o" editor-handler/open-link-in-sidebar!
-    "ctrl+b" editor-handler/bold-format!
-    "ctrl+i" editor-handler/italics-format!
-    "ctrl+k" editor-handler/html-link-format!
-    "ctrl+h" editor-handler/highlight-format!
-    "ctrl+shift+a" editor-handler/select-all-blocks!
-    "alt+shift+up" (fn [state e] (editor-handler/move-up-down e true))
-    "alt+shift+down" (fn [state e] (editor-handler/move-up-down e false))}
-   (medley/map-keys util/->system-modifier)))
-
-(defn chord-aux
+(defn prevent-default-behavior
   [f]
   (fn [state e]
     (f state e)
@@ -86,20 +22,68 @@
     ;; and stop event from bubbling
     false))
 
+(defn enable-when-not-editing-mode!
+  [f]
+  (fn [state e]
+    (when-not (state/editing?)
+      (f state e))))
+
+(def shortcut state/get-shortcut)
+
 (defonce chords
-  {;; Toggle
-   "t d" state/toggle-document-mode!
-   "t t" state/toggle-theme!
-   "t r" ui-handler/toggle-right-sidebar!
-   "t e" state/toggle-new-block-shortcut!
-   "s" (chord-aux route-handler/toggle-between-page-and-file!)
-   "mod+s" (chord-aux editor-handler/save!)
-   "mod+c mod+s" (chord-aux search-handler/rebuild-indices!)
-   "mod+c mod+b" (chord-aux config-handler/toggle-ui-show-brackets!)})
+  {
+   ;; non-editing mode
+   (or (shortcut :editor/toggle-document-mode) "t d")
+   (enable-when-not-editing-mode! state/toggle-document-mode!)
+   (or (shortcut :ui/toggle-theme) "t t")
+   (enable-when-not-editing-mode! state/toggle-theme!)
+   (or (shortcut :ui/toggle-right-sidebar) "t r")
+   (enable-when-not-editing-mode! ui-handler/toggle-right-sidebar!)
+   (or (shortcut :ui/toggle-new-block) "t e")
+   (enable-when-not-editing-mode! state/toggle-new-block-shortcut!)
+   (or (shortcut :ui/toggle-between-page-and-file) "s")
+   (enable-when-not-editing-mode! route-handler/toggle-between-page-and-file!)
+   "tab" (-> (editor-handler/on-tab :right)
+             enable-when-not-editing-mode!)
+   "shift+tab" (-> (editor-handler/on-tab :left)
+                   enable-when-not-editing-mode!)
+
+   (or (shortcut :editor/undo) "mod+z") [history-handler/undo! true]
+   (or (shortcut :editor/redo) "mod+y") [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)
+       (if util/mac? "mod+." "alt+right")) [editor-handler/zoom-in! true]
+   (or (shortcut :editor/zoom-out)
+       (if util/mac? "mod+," "alt+left")) [editor-handler/zoom-out! true]
+   (or (shortcut :editor/cycle-todo)
+       "mod+enter") [editor-handler/cycle-todo! true]
+   (or (shortcut :editor/expand-block-children) "mod+down") [editor-handler/expand! true]
+   (or (shortcut :editor/collapse-block-children) "mod+up") [editor-handler/collapse! true]
+   (or (shortcut :editor/follow-link) "mod+o") [editor-handler/follow-link-under-cursor! true]
+   (or (shortcut :editor/open-link-in-sidebar) "mod+shift+o") [editor-handler/open-link-in-sidebar! true]
+   (or (shortcut :editor/bold) "mod+b") [editor-handler/bold-format! true]
+   (or (shortcut :editor/italics) "mod+i") [editor-handler/italics-format! true]
+   (or (shortcut :editor/highlight) "mod+h") [editor-handler/highlight-format! true]
+   (or (shortcut :editor/insert-link) "mod+k") [editor-handler/html-link-format! true]
+   (or (shortcut :editor/select-all-blocks) "mod+shift+a") [editor-handler/select-all-blocks! true]
+   (or (shortcut :editor/move-block-up) (if util/mac? "mod+shift+up" "alt+shift+up")) [(fn [state e] (editor-handler/move-up-down e true)) true]
+   (or (shortcut :editor/move-block-down) (if util/mac? "mod+shift+down" "alt+shift+down")) [(fn [state e] (editor-handler/move-up-down e false)) true]
+   (or (shortcut :editor/save) "mod+s") [editor-handler/save! true]
+
+   (or (shortcut :editor/next) "down") (fn [state e] (editor-handler/open-block! true))
+   (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 :ui/toggle-brackets) "mod+c mod+b") [config-handler/toggle-ui-show-brackets! true]})
 
 (defonce bind! (gobj/get mousetrap "bind"))
 
 (defn bind-shortcuts!
   []
   (doseq [[k f] chords]
-    (bind! k f)))
+    (let [[f prevent?] (if (coll? f)
+                         f
+                         [f false])
+          f (if prevent? (prevent-default-behavior f) f)]
+      (bind! k f))))

+ 0 - 27
src/main/frontend/mixins.cljs

@@ -187,33 +187,6 @@
             (f))
           (dissoc state k)))})))
 
-(defn keyboards-mixin
-  ([m] (keyboards-mixin m (fn [_] true) js/window))
-  ([m enable-f] (keyboards-mixin m enable-f js/window))
-  ([m enable-f target]
-   (when (seq m)
-     (let [target-fn (if (fn? target) target (fn [_] target))]
-       {:did-mount
-        (fn [state]
-          (if (enable-f state)
-            (let [keyboards (doall
-                             (map
-                              (fn [[key f]]
-                                [key
-                                 (keyboard/install-shortcut! key
-                                                             (fn [e] (f state e))
-                                                             false
-                                                             (target-fn state))])
-                              m))]
-              (assoc state ::keyboards-listener keyboards))
-            state))
-        :will-unmount
-        (fn [state]
-          (when (enable-f state)
-            (doseq [[_k f] (get state ::keyboards-listener)]
-              (f)))
-          state)}))))
-
 ;; also, from https://github.com/tonsky/rum/blob/75174b9ea0cf4b7a761d9293929bd40c95d35f74/doc/useful-mixins.md
 
 

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

@@ -12,25 +12,31 @@
             [cljs-bean.core :as bean]
             [goog.object :as gobj]
             ["fuzzysort" :as fuzzy]
-            [medley.core :as medley]))
+            ["flexsearch" :as flexsearch]
+            [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 opts]
-  (fuzzy-go q indice opts))
+  [q indice-type indice opts]
+  (case indice-type
+    :page
+    (fuzzy-go q indice opts)
+
+    :block
+    (.search indice q opts)))
 
 (defn block->index
   [{:block/keys [uuid content format] :as block}]
-  (when (<= (count content) 1000) ; performance
-    (when-let [result (->> (text/remove-level-spaces content format)
-                           (text/remove-properties!)
-                           (prepare))]
-      (gobj/set result "id" (:db/id block))
-      (gobj/set result "uuid" (str uuid))
-      result)))
+  (when-let [result (->> (text/remove-level-spaces content format)
+                         (text/remove-properties!))]
+    {:id (:db/id block)
+     :uuid (str uuid)
+     :content result}))
 
 (defn make-blocks-indice!
   []
@@ -38,9 +44,17 @@
     (let [blocks (->> (db/get-all-block-contents)
                       (map block->index)
                       (remove nil?)
-                      (bean/->js))]
-      (swap! indices assoc-in [repo :blocks] blocks)
-      blocks)))
+                      (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)))
 
 (defn make-pages-indice!
   []
@@ -136,10 +150,9 @@
                                      :score (score query (.toLowerCase s))})))))
          (map :data))))
 
-(defn search
-  "Block search"
+(defn block-search
   ([q]
-   (search q 10))
+   (block-search q 10))
   ([q limit]
    (when-let [repo (state/get-current-repo)]
      (when-not (string/blank? q)
@@ -147,21 +160,17 @@
              q (escape-str q)]
          (when-not (string/blank? q)
            (let [indice (or (get-in @indices [repo :blocks])
-                            (make-blocks-indice!))
-                 result (->
-                         (go q indice (clj->js {:limit limit
-                                                :allowTypo false
-                                                :threshold -10000}))
-                         (bean/->clj))]
-             (->>
-              (map
-               (fn [{:keys [target uuid indexes]}]
-                 {:block/uuid uuid
-                  :block/content target
-                  :block/page (:block/page (db/entity [:block/uuid (medley/uuid (str uuid))]))
-                  :block/indexes indexes}) ; For result highlight
-               result)
-              (remove nil?)))))))))
+                            (make-blocks-indice!))]
+             (p/let [result (go q :block 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))]))})
+                 result)
+                (remove nil?))))))))))
 
 (defn page-search
   ([q]
@@ -173,11 +182,12 @@
        (when-not (string/blank? q)
          (let [indice (or (get-in @indices [repo :pages])
                           (make-pages-indice!))
-               result (->> (go q indice (clj->js {:limit limit
-                                                  :key "name"
-                                                  :allowTypo false
-                                                  :threshold -10000}))
+               result (->> (go q :page indice (clj->js {:limit limit
+                                                        :key "name"
+                                                        :allowTypo false
+                                                        :threshold -10000}))
                            (bean/->clj))]
+           ;; TODO: add indexes for highlights
            (->> (map
                  (fn [{:keys [obj]}]
                    (:name obj))
@@ -250,8 +260,10 @@
                                           (map :e)
                                           (set))]
             (swap! search-db/indices update-in [repo :blocks]
-                   (fn [blocks]
-                     (let [blocks (or blocks (array))
-                           blocks (.filter blocks (fn [block]
-                                                    (not (contains? blocks-to-remove-set (gobj/get block "id")))))]
-                       (.concat blocks (bean/->js blocks-to-add)))))))))))
+                   (fn [indice]
+                     (when indice
+                       (doseq [block-id blocks-to-remove-set]
+                         (.remove indice #js {:id block-id}))
+                       (when (seq blocks-to-add)
+                         (.add indice (bean/->js blocks-to-add))))
+                     indice))))))))

+ 23 - 0
src/main/frontend/state.cljs

@@ -71,6 +71,8 @@
     :editor/show-input nil
     :editor/last-saved-cursor nil
     :editor/editing? nil
+    :editor/last-edit-block-id nil
+    :editor/in-composition? false
     :editor/pos 0
     :editor/content {}
     :editor/block nil
@@ -370,6 +372,10 @@
   []
   (ffirst (:editor/editing? @state)))
 
+(defn get-last-edit-input-id
+  []
+  (:editor/last-edit-block-id @state))
+
 (defn editing?
   []
   (some? (get-edit-input-id)))
@@ -666,6 +672,7 @@
                    (assoc
                     :editor/block block
                     :editor/editing? {edit-input-id true}
+                    :editor/last-edit-block-id edit-input-id
                     :cursor-range cursor-range)))))))
 
 (defn clear-edit!
@@ -981,6 +988,14 @@
   [value]
   (set-state! :graph/syncing? value))
 
+(defn set-editor-in-composition!
+  [value]
+  (set-state! :editor/in-composition? value))
+
+(defn editor-in-composition?
+  []
+  (:editor/in-composition? @state))
+
 (defn set-loading-files!
   [value]
   (set-state! :repo/loading-files? value))
@@ -1066,6 +1081,14 @@
   []
   (:nfs/refreshing? @state))
 
+(defn set-search-result!
+  [value]
+  (set-state! :search/result value))
+
+(defn clear-search-result!
+  []
+  (set-search-result! nil))
+
 ;; TODO: Move those to the uni `state`
 
 (defonce editor-op (atom nil))

+ 5 - 5
src/main/frontend/ui.cljs

@@ -28,16 +28,16 @@
                            (state/sub :editor/show-page-search?)
                            (state/sub :editor/show-block-search?)
                            (state/sub :editor/show-template-search?))
-        composition? (atom false)
-        set-composition? #(reset! composition? %)
         on-composition (fn [e]
                          (if skip-composition?
                            (on-change e)
                            (case e.type
-                             "compositionend" (do (set-composition? false) (on-change e))
-                             (set-composition? true))))
+                             "compositionend" (do
+                                                (state/set-editor-in-composition! false)
+                                                (on-change e))
+                             (state/set-editor-in-composition! true))))
         props (assoc props
-                     :on-change (fn [e] (when-not @composition?
+                     :on-change (fn [e] (when-not (state/editor-in-composition?)
                                           (on-change e)))
                      :on-composition-start on-composition
                      :on-composition-update on-composition

+ 12 - 12
src/main/frontend/util.cljc

@@ -8,6 +8,7 @@
       #?(:cljs ["/frontend/caret_pos" :as caret-pos])
       #?(:cljs ["/frontend/selection" :as selection])
       #?(:cljs ["/frontend/utils" :as utils])
+      #?(:cljs ["path" :as nodePath])
       #?(:cljs [goog.dom :as gdom])
       #?(:cljs [goog.object :as gobj])
       #?(:cljs [goog.string :as gstring])
@@ -29,7 +30,7 @@
       (-pr-writer [sym writer _]
         (-write writer (str "\"" (.toString sym) "\"")))))
 
-;; doms
+#?(:cljs (defonce ^js node-path nodePath))
 #?(:cljs (defn app-scroll-container-node []  js/document.documentElement))
 
 #?(:cljs
@@ -399,20 +400,20 @@
                           :behavior "smooth"}))))))
 
 #?(:cljs
-    (defn scroll-to
-      ([pos]
-       (scroll-to pos true))
-      ([pos animate?]
-       (scroll-to (app-scroll-container-node) pos animate?))
-      ([node pos animate?]
-       (.scroll node
-                #js {:top      pos
-                     :behavior (if animate? "smooth" "auto")}))))
+   (defn scroll-to
+     ([pos]
+      (scroll-to (app-scroll-container-node) pos))
+     ([node pos]
+      (scroll-to node pos true))
+     ([node pos animate?]
+      (.scroll node
+               #js {:top      pos
+                    :behavior (if animate? "smooth" "auto")}))))
 
 #?(:cljs
     (defn scroll-to-top
       []
-      (scroll-to 0 false)))
+      (scroll-to (app-scroll-container-node) 0 false)))
 
 (defn url-encode
   [string]
@@ -1014,7 +1015,6 @@
 (defn page-name-sanity
   [page-name]
   (-> page-name
-      (string/replace #"\s+" "_")
       ;; Windows reserved path characters
       (string/replace #"[\\/:\\*\\?\"<>|]+" "_")))
 

+ 11 - 1
src/main/frontend/utils.js

@@ -203,4 +203,14 @@ 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.7")
+(defonce version "0.0.8")

+ 22 - 7
yarn.lock

@@ -1101,6 +1101,13 @@ chokidar@^3.3.0, chokidar@^3.3.1, chokidar@^3.5.1:
   optionalDependencies:
     fsevents "~2.3.1"
 
+chrono-node@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/chrono-node/-/chrono-node-2.2.1.tgz#6591c95724011fd7cadd8ae394bacb5004357bf2"
+  integrity sha512-Cdw4LxAlVb3BbfoAIw50v8MhVmFGBzzvTpQUO3F5ezyo/MnZ3db3G73cHNE2aiNsAhK9qpg39RpUl6jxTIUGeA==
+  dependencies:
+    dayjs "^1.10.0"
+
 cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de"
@@ -1605,6 +1612,11 @@ d@1, d@^1.0.1:
     es5-ext "^0.10.50"
     type "^1.0.1"
 
+dayjs@^1.10.0:
+  version "1.10.4"
+  resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.4.tgz#8e544a9b8683f61783f570980a8a80eaf54ab1e2"
+  integrity sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw==
+
 debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
   version "2.6.9"
   resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -2298,6 +2310,10 @@ 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"
@@ -2380,10 +2396,9 @@ function-bind@^1.1.1:
   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
   integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
 
-fuzzysort@^1.1.4:
+"fuzzysort@git+https://github.com/getstation/fuzzysort#a66f5813825d2415b606cc69129070c4eb612ae2":
   version "1.1.4"
-  resolved "https://registry.yarnpkg.com/fuzzysort/-/fuzzysort-1.1.4.tgz#a0510206ed44532cbb52cf797bf5a3cb12acd4ba"
-  integrity sha512-JzK/lHjVZ6joAg3OnCjylwYXYVjRiwTY6Yb25LvfpJHK8bjisfnZJ5bY8aVWwTwCXgxPNgLAtmHL+Hs5q1ddLQ==
+  resolved "git+https://github.com/getstation/fuzzysort#a66f5813825d2415b606cc69129070c4eb612ae2"
 
 gensync@^1.0.0-beta.1:
   version "1.0.0-beta.2"
@@ -3894,10 +3909,10 @@ mkdirp@^0.5.4, mkdirp@~0.5.1:
   dependencies:
     minimist "^1.2.5"
 
-mldoc@^0.3.8:
-  version "0.3.8"
-  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-0.3.8.tgz#1e4500ab50f1bb7a6cb292a80d1e9ab92c4d48cc"
-  integrity sha512-b2qieoYJ71E+c4TVOIcylh87COl6Rxt6du4o3mB1yL0qDSfK1OW6XICGhYVrJi8MO5ioscf3Y4aRyWGYmwCBlQ==
+mldoc@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-0.4.0.tgz#3e372ec2a40856084d9051882fef609546b48174"
+  integrity sha512-FLb09RfqWltA3XEhUzLfxZFjGtbnfxhCCtSoB+AQo37WnpuDvPldGgmw8VtRuv05vvNh89q/onQ+8bYllWI8bQ==
   dependencies:
     yargs "^12.0.2"