浏览代码

Merge branch 'master' into feat/pdf

Tienson Qin 4 年之前
父节点
当前提交
da90255ab8
共有 43 个文件被更改,包括 552 次插入305 次删除
  1. 0 2
      README.md
  2. 3 2
      package.json
  3. 1 1
      shadow-cljs.edn
  4. 7 3
      src/electron/electron/core.cljs
  5. 4 1
      src/main/frontend/commands.cljs
  6. 103 46
      src/main/frontend/components/block.cljs
  7. 6 6
      src/main/frontend/components/content.cljs
  8. 48 20
      src/main/frontend/components/export.cljs
  9. 2 2
      src/main/frontend/components/header.cljs
  10. 1 1
      src/main/frontend/components/page.cljs
  11. 25 13
      src/main/frontend/components/query_table.cljs
  12. 11 5
      src/main/frontend/components/repo.cljs
  13. 4 1
      src/main/frontend/components/right_sidebar.cljs
  14. 16 8
      src/main/frontend/components/search.cljs
  15. 9 9
      src/main/frontend/components/settings.cljs
  16. 3 2
      src/main/frontend/components/widgets.cljs
  17. 14 12
      src/main/frontend/date.cljs
  18. 1 1
      src/main/frontend/db.cljs
  19. 19 0
      src/main/frontend/db/debug.cljs
  20. 22 26
      src/main/frontend/db/model.cljs
  21. 0 1
      src/main/frontend/db/query_react.cljs
  22. 2 3
      src/main/frontend/extensions/code.cljs
  23. 9 10
      src/main/frontend/extensions/zotero/extractor.cljs
  24. 14 2
      src/main/frontend/extensions/zotero/handler.cljs
  25. 2 2
      src/main/frontend/extensions/zotero/setting.cljs
  26. 10 5
      src/main/frontend/format/block.cljs
  27. 15 2
      src/main/frontend/format/mldoc.cljs
  28. 1 1
      src/main/frontend/handler.cljs
  29. 14 3
      src/main/frontend/handler/editor.cljs
  30. 7 0
      src/main/frontend/handler/events.cljs
  31. 2 2
      src/main/frontend/handler/export.cljs
  32. 20 11
      src/main/frontend/handler/extract.cljs
  33. 51 32
      src/main/frontend/handler/page.cljs
  34. 12 26
      src/main/frontend/handler/repo.cljs
  35. 1 1
      src/main/frontend/handler/web/nfs.cljs
  36. 20 18
      src/main/frontend/modules/outliner/core.cljs
  37. 4 1
      src/main/frontend/modules/shortcut/config.cljs
  38. 10 0
      src/main/frontend/state.cljs
  39. 25 9
      src/main/frontend/ui.cljs
  40. 4 0
      src/main/frontend/util.cljc
  41. 7 3
      src/main/frontend/util/property.cljs
  42. 10 2
      src/test/frontend/util/property_test.cljs
  43. 13 10
      yarn.lock

+ 0 - 2
README.md

@@ -22,8 +22,6 @@ Use it to organize your todo list, to write your journals, or to record your uni
 
 [Logseq](https://logseq.com) is a platform for knowledge management and collaboration. It focuses on privacy, longevity, and [user control](https://www.gnu.org/philosophy/free-sw.en.html).
 
-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).
 
 In the unlikely event that the website is down or cannot be maintained, your data is, and will always be yours.

+ 3 - 2
package.json

@@ -82,9 +82,9 @@
         "ignore": "^5.1.8",
         "is-svg": "4.2.2",
         "jszip": "^3.5.0",
-        "mldoc": "0.9.2",
+        "mldoc": "0.9.4",
         "path": "^0.12.7",
-        "pixi-graph-fork": "^0.1.3",
+        "pixi-graph-fork": "^0.1.4",
         "posthog-js": "^1.10.2",
         "react": "^17.0.2",
         "react-dom": "^17.0.2",
@@ -94,6 +94,7 @@
         "react-textarea-autosize": "^8.0.1",
         "react-tippy": "^1.4.0",
         "react-transition-group": "^4.3.0",
+        "react-tweet-embed": "^1.2.2",
         "reakit": "^0.11.1",
         "yargs-parser": "^20.2.4"
     }

+ 1 - 1
shadow-cljs.edn

@@ -87,7 +87,7 @@
               :depends-on #{:main}}}
 
    :output-dir "./static/js/publishing"
-   :asset-path "/static/js"
+   :asset-path "static/js"
 
    :closure-defines {frontend.config/PUBLISHING true
                      goog.debug.LOGGING_ENABLED true}

+ 7 - 3
src/electron/electron/core.cljs

@@ -87,9 +87,13 @@
                             (. fs copy (path/join app-path "404.html") (path/join root-dir "404.html"))]
 
                            (map
-                            (fn [filename] (. fs copy (path/join assets-from-dir filename) (path/join assets-to-dir filename)))
-
-                            asset-filenames)
+                             (fn [filename]
+                               (-> (. fs copy (path/join assets-from-dir filename) (path/join assets-to-dir filename))
+                                   (p/catch
+                                       (fn [e]
+                                         (println (str "Failed to copy " (path/join assets-from-dir filename) " to " (path/join assets-to-dir filename)))
+                                         (js/console.error e)))))
+                             asset-filenames)
 
                            (map
                             (fn [part]

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

@@ -273,7 +273,10 @@
                                                             :backward-pos 2}]]]
 
      ["Embed Vimeo Video" [[:editor/input "{{vimeo }}" {:last-pattern slash
-                                                        :backward-pos 2}]]]]
+                                                        :backward-pos 2}]]]
+
+     ["Embed Twitter" [[:editor/input "{{tweet }}" {:last-pattern slash
+                                                    :backward-pos 2}]]]]
 
     @*extend-slash-commands
     ;; Allow user to modify or extend, should specify how to extend.

+ 103 - 46
src/main/frontend/components/block.cljs

@@ -339,28 +339,27 @@
     [:a
      {:class (if tag? "tag" "page-ref")
       :data-ref page-name
-      :href href
-      :on-click (fn [e]
-                  (util/stop e)
-                  (let [create-first-block! (fn []
-                                              (when-not (editor-handler/add-default-title-property-if-needed! redirect-page-name)
-                                                (editor-handler/insert-first-page-block-if-not-exists! redirect-page-name)))]
-                    (if (gobj/get e "shiftKey")
-                      (do
-                        (js/setTimeout create-first-block! 310)
-                        (when-let [page-entity (db/entity [:block/name redirect-page-name])]
-                          (state/sidebar-add-block!
-                           (state/get-current-repo)
-                           (:db/id page-entity)
-                           :page
-                           {:page page-entity})))
-                      (do
-                        (create-first-block!)
-                        (route-handler/redirect! {:to :page
-                                                  :path-params {:name redirect-page-name}}))))
-                  (when (and contents-page?
-                             (state/get-left-sidebar-open?))
-                    (ui-handler/close-left-sidebar!)))}
+      :on-mouse-down
+      (fn [e]
+        (util/stop e)
+        (let [create-first-block! (fn []
+                                    (when-not (editor-handler/add-default-title-property-if-needed! redirect-page-name)
+                                      (editor-handler/insert-first-page-block-if-not-exists! redirect-page-name)))]
+          (if (gobj/get e "shiftKey")
+            (do
+              (js/setTimeout create-first-block! 310)
+              (when-let [page-entity (db/entity [:block/name redirect-page-name])]
+                (state/sidebar-add-block!
+                 (state/get-current-repo)
+                 (:db/id page-entity)
+                 :page
+                 {:page page-entity})))
+            (do
+              (create-first-block!)
+              (route-handler/redirect! {:to :page
+                                        :path-params {:name redirect-page-name}}))))
+        (when (and contents-page? (state/get-left-sidebar-open?))
+          (ui-handler/close-left-sidebar!)))}
 
      (if (and (coll? children) (seq children))
        (for [child children]
@@ -1095,6 +1094,16 @@
                     :width width
                     :height (max 500 height)}])))))
 
+
+        (contains? #{"tweet" "twitter"} name)
+        (when-let [url (first arguments)]
+          (let [id-regex #"/status/(\d+)"]
+            (when-let [id (cond
+                            (<= (count url) 15) url
+                            :else
+                            (last (util/safe-re-find id-regex url)))]
+              (ui/tweet-embed id))))
+
         (= name "embed")
         (let [a (first arguments)]
           (cond
@@ -1435,7 +1444,7 @@
 (declare block-content)
 
 (defn build-block-title
-  [{:keys [slide?] :as config} {:block/keys [uuid title tags marker priority anchor meta format content pre-block? block-refs-count page properties unordered level heading-level]
+  [{:keys [slide?] :as config} {:block/keys [uuid title tags marker priority anchor meta format content pre-block? page properties unordered level heading-level]
                                 :as t}]
   (let [config (assoc config :block t)
         slide? (boolean (:slide? config))
@@ -1722,7 +1731,7 @@
                   (str uuid "-" idx)))))])]]]))
 
 (rum/defc block-content-or-editor < rum/reactive
-  [config {:block/keys [uuid title body meta content page format repo children marker properties block-refs-count pre-block? idx] :as block} edit-input-id block-id slide? heading-level]
+  [config {:block/keys [uuid title body meta content page format repo children marker properties pre-block? idx] :as block} edit-input-id block-id slide? heading-level]
   (let [editor-box (get config :editor-box)
         edit? (state/sub [:editor/editing? edit-input-id])
         editor-id (str "editor-" edit-input-id)
@@ -1768,18 +1777,19 @@
                [:a.opacity-30.hover:opacity-100
                 (utils/timeConversion (- finish-time start-time))]])))
 
-        (when (and block-refs-count (> block-refs-count 0))
-          [:div
-           [:a.open-block-ref-link.bg-base-2.text-sm.ml-2
-            {:title "Open block references"
-             :style {:margin-top -1}
-             :on-click (fn []
-                         (state/sidebar-add-block!
-                          (state/get-current-repo)
-                          (:db/id block)
-                          :block-ref
-                          {:block block}))}
-            block-refs-count]])]])))
+        (let [block-refs-count (count (:block/_refs (db/entity (:db/id block))))]
+          (when (and block-refs-count (> block-refs-count 0))
+           [:div
+            [:a.open-block-ref-link.bg-base-2.text-sm.ml-2
+             {:title "Open block references"
+              :style {:margin-top -1}
+              :on-click (fn []
+                          (state/sidebar-add-block!
+                           (state/get-current-repo)
+                           (:db/id block)
+                           :block-ref
+                           {:block block}))}
+             block-refs-count]]))]])))
 
 (defn non-dragging?
   [e]
@@ -1789,8 +1799,24 @@
        (not @*dragging?)))
 
 (rum/defc breadcrumb-fragment
-  [href label]
-  [:a {:href href} label])
+  [config block href label]
+  (if (= block :page)                   ; page
+    (when label
+      (let [page (db/entity [:block/name (string/lower-case label)])]
+       (page-cp config page)))
+    [:a {:on-mouse-down
+         (fn [e]
+           (if (gobj/get e "shiftKey")
+             (do
+               (util/stop e)
+               (state/sidebar-add-block!
+                (state/get-current-repo)
+                (:db/id block)
+                :block-ref
+                {:block block}))
+             (route-handler/redirect! {:to :page
+                                       :path-params {:name (str (:block/uuid block))}})))}
+     label]))
 
 (rum/defc breadcrumb-separator [] [:span.mx-2.opacity-50 "➤"])
 
@@ -1805,17 +1831,19 @@
          show? (or (seq parents) show-page? page-name)]
      (when show?
        (let [page-name-props (when show-page?
-                               [(rfe/href :page {:name page-name})
+                               [:page
+                                (rfe/href :page {:name page-name})
                                 (or page-original-name page-name)])
              parents-props (doall
-                            (for [{:block/keys [uuid title name]} parents]
+                            (for [{:block/keys [uuid title name] :as block} parents]
                               (when-not name ; not page
-                                [(rfe/href :page {:name uuid})
+                                [block
+                                 (rfe/href :page {:name uuid})
                                  (->elem :span (map-inline config title))])))
              breadcrumb (->> (into [] parents-props)
                              (concat [page-name-props])
                              (filterv identity)
-                             (map (fn [[href label]] (breadcrumb-fragment href label)))
+                             (map (fn [[block href label]] (breadcrumb-fragment config block href label)))
                              (interpose (breadcrumb-separator)))]
          [:div.block-parents.flex-row.flex-1 breadcrumb])))))
 
@@ -1983,7 +2011,7 @@
 
      (when (and ref? breadcrumb-show?)
        (when-let [comp (block-parents config repo uuid format false)]
-         [:div.my-2.opacity-50.ml-4 comp]))
+         [:div.my-2.opacity-70.ml-4.hover:opacity-100 comp]))
 
      ;; only render this for the first block in each container
      (when top?
@@ -2180,7 +2208,7 @@
                      (state/remove-custom-query-component! query)
                      (db/remove-custom-query! (state/get-current-repo) query))
                    state)}
-  [state config {:keys [title query inputs view collapsed? children?] :as q}]
+  [state config {:keys [title query inputs view collapsed? children? breadcrumb-show?] :as q}]
   (ui/catch-error
    [:div.warning
     [:p "Query failed: "]
@@ -2264,7 +2292,9 @@
               (and (seq result) (or only-blocks? blocks-grouped-by-page?))
               (->hiccup result (cond-> (assoc config
                                               :custom-query? true
-                                              :breadcrumb-show? true
+                                              :breadcrumb-show? (if (some? breadcrumb-show?)
+                                                                  breadcrumb-show?
+                                                                  true)
                                               :group-by-page? blocks-grouped-by-page?
                                               :ref? true)
                                  children?
@@ -2599,7 +2629,32 @@
   [:div.content
    (cond-> option
      (:document/mode? config) (assoc :class "doc-mode"))
-   (if (and (:group-by-page? config)
+   (cond
+     (:custom-query? config)
+     [:div.flex.flex-col
+      (let [blocks (sort-by (comp :block/journal-day first) > blocks)]
+        (for [[page blocks] blocks]
+          (let [alias? (:block/alias? page)
+                page (db/entity (:db/id page))
+                parent-blocks (group-by :block/parent blocks)]
+            [: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"])]
+              (for [[parent blocks] parent-blocks]
+                (let [block (first blocks)]
+                  [:div
+                   (when (:breadcrumb-show? config)
+                     [:div.my-2.opacity-70.ml-4.hover:opacity-100
+                      (block-parents config (state/get-current-repo) (:block/uuid block)
+                                     (:block/format block)
+                                     false)])
+                   (blocks-container blocks (assoc config :breadcrumb-show? false))])))])))]
+
+     (and (:group-by-page? config)
             (vector? (first blocks)))
      [:div.flex.flex-col
       (let [blocks (sort-by (comp :block/journal-day first) > blocks)]
@@ -2614,4 +2669,6 @@
                (page-cp config page)
                (when alias? [:span.text-sm.font-medium.opacity-50 " Alias"])]
               (blocks-container blocks config))])))]
+
+     :else
      (blocks-container blocks config))])

+ 6 - 6
src/main/frontend/components/content.cljs

@@ -176,10 +176,10 @@
           (block-template block-id)
 
           (ui/menu-link
-           {:key "Export"
+           {:key "Copy as"
             :on-click (fn [_]
                         (state/set-modal! #(export/export-blocks block-id)))}
-           "Export")
+           "Copy as")
 
           (if (srs/card-block? block)
             (ui/menu-link
@@ -246,25 +246,25 @@
                       (let [target (gobj/get e "target")
                             block-id (d/attr target "blockid")]
                         (cond
-                          (and block-id (util/uuid-string? block-id))
+                          (state/selection?)
                           (do
                             (util/stop e)
                             (let [client-x (gobj/get e "clientX")
                                   client-y (gobj/get e "clientY")
                                   scroll-y (util/cur-doc-top)]
-                              (state/show-custom-context-menu! (block-context-menu-content target (cljs.core/uuid block-id)))
+                              (state/show-custom-context-menu! (custom-context-menu-content))
                               (when-let [context-menu (d/by-id "custom-context-menu")]
                                 (d/set-style! context-menu
                                               :left (str client-x "px")
                                               :top (str (+ scroll-y client-y) "px")))))
 
-                          (state/selection?)
+                          (and block-id (util/uuid-string? block-id))
                           (do
                             (util/stop e)
                             (let [client-x (gobj/get e "clientX")
                                   client-y (gobj/get e "clientY")
                                   scroll-y (util/cur-doc-top)]
-                              (state/show-custom-context-menu! (custom-context-menu-content))
+                              (state/show-custom-context-menu! (block-context-menu-content target (cljs.core/uuid block-id)))
                               (when-let [context-menu (d/by-id "custom-context-menu")]
                                 (d/set-style! context-menu
                                               :left (str client-x "px")

+ 48 - 20
src/main/frontend/components/export.cljs

@@ -70,22 +70,21 @@
                                 {:label "no-indent"
                                  :selected false}])
 
-(def *export-block-text-indent-style (atom "dashes"))
-
 (rum/defcs export-blocks
   < rum/reactive
   (rum/local false ::copied?)
   [state root-block-id]
   (let [current-repo (state/get-current-repo)
         type (rum/react *export-block-type)
-        text-indent-style (rum/react *export-block-text-indent-style)
+        text-indent-style (rum/react (state/get-export-block-text-indent-style))
+        text-remove-options (rum/react (state/get-export-block-text-remove-options))
         copied? (::copied? state)
         content
         (case type
-          :text (export/export-blocks-as-markdown current-repo root-block-id text-indent-style)
+          :text (export/export-blocks-as-markdown current-repo root-block-id text-indent-style (into [] text-remove-options))
           :opml (export/export-blocks-as-opml current-repo root-block-id)
           :html (export/export-blocks-as-html current-repo root-block-id)
-          (export/export-blocks-as-markdown current-repo root-block-id text-indent-style))]
+          (export/export-blocks-as-markdown current-repo root-block-id text-indent-style (into [] text-remove-options)))]
     [:div.export.w-96.resize
      [:div
       {:class "mb-2"}
@@ -104,21 +103,50 @@
                                 (if (= text-indent-style (:label opt))
                                   (assoc opt :selected true)
                                   opt))))]
-       [:div.flex.items-center
-        [:label.mr-8 "Indentation style:"]
-        [:select.block.my-2.text-lg.rounded.border
-         {:style     {:padding "0 0 0 12px"
-                      :visibility (if (= :text type) "visible" "hidden")}
-          :on-change (fn [e]
-                       (let [value (util/evalue e)]
-                         (#(reset! *export-block-text-indent-style %) value)))}
-         (for [{:keys [label value selected]} options]
-           [:option (cond->
-                     {:key   label
-                      :value (or value label)}
-                      selected
-                      (assoc :selected selected))
-            label])]])
+       [:div [:div.flex.items-center
+              [:label.mr-8
+               {:style {:visibility (if (= :text type) "visible" "hidden")}}
+               "Indentation style:"]
+              [:select.block.my-2.text-lg.rounded.border
+               {:style     {:padding "0 0 0 12px"
+                            :visibility (if (= :text type) "visible" "hidden")}
+                :on-change (fn [e]
+                             (let [value (util/evalue e)]
+                               (reset! (state/get-export-block-text-indent-style) value)))}
+               (for [{:keys [label value selected]} options]
+                 [:option (cond->
+                           {:key   label
+                            :value (or value label)}
+                            selected
+                            (assoc :selected selected))
+                  label])]]
+        [:div.flex.items-center
+         (ui/checkbox {:style {:margin-right 6
+                               :visibility (if (= :text type) "visible" "hidden")}
+                       :checked (contains? text-remove-options :page-ref)
+                       :on-change (fn [e] (if (util/echecked? e)
+                                            (swap! (state/get-export-block-text-remove-options)
+                                                   #(conj % :page-ref))
+                                            (swap! (state/get-export-block-text-remove-options)
+                                                   #(disj % :page-ref))))})
+
+         [:div
+          {:style {:visibility (if (= :text type) "visible" "hidden")}}
+          "[[text]] -> text"]
+         (ui/checkbox {:style {:margin-right 6
+                               :margin-left 10
+                               :visibility (if (= :text type) "visible" "hidden")}
+                       :checked (contains? text-remove-options :emphasis)
+                       :on-change (fn [e] (if (util/echecked? e)
+                                            (swap! (state/get-export-block-text-remove-options)
+                                                   #(conj % :emphasis))
+                                            (swap! (state/get-export-block-text-remove-options)
+                                                   #(disj % :emphasis))))})
+
+         [:div
+          {:style {:visibility (if (= :text type) "visible" "hidden")}}
+          "remove emphasis"]]])
+
      (ui/button (if @copied? "Copied to clipboard!" "Copy to clipboard")
                 :on-click (fn []
                             (util/copy-to-clipboard! content)

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

@@ -18,6 +18,7 @@
             [frontend.components.export :as export]
             [frontend.components.plugins :as plugins]
             [frontend.components.right-sidebar :as sidebar]
+            [frontend.modules.shortcut.core :as shortcut]
             [frontend.handler.page :as page-handler]
             [frontend.handler.web.nfs :as nfs]
             [frontend.mixins :as mixins]
@@ -212,8 +213,7 @@
        (when (and (nfs/supported?) (empty? repos)
                   (not config/publishing?))
          [:a.text-sm.font-medium.button
-          {:on-click (fn []
-                       (page-handler/ls-dir-files!))}
+          {:on-click #(page-handler/ls-dir-files! shortcut/refresh!)}
           [:div.flex.flex-row.text-center.open-button__inner.items-center
            [:span.inline-block.open-button__icon-wrapper svg/folder-add]
            (when-not config/mobile?

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

@@ -407,7 +407,7 @@
                (let [config {:id "block-parent"
                              :block? true}]
                  [:div.mb-4
-                  (block/block-parents config repo block-id format)]))
+                  (block/block-parents config repo block-id format true)]))
 
              ;; blocks
              (let [page (if block?

+ 25 - 13
src/main/frontend/components/query_table.cljs

@@ -8,23 +8,25 @@
             [frontend.state :as state]
             [clojure.string :as string]
             [frontend.components.svg :as svg]
-            [frontend.handler.common :as common-handler]))
+            [frontend.handler.common :as common-handler]
+            [frontend.handler.editor :as editor-handler]))
 
 ;; TODO: extract to table utils
 (defn- sort-result-by
   [by-item desc? result]
-  (def result result)
-  (def by-item by-item)
-  (def desc? desc?)
   (let [comp (if desc? > <)]
     (sort-by by-item comp result)))
 
 (rum/defc sortable-title
-  [title key by-item desc?]
+  [title key by-item desc? block-id]
   [:th.whitespace-nowrap
    [:a {:on-click (fn []
                     (reset! by-item key)
-                    (swap! desc? not))}
+                    (swap! desc? not)
+                    (when block-id
+                      (when key
+                        (editor-handler/set-block-property! block-id :query-sort-by (name key)))
+                      (editor-handler/set-block-property! block-id :query-sort-desc @desc?)))}
     [:div.flex.items-center
      [:span.mr-1 title]
      (when (= @by-item key)
@@ -37,18 +39,28 @@
                   (remove (property/built-in-properties))
                   (remove #{:template}))
         keys (if page? (cons :page keys) (cons :block keys))
-        keys (concat keys [:created-at :updated-at])]
+        keys (if page? (concat keys [:created-at :updated-at]) keys)]
     keys))
 
 (rum/defcs result-table < rum/reactive
-  (rum/local :updated-at ::sort-by-item)
-  (rum/local true ::desc?)
+  (rum/local nil ::sort-by-item)
+  (rum/local nil ::desc?)
   (rum/local false ::select?)
   [state config current-block result {:keys [page?]} map-inline page-cp ->elem inline-text]
   (when current-block
-    (let [select? (get state ::select?)
+    (let [p-sort-by (keyword (get-in current-block [:block/properties :query-sort-by]))
+          p-desc? (get-in current-block [:block/properties :query-sort-desc])
+          select? (get state ::select?)
           *sort-by-item (get state ::sort-by-item)
           *desc? (get state ::desc?)
+          sort-by-item (or @*sort-by-item (some-> p-sort-by keyword) :updated-at)
+          desc? (cond
+                  (some? @*desc?)
+                  @*desc?
+                  (some? p-desc?)
+                  p-desc?
+                  :else
+                  true)
           editor-box (get config :editor-box)
           ;; remove templates
           result (remove (fn [b] (some? (get-in b [:block/properties :template]))) result)
@@ -63,7 +75,7 @@
                   (filter #{:created-at :updated-at} keys))
                  keys)
           sort-by-fn (fn [item]
-                       (let [key @*sort-by-item]
+                       (let [key sort-by-item]
                          (case key
                            :created-at
                            (:block/created-at item)
@@ -74,12 +86,12 @@
                            :page
                            (:block/name item)
                            (get-in item [:block/properties key]))))
-          result (sort-result-by sort-by-fn @*desc? result)]
+          result (sort-result-by sort-by-fn desc? result)]
       [:div.overflow-x-auto {:on-mouse-down (fn [e] (.stopPropagation e))
                              :style {:width "100%"}}
        [:table.table-auto
         (for [key keys]
-          (sortable-title (name key) key *sort-by-item *desc?))
+          (sortable-title (name key) key *sort-by-item *desc? (:block/uuid current-block)))
         (for [item result]
           (let [format (:block/format item)
                 edit-input-id (str "edit-block-" (:id config) "-" (:block/uuid item))

+ 11 - 5
src/main/frontend/components/repo.cljs

@@ -6,6 +6,7 @@
             [frontend.db :as db]
             [frontend.encrypt :as e]
             [frontend.handler.repo :as repo-handler]
+            [frontend.handler.page :as page-handler]
             [frontend.handler.common :as common-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.export :as export-handler]
@@ -21,7 +22,8 @@
             [frontend.components.encryption :as encryption]
             [frontend.context.i18n :as i18n]
             [clojure.string :as string]
-            [clojure.string :as str]))
+            [clojure.string :as str]
+            [frontend.modules.shortcut.core :as shortcut]))
 
 (rum/defc add-repo
   [args]
@@ -52,7 +54,7 @@
              [:div.mr-8
               (ui/button
                 (t :open-a-directory)
-                :on-click page-handler/ls-dir-files!)])
+                :on-click #(page-handler/ls-dir-files! shortcut/refresh!))])
            (when (and (state/logged?) (not (util/electron?)))
              (ui/button
                "Add another git repo"
@@ -77,7 +79,9 @@
                                            "Sync with the local directory"
                                            "Clone again and re-index the db")
                                   :on-click (fn []
-                                              (repo-handler/re-index! nfs-handler/rebuild-index!))}
+                                              (repo-handler/re-index!
+                                               nfs-handler/rebuild-index!
+                                               page-handler/create-today-journal!))}
                  "Re-index"]
                 [:a.text-gray-400.ml-4 {:title "No worries, unlink this graph will clear its cache only, it does not remove your files on the disk."
                                         :on-click (fn []
@@ -86,7 +90,7 @@
         (widgets/add-graph)))))
 
 (defn refresh-cb []
-  (repo-handler/create-today-journal!)
+  (page-handler/create-today-journal!)
   (shortcut/refresh!))
 
 (rum/defc sync-status < rum/reactive
@@ -240,7 +244,9 @@
                               (t :all-graphs)]
                              [:a {:class "block px-4 py-2 text-sm transition ease-in-out duration-150 cursor menu-link"
                                   :on-click (fn []
-                                              (repo-handler/re-index! nfs-handler/rebuild-index!))}
+                                              (repo-handler/re-index!
+                                               nfs-handler/rebuild-index!
+                                               page-handler/create-today-journal!))}
                               (t :re-index)]]}
              (seq switch-repos)
              (assoc :links-header [:div.font-medium.text-sm.opacity-70.px-4.py-2

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

@@ -112,7 +112,10 @@
 
     :page
     (let [page-name (or (:block/name block-data)
-                        db-id)]
+                        db-id)
+          page-name (if (integer? db-id)
+                      (:block/name (db/entity db-id))
+                      page-name)]
       [[:a.page-title {:href     (rfe/href :page {:name page-name})
             :on-click (fn [e]
                         (when (gobj/get e "shiftKey")

+ 16 - 8
src/main/frontend/components/search.cljs

@@ -6,6 +6,7 @@
             [frontend.handler.route :as route]
             [frontend.handler.page :as page-handler]
             [frontend.db :as db]
+            [frontend.db.model :as model]
             [frontend.handler.search :as search-handler]
             [frontend.ui :as ui]
             [frontend.state :as state]
@@ -140,7 +141,9 @@
 (rum/defc search-auto-complete
   [{:keys [pages files blocks has-more?] :as result} search-q all?]
   (rum/with-context [[t] i18n/*tongue-context*]
-    (let [pages (when-not all? (map (fn [page] {:type :page :data page}) pages))
+    (let [pages (when-not all? (map (fn [page] {:type :page
+                                               :data page
+                                               :alias (model/get-redirect-page-name page)}) pages))
           files (when-not all? (map (fn [file] {:type :file :data file}) files))
           blocks (map (fn [block] {:type :block :data block}) blocks)
           search-mode (state/sub :search/mode)
@@ -170,7 +173,7 @@
        (ui/auto-complete
         result
         {:class "search-results"
-         :on-chosen (fn [{:keys [type data]}]
+         :on-chosen (fn [{:keys [type data alias]}]
                       (search-handler/clear-search!)
                       (leave-focus)
                       (case type
@@ -181,8 +184,9 @@
                         (page-handler/create! search-q)
 
                         :page
-                        (route/redirect! {:to :page
-                                          :path-params {:name data}})
+                        (let [data (or alias data)]
+                          (route/redirect! {:to :page
+                                            :path-params {:name data}}))
 
                         :file
                         (route/redirect! {:to :file
@@ -199,10 +203,11 @@
                                                :path-params {:name page}
                                                :query-params {:anchor (str "ls-block-" (:block/uuid data))}}))))
                         nil))
-         :on-shift-chosen (fn [{:keys [type data]}]
+         :on-shift-chosen (fn [{:keys [type data alias]}]
                             (case type
                               :page
-                              (let [page (db/entity [:block/name (string/lower-case data)])]
+                              (let [data (or alias data)
+                                    page (db/entity [:block/name (string/lower-case data)])]
                                 (state/sidebar-add-block!
                                  (state/get-current-repo)
                                  (:db/id page)
@@ -226,7 +231,7 @@
                                                 :path-params {:path data}})
 
                               nil))
-         :item-render (fn [{:keys [type data]}]
+         :item-render (fn [{:keys [type data alias]}]
                         (let [search-mode (state/get-search-mode)]
                           [:div {:class "py-2"} (case type
                                                   :graph-add-filter
@@ -237,7 +242,10 @@
                                                    [:span.ml-1 (str "\"" search-q "\"")]]
 
                                                   :page
-                                                  (search-result-item "Page" (highlight-exact-query data search-q))
+                                                  [:span
+                                                   (when alias
+                                                     [:span.mr-2.text-sm.font-medium.mb-2 (str "Alias -> " alias)])
+                                                   (search-result-item "Page" (highlight-exact-query data search-q))]
 
                                                   :file
                                                   (search-result-item "File" (highlight-exact-query data search-q))

+ 9 - 9
src/main/frontend/components/settings.cljs

@@ -342,13 +342,13 @@
             (let [value (not enable-all-pages-public?)]
               (config-handler/set-config! :publishing/all-pages-public? value)))))
 
-(defn enable-block-timestamps-row [t enable-block-timestamps?]
-  (toggle "block timestamps"
-          (t :settings-page/enable-block-time)
-          enable-block-timestamps?
-          (fn []
-            (let [value (not enable-block-timestamps?)]
-              (config-handler/set-config! :feature/enable-block-timestamps? value)))))
+;; (defn enable-block-timestamps-row [t enable-block-timestamps?]
+;;   (toggle "block timestamps"
+;;           (t :settings-page/enable-block-time)
+;;           enable-block-timestamps?
+;;           (fn []
+;;             (let [value (not enable-block-timestamps?)]
+;;               (config-handler/set-config! :feature/enable-block-timestamps? value)))))
 
 (defn encryption-row [t enable-encryption?]
   (toggle "enable_encryption"
@@ -454,7 +454,7 @@
         logical-outdenting? (state/logical-outdenting?)
         enable-tooltip? (state/enable-tooltip?)
         enable-git-auto-push? (state/enable-git-auto-push? current-repo)
-        enable-block-timestamps? (state/enable-block-timestamps?)
+        ;; enable-block-timestamps? (state/enable-block-timestamps?)
         show-brackets? (state/show-brackets?)
         github-token (state/sub [:me :access-token])
         cors-proxy (state/sub [:me :cors_proxy])
@@ -481,7 +481,7 @@
         (file-format-row t preferred-format)
         (date-format-row t preferred-date-format)
         (workflow-row t preferred-workflow)
-        (enable-block-timestamps-row t enable-block-timestamps?)
+        ;; (enable-block-timestamps-row t enable-block-timestamps?)
         (show-brackets-row t show-brackets?)
         (outdenting-row t logical-outdenting?)
         (tooltip-row t enable-tooltip?)

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

@@ -10,7 +10,8 @@
             [clojure.string :as string]
             [frontend.ui :as ui]
             [frontend.context.i18n :as i18n]
-            [frontend.handler.web.nfs :as nfs]))
+            [frontend.handler.web.nfs :as nfs]
+            [frontend.modules.shortcut.core :as shortcut]))
 
 (rum/defc choose-preferred-format
   []
@@ -88,7 +89,7 @@
        [:div.cp__widgets-open-local-directory
         [:div.select-file-wrap.cursor
          (when nfs-supported?
-           {:on-click page-handler/ls-dir-files!})
+           {:on-click #(page-handler/ls-dir-files! shortcut/refresh!)})
          [:div
           [:h1.title "Open a local directory"]
           [:p "Logseq supports both Markdown and Org-mode. You can open an existing directory or create a new one on your device, a directory is also known simply as a folder. Your data will be stored only on this device."]

+ 14 - 12
src/main/frontend/date.cljs

@@ -190,18 +190,20 @@
        (valid? (util/capitalize-all title))))
 
 (defn journal-title->
-  [journal-title then-fn]
-  (when-not (string/blank? journal-title)
-    (when-let [time (->> (map
-                           (fn [formatter]
-                             (try
-                               (tf/parse (tf/formatter formatter) (util/capitalize-all journal-title))
-                               (catch js/Error _e
-                                 nil)))
-                           safe-journal-title-formatters)
-                         (filter some?)
-                         first)]
-      (then-fn time))))
+  ([journal-title then-fn]
+   (journal-title-> journal-title then-fn (journal-title-formatters)))
+  ([journal-title then-fn formatters]
+   (when-not (string/blank? journal-title)
+     (when-let [time (->> (map
+                            (fn [formatter]
+                              (try
+                                (tf/parse (tf/formatter formatter) (util/capitalize-all journal-title))
+                                (catch js/Error _e
+                                  nil)))
+                            formatters)
+                          (filter some?)
+                          first)]
+       (then-fn time)))))
 
 (defn journal-title->int
   [journal-title]

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

@@ -48,7 +48,7 @@
   get-page-referenced-blocks get-page-referenced-pages get-page-unlinked-references get-page-referenced-blocks-no-cache
   get-all-pages get-pages get-pages-relation get-pages-that-mentioned-page get-public-pages get-tag-pages
   journal-page? local-native-fs? mark-repo-as-cloned! page-alias-set page-blocks-transform pull-block
-  set-file-last-modified-at! transact-files-db! with-block-refs-count get-modified-pages page-empty? page-empty-or-dummy? get-alias-source-page
+  set-file-last-modified-at! transact-files-db! get-modified-pages page-empty? page-empty-or-dummy? get-alias-source-page
   set-file-content! has-children? get-namespace-pages get-all-namespace-relation]
 
  [frontend.db.react

+ 19 - 0
src/main/frontend/db/debug.cljs

@@ -29,6 +29,25 @@
                                              count-1
                                              (- count-1 count-2)))))
 
+(defn block-uuid-nil?
+  [block]
+  (->>
+   (concat
+    [(:block/parent block)
+     (:block/left block)
+     (:block/page block)
+     (:block/namespace block)]
+    (:block/tags block)
+    (:block/alias block)
+    (:block/refs block)
+    (:block/path-refs block))
+   (remove nil?)
+   (some (fn [x]
+           (and
+            (vector? x)
+            (= :block/uuid (first x))
+            (nil? (second x)))))))
+
 (comment
   (defn debug!
     []

+ 22 - 26
src/main/frontend/db/model.cljs

@@ -410,17 +410,9 @@
              (map (fn [m]
                     (or (:block/original-name m) (:block/name m)))))))))
 
-(defn with-block-refs-count
-  [repo blocks]
-  (map (fn [block]
-         (let [refs-count (count (:block/_refs (db-utils/entity (:db/id block))))]
-           (assoc block :block/block-refs-count refs-count)))
-    blocks))
-
 (defn page-blocks-transform
   [repo-url result]
-  (->> (db-utils/with-repo repo-url result)
-       (with-block-refs-count repo-url)))
+  (db-utils/with-repo repo-url result))
 
 (defn with-pages
   [blocks]
@@ -556,7 +548,10 @@
 
 (defn page-empty?
   [repo page-id]
-  (empty? (:block/_parent (db-utils/entity repo page-id))))
+  (let [page-id (if (integer? page-id)
+                  page-id
+                  [:block/name page-id])]
+    (empty? (:block/_parent (db-utils/entity repo page-id)))))
 
 (defn page-empty-or-dummy?
   [repo page-id]
@@ -618,8 +613,7 @@
   [result repo-url block-uuid]
   (some->> result
            db-utils/seq-flatten
-           (db-utils/with-repo repo-url)
-           (with-block-refs-count repo-url)))
+           (db-utils/with-repo repo-url)))
 
 (defn get-block-children-ids
   [repo block-uuid]
@@ -757,23 +751,25 @@
 (defn get-redirect-page-name
   ([page-name] (get-redirect-page-name page-name false))
   ([page-name alias?]
-   (let [page-entity (db-utils/entity [:block/name page-name])]
-     (cond
-       alias?
-       page-name
-
-       (page-empty-or-dummy? (state/get-current-repo) (:db/id page-entity))
-       (let [source-page (get-alias-source-page (state/get-current-repo)
-                                                      (string/lower-case page-name))]
-         (or (when source-page (:block/name source-page))
-             page-name))
-
-       :else
-       page-name))))
+   (when page-name
+     (let [page-name (string/lower-case page-name)
+           page-entity (db-utils/entity [:block/name page-name])]
+       (cond
+         alias?
+         page-name
+
+         (page-empty-or-dummy? (state/get-current-repo) (:db/id page-entity))
+         (let [source-page (get-alias-source-page (state/get-current-repo)
+                                                  (string/lower-case page-name))]
+           (or (when source-page (:block/name source-page))
+               page-name))
+
+         :else
+         page-name)))))
 
 (defn get-page-original-name
   [page-name]
-  (when page-name
+  (when (string? page-name)
     (let [page (db-utils/pull [:block/name (string/lower-case page-name)])]
       (or (:block/original-name page)
           (:block/name page)))))

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

@@ -75,7 +75,6 @@
                                 remove-nested-children-blocks
                                 (model/sort-by-left-recursive)
                                 (db-utils/with-repo repo)
-                                (model/with-block-refs-count repo)
                                 (model/with-pages)))
                      result)]
         (if-let [result-transform (:result-transform q)]

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

@@ -12,7 +12,6 @@
             [clojure.string :as string]
             [dommy.core :as dom]
             [frontend.utf8 :as utf8]
-            [frontend.util.property :as property]
             ["codemirror" :as cm]
             ["codemirror/addon/edit/matchbrackets"]
             ["codemirror/addon/edit/closebrackets"]
@@ -64,8 +63,8 @@
         (:block/uuid config)
         (let [block (db/pull [:block/uuid (:block/uuid config)])
               format (:block/format block)
-              content (property/remove-properties format (:block/content block))
-              full-content (property/remove-built-in-properties format (:full_content (last (:rum/args state))))]
+              content (:block/content block)
+              full-content (:full_content (last (:rum/args state)))]
           (when (and full-content (string/includes? content full-content))
             (let [lines (string/split-lines full-content)
                   fl (first lines)

+ 9 - 10
src/main/frontend/extensions/zotero/extractor.cljs

@@ -41,7 +41,7 @@
           (-> item :data :name-of-act)
           ;; default use title
           (title item))]
-    (str (setting/setting :page-insert-prefix) page-title "_" (item-key item))))
+    (str (setting/setting :page-insert-prefix) page-title)))
 
 (defn authors [item]
   (let [creators (-> item :data :creators)
@@ -138,16 +138,15 @@
 
 (defmethod extract "attachment"
   [item]
-  (let [{:keys [title url link-mode path]} (-> item :data)]
-    (case link-mode
-      "imported_file"
-      (markdown-link title (local-link item))
-      "imported_url"
-      (markdown-link title url)
-      "linked_file"
-      (markdown-link title (str "file://" path))
-      "linked_url"
+  (let [{:keys [title filename url link-mode path]} (-> item :data)]
+    (cond
+      (contains? #{"imported_file" "imported_url" "linked_file"} link-mode)
+      (markdown-link (or title filename) (local-link item))
+
+      (some? url)
       (markdown-link title url)
+
+      :else
       nil)))
 
 (defmethod extract :default

+ 14 - 2
src/main/frontend/extensions/zotero/handler.cljs

@@ -43,6 +43,15 @@
   (state/set-editor-show-zotero! false)
   (editor-handler/insert-command! id (str "[[" page-name "]]") nil {}))
 
+(defn- create-abstract-note!
+  [page-name abstract-note]
+  (when-not (str/blank? abstract-note)
+    (let [block (editor-handler/api-insert-new-block!
+                 "[[Abstract]]" {:page page-name})]
+      (editor-handler/api-insert-new-block!
+       abstract-note {:block-uuid (:block/uuid block)
+                      :sibling? false}))))
+
 (defn create-zotero-page
   ([item]
    (create-zotero-page item {}))
@@ -50,8 +59,9 @@
           :or {insert-command? true notification? true}
           :as opt}]
    (go
-     (let [{:keys [page-name properties]} (extractor/extract item)]
-
+     (let [{:keys [page-name properties]} (extractor/extract item)
+           abstract-note (get properties :abstract-note)
+           properties (dissoc properties :abstract-note)]
        (when insert-command?
          (handle-command-zotero block-dom-id page-name)
          (editor-handler/save-current-block!))
@@ -68,6 +78,8 @@
            :create-first-block? false
            :properties properties}))
 
+       (create-abstract-note! page-name abstract-note)
+
        (<! (add page-name :attachments item))
 
        (<! (add page-name :notes item))

+ 2 - 2
src/main/frontend/extensions/zotero/setting.cljs

@@ -7,9 +7,9 @@
 (def default-settings
   {:type                   :user
    :include-attachments?   true
-   :attachments-block-text "[[attachments]]"
+   :attachments-block-text "[[Attachments]]"
    :include-notes?         true
-   :notes-block-text       "[[notes]]"
+   :notes-block-text       "[[Notes]]"
    :page-insert-prefix     "@"})
 
 (defn api-key []

+ 10 - 5
src/main/frontend/format/block.cljs

@@ -677,11 +677,16 @@
                 (let [[f r] (split-with (fn [p] (<= (:block/level-spaces p) level-spaces)) parents)
                       left (first r)
                       parents' (->> (concat f [left]) vec)
-                      block (assoc block
-                                   :block/parent [:block/uuid (:block/uuid (last f))]
-                                   :block/left [:block/uuid (:block/uuid left)]
-                                   :block/level (:block/level left)
-                                   :block/level-spaces (:block/level-spaces left))
+                      parent-id (if-let [block-id (:block/uuid (last f))]
+                                  [:block/uuid block-id]
+                                  page-id)
+                      block (cond->
+                              (assoc block
+                                     :block/parent parent-id
+                                     :block/left [:block/uuid (:block/uuid left)]
+                                     :block/level (:block/level left)
+                                     :block/level-spaces (:block/level-spaces left)))
+
                       parents' (->> (concat f [block]) vec)
                       result' (conj result block)]
                   [others parents' block result'])))]

+ 15 - 2
src/main/frontend/format/mldoc.cljs

@@ -21,10 +21,21 @@
 (defonce parseAndExportOPML (gobj/get Mldoc "parseAndExportOPML"))
 (defonce astExportMarkdown (gobj/get Mldoc "astExportMarkdown"))
 
+(defn convert-export-md-remove-options [opts]
+  (->>
+   (mapv (fn [opt]
+             (case opt
+               :page-ref ["Page_ref"]
+               :emphasis ["Emphasis"]
+               []))
+         opts)
+   (remove empty?)))
+
+
 (defn default-config
   ([format]
    (default-config format {:export-heading-to-list? false}))
-  ([format {:keys [export-heading-to-list? export-keep-properties? export-md-indent-style]}]
+  ([format {:keys [export-heading-to-list? export-keep-properties? export-md-indent-style export-md-remove-options]}]
    (let [format (string/capitalize (name (or format :markdown)))]
      (->> {:toc false
            :heading_number false
@@ -32,7 +43,9 @@
            :format format
            :heading_to_list (or export-heading-to-list? false)
            :exporting_keep_properties export-keep-properties?
-           :export_md_indent_style export-md-indent-style}
+           :export_md_indent_style export-md-indent-style
+           :export_md_remove_options
+           (convert-export-md-remove-options export-md-remove-options)}
           (filter #(not(nil? (second %))))
           (into {})
           (bean/->js)

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

@@ -47,7 +47,7 @@
   (let [f (fn []
             (when-not (state/nfs-refreshing?)
               ;; Don't create the journal file until user writes something
-              (repo-handler/create-today-journal! false))
+              (page-handler/create-today-journal!))
             (when-let [repo (state/get-current-repo)]
               (when (and (search-db/empty? repo)
                          (state/input-idle? repo))

+ 14 - 3
src/main/frontend/handler/editor.cljs

@@ -330,7 +330,7 @@
                         (:block/properties block))]
     (-> block
         (dissoc :block/top?
-                :block/block-refs-count)
+                :block/bottom?)
         (assoc :block/content content
                :block/properties new-properties)
         (merge (if level {:block/level level} {})))))
@@ -348,7 +348,15 @@
        (when refresh?
          (let [opts {:key :block/change
                      :data [block]}]
-           (db/refresh! repo opts)))))
+           (db/refresh! repo opts)))
+
+       ;; page title changed
+       (when-let [title (get-in block [:block/properties :title])]
+         (when-let [old-title (:block/name (db/entity (:db/id (:block/page block))))]
+           (when (and (:block/pre-block? block)
+                     (not (string/blank? title))
+                     (not= (string/lower-case title) old-title))
+             (state/pub-event! [:page/title-property-changed old-title title]))))))
 
     (repo-handler/push-if-auto-enabled! repo)))
 
@@ -1074,7 +1082,10 @@
         level-blocks (mapv (fn [uuid] (get level-blocks-uuid-map uuid)) block-ids*)
         tree (blocks-vec->tree level-blocks)
         top-level-block-uuids (mapv :block/uuid (filterv #(not (vector? %)) tree))
-        exported-md-contents (mapv #(export/export-blocks-as-markdown repo % "spaces")
+        exported-md-contents (mapv #(export/export-blocks-as-markdown
+                                     repo %
+                                     @(state/get-export-block-text-indent-style)
+                                     (into [] @(state/get-export-block-text-remove-options)))
                                    top-level-block-uuids)]
     [(string/join "\n" (mapv string/trim-newline exported-md-contents)) tree]))
 

+ 7 - 0
src/main/frontend/handler/events.cljs

@@ -9,6 +9,7 @@
             [frontend.handler.notification :as notification]
             [frontend.handler.common :as common-handler]
             [frontend.handler.editor :as editor-handler]
+            [frontend.handler.page :as page-handler]
             [frontend.components.encryption :as encryption]
             [frontend.fs.nfs :as nfs]
             [frontend.db.conn :as conn]
@@ -133,6 +134,12 @@
 (defmethod handle :modal/show-cards [_]
   (state/set-modal! srs/global-cards))
 
+(defmethod handle :page/title-property-changed [[_ old-title new-title]]
+  (page-handler/rename! old-title new-title))
+
+(defmethod handle :page/create-today-journal [[_ repo]]
+  (page-handler/create-today-journal!))
+
 (defmethod handle :after-db-restore [[_ repos]]
   (mapv (fn [{url :url} repo]
           ;; compare :ast/version

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

@@ -448,7 +448,7 @@
                    (js/JSON.stringify (clj->js refs)))))
 
 (defn export-blocks-as-markdown
-  [repo root-block-uuid indent-style]
+  [repo root-block-uuid indent-style remove-options]
   (let [get-page&block-refs-by-query-aux (get-embed-and-refs-blocks-pages-aux)
         f #(get-page&block-refs-by-query repo % get-page&block-refs-by-query-aux {:is-block? true})
         root-block (db/entity [:block/uuid root-block-uuid])
@@ -457,7 +457,7 @@
         content (get-blocks-contents repo root-block-uuid)
         format (or (:block/format root-block) (state/get-preferred-format))]
     (fp/exportMarkdown f/mldoc-record content
-                       (f/get-default-config format {:export-md-indent-style indent-style})
+                       (f/get-default-config format {:export-md-indent-style indent-style :export-md-remove-options remove-options})
                        (js/JSON.stringify (clj->js refs)))))
 
 (defn export-blocks-as-html

+ 20 - 11
src/main/frontend/handler/extract.cljs

@@ -130,9 +130,11 @@
                      ;; remove block references
                      (remove vector?))
           pages (util/distinct-by :block/name pages)
-          block-ids (mapv (fn [block]
-                            {:block/uuid (:block/uuid block)})
-                          (remove nil? blocks))
+          block-ids (->>
+                     (mapv (fn [block]
+                             {:block/uuid (:block/uuid block)})
+                           (remove nil? blocks))
+                     (remove nil?))
           pages (remove nil? pages)
           pages (map (fn [page] (assoc page :block/uuid (db/new-block-id))) pages)]
       [pages
@@ -189,6 +191,18 @@
          (map (partial apply merge))
          (with-block-uuid))))
 
+(defn- remove-illegal-refs
+  [block block-ids-set refresh?]
+  (let [aux-fn (fn [refs]
+                 (let [block-refs (if refresh? (set refs)
+                                      (set/intersection (set refs) block-ids-set))]
+                   (set/union
+                    (filter :block/name refs)
+                    block-refs)))]
+    (-> block
+        (update :block/refs aux-fn)
+        (update :block/path-refs aux-fn))))
+
 (defn extract-all-blocks-pages
   [repo-url files metadata refresh?]
   (when (seq files)
@@ -207,6 +221,8 @@
                       (remove empty?))]
       (when (seq result)
         (let [[pages block-ids blocks] (apply map concat result)
+              block-ids (remove (fn [b] (or (nil? b)
+                                           (nil? (:block/uuid b)))) block-ids)
               pages (with-ref-pages pages blocks)
               blocks (map (fn [block]
                             (let [id (:block/uuid block)
@@ -216,12 +232,5 @@
               ;; To prevent "unique constraint" on datascript
               pages-index (map #(select-keys % [:block/name]) pages)
               block-ids-set (set (map (fn [{:block/keys [uuid]}] [:block/uuid uuid]) block-ids))
-              blocks (map (fn [b]
-                            (update b :block/refs
-                                    (fn [refs]
-                                      (let [block-refs (if refresh? (set refs)
-                                                           (set/intersection (set refs) block-ids-set))]
-                                        (set/union
-                                         (filter :block/name refs)
-                                         block-refs))))) blocks)]
+              blocks (map #(remove-illegal-refs % block-ids-set refresh?) blocks)]
           (apply concat [pages-index pages block-ids blocks]))))))

+ 51 - 32
src/main/frontend/handler/page.cljs

@@ -13,7 +13,6 @@
             [frontend.handler.web.nfs :as web-nfs]
             [frontend.handler.notification :as notification]
             [frontend.handler.config :as config-handler]
-            [frontend.modules.shortcut.core :as shortcut]
             [frontend.handler.ui :as ui-handler]
             [frontend.modules.outliner.file :as outliner-file]
             [frontend.modules.outliner.core :as outliner-core]
@@ -81,35 +80,36 @@
   ([title {:keys [redirect? create-first-block? format properties]
            :or   {redirect?           true
                   create-first-block? true
-                  format              false
-                  properties          false}}]
-   (let [title    (string/trim title)
-         pages    (util/split-namespace-pages title)
-         page     (string/lower-case title)
-         format   (or format (state/get-preferred-format))
-         pages    (map (fn [page]
-                         (-> (block/page-name->map page true)
-                             (assoc :block/format format)))
-                       pages)
-         txs      (->> pages
-                       ;; for namespace pages, only last page need properties
-                       drop-last
-                       (mapcat #(build-page-tx format false %))
-                       (remove nil?))
-         last-txs (build-page-tx format properties (last pages))
-         txs      (concat txs last-txs)]
-
-     (db/transact! txs)
-
-     (when create-first-block?
-       (editor-handler/insert-first-page-block-if-not-exists! page))
-
-     (when-let [page (db/entity [:block/name page])]
-       (outliner-file/sync-to-file page))
-
-     (when redirect?
-       (route-handler/redirect! {:to          :page
-                                 :path-params {:name page}})))))
+                  format              nil
+                  properties          nil}}]
+   (let [page (string/lower-case title)]
+     (when-not (db/entity [:block/name page])
+       (let [title    (string/trim title)
+             pages    (util/split-namespace-pages title)
+             format   (or format (state/get-preferred-format))
+             pages    (map (fn [page]
+                             (-> (block/page-name->map page true)
+                                 (assoc :block/format format)))
+                        pages)
+             txs      (->> pages
+                           ;; for namespace pages, only last page need properties
+                           drop-last
+                           (mapcat #(build-page-tx format false %))
+                           (remove nil?))
+             last-txs (build-page-tx format properties (last pages))
+             txs      (concat txs last-txs)]
+
+         (db/transact! txs)
+
+         (when create-first-block?
+           (editor-handler/insert-first-page-block-if-not-exists! page))
+
+         (when-let [page (db/entity [:block/name page])]
+           (outliner-file/sync-to-file page))
+
+         (when redirect?
+           (route-handler/redirect! {:to          :page
+                                     :path-params {:name page}})))))))
 
 (defn page-add-property!
   [page-name key value]
@@ -368,6 +368,9 @@
 
                     (d/transact! (db/get-conn repo false) page-txs)
 
+                    (when (not= (util/page-name-sanity new-name) new-name)
+                      (page-add-property! new-name :title new-name))
+
                     (when (and file (not journal?) name-changed?)
                       (rename-file! file new-name (fn [] nil)))
 
@@ -481,11 +484,11 @@
           (contains? (set templates) (string/lower-case title)))))))
 
 (defn ls-dir-files!
-  []
+  [ok-handler]
   (web-nfs/ls-dir-files-with-handler!
    (fn []
      (init-commands!)
-     (shortcut/refresh!))))
+     (when ok-handler (ok-handler)))))
 
 (defn get-all-pages
   [repo]
@@ -570,3 +573,19 @@
                                           format
                                           {:last-pattern (str "[[" (if @editor-handler/*selected-text "" q))
                                            :postfix-fn   (fn [s] (util/replace-first "]]" s ""))}))))))
+
+(defn create-today-journal!
+  []
+  (state/set-today! (date/today))
+  (when-let [repo (state/get-current-repo)]
+    (when (or (db/cloned? repo)
+              (and (or (config/local-db? repo)
+                       (= "local" repo))
+                   ;; config file exists
+                   (let [path (config/get-config-path)]
+                     (db/get-file path))))
+      (let [title (date/today)
+            today-page (string/lower-case title)]
+        (when (db/page-empty? repo today-page)
+          (create! title {:redirect? false
+                          :create-first-block? true}))))))

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

@@ -114,9 +114,8 @@
                      (config/get-file-extension format))
            file-path (str "/" path)
            page-exists? (db/entity repo-url [:block/name (string/lower-case title)])
-           empty-blocks? (empty? (db/get-page-blocks-no-cache repo-url (string/lower-case title)))]
-       (when (or empty-blocks?
-                 (not page-exists?))
+           empty-blocks? (db/page-empty? repo-url (string/lower-case title))]
+       (when (or empty-blocks? (not page-exists?))
          (p/let [_ (nfs/check-directory-permission! repo-url)
                  _ (fs/mkdir-if-not-exists (str repo-dir "/" (config/get-journals-directory)))
                  file-exists? (fs/file-exists? repo-dir file-path)]
@@ -130,22 +129,6 @@
                (when-not (state/editing?)
                  (ui-handler/re-render-root!))))))))))
 
-(defn create-today-journal!
-  ([]
-   (create-today-journal! true))
-  ([write-file?]
-   (state/set-today! (date/today))
-   (when-let [repo (state/get-current-repo)]
-     (when (or (db/cloned? repo)
-               (and (or (config/local-db? repo)
-                        (= "local" repo))
-                    ;; config file exists
-                    (let [path (config/get-config-path)]
-                      (db/get-file path))))
-       (let [today-page (string/lower-case (date/today))]
-         (when (empty? (db/get-page-blocks-no-cache repo today-page))
-           (create-today-journal-if-not-exists repo {:write-file? write-file?})))))))
-
 (defn create-default-files!
   ([repo-url]
    (create-default-files! repo-url false))
@@ -153,13 +136,14 @@
    (spec/validate :repos/url repo-url)
    (let [repo-dir (config/get-repo-dir repo-url)]
      (p/let [_ (fs/mkdir-if-not-exists (str repo-dir "/" config/app-name))
-             _ (fs/mkdir-if-not-exists (str repo-dir "/" config/app-name "/" config/recycle-dir))]
+             _ (fs/mkdir-if-not-exists (str repo-dir "/" config/app-name "/" config/recycle-dir))
+             _ (fs/mkdir-if-not-exists (str repo-dir "/" (config/get-journals-directory)))]
        (file-handler/create-metadata-file repo-url encrypted?)
        ;; TODO: move to frontend.handler.file
        (create-config-file-if-not-exists repo-url)
-       (create-today-journal-if-not-exists repo-url {:write-file? false})
        (create-contents-file repo-url)
-       (create-custom-theme repo-url)))))
+       (create-custom-theme repo-url)
+       (state/pub-event! [:page/create-today-journal repo-url])))))
 
 (defn- remove-non-exists-refs!
   [data]
@@ -275,6 +259,8 @@
   [repo-url {:keys [first-clone? diffs nfs-files refresh?]
              :as opts}]
   (spec/validate :repos/url repo-url)
+  (when (= :repos (state/get-current-route))
+    (route-handler/redirect-to-home!))
   (let [config (or (state/get-config repo-url)
                    (some-> (first (filter #(= (config/get-config-path repo-url) (:file/path %)) nfs-files))
                            :file/content
@@ -329,11 +315,11 @@
               options {:first-clone? first-clone?
                        :delete-files (concat delete-files delete-pages)
                        :delete-blocks delete-blocks
-                       :re-render? true
-                       :refresh? true}]
+                       :re-render? true}]
           (if (seq nfs-files)
             (parse-files-and-load-to-db! repo-url nfs-files
                                          (assoc options
+                                                :refresh? refresh?
                                                 :re-render-opts {:clear-all-query-state? true}))
             (load-contents add-or-modify-files options)))))))
 
@@ -636,11 +622,11 @@
                    (prn "Delete repo failed, error: " error))))))
 
 (defn re-index!
-  [nfs-rebuild-index!]
+  [nfs-rebuild-index! ok-handler]
   (when-let [repo (state/get-current-repo)]
     (let [local? (config/local-db? repo)]
       (if local?
-        (nfs-rebuild-index! repo create-today-journal!)
+        (nfs-rebuild-index! repo ok-handler)
         (rebuild-index! repo))
       (js/setTimeout
        (fn []

+ 1 - 1
src/main/frontend/handler/web/nfs.cljs

@@ -248,7 +248,7 @@
                       (repo-handler/load-repo-to-db! repo
                                                      {:diffs     diffs
                                                       :nfs-files modified-files
-                                                      :refresh? true}))))))))
+                                                      :refresh? (not re-index?)}))))))))
 
 (defn- reload-dir!
   ([repo]

+ 20 - 18
src/main/frontend/modules/outliner/core.cljs

@@ -76,11 +76,12 @@
                 (assoc block :block/updated-at updated-at)
                 (nil? (:block/created-at block))
                 (assoc :block/created-at updated-at))
-        content (property/insert-properties (:block/format block)
-                                            (or (:block/content block) "")
-                                            {:created-at (:block/created-at block)
-                                             :updated-at (:block/updated-at block)})]
-    (assoc block :block/content content)))
+        ;; content (property/insert-properties (:block/format block)
+        ;;                                     (or (:block/content block) "")
+        ;;                                     {:created-at (:block/created-at block)
+        ;;                                      :updated-at (:block/updated-at block)})
+        ]
+    block))
 
 ;; -get-id, -get-parent-id, -get-left-id return block-id
 ;; the :block/parent, :block/left should be datascript lookup ref
@@ -96,8 +97,8 @@
          (if uuid
            uuid
            (let [new-id (db/new-block-id)]
-             (db/transact! {:db/id db-id
-                            :block/uuid new-id})
+             (db/transact! [{:db/id db-id
+                             :block/uuid new-id}])
              new-id))))))
 
   (-get-parent-id [this]
@@ -243,17 +244,18 @@
   "Insert a node as sibling."
   [txs-state new-node left-node]
   {:pre [(every? tree/satisfied-inode? [new-node left-node])]}
-  (let [node (-> (tree/-set-left-id new-node (tree/-get-id left-node))
-               (tree/-set-parent-id (tree/-get-parent-id left-node)))
-        right-node (tree/-get-right left-node)]
-    (if (tree/satisfied-inode? right-node)
-      (let [new-right-node (tree/-set-left-id right-node (tree/-get-id new-node))
-            saved-new-node (tree/-save node txs-state)]
-        (tree/-save new-right-node txs-state)
-        [saved-new-node new-right-node])
-      (do
-        (tree/-save node txs-state)
-        [node]))))
+  (when-let [left-id (tree/-get-id left-node)]
+    (let [node (-> (tree/-set-left-id new-node left-id)
+                   (tree/-set-parent-id (tree/-get-parent-id left-node)))
+          right-node (tree/-get-right left-node)]
+      (if (tree/satisfied-inode? right-node)
+        (let [new-right-node (tree/-set-left-id right-node (tree/-get-id new-node))
+              saved-new-node (tree/-save node txs-state)]
+          (tree/-save new-right-node txs-state)
+          [saved-new-node new-right-node])
+        (do
+          (tree/-save node txs-state)
+          [node])))))
 
 
 (defn- insert-node-aux

+ 4 - 1
src/main/frontend/modules/shortcut/config.cljs

@@ -4,6 +4,7 @@
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.history :as history]
             [frontend.handler.repo :as repo-handler]
+            [frontend.handler.page :as page-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.search :as search-handler]
             [frontend.handler.ui :as ui-handler]
@@ -303,7 +304,9 @@
     :graph/re-index
     {:desc    "Re-index the whole graph"
      :binding "mod+c mod+r"
-     :fn      #(repo-handler/re-index! nfs-handler/rebuild-index!)}}
+     :fn      #(repo-handler/re-index!
+                nfs-handler/rebuild-index!
+                page-handler/create-today-journal!)}}
 
    :shortcut.handler/misc
    ;; always overrides the copy due to "mod+c mod+s"

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

@@ -144,6 +144,9 @@
       ;; copied blocks
       :copy/blocks {:copy/content nil :copy/block-tree nil}
 
+      :copy/export-block-text-indent-style  (atom "dashes")
+      :copy/export-block-text-remove-options (atom #{})
+
       :date-picker/date nil
 
       :view/components {}})))
@@ -1350,6 +1353,13 @@
   [content ids]
   (set-state! :copy/blocks {:copy/content content :copy/block-tree ids}))
 
+(defn get-export-block-text-indent-style []
+  (:copy/export-block-text-indent-style @state))
+
+(defn get-export-block-text-remove-options []
+  (:copy/export-block-text-remove-options @state))
+
+
 (defn set-editor-args!
   [args]
   (set-state! :editor/args args))

+ 25 - 9
src/main/frontend/ui.cljs

@@ -5,6 +5,7 @@
             ["react-textarea-autosize" :as TextareaAutosize]
             ["react-resize-context" :as Resize]
             ["react-tippy" :as react-tippy]
+            ["react-tweet-embed" :as react-tweet-embed]
             [frontend.util :as util]
             [frontend.mixins :as mixins]
             [frontend.handler.notification :as notification-handler]
@@ -27,6 +28,7 @@
 (def resize-provider (r/adapt-class (gobj/get Resize "ResizeProvider")))
 (def resize-consumer (r/adapt-class (gobj/get Resize "ResizeConsumer")))
 (def Tippy (r/adapt-class (gobj/get react-tippy "Tooltip")))
+(def ReactTweetEmbed (r/adapt-class react-tweet-embed))
 
 (rum/defc ls-textarea < rum/reactive
   [{:keys [on-change] :as props}]
@@ -652,19 +654,23 @@
                     :trigger (if manual "manual" "mouseenter focus")
                     ;; See https://github.com/tvkhoa/react-tippy/issues/13
                     :popperOptions (if fixed-position?
-                                      {:modifiers {:flip {:enabled false}
-                                                   :hide {:enabled false}
-                                                   :preventOverflow {:enabled false}}}
-                                      {})
+                                     {:modifiers {:flip {:enabled false}
+                                                  :hide {:enabled false}
+                                                  :preventOverflow {:enabled false}}}
+                                     {})
                     :onShow #(reset! *mounted? true)
                     :onHide #(reset! *mounted? false)}
                    opts)
             (assoc :html (if (or open? mounted?)
-                           (when-let [html (:html opts)]
-                             (if (fn? html)
-                               (html)
-                               [:div.pr-3.py-1
-                                html]))
+                           (try
+                             (when-let [html (:html opts)]
+                              (if (fn? html)
+                                (html)
+                                [:div.pr-3.py-1
+                                 html]))
+                             (catch js/Error e
+                               (log/error :exception e)
+                               [:div]))
                            [:div {:key "tippy"} ""])))
            child)))
 
@@ -678,3 +684,13 @@
     :style {:width "100%"}
     :on-change #(let [value (util/evalue %)]
                   (on-change value))}])
+
+(rum/defcs tweet-embed < (rum/local true :loading?)
+  [state id]
+  (let [*loading? (:loading? state)]
+    [:div [(when @*loading? [:span.flex.items-center [svg/loading " ... loading"]])
+           (ReactTweetEmbed
+             {:id                    id
+              :class                 "contents"
+              :options               {:theme (when (= (state/sub :ui/theme) "dark") "dark")}
+              :on-tweet-load-success #(reset! *loading? false)})]]))

+ 4 - 0
src/main/frontend/util.cljc

@@ -90,6 +90,10 @@
    (defn ekey [event]
      (gobj/getValueByKeys event "key")))
 
+#?(:cljs
+   (defn echecked? [event]
+     (gobj/getValueByKeys event "target" "checked")))
+
 #?(:cljs
    (defn set-change-value
      "compatible change event for React"

+ 7 - 3
src/main/frontend/util/property.cljs

@@ -20,7 +20,7 @@
 (defn built-in-properties
   []
   (set/union
-   #{:id :custom-id :background-color :heading :collapsed :created-at :updated-at :last-modified-at :created_at :last_modified_at :query-table :query-properties}
+   #{:id :custom-id :background-color :heading :collapsed :created-at :updated-at :last-modified-at :created_at :last_modified_at :query-table :query-properties :query-sort-by :query-sort-desc}
    (set (map keyword config/markers))
    @built-in-extended-properties))
 
@@ -89,8 +89,11 @@
 
       (not org?)
       (let [lines (string/split-lines content)
-            non-properties (get (group-by simplified-property? lines) false)]
-        (string/join "\n" non-properties))
+            lines (if (simplified-property? (first lines))
+                    (drop-while simplified-property? lines)
+                    (cons (first lines)
+                          (drop-while simplified-property? (rest lines))))]
+        (string/join "\n" lines))
 
       :else
       content)))
@@ -271,6 +274,7 @@
   [format content]
   (remove-property format "id" content false))
 
+;; FIXME: only remove from the properties area
 (defn remove-built-in-properties
   [format content]
   (let [built-in-properties* (built-in-properties)

+ 10 - 2
src/test/frontend/util/property_test.cljs

@@ -42,10 +42,18 @@
       "** hello"
 
       (property/remove-properties :org "** hello\n:PROPERTIES:\n:x: y\n\na:b\n:END:\n")
-      "** hello"
+      "** hello"))
+
+  (testing "invalid-properties"
+    (are [x y] (= x y)
+      (property/remove-properties :markdown "hello\nnice\nfoo:: bar")
+      "hello\nnice\nfoo:: bar"
+
+      (property/remove-properties :markdown "hello\nnice\nfoo:: bar\ntest")
+      "hello\nnice\nfoo:: bar\ntest"
 
       (property/remove-properties :markdown "** hello\nx:: y\n\na:: b\n")
-      "** hello\n")))
+      "** hello\n\na:: b")))
 
 (deftest test-insert-property
   (are [x y] (= x y)

+ 13 - 10
yarn.lock

@@ -6165,10 +6165,10 @@ mkdirp@^0.5.0, mkdirp@^0.5.4, mkdirp@~0.5.1:
   dependencies:
     minimist "^1.2.5"
 
[email protected].2:
-  version "0.9.2"
-  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-0.9.2.tgz#0d88a049bb9564a1567134178b2dea9196f30148"
-  integrity sha512-e1JFKKkX6IYHFP8XwN0pqcEn2L8biVHWXGUyaUU8RHsCq2poN1fPIAZ5waosS4EffC+CQk68PycEhCobYwbndA==
[email protected].4:
+  version "0.9.4"
+  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-0.9.4.tgz#80dd027e06a297ef4bbbabd28793a40f64bcb451"
+  integrity sha512-Pt994A4XGG1rDdU5tvR8quk1WdlD0eTmo+UWo37qndLaHFxt++lTT7UK8WX3cj0HamJlx8WiYWQO3gOWIzL+dw==
   dependencies:
     yargs "^12.0.2"
 
@@ -6891,10 +6891,10 @@ pinkie@^2.0.0:
   resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz"
   integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
 
-pixi-graph-fork@^0.1.3:
-  version "0.1.3"
-  resolved "https://registry.yarnpkg.com/pixi-graph-fork/-/pixi-graph-fork-0.1.3.tgz#fc7beee363ba3ec71b35df30fce1d5b2ddf27732"
-  integrity sha512-NHmlG0FI2/xme/p9KXOPLbfrQ0UIcCq4GK36IsSYPGJznhUoJTlNbfT4ML7I3DL84vQx0TliGPAA+EmoVz6CDg==
+pixi-graph-fork@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/pixi-graph-fork/-/pixi-graph-fork-0.1.4.tgz#9191daa6512e7440c4a767c6d7bcef547a33a5a2"
+  integrity sha512-qR8MJ/q1TL812LuRqaGjHA6xIkZomOINZGH+7tKMA15feow1ud+tzFVb1pKsnvmSy/uWhhOUOVLrqLTf3vYr3Q==
   dependencies:
     "@pixi-essentials/cull" "^1.1.0"
     "@pixi/app" "^6.0.2"
@@ -7607,8 +7607,6 @@ react-icons@^2.2.7:
   version "2.2.7"
   resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-2.2.7.tgz#d7860826b258557510dac10680abea5ca23cf650"
   integrity sha512-0n4lcGqzJFcIQLoQytLdJCE0DKSA9dkwEZRYoGrIDJZFvIT6Hbajx5mv9geqhqFiNjUgtxg8kPyDfjlhymbGFg==
-  dependencies:
-    react-icon-base "2.1.0"
 
 react-is@^16.3.1, react-is@^16.8.1:
   version "16.13.1"
@@ -7664,6 +7662,11 @@ react-transition-group@^4.3.0:
     loose-envify "^1.4.0"
     prop-types "^15.6.2"
 
+react-tweet-embed@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/react-tweet-embed/-/react-tweet-embed-1.2.2.tgz#b518510a6e6ef17609f9c9b16bc4775624730d8a"
+  integrity sha512-Y932BlSaJsDUsKDucC2opzzd+uhc0YNhrlTa/4Beb2be1od+AjLGo6Fhuo2wPT0D+fF4VTXOyoZyA8Yc88RdYA==
+
 react@^17.0.2:
   version "17.0.2"
   resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"