浏览代码

Merge branch 'master' into feat/integration-plugins-core

Charlie 4 年之前
父节点
当前提交
b2ebabccb5
共有 49 个文件被更改,包括 786 次插入948 次删除
  1. 3 3
      .github/workflows/build-desktop-release.yml
  2. 3 1
      package.json
  3. 7 7
      resources/electron-dev.html
  4. 7 7
      resources/electron.html
  5. 1 0
      resources/forge.config.js
  6. 二进制
      resources/img/theme-modes.png
  7. 0 0
      resources/js/sentry.min.js
  8. 7 5
      src/electron/electron/core.cljs
  9. 3 2
      src/main/frontend/commands.cljs
  10. 38 42
      src/main/frontend/components/block.cljs
  11. 1 1
      src/main/frontend/components/journal.cljs
  12. 7 3
      src/main/frontend/components/onboarding.cljs
  13. 9 17
      src/main/frontend/components/page.cljs
  14. 0 497
      src/main/frontend/components/page.cljs.~e600c29046589a778b9a28a2122606358d7ab458~
  15. 48 29
      src/main/frontend/components/settings.cljs
  16. 49 0
      src/main/frontend/components/settings.css
  17. 3 1
      src/main/frontend/components/sidebar.cljs
  18. 13 0
      src/main/frontend/components/sidebar.css
  19. 7 1
      src/main/frontend/components/theme.cljs
  20. 8 6
      src/main/frontend/db/model.cljs
  21. 25 16
      src/main/frontend/db/query_dsl.cljs
  22. 9 9
      src/main/frontend/dicts.cljs
  23. 1 1
      src/main/frontend/extensions/code.cljs
  24. 21 35
      src/main/frontend/format/block.cljs
  25. 2 2
      src/main/frontend/format/mldoc.cljs
  26. 32 34
      src/main/frontend/handler.cljs
  27. 4 0
      src/main/frontend/handler/config.cljs
  28. 36 33
      src/main/frontend/handler/editor.cljs
  29. 7 4
      src/main/frontend/handler/extract.cljs
  30. 2 1
      src/main/frontend/handler/file.cljs
  31. 7 3
      src/main/frontend/handler/page.cljs
  32. 4 2
      src/main/frontend/handler/web/nfs.cljs
  33. 18 0
      src/main/frontend/modules/instrumentation/core.cljs
  34. 41 0
      src/main/frontend/modules/instrumentation/posthog.cljs
  35. 17 0
      src/main/frontend/modules/instrumentation/sentry.cljs
  36. 4 8
      src/main/frontend/modules/outliner/tree.cljs
  37. 15 11
      src/main/frontend/modules/shortcut/core.cljs
  38. 9 1
      src/main/frontend/publishing.cljs
  39. 25 14
      src/main/frontend/state.cljs
  40. 28 9
      src/main/frontend/text.cljs
  41. 9 1
      src/main/frontend/ui.cljs
  42. 20 4
      src/main/frontend/util.cljc
  43. 39 5
      src/main/frontend/util/property.cljs
  44. 1 1
      src/main/frontend/version.cljs
  45. 91 99
      src/test/frontend/db/query_dsl_test.cljs
  46. 6 7
      src/test/frontend/format/block_test.cljs
  47. 4 0
      templates/config.edn
  48. 1 1
      templates/tutorial-en.md
  49. 94 25
      yarn.lock

+ 3 - 3
.github/workflows/build-desktop-release.yml

@@ -23,7 +23,7 @@ on:
 
 jobs:
   compile-cljs:
-    runs-on: ubuntu-latest
+    runs-on: ubuntu-18.04
     steps:
       - name: Check out Git repository
         uses: actions/checkout@v1
@@ -82,7 +82,7 @@ jobs:
           path: static.zip
 
   build-linux:
-    runs-on: ubuntu-latest
+    runs-on: ubuntu-18.04
     needs: [ compile-cljs ]
     steps:
       - name: Download The Static Asset
@@ -232,7 +232,7 @@ jobs:
 
   release:
     needs: [ build-macos, build-linux, build-windows ]
-    runs-on: ubuntu-latest
+    runs-on: ubuntu-18.04
 
     steps:
       - name: Download The MacOS X64 DMG Artifact

+ 3 - 1
package.json

@@ -60,6 +60,7 @@
     "dependencies": {
         "@excalidraw/excalidraw": "^0.4.2",
         "@kanru/rage-wasm": "^0.2.1",
+        "@sentry/browser": "^6.4.1",
         "@tippyjs/react": "^4.2.5",
         "chokidar": "^3.5.1",
         "chrono-node": "^2.2.4",
@@ -74,8 +75,9 @@
         "ignore": "^5.1.8",
         "is-svg": "4.2.2",
         "jszip": "^3.5.0",
-        "mldoc": "0.6.18",
+        "mldoc": "0.6.22",
         "path": "^0.12.7",
+        "posthog-js": "^1.10.2",
         "react": "^17.0.2",
         "react-dom": "^17.0.2",
         "react-resize-context": "^3.0.0",

+ 7 - 7
resources/electron-dev.html

@@ -13,19 +13,19 @@
   <meta content="black-translucent" name="apple-mobile-web-app-status-bar-style">
   <meta content="yes" name="mobile-web-app-capable">
   <meta content="summary" name="twitter:card">
-  <meta content="A local-first knowledge base which can be synced using Git." name="twitter:description">
+  <meta content="A privacy-first, open-source platform for knowledge management and collaboration." name="twitter:description">
   <meta content="@logseq" name="twitter:site">
-  <meta content="A local-first knowledge base." name="twitter:title">
+  <meta content="A privacy-first, open-source platform for knowledge management and collaboration." name="twitter:title">
   <meta content="https://asset.logseq.com/static/img/logo.png" name="twitter:image:src">
-  <meta content="A local-first knowledge base which can be synced using Git." name="twitter:image:alt">
-  <meta content="A local-first knowledge base." property="og:title">
+  <meta content="A privacy-first, open-source platform for knowledge management and collaboration." name="twitter:image:alt">
+  <meta content="A privacy-first, open-source platform for knowledge management and collaboration." property="og:title">
   <meta content="site" property="og:type">
   <meta content="https://logseq.com" property="og:url">
   <meta content="https://asset.logseq.com/static/img/logo.png" property="og:image">
-  <meta content="A local-first knowledge base which can be synced using Git." property="og:description">
-  <title>Logseq: A local-first knowledge base</title>
+  <meta content="A privacy-first, open-source platform for knowledge management and collaboration." property="og:description">
+  <title>Logseq: A privacy-first platform for knowledge management and collaboration</title>
   <meta content="logseq" property="og:site_name">
-  <meta content="A local-first knowledge base which can be synced using Git." name="description">
+  <meta content="A privacy-first, open-source platform for knowledge management and collaboration." name="description">
   <script>window.localStorage.getItem('http-entry-port') && (location.href = 'http://localhost:' + window.localStorage.getItem('http-entry-port'))</script>
 </head>
 <body>

+ 7 - 7
resources/electron.html

@@ -13,19 +13,19 @@
   <meta content="black-translucent" name="apple-mobile-web-app-status-bar-style">
   <meta content="yes" name="mobile-web-app-capable">
   <meta content="summary" name="twitter:card">
-  <meta content="A local-first knowledge base which can be synced using Git." name="twitter:description">
+  <meta content="A privacy-first, open-source platform for knowledge management and collaboration." name="twitter:description">
   <meta content="@logseq" name="twitter:site">
-  <meta content="A local-first knowledge base." name="twitter:title">
+  <meta content="A privacy-first, open-source platform for knowledge management and collaboration." name="twitter:title">
   <meta content="https://asset.logseq.com/static/img/logo.png" name="twitter:image:src">
-  <meta content="A local-first knowledge base which can be synced using Git." name="twitter:image:alt">
-  <meta content="A local-first knowledge base." property="og:title">
+  <meta content="A privacy-first, open-source platform for knowledge management and collaboration." name="twitter:image:alt">
+  <meta content="A privacy-first, open-source platform for knowledge management and collaboration." property="og:title">
   <meta content="site" property="og:type">
   <meta content="https://logseq.com" property="og:url">
   <meta content="https://asset.logseq.com/static/img/logo.png" property="og:image">
-  <meta content="A local-first knowledge base which can be synced using Git." property="og:description">
-  <title>Logseq: A local-first knowledge base</title>
+  <meta content="A privacy-first, open-source platform for knowledge management and collaboration." property="og:description">
+  <title>Logseq: "A privacy-first platform for knowledge management and collaboration."</title>
   <meta content="logseq" property="og:site_name">
-  <meta content="A local-first knowledge base which can be synced using Git." name="description">
+  <meta content="A privacy-first, open-source platform for knowledge management and collaboration." name="description">
   </script>
 </head>
 <body>

+ 1 - 0
resources/forge.config.js

@@ -2,6 +2,7 @@ const path = require('path')
 
 module.exports = {
   packagerConfig: {
+    name: 'Logseq',
     icon: './icons/logseq_big_sur.icns',
     osxSign: {
       identity: 'Developer ID Application: Tiansheng Qin',

二进制
resources/img/theme-modes.png


文件差异内容过多而无法显示
+ 0 - 0
resources/js/sentry.min.js


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

@@ -36,6 +36,9 @@
                     :nodeIntegrationInWorker false
                     :contextIsolation        true
                     :spellcheck              true
+                    ;; Remove OverlayScrollbars and transition `.scrollbar-spacing`
+                    ;; to use `scollbar-gutter` after the feature is implemented in browsers.
+                    :enableBlinkFeatures     'OverlayScrollbars'
                     :preload                 (path/join js/__dirname "js/preload.js")}}
                    linux?
                    (assoc :icon (path/join js/__dirname "icons/logseq.png")))
@@ -48,11 +51,10 @@
 
 (defn setup-updater! [^js win]
   ;; manual/auto updater
-  ;;(when-not linux?
-  ;;  (init-updater {:repo   "logseq/logseq"
-  ;;                 :logger logger
-  ;;                 :win    win}))
-  )
+  (when-not linux?
+    (init-updater {:repo   "logseq/logseq"
+                   :logger logger
+                   :win    win})))
 
 (defn setup-interceptor! []
   (.registerFileProtocol

+ 3 - 2
src/main/frontend/commands.cljs

@@ -196,7 +196,7 @@
   ([type]
    (->block type nil))
   ([type optional]
-   (let [format (get state/get-edit-block :block/format :markdown)
+   (let [format (get (state/get-edit-block) :block/format :markdown)
          org? (= format :org)
          t (string/lower-case type)
          markdown-src? (and (= format :markdown) (= t "src"))
@@ -238,7 +238,8 @@
      ["Src" (->block "src" "")]
      ["Query" (->block "query")]
      ["Latex export" (->block "export" "latex")]
-     (when-not (= :markdown (state/get-preferred-format))
+     ;; FIXME: current page's format
+     (when (= :org (state/get-preferred-format))
        ["Properties" (->properties)])
      ["Note" (->block "note")]
      ["Tip" (->block "tip")]

+ 38 - 42
src/main/frontend/components/block.cljs

@@ -85,8 +85,7 @@
 (defonce virtual-list-scroll-step 450)
 (defonce virtual-list-previous 50)
 
-(defonce container-ids (atom {}))
-(defonce container-idx (atom 0))
+(defonce *blocks-container-id (atom 0))
 
 ;; TODO:
 ;; add `key`
@@ -394,10 +393,11 @@
                                  :text-align "left"
                                  :font-weight 500
                                  :max-height 600
-                                 :padding-bottom 200}}
+                                 :padding-bottom 64}}
                         [:h2.font-bold.text-lg page-name]
                         (let [page (db/entity [:block/name (string/lower-case page-name)])]
-                          ((state/get-page-blocks-cp) (state/get-current-repo) page (:sidebar? config)))]
+                          (when-let [f (state/get-page-blocks-cp)]
+                            (f (state/get-current-repo) page {:sidebar? (:sidebar? config)})))]
                  :interactive true
                  :delay 1000}
                 inner))))
@@ -476,6 +476,7 @@
      [:div.px-3.pt-1.pb-2
       (blocks-container blocks (assoc config
                                       :id (str id)
+                                      :embed-id id
                                       :embed? true
                                       :ref? false))]]))
 
@@ -548,7 +549,7 @@
                              {:style {:width 735
                                       :text-align "left"
                                       :max-height 600}}
-                             (block-container config block)]
+                             (blocks-container [block] config)]
                       :interactive true
                       :delay 1000}
                      (if label
@@ -1047,7 +1048,8 @@
 (rum/defc block-children < rum/reactive
   [config children collapsed? *ref-collapsed?]
   (let [ref? (:ref? config)
-        collapsed? (if ref? (rum/react *ref-collapsed?) collapsed?)]
+        collapsed? (if ref? (rum/react *ref-collapsed?) collapsed?)
+        children (filter map? children)]
     (when (and (seq children) (not collapsed?))
       (let [doc-mode? (:document/mode? config)]
        [:div.block-children {:style {:margin-left (if doc-mode? 12 21)
@@ -1115,18 +1117,6 @@
                       "hide-inner-bullet"))}
        [:span.bullet {:blockid (str uuid)}]]]]))
 
-(defn- build-id
-  [config]
-  (let [ref? (:ref? config)
-        sidebar? (:sidebar? config)
-        k (pr-str config)
-        n (or
-           (get @container-ids k)
-           (let [n' (if (and ref? (not sidebar?)) @container-idx (swap! container-idx inc))]
-             (swap! container-ids assoc k n')
-             n'))]
-    (str n "-")))
-
 (rum/defc dnd-separator
   [block margin-left bottom top? nested?]
   (let [id (str (:block/uuid block)
@@ -1534,9 +1524,10 @@
 (rum/defc block-content-or-editor < rum/reactive
   [config {:block/keys [uuid title body meta content dummy? page format repo children marker properties block-refs-count 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])]
+        edit? (state/sub [:editor/editing? edit-input-id])
+        editor-id (str "editor-" edit-input-id)]
     (if (and edit? editor-box)
-      [:div.editor-wrapper {:id (str "editor-" edit-input-id)}
+      [:div.editor-wrapper {:id editor-id}
        (editor-box {:block block
                     :block-id uuid
                     :block-parent-id block-id
@@ -1552,7 +1543,9 @@
        [:div.flex.flex-1
         (block-content config block edit-input-id block-id slide?)]
        [:div.flex.flex-row
-        (when (and (:embed? config) (not (:page-embed? config)))
+        (when (and (:embed? config)
+                   (not (:page-embed? config))
+                   (= (:embed-id config) uuid))
           [:a.opacity-30.hover:opacity-100.svg-small.inline
            {:on-mouse-down (fn [e]
                              (util/stop e)
@@ -1720,17 +1713,12 @@
    :on-mouse-leave (fn [e]
                      (block-mouse-leave e has-child? *control-show? block-id doc-mode?))})
 
-(defn- get-data-refs-and-self
-  [block refs-with-children]
+(defn- build-refs-data-value
+  [block refs]
   (let [refs (model/get-page-names-by-ids
-              (->> (map :db/id refs-with-children)
-                   (remove nil?)))
-        data-refs (text/build-data-value refs)
-        refs (model/get-page-names-by-ids
-              (->> (map :db/id (:block/ref-pages block))
-                   (remove nil?)))
-        data-refs-self (text/build-data-value refs)]
-    [data-refs data-refs-self]))
+              (->> (map :db/id refs)
+                   (remove nil?)))]
+    (text/build-data-value refs)))
 
 ;; (rum/defc block-immediate-children < rum/reactive
 ;;   [repo config uuid ref? collapsed?]
@@ -1756,8 +1744,9 @@
    :should-update (fn [old-state new-state]
                     (not= (:block/content (second (:rum/args old-state)))
                           (:block/content (second (:rum/args new-state)))))}
-  [state config {:block/keys [uuid title body meta content dummy? page format repo children pre-block? top? properties refs-with-children heading-level level type] :as block}]
-  (let [heading? (and (= type :heading) heading-level (<= heading-level 6))
+  [state config {:block/keys [uuid title body meta content dummy? page format repo children pre-block? top? properties refs path-refs heading-level level type] :as block}]
+  (let [blocks-container-id (:blocks-container-id config)
+        heading? (and (= type :heading) heading-level (<= heading-level 6))
         *control-show? (get state ::control-show?)
         *ref-collapsed? (get state ::ref-collapsed?)
         collapsed? (or @*ref-collapsed? (get properties :collapsed))
@@ -1768,15 +1757,15 @@
         doc-mode? (:document/mode? config)
         embed? (:embed? config)
         reference? (:reference? config)
-        unique-dom-id (build-id (dissoc config :block/uuid))
-        block-id (str "ls-block-" unique-dom-id uuid)
+        block-id (str "ls-block-" blocks-container-id "-" uuid)
         has-child? (boolean
                     (and
                      (not pre-block?)
                      (or (seq children)
                          (seq body))))
         attrs (on-drag-and-mouse-attrs block uuid top? block-id *move-to-top? has-child? *control-show? doc-mode?)
-        [data-refs data-refs-self] (get-data-refs-and-self block refs-with-children)]
+        data-refs (build-refs-data-value block (remove (set refs) path-refs))
+        data-refs-self (build-refs-data-value block refs)]
     [:div.ls-block.flex.flex-col.rounded-sm
      (cond->
       {:id block-id
@@ -1807,7 +1796,7 @@
       (when (not slide?)
         (block-control config block uuid block-id body children dummy? collapsed? *ref-collapsed? *control-show?))
 
-      (let [edit-input-id (str "edit-block-" unique-dom-id uuid)]
+      (let [edit-input-id (str "edit-block-" blocks-container-id "-" uuid)]
         (block-content-or-editor config block edit-input-id block-id slide? heading-level))]
 
      (block-children config children collapsed? *ref-collapsed?)
@@ -2308,16 +2297,23 @@
                      blocks)]
        sections))))
 
-(defn blocks-container
-  [blocks config]
-  (let [
-        ;; blocks (map #(dissoc % :block/children) blocks)
+(rum/defcs blocks-container <
+  {:init (fn [state]
+           (assoc state ::init-blocks-container-id (atom nil)))}
+  [state blocks config]
+  (let [*init-blocks-container-id (::init-blocks-container-id state)
+        blocks-container-id (if @*init-blocks-container-id
+                              @*init-blocks-container-id
+                              (let [id' (swap! *blocks-container-id inc)]
+                                (reset! *init-blocks-container-id id')
+                                id'))
         sidebar? (:sidebar? config)
         ref? (:ref? config)
         custom-query? (:custom-query? config)
         blocks->vec-tree #(if (or custom-query? ref?) % (tree/blocks->vec-tree % (:id config)))
         blocks' (blocks->vec-tree blocks)
-        blocks (if (seq blocks') blocks' blocks)]
+        blocks (if (seq blocks') blocks' blocks)
+        config (assoc config :blocks-container-id blocks-container-id)]
     (when (seq blocks)
       [:div.blocks-container.flex-1
        {:style {:margin-left (cond

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

@@ -114,7 +114,7 @@
 (rum/defc journals < rum/reactive
   [latest-journals]
   [:div#journals
-   (ui/infinite-list "left-container"
+   (ui/infinite-list "main-container"
                      (for [[journal-name format] latest-journals]
                        [:div.journal-item.content {:key journal-name}
                         (journal-cp [journal-name format])])

+ 7 - 3
src/main/frontend/components/onboarding.cljs

@@ -15,9 +15,9 @@
      [:div.flex-1
       [:div.flex.flex-col.pl-1.ls-block
        [:hr {:style {:margin-top 200}}]
-       [:div.flex.flex-row.admonitionblock.align-items {:class "warning"}
+       [:div.flex.flex-row.admonitionblock.align-items {:class "important"}
         [:div.pr-4.admonition-icon.flex.flex-col.justify-center
-         {:title "Warning"} (svg/warning)]
+         {:title "Important"} (svg/tip)]
         [:div.ml-4.text-lg
          (t :on-boarding/notice)]]
        [:p
@@ -210,6 +210,10 @@
        [:a {:href "/blog/terms"
             :target "_blank"}
         (t :help/terms)]]
+      [:li
+       [:a {:href "https://github.com/logseq/awesome-logseq"
+            :target "_blank"}
+        (t :help/awesome-logseq)]]
       [:li
        [:a {:href "https://discord.gg/KpN4eHY"
             :target "_blank"}
@@ -250,7 +254,7 @@
          "(println \"Hello world!\")")]]
          [:tr [:td "[label](https://www.example.com)"]
           [:td.text-right
-           [:a {:href "https://www.example.com"}
+           [:a {:href "https://www.example.com" :target "_blank"}
             "label"]]]
          [:tr [:td "![image](https://asset.logseq.com/static/img/logo.png)"]
           [:td.text-right

+ 9 - 17
src/main/frontend/components/page.cljs

@@ -89,7 +89,7 @@
 
 (rum/defc page-blocks-cp < rum/reactive
   db-mixins/query
-  [repo page-e sidebar?]
+  [repo page-e {:keys [sidebar? preview?] :as config}]
   (when page-e
     (let [page-name (or (:block/name page-e)
                         (str (:block/uuid page-e)))
@@ -105,11 +105,12 @@
                            :block/file {:db/id (:db/id (:block/file page-e))}})
                         {:journal? journal?
                          :page-name page-name})
-          hiccup-config {:id (if block? (str block-id) page-name)
-                         :sidebar? sidebar?
-                         :block? block?
-                         :editor-box editor/box
-                         :page page}
+          hiccup-config (merge
+                         {:id (if block? (str block-id) page-name)
+                          :block? block?
+                          :editor-box editor/box
+                          :page page}
+                         config)
           hiccup-config (common-handler/config-with-document-mode hiccup-config)
           hiccup (block/->hiccup page-blocks hiccup-config {})]
       (page-blocks-inner page-name page-blocks hiccup sidebar?))))
@@ -117,7 +118,7 @@
 (defn contents-page
   [page]
   (when-let [repo (state/get-current-repo)]
-    (page-blocks-cp repo page true)))
+    (page-blocks-cp repo page {:sidebar? true})))
 
 (rum/defc today-queries < rum/reactive
   [repo today? sidebar?]
@@ -363,15 +364,6 @@
                                    "origin-top-right.absolute.right-0.top-10.mt-2.rounded-md.shadow-lg.whitespace-no-wrap.dropdown-overflow-auto.page-drop-options")
                      :z-index     1})]))])
            [:div
-            (when (and repo (not block?))
-              (let [alias (db/get-page-alias-names repo page-name)]
-                (when (seq alias)
-                  [:div.text-sm.ml-1.mb-4 {:key "page-file"}
-                   [:span.opacity-50 "Alias: "]
-                   (for [item alias]
-                     [:a.ml-1.mr-1 {:href (rfe/href :page {:name item})}
-                      item])])))
-
             (when (and block? (not sidebar?))
               (let [config {:id "block-parent"
                             :block? true}]
@@ -382,7 +374,7 @@
             (let [page (if block?
                          (db/entity repo [:block/uuid block-id])
                          page)]
-              (page-blocks-cp repo page sidebar?))]]
+              (page-blocks-cp repo page {:sidebar? sidebar?}))]]
 
            (when-not block?
              (today-queries repo today? sidebar?))

+ 0 - 497
src/main/frontend/components/page.cljs.~e600c29046589a778b9a28a2122606358d7ab458~

@@ -1,497 +0,0 @@
-(ns frontend.components.page
-  (:require [rum.core :as rum]
-            [frontend.util :as util :refer-macros [profile]]
-            [frontend.util.marker :as marker]
-            [frontend.tools.html-export :as html-export]
-            [frontend.handler.file :as file]
-            [frontend.handler.page :as page-handler]
-            [frontend.handler.ui :as ui-handler]
-            [frontend.handler.common :as common-handler]
-            [frontend.handler.route :as route-handler]
-            [frontend.handler.graph :as graph-handler]
-            [frontend.handler.notification :as notification]
-            [frontend.handler.editor :as editor-handler]
-            [frontend.state :as state]
-            [clojure.string :as string]
-            [frontend.components.block :as block]
-            [frontend.components.editor :as editor]
-            [frontend.components.reference :as reference]
-            [frontend.components.svg :as svg]
-            [frontend.components.export :as export]
-            [frontend.extensions.graph-2d :as graph-2d]
-            [frontend.ui :as ui]
-            [frontend.components.content :as content]
-            [frontend.config :as config]
-            [frontend.db :as db]
-            [frontend.db.model :as model]
-            [frontend.db.utils :as db-utils]
-            [frontend.mixins :as mixins]
-            [frontend.db-mixins :as db-mixins]
-            [goog.dom :as gdom]
-            [goog.object :as gobj]
-            [frontend.utf8 :as utf8]
-            [frontend.date :as date]
-            [frontend.graph :as graph]
-            [frontend.format.mldoc :as mldoc]
-            [cljs-time.coerce :as tc]
-            [cljs-time.core :as t]
-            [cljs.pprint :as pprint]
-            [frontend.context.i18n :as i18n]
-            [reitit.frontend.easy :as rfe]
-            [frontend.text :as text]
-            [frontend.modules.shortcut.core :as shortcut]
-            [frontend.handler.block :as block-handler]))
-
-(defn- get-page-name
-  [state]
-  (let [route-match (first (:rum/args state))]
-    (get-in route-match [:parameters :path :name])))
-
-(defn- get-blocks
-  [repo page-name page-original-name block? block-id]
-  (when page-name
-    (if block?
-      (db/get-block-and-children repo block-id)
-      (do
-        (page-handler/add-page-to-recent! repo page-original-name)
-        (db/get-page-blocks repo page-name)))))
-
-(defn- open-first-block!
-  [state]
-  (let [blocks (nth (:rum/args state) 1)
-        block (first blocks)]
-    (when (:block/dummy? block)
-      (editor-handler/edit-block! block :max (:block/format block) (:block/uuid block))))
-  state)
-(rum/defc page-blocks-inner <
-  {:did-mount open-first-block!
-   :did-update open-first-block!}
-  [page-name page-blocks hiccup sidebar?]
-  [:div.page-blocks-inner
-   (rum/with-key
-     (content/content page-name
-                      {:hiccup   hiccup
-                       :sidebar? sidebar?})
-     (str page-name "-hiccup"))])
-
-(declare page)
-
-(defn- get-page-format
-  [page-name]
-  (let [block? (util/uuid-string? page-name)
-        block-id (and block? (uuid page-name))
-        page (if block-id
-               (:block/name (:block/page (db/entity [:block/uuid block-id])))
-               page-name)]
-    (db/get-page-format page)))
-
-(rum/defc page-blocks-cp < rum/reactive
-  db-mixins/query
-  [repo page-e sidebar?]
-  (let [page-name (or (:block/name page-e)
-                      (str (:block/uuid page-e)))
-        page-original-name (or (:block/original-name page-e) page-name)
-        format (get-page-format page-name)
-        journal? (db/journal-page? page-name)
-        block? (util/uuid-string? page-name)
-        block-id (and block? (uuid page-name))
-        raw-page-blocks (get-blocks repo page-name page-original-name block? block-id)
-        page-blocks (block-handler/with-dummy-block raw-page-blocks format
-                      (if (empty? raw-page-blocks)
-                        {:block/page {:db/id (:db/id page-e)}
-                         :block/file {:db/id (:db/id (:block/file page-e))}})
-                      {:journal? journal?
-                       :page-name page-name})
-        hiccup-config {:id (if block? (str block-id) page-name)
-                       :sidebar? sidebar?
-                       :block? block?
-                       :editor-box editor/box
-                       :page page}
-        hiccup-config (common-handler/config-with-document-mode hiccup-config)
-        hiccup (block/->hiccup page-blocks hiccup-config {})]
-    (page-blocks-inner page-name page-blocks hiccup sidebar?)))
-
-(defn contents-page
-  [page]
-  (when-let [repo (state/get-current-repo)]
-    (page-blocks-cp repo page true)))
-
-(rum/defc today-queries < rum/reactive
-  [repo today? sidebar?]
-  (when (and today? (not sidebar?))
-    (let [queries (state/sub [:config repo :default-queries :journals])]
-      (when (seq queries)
-        [:div#today-queries.mt-10
-         (for [{:keys [title] :as query} queries]
-           (rum/with-key
-             (block/custom-query {:attr {:class "mt-10"}
-                                  :editor-box editor/box
-                                  :page page} query)
-             (str repo "-custom-query-" (:query query))))]))))
-
-(defn- delete-page!
-  [page-name]
-  (page-handler/delete! page-name
-                        (fn []
-                          (notification/show! (str "Page " page-name " was deleted successfully!")
-                                              :success)))
-  (state/close-modal!)
-  (route-handler/redirect-to-home!))
-
-(defn delete-page-dialog
-  [page-name]
-  (fn [close-fn]
-    (rum/with-context [[t] i18n/*tongue-context*]
-      [:div
-       [:div.sm:flex.sm:items-start
-        [:div.mx-auto.flex-shrink-0.flex.items-center.justify-center.h-12.w-12.rounded-full.bg-red-100.sm:mx-0.sm:h-10.sm:w-10
-         [:svg.h-6.w-6.text-red-600
-          {:stroke "currentColor", :view-box "0 0 24 24", :fill "none"}
-          [:path
-           {:d
-            "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
-            :stroke-width "2"
-            :stroke-linejoin "round"
-            :stroke-linecap "round"}]]]
-        [:div.mt-3.text-center.sm:mt-0.sm:ml-4.sm:text-left
-         [:h3#modal-headline.text-lg.leading-6.font-medium
-          (t :page/delete-confirmation)]]]
-
-       [: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 (fn []
-                       (delete-page! page-name))}
-          (t :yes)]]
-        [: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}
-          (t :cancel)]]]])))
-
-(rum/defcs rename-page-dialog-inner <
-  (shortcut/disable-all-shortcuts)
-  (rum/local "" ::input)
-  [state title page-name close-fn]
-  (let [input (get state ::input)]
-    (rum/with-context [[t] i18n/*tongue-context*]
-      [:div.w-full.sm:max-w-lg.sm:w-96
-       [:div.sm:flex.sm:items-start
-        [:div.mt-3.text-center.sm:mt-0.sm:text-left
-         [:h3#modal-headline.text-lg.leading-6.font-medium
-          (t :page/rename-to title)]]]
-
-       [:input.form-input.block.w-full.sm:text-sm.sm:leading-5.my-2
-        {:auto-focus true
-         :on-change (fn [e]
-                      (reset! input (util/evalue e)))}]
-
-       [: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 (fn []
-                       (let [value (string/trim @input)]
-                         (when-not (string/blank? value)
-                           (page-handler/rename! page-name value)
-                           (state/close-modal!))))}
-          (t :submit)]]
-        [: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}
-          (t :cancel)]]]])))
-
-(defn rename-page-dialog
-  [title page-name]
-  (fn [close-fn]
-    (rename-page-dialog-inner title page-name close-fn)))
-
-(defn tagged-pages
-  [repo tag]
-  (let [pages (db/get-tag-pages repo tag)]
-    (when (seq pages)
-      [:div.references.mt-6.flex-1.flex-row
-       [:div.content
-        (ui/foldable
-         [:h2.font-bold.opacity-50 (util/format "Pages tagged with \"%s\"" tag)]
-         [:ul.mt-2
-          (for [[original-name name] pages]
-            [:li {:key (str "tagged-page-" name)}
-             [:a {:href (rfe/href :page {:name name})}
-              original-name]])] false)]])))
-
-;; A page is just a logical block
-(rum/defcs page < rum/reactive
-  [state {:keys [repo page-name preview?] :as option}]
-  (when-let [path-page-name (or page-name
-                                (get-page-name state)
-                                (state/get-current-page))]
-    (let [current-repo (state/sub :git/current-repo)
-         repo (or repo current-repo)
-         page-name (string/lower-case path-page-name)
-         block? (util/uuid-string? page-name)
-         block-id (and block? (uuid page-name))
-         format (let [page (if block-id
-                             (:block/name (:block/page (db/entity [:block/uuid block-id])))
-                             page-name)]
-                  (db/get-page-format page))
-         journal? (db/journal-page? page-name)
-         sidebar? (:sidebar? option)]
-     (rum/with-context [[t] i18n/*tongue-context*]
-       (let [route-page-name path-page-name
-             page (if block?
-                    (->> (:db/id (:block/page (db/entity repo [:block/uuid block-id])))
-                         (db/entity repo))
-                    (db/entity repo [:block/name page-name]))
-             ;; TODO: replace page with frontend.format.block/page->map
-             page (if page page (do
-                                  (db/transact! repo [{:block/name page-name
-                                                       :block/original-name path-page-name
-                                                       :block/uuid (db/new-block-id)}])
-                                  (db/entity repo [:block/name page-name])))
-             {:keys [title] :as properties} (:block/properties page)
-             page-name (:block/name page)
-             page-original-name (:block/original-name page)
-             title (or title page-original-name page-name)
-             today? (and
-                     journal?
-                     (= page-name (string/lower-case (date/journal-name))))
-             developer-mode? (state/sub [:ui/developer-mode?])
-             public? (true? (:public properties))]
-         [:div.flex-1.page.relative (if (seq (:block/tags page))
-                                      (let [page-names (model/get-page-names-by-ids (map :db/id (:block/tags page)))]
-                                        {:data-page-tags (text/build-data-value page-names)})
-                                      {})
-          [:div.relative
-           (when (and (not sidebar?)
-                      (not block?))
-             [:div.flex.flex-row.space-between
-              [:div.flex-1.flex-row
-               [:a {:on-click (fn [e]
-                                (.preventDefault e)
-                                (when (gobj/get e "shiftKey")
-                                  (when-let [page (db/pull repo '[*] [:block/name page-name])]
-                                    (state/sidebar-add-block!
-                                     repo
-                                     (:db/id page)
-                                     :page
-                                     {:page page}))))}
-                [:h1.title {:style {:margin-left -2}}
-                 (if page-original-name
-                   (if (and (string/includes? page-original-name "[[")
-                            (string/includes? page-original-name "]]"))
-                     (let [ast (mldoc/->edn page-original-name (mldoc/default-config format))]
-                       (block/markup-element-cp {} (ffirst ast)))
-                     page-original-name)
-                   (or
-                    page-name
-                    path-page-name))]]]
-              (when (not config/publishing?)
-                (let [contents? (= (string/lower-case (str page-name)) "contents")
-                      links (fn [] (->>
-                                   [(when-not contents?
-                                      {:title   (t :page/add-to-contents)
-                                       :options {:on-click (fn [] (page-handler/handle-add-page-to-contents! page-original-name))}})
-
-                                    {:title "Go to presentation mode"
-                                     :options {:on-click (fn []
-                                                           (state/sidebar-add-block!
-                                                            repo
-                                                            (:db/id page)
-                                                            :page-presentation
-                                                            {:page page}))}}
-                                    (when-not contents?
-                                      {:title   (t :page/rename)
-                                       :options {:on-click #(state/set-modal! (rename-page-dialog title page-name))}})
-
-                                    (when-let [file-path (and (util/electron?) (page-handler/get-page-file-path))]
-                                      [{:title   (t :page/open-in-finder)
-                                        :options {:on-click #(js/window.apis.showItemInFolder file-path)}}
-                                       {:title   (t :page/open-with-default-app)
-                                        :options {:on-click #(js/window.apis.openPath file-path)}}])
-
-                                    (when-not contents?
-                                      {:title   (t :page/delete)
-                                       :options {:on-click #(state/set-modal! (delete-page-dialog page-name))}})
-
-                                    (when (state/get-current-page)
-                                      {:title   (t :export)
-                                       :options {:on-click #(state/set-modal! export/export-page)}})
-
-                                    (when (util/electron?)
-                                      {:title   (t (if public? :page/make-private :page/make-public))
-                                       :options {:on-click
-                                                 (fn []
-                                                   (page-handler/update-public-attribute!
-                                                    page-name
-                                                    (if public? false true))
-                                                   (state/close-modal!))}})
-
-                                    (when developer-mode?
-                                      {:title   "(Dev) Show page data"
-                                       :options {:on-click (fn []
-                                                             (let [page-data (with-out-str (pprint/pprint (db/pull (:db/id page))))]
-                                                               (println page-data)
-                                                               (notification/show!
-                                                                [:div
-                                                                 [:pre.code page-data]
-                                                                 [:br]
-                                                                 (ui/button "Copy to clipboard"
-                                                                   :on-click #(.writeText js/navigator.clipboard page-data))]
-                                                                :success
-                                                                false)))}})]
-                                   (flatten)
-                                   (remove nil?)))]
-                  [:div.flex.flex-row
-                   [:a.opacity-30.hover:opacity-100.page-op.mr-1
-                    {:title "Search in current page"
-                     :on-click #(route-handler/go-to-search! :page)}
-                    svg/search]
-                   (ui/dropdown-with-links
-                    (fn [{:keys [toggle-fn]}]
-                      [:a.cp__vertial-menu-button
-                       {:title    "More options"
-                        :on-click toggle-fn}
-                       (svg/vertical-dots nil)])
-                    links
-                    {:modal-class (util/hiccup->class
-                                   "origin-top-right.absolute.right-0.top-10.mt-2.rounded-md.shadow-lg.whitespace-no-wrap.dropdown-overflow-auto.page-drop-options")
-                     :z-index     1})]))])
-           [:div
-            (when (and repo (not block?))
-              (let [alias (db/get-page-alias-names repo page-name)]
-                (when (seq alias)
-                  [:div.text-sm.ml-1.mb-4 {:key "page-file"}
-                   [:span.opacity-50 "Alias: "]
-                   (for [item alias]
-                     [:a.ml-1.mr-1 {:href (rfe/href :page {:name item})}
-                      item])])))
-
-            (when (and block? (not sidebar?))
-              (let [config {:id "block-parent"
-                            :block? true}]
-                [:div.mb-4
-                 (block/block-parents config repo block-id format)]))
-
-            ;; blocks
-            (let [page (if block?
-                         (db/entity repo [:block/uuid block-id])
-                         page)]
-              (page-blocks-cp repo page sidebar?))]]
-
-          (when-not block?
-            (today-queries repo today? sidebar?))
-
-          (tagged-pages repo page-name)
-
-          ;; referenced blocks
-          [:div {:key "page-references"}
-           (rum/with-key
-             (reference/references route-page-name false)
-             (str route-page-name "-refs"))]
-
-          ;; TODO: or we can lazy load them
-          (when-not sidebar?
-            [:div {:key "page-unlinked-references"}
-             (reference/unlinked-references route-page-name)])])))))
-
-(defonce layout (atom [js/window.outerWidth js/window.outerHeight]))
-
-(defonce graph-ref (atom nil))
-(defonce show-journal? (atom false))
-
-(rum/defcs global-graph < rum/reactive
-  (mixins/event-mixin
-   (fn [state]
-     (mixins/listen state js/window "resize"
-                    (fn [e]
-                      (reset! layout [js/window.outerWidth js/window.outerHeight])))))
-  [state]
-  (let [theme (state/sub :ui/theme)
-        sidebar-open? (state/sub :ui/sidebar-open?)
-        [width height] (rum/react layout)
-        dark? (= theme "dark")
-        graph (graph-handler/build-global-graph theme (rum/react show-journal?))]
-    (rum/with-context [[t] i18n/*tongue-context*]
-      [:div.relative#global-graph
-       (if (seq (:nodes graph))
-         (graph-2d/graph
-          (graph/build-graph-opts
-           graph
-           dark?
-           {:width (if (and (> width 1280) sidebar-open?)
-                     (- width 24 600)
-                     (- width 24))
-            :height height
-            :ref (fn [v] (reset! graph-ref v))
-            :ref-atom graph-ref}))
-         [:div.ls-center.mt-20
-          [:p.opacity-70.font-medium "Empty"]])
-       [:div.absolute.top-10.left-5
-        [:div.flex.flex-col
-         [:a.text-sm.font-medium
-          {:on-click (fn [_e]
-                       (swap! show-journal? not))}
-          (str (t :page/show-journals)
-               (if @show-journal? " (ON)"))]]]])))
-
-(rum/defc all-pages < rum/reactive
-  ;; {:did-mount (fn [state]
-  ;;               (let [current-repo (state/sub :git/current-repo)]
-  ;;                 (js/setTimeout #(db/remove-orphaned-pages! current-repo) 0))
-  ;;               state)}
-  []
-  (let [current-repo (state/sub :git/current-repo)]
-    (rum/with-context [[t] i18n/*tongue-context*]
-      [:div.flex-1
-       [:h1.title (t :all-pages)]
-       (when current-repo
-         (let [pages (page-handler/get-pages-with-modified-at current-repo)]
-           [:table.table-auto
-            [:thead
-             [:tr
-              [:th (t :block/name)]
-              [:th (t :file/last-modified-at)]]]
-            [:tbody
-             (for [page pages]
-               [:tr {:key page}
-                [:td [:a {:on-click (fn [e]
-                                      (let [repo (state/get-current-repo)
-                                            page (db/pull repo '[*] [:block/name (string/lower-case page)])]
-                                        (when (gobj/get e "shiftKey")
-                                          (state/sidebar-add-block!
-                                           repo
-                                           (:db/id page)
-                                           :page
-                                           {:page page}))))
-                          :href (rfe/href :page {:name page})}
-                      page]]
-                [:td [:span.text-gray-500.text-sm
-                      (t :file/no-data)]]])]]))])))
-
-(rum/defcs new < rum/reactive
-  (rum/local "" ::title)
-  (mixins/event-mixin
-   (fn [state]
-     (mixins/on-enter state
-                      :node (gdom/getElement "page-title")
-                      :on-enter (fn []
-                                  (let [title @(get state ::title)]
-                                    (when-not (string/blank? title)
-                                      (page-handler/create! title)))))))
-  [state]
-  (rum/with-context [[t] i18n/*tongue-context*]
-    (let [title (get state ::title)]
-      [:div#page-new.flex-1.flex-col {:style {:flex-wrap "wrap"}}
-       [:div.mt-10.mb-2 {:style {:font-size "1.5rem"}}
-        (t :page/new-title)]
-       [:input#page-title.focus:outline-none.ml-1
-        {:style {:border "none"
-                 :font-size "1.8rem"
-                 :max-width 300}
-         :auto-focus true
-         :auto-complete "off"
-         :on-change (fn [e]
-                      (reset! title (util/evalue e)))}]])))

+ 48 - 29
src/main/frontend/components/settings.cljs

@@ -1,25 +1,25 @@
 (ns frontend.components.settings
-  (:require [rum.core :as rum]
-            [frontend.ui :as ui]
+  (:require [clojure.string :as string]
             [frontend.components.svg :as svg]
-            [frontend.handler.notification :as notification]
-            [frontend.handler.user :as user-handler]
-            [frontend.handler.ui :as ui-handler]
-            [frontend.handler.repo :as repo-handler]
+            [frontend.config :as config]
+            [frontend.context.i18n :as i18n]
+            [frontend.date :as date]
+            [frontend.dicts :as dicts]
+            [frontend.handler :as handler]
             [frontend.handler.config :as config-handler]
+            [frontend.handler.notification :as notification]
             [frontend.handler.page :as page-handler]
             [frontend.handler.route :as route-handler]
-            [frontend.handler :as handler]
+            [frontend.handler.ui :as ui-handler]
+            [frontend.handler.user :as user-handler]
+            [frontend.modules.instrumentation.core :as instrument]
             [frontend.state :as state]
+            [frontend.ui :as ui]
+            [frontend.util :refer [classnames] :as util]
             [frontend.version :refer [version]]
-            [frontend.util :as util]
-            [frontend.config :as config]
-            [frontend.dicts :as dicts]
-            [clojure.string :as string]
             [goog.object :as gobj]
-            [frontend.context.i18n :as i18n]
             [reitit.frontend.easy :as rfe]
-            [frontend.date :as date]))
+            [rum.core :as rum]))
 
 (rum/defcs set-email < (rum/local "" ::email)
   [state]
@@ -155,8 +155,9 @@
         current-repo (state/get-current-repo)
         enable-journals? (state/enable-journals? current-repo)
         enable-encryption? (state/enable-encryption? current-repo)
-        sentry-disabled? (state/sub :sentry/disabled?)
+        instrument-disabled? (state/sub :instrument/disabled?)
         logical-outdenting? (state/logical-outdenting?)
+        enable-tooltip? (state/enable-tooltip?)
         enable-git-auto-push? (state/enable-git-auto-push? current-repo)
         enable-block-time? (state/enable-block-time?)
         show-brackets? (state/show-brackets?)
@@ -166,23 +167,31 @@
         developer-mode? (state/sub [:ui/developer-mode?])
         theme (state/sub :ui/theme)
         dark? (= "dark" theme)
+        system-theme? (state/sub :ui/system-theme?)
         switch-theme (if dark? "white" "dark")]
     (rum/with-context [[t] i18n/*tongue-context*]
       [:div#settings.cp__settings-main
        [:h1.title (t :settings)]
 
        [:div.panel-wrap
-        [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
+
+        ;;; theme modes
+        [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4
          [:label.block.text-sm.font-medium.leading-5.opacity-70
           {:for "toggle_theme"}
           (t :right-side-bar/switch-theme (string/capitalize switch-theme))]
-         [:div.flex.flex-row.mt-1.sm:mt-0.sm:col-span-2
+         [:div.flex.flex-row.mt-1.sm:mt-0.sm:col-span-1
           [:div.rounded-md.sm:max-w-xs
-           (ui/toggle dark?
-                      (fn []
-                        (state/set-theme! switch-theme))
-                      true)]
-          [:span.ml-4.opacity-50.text-sm "t t"]]]
+
+           [:ul.theme-modes-options
+            [:li {:on-click (partial state/use-theme-mode! "light")
+                  :class    (classnames [{:active (and (not system-theme?) (not dark?))}])} [:i.mode-light] [:strong "light"]]
+            [:li {:on-click (partial state/use-theme-mode! "dark")
+                  :class    (classnames [{:active (and (not system-theme?) dark?)}])} [:i.mode-dark] [:strong "dark"]]
+            [:li {:on-click (partial state/use-theme-mode! "system")
+                  :class    (classnames [{:active system-theme?}])} [:i.mode-system] [:strong "system"]]]]]
+
+         [:span.ml-4.opacity-50.text-sm.px-5 "t t"]]
 
         [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
          [:label.block.text-sm.font-medium.leading-5.opacity-70
@@ -292,12 +301,19 @@
         (toggle "preferred_outdenting"
                 (ui/tippy {:html (outdenting-hint)
                            :interactive true
-                           :theme "customized"}
+                           :theme "customized"
+                           :disabled false}
                           (t :settings-page/preferred-outdenting))
                 logical-outdenting?
                 (fn []
                   (config-handler/toggle-logical-outdenting!)))
 
+        (toggle "enable_tooltip"
+                (t :settings-page/enable-tooltip)
+                enable-tooltip?
+                (fn []
+                  (config-handler/toggle-ui-enable-tooltip!)))
+
         (toggle "enable_timetracking"
                 (t :settings-page/enable-timetracking)
                 enable-timetracking?
@@ -363,6 +379,16 @@
 
        [:hr]
 
+       [:div.panel-wrap
+        [:p "Logseq will never collect your local graph database or sell your data."]
+        (toggle "usage-diagnostics"
+                (t :settings-page/disable-sentry)
+                (not instrument-disabled?)
+                (fn [] (instrument/disable-instrument
+                        (not instrument-disabled?))))]
+
+       [:hr]
+
        [:div.panel-wrap
 
         [:div.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-center.sm:pt-5
@@ -382,13 +408,6 @@
           [:div.ver version]
           (if (util/electron?) (app-updater))]]
 
-        (toggle "disable_sentry"
-                (t :settings-page/disable-sentry)
-                sentry-disabled?
-                (fn []
-                  (let [value (not sentry-disabled?)]
-                    (state/set-sentry-disabled! value))))
-
         [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
          [:label.block.text-sm.font-medium.leading-5.opacity-70
           {:for "developer_mode"}

+ 49 - 0
src/main/frontend/components/settings.css

@@ -58,6 +58,55 @@
         @apply text-sm;
       }
     }
+
+    .theme-modes-options {
+      @apply flex items-center list-none m-0;
+
+      > li {
+        @apply m-0 pr-2 opacity-90 hover:opacity-100;
+
+        &:hover {
+          cursor: pointer;
+        }
+
+        &.active {
+          @apply opacity-100;
+          cursor: inherit;
+
+          > i {
+            border-color: var(--ls-link-text-color);
+            border-width: 2px;
+          }
+        }
+
+        > i {
+          display: block;
+          width: 80px;
+          height: 57px;
+          background-color: #a4b5b6;
+          background: url("../img/theme-modes.png") no-repeat;
+          background-size: 355%;
+          border-radius: 4px;
+          overflow: hidden;
+          border-width: 0;
+          border-style: solid;
+          border-color: transparent;
+
+
+          &.mode-dark {
+            background-position-x: -97px;
+          }
+
+          &.mode-system {
+            background-position-x: -194px;
+          }
+        }
+
+        > strong {
+          @apply block pr-2 pt-1.5 text-center text-xs font-medium;
+        }
+      }
+    }
   }
 
   &-app-updater {

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

@@ -288,6 +288,7 @@
         current-repo (state/sub :git/current-repo)
         granted? (state/sub [:nfs/user-granted? (state/get-current-repo)])
         theme (state/sub :ui/theme)
+        system-theme? (state/sub :ui/system-theme?)
         white? (= "white" (state/sub :ui/theme))
         settings-open? (state/sub :ui/settings-open?)
         sidebar-open?  (state/sub :ui/sidebar-open?)
@@ -306,6 +307,7 @@
         :nfs-granted?  granted?
         :db-restoring? db-restoring?
         :sidebar-open? sidebar-open?
+        :system-theme? system-theme?
         :on-click      editor-handler/unhighlight-blocks!}
 
        [:div.theme-inner
@@ -315,7 +317,7 @@
           :route-match route-match})
         [:div.#app-container.h-screen.flex
          [:div.flex-1.h-full.flex.flex-col.overflow-y-auto#left-container.relative
-          [:div
+          [:div.scrollbar-spacing#main-container
            (header/header {:open-fn        open-fn
                            :white?         white?
                            :current-repo   current-repo

+ 13 - 0
src/main/frontend/components/sidebar.css

@@ -198,3 +198,16 @@
 .cp__sidebar-main-content[data-is-global-graph-pages='true'] {
   padding: 0;
 }
+
+@supports not (overflow-y: overlay) {
+  .scrollbar-spacing {
+    overflow-y: auto;
+  }
+}
+
+@supports (overflow-y: overlay) {
+  .scrollbar-spacing {
+    overflow-y: overlay;
+  }
+}
+

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

@@ -1,12 +1,13 @@
 (ns frontend.components.theme
   (:require [rum.core :as rum]
             [frontend.util :as util]
+            [frontend.ui :as ui]
             [frontend.handler.route :as route-handler]
             [frontend.handler.plugin :as plugin-handler]
             [frontend.components.svg :as svg]))
 
 (rum/defc container
-  [{:keys [route theme on-click nfs-granted? db-restoring? sidebar-open?] :as props} child]
+  [{:keys [route theme on-click nfs-granted? db-restoring? sidebar-open? system-theme?] :as props} child]
   (rum/use-effect!
    #(let [doc js/document.documentElement
           cls (.-classList doc)]
@@ -29,6 +30,11 @@
           (route-handler/update-page-title! route))))
    [nfs-granted? db-restoring? route])
 
+  (rum/use-effect!
+   #(when system-theme?
+      (ui/setup-system-theme-effect!))
+   [system-theme?])
+
   [:div
    {:class    (str theme "-theme")
     :on-click on-click}

+ 8 - 6
src/main/frontend/db/model.cljs

@@ -73,6 +73,7 @@
     :block/file
     :block/parent
     :block/unordered
+    :block/heading-level
     {:block/page [:db/id :block/name :block/original-name :block/journal-day]}
     {:block/_parent ...}])
 
@@ -318,9 +319,12 @@
 (defn get-page-format
   [page-name]
   (or
-   (when-let [file (:block/file (db-utils/entity [:block/name page-name]))]
-     (when-let [path (:file/path (db-utils/entity (:db/id file)))]
-       (format/get-format path)))
+   (let [page (db-utils/entity [:block/name page-name])]
+     (or
+      (:block/format page)
+      (when-let [file (:block/file page)]
+        (when-let [path (:file/path (db-utils/entity (:db/id file)))]
+          (format/get-format path)))))
    :markdown))
 
 (defn page-alias-set
@@ -520,9 +524,7 @@
 ;; FIXME: alert
 (defn- keep-only-one-file
   [blocks parent]
-  (if-let [file (:db/id (:block/file parent))]
-    (filter (fn [b] (= (:db/id (:block/file b)) file)) blocks)
-    blocks))
+  (filter (fn [b] (= (:block/file b) (:block/file (first blocks)))) blocks))
 
 (defn sort-by-left
   [blocks parent]

+ 25 - 16
src/main/frontend/db/query_dsl.cljs

@@ -14,7 +14,8 @@
             [clojure.walk :as walk]
             [clojure.core]
             [clojure.set :as set]
-            [frontend.template :as template]))
+            [frontend.template :as template]
+            [frontend.util.property :as property]))
 
 ;; Query fields:
 
@@ -218,16 +219,18 @@
 
        (and (= 'property fe)
             (= 3 (count e)))
-       (let [v (nth e 2)
-             v (if (or (string? v) (symbol? v))
-                 (some-> (name v)
-                         (text/page-ref-un-brackets!))
+       (let [k (string/replace (name (nth e 1)) "_" "-")
+             v (nth e 2)
+             v (if-not (nil? v)
+                 (property/parse-property k v)
                  v)
+             v (if (coll? v) (first v) v)
              sym (if (= current-filter 'or)
                    '?v
-                     (uniq-symbol counter "?v"))]
+                   (uniq-symbol counter "?v"))]
          [['?b :block/properties '?prop]
-          [(list 'get '?prop (keyword (nth e 1))) sym]
+          [(list 'missing? '$ '?b :block/name)]
+          [(list 'get '?prop (keyword k)) sym]
           (list
            'or
            [(list '= sym v)]
@@ -235,8 +238,11 @@
 
        (and (= 'property fe)
             (= 2 (count e)))
-       [['?b :block/properties '?prop]
-        [(list 'get '?prop (keyword (nth e 1)))]]
+       (let [k (string/replace (name (nth e 1)) "_" "-")]
+         [['?b :block/properties '?prop]
+          [(list 'missing? '$ '?b :block/name)]
+          [(list 'get '?prop (keyword k)) '?prop-v]
+          [true]])
 
        (or (= 'todo fe) (= 'task fe))
        (let [markers (if (coll? (first (rest e)))
@@ -280,20 +286,23 @@
          [['?b :block/page [:block/name page-name]]])
 
        (= 'page-property fe)
-       (let [[k v] (rest e)]
-         (if v
-           (let [v (some->> (name (nth e 2))
-                            (text/page-ref-un-brackets!))
+       (let [[k v] (rest e)
+             k (string/replace (name k) "_" "-")]
+         (if-not (nil? v)
+           (let [v (property/parse-property k v)
+                 v (if (coll? v) (first v) v)
                  sym '?v]
              [['?p :block/name]
               ['?p :block/properties '?prop]
-              [(list 'get '?prop (keyword (nth e 1))) sym]
+              [(list 'get '?prop (keyword k)) sym]
               (list
                'or
                [(list '= sym v)]
                [(list 'contains? sym v)])])
-           [['?p :block/properties '?prop]
-            [(list 'get '?prop (keyword (nth e 1)))]]))
+           [['?p :block/name]
+            ['?p :block/properties '?prop]
+            [(list 'get '?prop (keyword k)) '?prop-v]
+            [true]]))
 
        (= 'page-tags fe)
        (do

+ 9 - 9
src/main/frontend/dicts.cljs

@@ -15,7 +15,7 @@
         :on-boarding/outliner "outliner"
         :on-boarding/notebook-for-organizing-and " notebook for organizing and "
         :on-boarding/your-personal-knowledge-base " your personal knowledge base."
-        :on-boarding/notice "Notice that this project is in its alpha testing and under quick development, files might be corrupted."
+        :on-boarding/notice "Notice that this project is in its Beta testing and under quick development, remember to have a regular backup at least 1x/day."
         :on-boarding/features-desc "Use it to organize your todo list, to write your journals, or to record your unique life."
         :on-boarding/privacy "The server will never store or analyze your private notes. Your data are plain text files, we support both Markdown and Emacs Org mode for the time being. Even if the website is down or can't be maintained, your data is always yours."
         :on-boarding/inspired-by " is hugely inspired by "
@@ -64,6 +64,7 @@
         :help/privacy "Privacy policy"
         :help/terms "Terms"
         :help/community "Discord community"
+        :help/awesome-logseq "Awesome Logseq"
         :help/shortcuts "Keyboard Shortcuts"
         :help/shortcuts-triggers "Triggers"
         :help/shortcut "Shortcut"
@@ -91,7 +92,7 @@
         :strikethrough "Strikethrough"
         :code "Code"
         :right-side-bar/help "Help"
-        :right-side-bar/switch-theme "Switch to {1} theme"
+        :right-side-bar/switch-theme "Theme modes"
         :right-side-bar/theme "{1} theme"
         :right-side-bar/page "Page graph"
         :right-side-bar/recent "Recent"
@@ -202,12 +203,13 @@
         :content/click-to-edit "Click to edit"
         :settings-page/edit-config-edn "Edit config.edn (for current repo)"
         :settings-page/show-brackets "Show brackets"
-        :settings-page/disable-sentry "Disable Sentry.io (for error tracking)"
+        :settings-page/disable-sentry "Send usage data and diagnostics to Logseq"
         :settings-page/preferred-outdenting "Enable logical outdenting"
         :settings-page/custom-date-format "Preferred journal format"
         :settings-page/preferred-file-format "Preferred file format"
         :settings-page/preferred-workflow "Preferred workflow"
         :settings-page/enable-timetracking "Enable timetracking"
+        :settings-page/enable-tooltip "Enable tooltip"
         :settings-page/enable-journals "Enable journals"
         :settings-page/enable-encryption "Enable encryption feature"
         :settings-page/home-default-page "Set the default home page"
@@ -456,7 +458,7 @@
         :new-file "Neue Datei"
         :graph "Graph"
         :publishing "Veröffentlichung"
-        :export "Öffentliche Seiten exportieren"
+        :export "Exportieren"
         :all-graphs "Alle Graphen"
         :all-pages "Alle Seiten"
         :all-files "Alle Dateien"
@@ -678,7 +680,7 @@
            :on-boarding/outliner "大纲"
            :on-boarding/notebook-for-organizing-and " 笔记本, 用来整理和"
            :on-boarding/your-personal-knowledge-base "你的个人知识库。"
-           :on-boarding/notice "请注意, 本项目正在快速开发中, 存在文件损坏的风险。"
+           :on-boarding/notice "请注意, 本项目正在快速开发中, 请至少每天自行备份数据一次。"
            :on-boarding/features-desc "使用它来组织您的待办事项列表、 写日志或记录您的独特生活。"
            :on-boarding/privacy "服务器将永远不会存储或分析您的私人笔记。 您的数据是纯文本文件,我们暂时支持 Markdown 和 Emacs Org 模式。 即使网站已经关闭或无法维护,您的数据也永远属于您。"
            :on-boarding/inspired-by " 受到这些产品启发:"
@@ -757,7 +759,7 @@
            :strikethrough "删除线"
            :code "代码"
            :right-side-bar/help "帮助"
-           :right-side-bar/switch-theme "切换到 {1} 主题"
+           :right-side-bar/switch-theme "主题模式"
            :right-side-bar/theme "{1}主题"
            :right-side-bar/page "页面图谱"
            :right-side-bar/recent "最近"
@@ -946,7 +948,6 @@
              :on-boarding/outliner "大綱"
              :on-boarding/notebook-for-organizing-and " 筆記本, 用來整理和"
              :on-boarding/your-personal-knowledge-base "你的個人知識庫。"
-             :on-boarding/notice "請注意, 本項目正在快速開發中, 存在文件損壞的風險。"
              :on-boarding/features-desc "使用它來組織您的待辦事項列表、 寫日志或記錄您的獨特生活。"
              :on-boarding/privacy "服務器將永遠不會存儲或分析您的私人筆記。 您的數據是純文本文件,我們暫時支持 Markdown 和 Emacs Org 模式。 即使網站已經關閉或無法維護,您的數據也永遠屬於您。"
              :on-boarding/inspired-by " 受到這些產品啟發:"
@@ -1017,7 +1018,7 @@
              :strikethrough "刪除線"
              :code "代碼"
              :right-side-bar/help "幫助"
-             :right-side-bar/switch-theme "切換到 {1} 主題"
+             :right-side-bar/switch-theme "主題模式"
              :right-side-bar/theme "{1}主題"
              :right-side-bar/page "頁面圖譜"
              :right-side-bar/recent "最近"
@@ -1175,7 +1176,6 @@
         :on-boarding/outliner "buitelyn"
         :on-boarding/notebook-for-organizing-and " notaboek vir organisasie en "
         :on-boarding/your-personal-knowledge-base "  jou persoonlike kennis basis."
-        :on-boarding/notice "Wees asseblief bewus daarvan dat die projek nog in die begin fase is, en jou leers kan korrupteer word."
         :on-boarding/features-desc "Gebruik dit om jou lyste vir aksie te organiseer, joernale te skryf, en jou unieke lewe op te neem."
         :on-boarding/privacy "The server will never store or analyze your private notes. Your data are plain text files, we support both Markdown and Emacs Org mode for the time being. Even if the website is down or can't be maintained, your data is always yours."
         :on-boarding/inspired-by " is geweldig geïnspireer deur "

+ 1 - 1
src/main/frontend/extensions/code.cljs

@@ -64,7 +64,7 @@
                   fl (first lines)
                   ll (last lines)]
               (when (and fl ll)
-                (let [value' (str fl "\n" value "\n" ll)
+                (let [value' (str (string/trim fl) "\n" value "\n" (string/trim ll))
                       ;; FIXME: What if there're multiple code blocks with the same value?
                       content' (string/replace-first content full-content value')]
                   (editor-handler/save-block-if-changed! block content'))))))

+ 21 - 35
src/main/frontend/format/block.cljs

@@ -158,51 +158,31 @@
 (defonce non-parsing-properties
   (atom #{"background-color" "background_color"}))
 
+;; TODO: we should move this to mldoc
 (defn extract-properties
   [properties]
   (let [properties (into {} properties)
         page-refs (->>
                    (map (fn [v]
                           (when (string? v)
-                            (let [page-refs (->> (re-seq text/page-ref-re v)
-                                                 (map second))
-                                  tags (->> (string/split v #",")
-                                            (filter (fn [s] (= \# (first s))))
-                                            (map (fn [s] (subs s 1))))]
-                              (concat page-refs tags))))
+                            (let [result (text/split-page-refs-without-brackets v {:un-brackets? false})]
+                              (if (coll? result)
+                                (map text/page-ref-un-brackets! result)
+                                []))))
                      (vals properties))
                    (apply concat)
                    (remove string/blank?))
         properties (->> properties
                         (medley/map-kv (fn [k v]
-                                         (let [v (if (coll? v)
+                                         (let [k (-> (string/lower-case (name k))
+                                                     (string/replace " " "-")
+                                                     (string/replace "_" "-"))
+                                               k (if (contains? #{"custom_id" "custom-id"} k)
+                                                   "id"
+                                                   k)
+                                               v (if (coll? v)
                                                    v
-                                                   (let [k (name k)
-                                                         v (string/trim v)
-                                                         k (string/replace k " " "-")
-                                                         k (string/lower-case k)
-                                                         v (cond
-                                                             (= v "true")
-                                                             true
-                                                             (= v "false")
-                                                             false
-
-                                                             (util/safe-re-find #"^\d+$" v)
-                                                             (util/safe-parse-int v)
-
-                                                             (and (= "\"" (first v) (last v))) ; wrapped in ""
-                                                             (string/trim (subs v 1 (dec (count v))))
-
-                                                             (contains? @non-parsing-properties (string/lower-case k))
-                                                             v
-
-                                                             :else
-                                                             (if (and k v
-                                                                      (contains? config/markers k)
-                                                                      (util/safe-parse-int v))
-                                                               (util/safe-parse-int v)
-                                                               (text/split-page-refs-without-brackets v true)))]
-                                                     v))
+                                                   (property/parse-property k v))
                                                k (keyword k)
                                                v (if (and
                                                       (string? v)
@@ -404,6 +384,10 @@
         content (cons f body)]
     (string/join "\n" content)))
 
+(defn src-block?
+  [block]
+  (some (fn [x] (and (vector? x) (= "Src" (first x)))) (:body block)))
+
 (defn- get-block-content
   [utf8-content block format]
   (let [meta (:meta block)
@@ -412,11 +396,13 @@
                                   (:start-pos meta)
                                   end-pos)
                   (utf8/substring utf8-content
-                                  (:start-pos meta)))]
+                                  (:start-pos meta)))
+        content-orig content]
     (let [content (when content
                     (let [content (text/remove-level-spaces content format)]
                       (if (or (:pre-block? block)
-                              (= (:format block) :org))
+                              (= (:format block) :org)
+                              (src-block? block))
                         content
                         (remove-indentation-spaces content (:level block)))))]
       (if (= format :org)

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

@@ -129,9 +129,9 @@
                       properties-ast
                       (map (fn [[k v]]
                              (let [k (keyword (string/lower-case k))
-                                   v (if (contains? #{:title :description :roam_tags} k)
+                                   v (if (contains? #{:title :description :filters :roam_tags} k)
                                        v
-                                       (text/split-page-refs-without-brackets v true))]
+                                       (text/split-page-refs-without-brackets v))]
                                [k v])))
                           (reverse)
                           (into {}))

+ 32 - 34
src/main/frontend/handler.cljs

@@ -1,36 +1,41 @@
 (ns frontend.handler
-  (:require [frontend.state :as state]
+  (:require [cljs-bean.core :as bean]
+            [electron.ipc :as ipc]
+            [electron.listener :as el]
+            [frontend.components.editor :as editor]
+            [frontend.components.page :as page]
+            [frontend.config :as config]
             [frontend.db :as db]
             [frontend.db-schema :as db-schema]
-            [frontend.util :as util :refer-macros [profile]]
-            [frontend.config :as config]
-            [frontend.storage :as storage]
-            [clojure.string :as string]
-            [promesa.core :as p]
-            [cljs-bean.core :as bean]
-            [frontend.date :as date]
-            [frontend.search :as search]
-            [frontend.search.db :as search-db]
+            [frontend.handler.common :as common-handler]
+            [frontend.handler.events :as events]
+            [frontend.handler.file :as file-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.page :as page-handler]
             [frontend.handler.repo :as repo-handler]
-            [frontend.handler.file :as file-handler]
-            [frontend.handler.editor :as editor-handler]
             [frontend.handler.ui :as ui-handler]
-            [frontend.handler.web.nfs :as nfs]
+            [frontend.idb :as idb]
+            [frontend.modules.instrumentation.core :as instrument]
             [frontend.modules.shortcut.core :as shortcut]
-            [frontend.handler.events :as events]
-            [frontend.fs.watcher-handler :as fs-watcher-handler]
-            [frontend.ui :as ui]
+            [frontend.search :as search]
+            [frontend.search.db :as search-db]
+            [frontend.state :as state]
+            [frontend.storage :as storage]
+            [frontend.util :as util]
+            [frontend.version :as version]
             [goog.object :as gobj]
-            [frontend.idb :as idb]
             [lambdaisland.glogi :as log]
-            [frontend.handler.common :as common-handler]
-            [electron.listener :as el]
-            [electron.ipc :as ipc]
-            [frontend.version :as version]
-            [frontend.components.page :as page]
-            [frontend.components.editor :as editor]))
+            [promesa.core :as p]))
+
+(defn set-global-error-notification!
+  []
+  (set! js/window.onerror
+        (fn [message, source, lineno, colno, error]
+          (notification/show!
+           (str "message=" message "\nsource=" source "\nlineno=" lineno "\ncolno=" colno "\nerror=" error)
+           :error
+           ;; Don't auto-hide
+           false))))
 
 (defn- watch-for-date!
   []
@@ -138,19 +143,11 @@
         [{:url config/local-repo
           :example? true}]))))
 
-(defn init-sentry
-  []
-  (when-not (state/sentry-disabled?)
-    (let [cfg
-          {:dsn "https://[email protected]/5311485"
-           :release (util/format "logseq@%s" version/version)}]
-      (.init js/window.Sentry (clj->js cfg)))))
-
 (defn on-load-events
   []
-  (let [f (fn []
-            (when-not config/dev? (init-sentry)))]
-    (set! js/window.onload f)))
+  (set! js/window.onload
+        (fn []
+          (instrument/init))))
 
 (defn clear-cache!
   []
@@ -166,6 +163,7 @@
 
 (defn start!
   [render]
+  (set-global-error-notification!)
   (let [{:keys [me logged? repos]} (get-me-and-repos)]
     (when me (state/set-state! :me me))
     (register-components-fns!)

+ 4 - 0
src/main/frontend/handler/config.cljs

@@ -16,3 +16,7 @@
 (defn toggle-logical-outdenting! []
   (let [logical-outdenting? (state/logical-outdenting?)]
     (set-config! :editor/logical-outdenting? (not logical-outdenting?))))
+
+(defn toggle-ui-enable-tooltip! []
+  (let [enable-tooltip? (state/enable-tooltip?)]
+    (set-config! :ui/enable-tooltip? (not enable-tooltip?))))

+ 36 - 33
src/main/frontend/handler/editor.cljs

@@ -227,7 +227,7 @@
                           :else
                           (subs content 0 pos))
              content (property/remove-built-in-properties (:block/format block)
-                                                       content)]
+                                                          content)]
          (clear-selection! nil)
          (state/set-editing! edit-input-id content block text-range move-cursor?))))))
 
@@ -641,7 +641,7 @@
         new-block))))
 
 (defn update-timestamps-content!
-  [{:block/keys [repeated? marker] :as block} content]
+  [{:block/keys [repeated? marker format] :as block} content]
   (if repeated?
     (let [scheduled-ast (block-handler/get-scheduled-ast block)
           deadline-ast (block-handler/get-deadline-ast block)
@@ -655,7 +655,7 @@
       (when content
         (str (string/trimr content)
              "\n"
-             (util/format "* %s -> DONE [%s]"
+             (util/format (str (if (= format :org) "-" "*") " %s -> DONE [%s]")
                           marker
                           (date/get-local-date-time-string)))))
     content))
@@ -1922,35 +1922,36 @@
           has-children? (db/has-children? (state/get-current-repo)
                                           (:block/uuid editing-block))
           collapsed? (:collapsed (:block/properties editing-block))]
-      (match (mapv boolean [dummy? (seq fst-block-text) (seq snd-block-text)
-                           block-self? has-children? (= parent left) collapsed?])
-        ;; if editing-block is dummy, insert after page-block
-        [true _ _ _ _ _ _]
-        [parent-block false]
-
-        ;; when zoom at editing-block
-        [false _ _ true _ _ _]
-        [editing-block false]
-
-        ;; insert after editing-block
-        [false true _ false true _ false]
-        [editing-block false]
-        [false true _ false true _ true]
-        [editing-block true]
-        [false true _ false false _ _]
-        [editing-block true]
-        [false false false false true _ false]
-        [editing-block false]
-        [false false false false true _ true]
-        [editing-block true]
-        [false false false false false _ _]
-        [editing-block true]
-
-        ;; insert before editing-block
-        [false false true false _ true _]
-        [parent-block false]
-        [false false true false _ false _]
-        [left-block true]))))
+      (conj (match (mapv boolean [dummy? (seq fst-block-text) (seq snd-block-text)
+                                  block-self? has-children? (= parent left) collapsed?])
+                   ;; if editing-block is dummy, insert after page-block
+                   [true _ _ _ _ _ _]
+                   [parent-block false]
+
+                   ;; when zoom at editing-block
+                   [false _ _ true _ _ _]
+                   [editing-block false]
+
+                   ;; insert after editing-block
+                   [false true _ false true _ false]
+                   [editing-block false]
+                   [false true _ false true _ true]
+                   [editing-block true]
+                   [false true _ false false _ _]
+                   [editing-block true]
+                   [false false false false true _ false]
+                   [editing-block false]
+                   [false false false false true _ true]
+                   [editing-block true]
+                   [false false false false false _ _]
+                   [editing-block true]
+
+                   ;; insert before editing-block
+                   [false false true false _ true _]
+                   [parent-block false]
+                   [false false true false _ false _]
+                   [left-block true])
+            editing-block))))
 
 (defn- paste-block-tree-at-point
   ([tree exclude-properties] (paste-block-tree-at-point tree exclude-properties nil))
@@ -1960,8 +1961,9 @@
                   (db/entity [:block/original-name (state/get-current-page)])
                   (:block/page (db/entity (:db/id (state/get-edit-block)))))
          file (:block/file page)]
-     (when-let [[target-block sibling?] (get-block-tree-insert-pos-at-point)]
+     (when-let [[target-block sibling? editing-block] (get-block-tree-insert-pos-at-point)]
        (let [target-block (outliner-core/block target-block)
+             editing-block (outliner-core/block editing-block)
              format (or (:block/format target-block) (state/get-preferred-format))
              new-block-uuids (atom #{})
              metadata-replaced-blocks
@@ -2017,6 +2019,7 @@
                                                           :block/meta
                                                           :block/content
                                                           :block/title))))))))))))
+             _ (outliner-core/save-node editing-block)
              _ (outliner-core/insert-nodes metadata-replaced-blocks target-block sibling?)
              new-blocks (db/pull-many repo '[*] (map (fn [id] [:block/uuid id]) @new-block-uuids))]
          (db/refresh! repo {:key :block/insert :data new-blocks}))))))

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

@@ -100,7 +100,7 @@
                                            aliases (if (seq aliases)
                                                      (map
                                                        (fn [alias]
-                                                         {:block/name alias})
+                                                         {:block/name (string/lower-case alias)})
                                                        aliases))]
                                        (if (seq aliases)
                                          {:block/name page-name
@@ -190,9 +190,12 @@
                          (println "Parsing : " path)
                          (when content
                            ;; TODO: remove `text/scheduled-deadline-dash->star` once migration is done
-                           (let [content (text/scheduled-deadline-dash->star content)
-                                 utf8-content (utf8/encode content)]
-                             (extract-blocks-pages repo-url path content utf8-content)))))
+                           (let [org? (= "org" (string/lower-case (util/get-file-ext path)))]
+                             (let [content (if org?
+                                             content
+                                             (text/scheduled-deadline-dash->star content))
+                                   utf8-content (utf8/encode content)]
+                              (extract-blocks-pages repo-url path content utf8-content))))))
                       (remove empty?))]
       (when (seq result)
         (let [[pages block-ids blocks] (apply map concat result)

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

@@ -115,7 +115,8 @@
                                          :file/content content})]
                     (ok-handler file-contents))))
         (p/catch (fn [error]
-                   (log/error :load-files-error error))))))
+                   (log/error :nfs/load-files-error repo-url)
+                   (log/error :exception error))))))
 
 (defn reset-file!
   [repo-url file content]

+ 7 - 3
src/main/frontend/handler/page.cljs

@@ -69,8 +69,9 @@
            :or {redirect? true}}]
    (let [title (string/trim title)
          page (string/lower-case title)
-         tx (block/page-name->map title true)
          format (state/get-preferred-format)
+         tx (-> (block/page-name->map title true)
+                (assoc :block/format format))
          page-entity [:block/uuid (:block/uuid tx)]
          create-title-property? (and title (util/include-windows-reserved-chars? title))
          default-properties (default-properties-block title format page-entity)
@@ -393,8 +394,11 @@
 
 (defn get-filters
   [page-name]
-  (let [properties (db/get-page-properties page-name)]
-    (reader/read-string (get properties :filters "{}"))))
+  (let [properties (db/get-page-properties page-name)
+        properties-str (get properties :filters "{}")]
+    (try (reader/read-string properties-str)
+         (catch js/Error e
+           (log/error :syntax/filters e)))))
 
 (defn save-filter!
   [page-name filter-state]

+ 4 - 2
src/main/frontend/handler/web/nfs.cljs

@@ -164,7 +164,8 @@
                        (when (util/electron?)
                          (fs/watch-dir! dir-name)))))
            (p/catch (fn [error]
-                      (log/error :nfs/load-files-error error)))))
+                      (log/error :nfs/load-files-error repo)
+                      (log/error :exception error)))))
      (p/catch (fn [error]
                 (if (contains? #{"AbortError" "Error"} (gobj/get error "name"))
                   (state/set-loading-files! false)
@@ -278,7 +279,8 @@
                         (set-files! @path-handles))]
               (handle-diffs! repo nfs? old-files new-files handle-path path-handles re-index?))))
         (p/catch (fn [error]
-                   (log/error :nfs/load-files-error error)))
+                   (log/error :nfs/load-files-error repo)
+                   (log/error :exception error)))
         (p/finally (fn [_]
                      (state/set-graph-syncing? false))))))))
 

+ 18 - 0
src/main/frontend/modules/instrumentation/core.cljs

@@ -0,0 +1,18 @@
+(ns frontend.modules.instrumentation.core
+  (:require [frontend.modules.instrumentation.posthog :as posthog]
+            [frontend.modules.instrumentation.sentry :as sentry]
+            [frontend.state :as state]
+            [frontend.storage :as storage]))
+
+(defn init
+  []
+  (when-not (:instrument/disabled? @state/state)
+    (posthog/init)
+    (sentry/init)))
+
+(defn disable-instrument [disable?]
+  (state/set-state! :instrument/disabled? disable?)
+  (storage/set "instrument-disabled" disable?)
+  (posthog/opt-out disable?)
+  (when-not disable?
+    (sentry/init)))

+ 41 - 0
src/main/frontend/modules/instrumentation/posthog.cljs

@@ -0,0 +1,41 @@
+(ns frontend.modules.instrumentation.posthog
+  (:require [frontend.config :as cfg]
+            [frontend.util :as util]
+            [frontend.version :refer [version]]
+            ["posthog-js" :as posthog]))
+
+(def ^:const token "qUumrWobEk2dKiKt1b32CMEZy8fgNS94rb_Bq4WutPA")
+(def ^:const masked "masked")
+
+(defn register []
+  (posthog/register
+   (clj->js
+    {:app_type (if (util/electron?) "electron" "web")
+     :app_env (if cfg/dev? "development" "production")
+     :app_ver version
+     :schema_ver 0
+     ;; hack, did not find ways to hack data on-the-fly with posthog-js
+     :$ip masked
+     :$current_url masked})))
+
+(def config
+  {:api_host "https://app.posthog.com"
+   :persistence "localStorage"
+   :autocapture false
+   :disable_session_recording true
+   :mask_all_text true
+   :mask_all_element_attributes true
+   :loaded (fn [_] (register))})
+
+(defn init []
+  (posthog/init token (clj->js config)))
+
+(defn opt-out [opt-out?]
+  (if opt-out?
+    (posthog/opt_out_capturing)
+    (do
+      (init)
+      (posthog/opt_in_capturing))))
+
+(comment
+  (posthog/debug))

+ 17 - 0
src/main/frontend/modules/instrumentation/sentry.cljs

@@ -0,0 +1,17 @@
+(ns frontend.modules.instrumentation.sentry
+  (:require [frontend.version :refer [version]]
+            [frontend.util :as util]
+            [frontend.config :as cfg]
+            ["@sentry/browser" :as Sentry]
+            ["posthog-js" :as posthog]))
+
+(def config
+  {:dsn "https://[email protected]/5311485"
+   :release (util/format "logseq@%s" version)
+   :environment (if cfg/dev? "development" "production")
+   :integrations [(new posthog/SentryIntegration posthog "logseq" 5311485)]
+   :debug cfg/dev?
+   :tracesSampleRate 1.0})
+
+(defn init []
+  (Sentry/init (clj->js config)))

+ 4 - 8
src/main/frontend/modules/outliner/tree.cljs

@@ -33,13 +33,9 @@
     (reset! blocks (remove (set children) @blocks))
     children))
 
-(defn- with-children-and-refs
+(defn- with-children
   [block children]
-  (let [all-refs (->> (mapcat :block/refs children)
-                      (distinct))]
-    (assoc block
-           :block/children children
-           :block/refs-with-children all-refs)))
+  (assoc block :block/children children))
 
 (defn- blocks->vec-tree-aux
   [blocks root]
@@ -48,7 +44,7 @@
      (get-children blocks root)
      (map (fn [block]
             (let [children (blocks->vec-tree-aux blocks block)]
-              (with-children-and-refs block children)))))))
+              (with-children block children)))))))
 
 (defn- get-root-and-page
   [root-id]
@@ -74,7 +70,7 @@
 
       :else                             ; include root block
       (let [root-block (some #(when (= (:db/id %) (:db/id root)) %) @blocks)
-            root-block (with-children-and-refs root-block result)]
+            root-block (with-children root-block result)]
         [root-block]))))
 
 (defn- sort-blocks-aux

+ 15 - 11
src/main/frontend/modules/shortcut/core.cljs

@@ -81,27 +81,31 @@
     (swap! *installed dissoc install-id)))
 
 
+(defn- uninstall-shortcut-aux!
+  [state handler-id]
+  (some-> (get state :shortcut-key)
+          uninstall-shortcut!))
+
+(defn- install-shortcut-aux!
+  [state handler-id]
+  (let [install-id (-> handler-id
+                       (install-shortcut! {:state state}))]
+    (assoc state :shortcut-key install-id)))
+
 (defn mixin [handler-id]
   {:did-mount
    (fn [state]
-     (let [install-id (-> handler-id
-                          (install-shortcut! {:state state}))]
-       (assoc state :shortcut-key install-id)))
+     (install-shortcut-aux! state handler-id))
 
    :did-remount (fn [old-state new-state]
-
                   ;; uninstall
-                  (-> (get old-state :shortcut-key)
-                      uninstall-shortcut!)
+                  (uninstall-shortcut-aux! old-state handler-id)
 
                   ;; update new states
-                  (let [install-id (-> handler-id
-                                       (install-shortcut! {:state new-state}))]
-                    (assoc new-state :shortcut-key install-id)))
+                  (install-shortcut-aux! new-state handler-id))
    :will-unmount
    (fn [state]
-     (-> (get state :shortcut-key)
-         uninstall-shortcut!)
+     (uninstall-shortcut-aux! state handler-id)
      (dissoc state :shortcut-key))})
 
 (defn unlisten-all []

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

@@ -10,7 +10,9 @@
             [frontend.routes :as routes]
             [reitit.frontend :as rf]
             [reitit.frontend.easy :as rfe]
-            [cljs.reader :as reader]))
+            [cljs.reader :as reader]
+            [frontend.components.page :as component-page]
+            [frontend.components.editor :as component-editor]))
 
 ;; The publishing site should be as thin as possible.
 ;; Both files and git libraries can be removed.
@@ -56,10 +58,16 @@
     (set-router!)
     (rum/mount (page/current-page) node)))
 
+(defn- register-components-fns!
+  []
+  (state/set-page-blocks-cp! component-page/page-blocks-cp)
+  (state/set-editor-cp! component-editor/box))
+
 (defn ^:export init []
   ;; init is called ONCE when the page loads
   ;; this is called in the index.html and must be exported
   ;; so it is available even in :advanced release builds
+  (register-components-fns!)
   (restore-from-transit-str!)
   (restore-state!)
   (start))

+ 25 - 14
src/main/frontend/state.cljs

@@ -29,7 +29,7 @@
     :repo/changed-files nil
     :nfs/user-granted? {}
     :nfs/refreshing? nil
-    :sentry/disabled? (storage/get "sentry-disabled")
+    :instrument/disabled? (storage/get "instrument-disabled")
     ;; TODO: how to detect the network reliably?
     :network/online? true
     :indexeddb/support? true
@@ -54,6 +54,7 @@
     :ui/sidebar-open? false
     :ui/left-sidebar-open? false
     :ui/theme (or (storage/get :ui/theme) "dark")
+    :ui/system-theme? ((fnil identity true) (storage/get :ui/system-theme?))
     :ui/wide-mode? false
     ;; :show-all, :hide-block-body, :hide-block-children
     :ui/cycle-collapse :show-all
@@ -745,8 +746,7 @@
              ;; use set-change-value for now
              ;; until somebody can figure out why set! value doesn't work here
              ;; it seems to me textarea autoresize is completely broken
-             #_
-             (set! (.-value input) (string/trim content)))
+             #_(set! (.-value input) (string/trim content)))
            (when move-cursor?
              (util/move-cursor-to input pos))))))))
 
@@ -771,6 +771,22 @@
   (set-state! :ui/theme theme)
   (storage/set :ui/theme theme))
 
+(defn sync-system-theme!
+  []
+  (let [system-dark? (.-matches (js/window.matchMedia "(prefers-color-scheme: dark)"))]
+    (set-theme! (if system-dark? "dark" "white"))
+    (set-state! :ui/system-theme? true)
+    (storage/set :ui/system-theme? true)))
+
+(defn use-theme-mode!
+  [theme-mode]
+  (if-not (= theme-mode "system")
+    (do
+      (set-theme! (if (= theme-mode "light") "white" "dark"))
+      (set-state! :ui/system-theme? false)
+      (storage/set :ui/system-theme? false))
+    (sync-system-theme!)))
+
 (defn dark?
   []
   (= "dark" (:ui/theme @state)))
@@ -1017,6 +1033,12 @@
   []
   (update-state! :editor/new-block-toggle? not))
 
+(defn enable-tooltip?
+  []
+  (get (get (sub-config) (get-current-repo))
+       :ui/enable-tooltip?
+       true))
+
 (defn set-config!
   [repo-url value]
   (set-state! [:config repo-url] value)
@@ -1237,17 +1259,6 @@
   [value]
   (set-state! :block/component-editing-mode? value))
 
-(defn sentry-disabled?
-  []
-  (:sentry/disabled? @state))
-
-(defn set-sentry-disabled!
-  [value]
-  (set-state! :sentry/disabled? value)
-  (storage/set "sentry-disabled" value)
-  (when value
-    (.close js/window.Sentry)))
-
 (defn logical-outdenting?
   []
   (:editor/logical-outdenting?

+ 28 - 9
src/main/frontend/text.cljs

@@ -23,6 +23,8 @@
 
 (defonce page-ref-re-2 #"(\[\[.*?\]\])")
 
+(def page-ref-re-without-nested #"\[\[([^\[\]]+)\]\]")
+
 (defonce between-re #"\(between ([^\)]+)\)")
 
 (defn page-ref-un-brackets!
@@ -54,13 +56,22 @@
        (> (count (re-seq #"\[\[" s))
           (count (re-seq #"\]\]" s)))))
 
+(defn- ref-matched?
+  [s]
+  (let [x (re-seq #"\[\[" s)
+        y (re-seq #"\]\]" s)]
+    (and (> (count x) 0) (= (count x) (count y)))))
+
 (defn- concat-nested-pages
   [coll]
   (first
    (reduce (fn [[acc not-matched-s] s]
              (cond
                (and not-matched-s (= s "]]"))
-               [(conj acc (str not-matched-s s)) nil]
+               (let [s' (str not-matched-s s)]
+                 (if (ref-matched? s')
+                   [(conj acc s') nil]
+                   [acc s']))
 
                not-matched-s
                [acc (str not-matched-s s)]
@@ -73,23 +84,31 @@
 
 (defn split-page-refs-without-brackets
   ([s]
-   (split-page-refs-without-brackets s true))
-  ([s comma?]
+   (split-page-refs-without-brackets s {}))
+  ([s {:keys [un-brackets?]
+       :or {un-brackets? true}}]
    (cond
      (and (string? s)
             ;; Either a page ref, a tag or a comma separated collection
             (or (util/safe-re-find page-ref-re s)
-                (util/safe-re-find (if comma? #"[\,|,|#]+" #"#") s)))
+                (util/safe-re-find #"[\,|,|#]+" s)))
      (let [result (->> (string/split s page-ref-re-2)
-                       (map (fn [s] (if (string/ends-with? (string/trimr s) "]],")
-                                     (let [s (string/trimr s)]
-                                       (subs s 0 (dec (count s))))
-                                     s)))
+                       (mapcat (fn [s] (if (string/includes? (string/trimr s) "]],")
+                                        (let [idx (string/index-of s "]],")]
+                                          [(subs s 0 idx)
+                                           "]]"
+                                           (subs s (+ idx 3))])
+                                        [s])))
+                       (remove #(= % ""))
+                       (mapcat (fn [s] (if (string/ends-with? s "]]")
+                                        [(subs s 0 (- (count s) 2))
+                                         "]]"]
+                                        [s])))
                        concat-nested-pages
                        (remove string/blank?)
                        (mapcat (fn [s]
                                  (if (page-ref? s)
-                                   [(page-ref-un-brackets! s)]
+                                   [(if un-brackets? (page-ref-un-brackets! s) s)]
                                    (sep-by-comma s))))
                        (distinct))]
        (if (or (coll? result)

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

@@ -284,6 +284,13 @@
         (.removeEventListener viewport "resize" handler)
         (.removeEventListener viewport "scroll" handler)))))
 
+(defn setup-system-theme-effect!
+  []
+  (let [^js schemaMedia (js/window.matchMedia "(prefers-color-scheme: dark)")]
+    (.addEventListener schemaMedia "change" state/sync-system-theme!)
+    (state/sync-system-theme!)
+    #(.removeEventListener schemaMedia "change" state/sync-system-theme!)))
+
 (defn on-scroll
   [node on-load on-top-reached]
   (let [full-height (gobj/get node "scrollHeight")
@@ -572,5 +579,6 @@
 
 (rum/defc tippy
   [opts child]
-  (Tippy (merge {:arrow true} opts)
+  (Tippy (merge {:arrow true
+                 :disabled (not (state/enable-tooltip?))} opts)
          child))

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

@@ -35,7 +35,7 @@
 
 #?(:cljs (defonce ^js node-path nodePath))
 #?(:cljs (defn app-scroll-container-node []
-           (gdom/getElement "left-container")))
+           (gdom/getElement "main-container")))
 
 #?(:cljs
    (defn ios?
@@ -417,9 +417,10 @@
      ([node pos]
       (scroll-to node pos true))
      ([node pos animate?]
-      (.scroll node
-               #js {:top      pos
-                    :behavior (if animate? "smooth" "auto")}))))
+      (when node
+        (.scroll node
+                 #js {:top      pos
+                      :behavior (if animate? "smooth" "auto")})))))
 
 #?(:cljs
    (defn scroll-to-top
@@ -1365,3 +1366,18 @@
                         (apply min))
                    (count val))]
        (.setRangeText input "" current (inc idx)))))
+
+(defn classnames
+  "Like react classnames utility:
+
+     ```
+      [:div {:class (classnames [:a :b {:c true}])}
+     ```
+  "
+  [args]
+  (into #{} (mapcat
+              #(if (map? %)
+                 (for [[k v] %]
+                   (when v (name k)))
+                 (name %))
+              args)))

+ 39 - 5
src/main/frontend/util/property.cljs

@@ -4,7 +4,8 @@
             [clojure.set :as set]
             [frontend.config :as config]
             [medley.core :as medley]
-            [frontend.format.mldoc :as mldoc]))
+            [frontend.format.mldoc :as mldoc]
+            [frontend.text :as text]))
 
 (defonce properties-start ":PROPERTIES:")
 (defonce properties-end ":END:")
@@ -115,7 +116,7 @@
                                                            (or (simplified-property? s)
                                                                (and org? (org-property? s)))) properties-and-body)
             body (if org?
-                   (remove (fn [s] (= (string/trim s) properties-start)) body)
+                   (remove (fn [s] (contains? #{properties-start properties-end} (string/trim s))) body)
                    body)
             properties-in-content (->> (map #(get-property-key % format) properties-lines)
                                        (remove nil?)
@@ -129,6 +130,8 @@
                          (when org? [properties-start])
                          built-in-properties-area
                          properties-lines
+                         (when org?
+                           [properties-end])
                          body)]
         (string/join "\n" body))
       content)))
@@ -244,8 +247,11 @@
 
 (defn remove-built-in-properties
   [format content]
-  (reduce (fn [content key]
-            (remove-property format key content)) content built-in-properties))
+  (let [content (reduce (fn [content key]
+                          (remove-property format key content)) content built-in-properties)]
+    (if (= format :org)
+      (string/replace-first content ":PROPERTIES:\n:END:" "")
+      content)))
 
 (defn ->new-properties
   "New syntax: key:: value"
@@ -260,7 +266,9 @@
                           (map (fn [text]
                                  (let [[k v] (util/split-first ":" (subs text 1))]
                                    (if (and k v)
-                                     (str k ":: " (string/trim v))
+                                     (let [k (string/replace k "_" "-")
+                                           k (if (contains? #{:id :custom_id :custom-id} (string/lower-case k)) "id" k)]
+                                       (str k ":: " (string/trim v)))
                                      text)))))
               after (subvec lines (inc end-idx))
               lines (concat before middle after)]
@@ -314,3 +322,29 @@
    (vector? block)
    (contains? #{"Property_Drawer" "Properties"}
               (first block))))
+
+(defonce non-parsing-properties
+  (atom #{"background-color" "background_color"}))
+
+(defn parse-property
+  [k v]
+  (let [k (name k)
+        v (if (or (symbol? v) (keyword? v)) (name v) (str v))
+        v (string/trim v)]
+    (cond
+      (= v "true")
+      true
+      (= v "false")
+      false
+
+      (util/safe-re-find #"^\d+$" v)
+      (util/safe-parse-int v)
+
+      (and (= "\"" (first v) (last v))) ; wrapped in ""
+      (string/trim (subs v 1 (dec (count v))))
+
+      (contains? @non-parsing-properties (string/lower-case k))
+      v
+
+      :else
+      (text/split-page-refs-without-brackets v))))

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

@@ -1,3 +1,3 @@
 (ns frontend.version)
 
-(defonce version "0.0.4")
+(defonce version "0.1.0")

+ 91 - 99
src/test/frontend/db/query_dsl_test.cljs

@@ -23,17 +23,17 @@ tags: [[page-tag-1]], page-tag-2
 parent: [[child page 1]]
 ---
 - DONE 26-b1 [[page 1]]
-created_at:: 1608968448113
-last_modified_at:: 1608968448113
-prop_a:: val_a
-prop_c:: [[page a]], [[page b]], [[page c]]
+created-at:: 1608968448113
+last-modified-at:: 1608968448113
+prop-a:: val-a
+prop-c:: [[page a]], [[page b]], [[page c]]
 - LATER 26-b2-modified-later [[page 2]] #tag1
-created_at:: 1608968448114
-last_modified_at:: 1608968448120
-prop_b:: val_b
+created-at:: 1608968448114
+last-modified-at:: 1608968448120
+prop-b:: val-b
 - DONE [#A] 26-b3 [[page 1]]
-created_at:: 1608968448115
-last_modified_at:: 1608968448115
+created-at:: 1608968448115
+last-modified-at:: 1608968448115
 "}
                {:file/path "journals/2020_12_27.md"
                 :file/content "---
@@ -42,35 +42,35 @@ tags: page-tag-2, [[page-tag-3]]
 parent: [[child page 1]], child page 2
 ---
 - NOW [#A] b1 [[page 1]]
-created_at:: 1609052958714
-last_modified_at:: 1609052958714
+created-at:: 1609052958714
+last-modified-at:: 1609052958714
 - LATER [#B] b2-modified-later [[page 2]]
-created_at:: 1609052959376
-last_modified_at:: 1609052974285
+created-at:: 1609052959376
+last-modified-at:: 1609052974285
 - b3 [[page 1]]
-created_at:: 1609052959954
-last_modified_at:: 1609052959954
-prop_a:: val_a
+created-at:: 1609052959954
+last-modified-at:: 1609052959954
+prop-a:: val-a
 - b4 [[page 2]]
-created_at:: 1609052961569
-last_modified_at:: 1609052961569
+created-at:: 1609052961569
+last-modified-at:: 1609052961569
 - b5
-created_at:: 1609052963089
-last_modified_at:: 1609052963089"}
+created-at:: 1609052963089
+last-modified-at:: 1609052963089"}
                {:file/path "journals/2020_12_28.md"
                 :file/content "---
 title: Dec 28th, 2020
 parent: child page 2
 ---
 - 28-b1 [[page 1]]
-created_at:: 1609084800000
-last_modified_at:: 1609084800000
+created-at:: 1609084800000
+last-modified-at:: 1609084800000
 - 28-b2-modified-later [[page 2]]
-created_at:: 1609084800001
-last_modified_at:: 1609084800020
+created-at:: 1609084800001
+last-modified-at:: 1609084800020
 - 28-b3 [[page 1]]
-created_at:: 1609084800002
-last_modified_at:: 1609084800002"}]]
+created-at:: 1609084800002
+last-modified-at:: 1609084800002"}]]
     (repo-handler/parse-files-and-load-to-db! test-db files {:re-render? false})))
 
 (def parse (partial dsl/parse test-db))
@@ -80,7 +80,7 @@ last_modified_at:: 1609084800002"}]]
   (db/clear-query-state!)
   (let [parse-result (parse s)
         query (:query parse-result)]
-    {:query query
+    {:query (if (seq query) (vec query) query)
      :result (dsl/query test-db s)}))
 
 (defn q-count
@@ -122,54 +122,39 @@ last_modified_at:: 1609084800002"}]]
 
   (testing "Block properties query"
     (are [x y] (= (q-count x) y)
-      "(property prop_a val_a)"
-      {:query '[[?b :block/properties ?prop]
-                [(get ?prop :prop_a) ?v]
-                (or
-                 [(= ?v "val_a")]
-                 [(contains? ?v "val_a")])]
+      "(property prop-a val-a)"
+      {:query '[[?b :block/properties ?prop] [(missing? $ ?b :block/name)] [(get ?prop :prop-a) ?v] (or [(= ?v "val-a")] [(contains? ?v "val-a")])]
        :count 2}
 
-      "(property prop_b val_b)"
-      {:query '[[?b :block/properties ?prop]
-                [(get ?prop :prop_b) ?v]
-                (or
-                 [(= ?v "val_b")]
-                 [(contains? ?v "val_b")])]
+      "(property prop-b val-b)"
+      {:query '[[?b :block/properties ?prop] [(missing? $ ?b :block/name)] [(get ?prop :prop-b) ?v] (or [(= ?v "val-b")] [(contains? ?v "val-b")])]
        :count 1}
 
-      "(and (property prop_b val_b))"
-      {:query '[[?b :block/properties ?prop]
-                [(get ?prop :prop_b) ?v]
-                (or
-                 [(= ?v "val_b")]
-                 [(contains? ?v "val_b")])]
+      "(and (property prop-b val-b))"
+      {:query '([?b :block/properties ?prop]
+                [(missing? $ ?b :block/name)]
+                [(get ?prop :prop-b) ?v]
+                (or [(= ?v "val-b")] [(contains? ?v "val-b")]))
        :count 1}
 
-      "(and (property prop_c \"page c\"))"
-      {:query '[[?b :block/properties ?prop]
-                [(get ?prop :prop_c) ?v]
-                (or
-                 [(= ?v "page c")]
-                 [(contains? ?v "page c")])]
+      "(and (property prop-c \"page c\"))"
+      {:query '[[?b :block/properties ?prop] [(missing? $ ?b :block/name)] [(get ?prop :prop-c) ?v] (or [(= ?v "page c")] [(contains? ?v "page c")])]
        :count 1}
 
       ;; TODO: optimize
-      "(and (property prop_c \"page c\") (property prop_c \"page b\"))"
-      {:query '([?b :block/properties ?prop]
-                [(get ?prop :prop_c) ?v]
+      "(and (property prop-c \"page c\") (property prop-c \"page b\"))"
+      {:query '[[?b :block/properties ?prop]
+                [(missing? $ ?b :block/name)]
+                [(get ?prop :prop-c) ?v]
                 (or [(= ?v "page c")] [(contains? ?v "page c")])
-                [(get ?prop :prop_c) ?v1]
-                (or [(= ?v1 "page b")] [(contains? ?v1 "page b")]))
+                [(get ?prop :prop-c) ?v1]
+                (or [(= ?v1 "page b")] [(contains? ?v1 "page b")])]
        :count 1}
 
-      "(or (property prop_c \"page c\") (property prop_b val_b))"
-      {:query '(or (and [?b :block/properties ?prop]
-                        [(get ?prop :prop_c) ?v]
-                        (or [(= ?v "page c")] [(contains? ?v "page c")]))
-                   (and [?b :block/properties ?prop]
-                        [(get ?prop :prop_b) ?v]
-                        (or [(= ?v "val_b")] [(contains? ?v "val_b")])))
+      "(or (property prop-c \"page c\") (property prop-b val-b))"
+      {:query '[or
+                (and [?b :block/properties ?prop] [(missing? $ ?b :block/name)] [(get ?prop :prop-c) ?v] (or [(= ?v "page c")] [(contains? ?v "page c")]))
+                (and [?b :block/properties ?prop] [(missing? $ ?b :block/name)] [(get ?prop :prop-b) ?v] (or [(= ?v "val-b")] [(contains? ?v "val-b")]))]
        :count 2}))
 
   (testing "task queries"
@@ -266,13 +251,17 @@ last_modified_at:: 1609084800002"}]]
 
   (testing "page-property queries"
     (are [x y] (= (q-count x) y)
+      "(page-property parent)"
+      {:query '[[?p :block/name]
+                [?p :block/properties ?prop]
+                [(get ?prop :parent) ?prop-v]
+                [true]], :count 3}
+
       "(page-property parent [[child page 1]])"
       {:query '[[?p :block/name]
                 [?p :block/properties ?prop]
                 [(get ?prop :parent) ?v]
-                (or
-                 [(= ?v "child page 1")]
-                 [(contains? ?v "child page 1")])]
+                (or [(= ?v "child page 1")] [(contains? ?v "child page 1")])]
        :count 2}
 
       "(page-property parent \"child page 1\")"
@@ -343,12 +332,12 @@ last_modified_at:: 1609084800002"}]]
       "(and (task now later done) (between [[Dec 27th, 2020]] [[Dec 28th, 2020]]))"
       2
 
-      ;; ;; between with created_at
-      ;; "(and (task now later done) (between created_at [[Dec 26th, 2020]] tomorrow))"
+      ;; ;; between with created-at
+      ;; "(and (task now later done) (between created-at [[Dec 26th, 2020]] tomorrow))"
       ;; 5
 
-      ;; ;; between with last_modified_at
-      ;; "(and (task now later done) (between last_modified_at [[Dec 26th, 2020]] tomorrow))"
+      ;; ;; between with last-modified-at
+      ;; "(and (task now later done) (between last-modified-at [[Dec 26th, 2020]] tomorrow))"
       ;; 5
       ))
 
@@ -383,10 +372,13 @@ last_modified_at:: 1609084800002"}]]
     ;; FIXME: not working
     ;; (are [x y] (= (q-count x) y)
     ;;   "(or (priority a) (not (priority a)))"
-    ;;   {:query '(or
-    ;;             (and [?b :block/priority ?priority] [(contains? #{"A"} ?priority)])
-    ;;             (and (not [?b :block/priority ?priority]
-    ;;                       [(contains? #{"A"} ?priority)])))
+    ;;   {:query '[(or-join [?b]
+    ;;                      (and
+    ;;                       [?b :block/priority ?priority]
+    ;;                       [(contains? #{"A"} ?priority)])
+    ;;                      (not-join [?b]
+    ;;                                [?b :block/priority ?priority]
+    ;;                                [(contains? #{"A"} ?priority)]))]
     ;;    :count 5})
 
     (are [x y] (= (q-count x) y)
@@ -399,63 +391,63 @@ last_modified_at:: 1609084800002"}]]
                  (and (not [?b :block/path-refs [:block/name "page 1"]]))))
        :count 5}))
 
-  ;; (testing "sort-by (created_at defaults to desc)"
+  ;; (testing "sort-by (created-at defaults to desc)"
   ;;   (db/clear-query-state!)
   ;;   (let [result (->> (q "(and (task now later done)
-  ;;                              (sort-by created_at))")
+  ;;                              (sort-by created-at))")
   ;;                     :result
   ;;                     deref
-  ;;                     (map #(get-in % [:block/properties "created_at"])))]
+  ;;                     (map #(get-in % [:block/properties "created-at"])))]
   ;;     (is (= result
   ;;            '(1609052959376 1609052958714 1608968448115 1608968448114 1608968448113)))))
 
-  ;; (testing "sort-by (created_at desc)"
+  ;; (testing "sort-by (created-at desc)"
   ;;   (db/clear-query-state!)
   ;;   (let [result (->> (q "(and (todo now later done)
-  ;;                              (sort-by created_at desc))")
+  ;;                              (sort-by created-at desc))")
   ;;                     :result
   ;;                     deref
-  ;;                     (map #(get-in % [:block/properties "created_at"])))]
+  ;;                     (map #(get-in % [:block/properties "created-at"])))]
   ;;     (is (= result
   ;;            '(1609052959376 1609052958714 1608968448115 1608968448114 1608968448113)))))
 
-  ;; (testing "sort-by (created_at asc)"
+  ;; (testing "sort-by (created-at asc)"
   ;;   (db/clear-query-state!)
   ;;   (let [result (->> (q "(and (todo now later done)
-  ;;                              (sort-by created_at asc))")
+  ;;                              (sort-by created-at asc))")
   ;;                     :result
   ;;                     deref
-  ;;                     (map #(get-in % [:block/properties "created_at"])))]
+  ;;                     (map #(get-in % [:block/properties "created-at"])))]
   ;;     (is (= result
   ;;            '(1608968448113 1608968448114 1608968448115 1609052958714 1609052959376)))))
 
-  ;; (testing "sort-by (last_modified_at defaults to desc)"
+  ;; (testing "sort-by (last-modified-at defaults to desc)"
   ;;   (db/clear-query-state!)
   ;;   (let [result (->> (q "(and (todo now later done)
-  ;;                              (sort-by last_modified_at))")
+  ;;                              (sort-by last-modified-at))")
   ;;                     :result
   ;;                     deref
-  ;;                     (map #(get-in % [:block/properties "last_modified_at"])))]
+  ;;                     (map #(get-in % [:block/properties "last-modified-at"])))]
   ;;     (is (= result
   ;;            '(1609052974285 1609052958714 1608968448120 1608968448115 1608968448113)))))
 
-  ;; (testing "sort-by (last_modified_at desc)"
+  ;; (testing "sort-by (last-modified-at desc)"
   ;;   (db/clear-query-state!)
   ;;   (let [result (->> (q "(and (todo now later done)
-  ;;                              (sort-by last_modified_at desc))")
+  ;;                              (sort-by last-modified-at desc))")
   ;;                     :result
   ;;                     deref
-  ;;                     (map #(get-in % [:block/properties "last_modified_at"])))]
+  ;;                     (map #(get-in % [:block/properties "last-modified-at"])))]
   ;;     (is (= result
   ;;            '(1609052974285 1609052958714 1608968448120 1608968448115 1608968448113)))))
 
-  ;; (testing "sort-by (last_modified_at desc)"
+  ;; (testing "sort-by (last-modified-at desc)"
   ;;   (db/clear-query-state!)
   ;;   (let [result (->> (q "(and (todo now later done)
-  ;;                              (sort-by last_modified_at asc))")
+  ;;                              (sort-by last-modified-at asc))")
   ;;                     :result
   ;;                     deref
-  ;;                     (map #(get-in % [:block/properties "last_modified_at"])))]
+  ;;                     (map #(get-in % [:block/properties "last-modified-at"])))]
   ;;     (is (= result
   ;;            '(1608968448113 1608968448115 1608968448120 1609052958714 1609052974285)))))
   )
@@ -479,10 +471,10 @@ last_modified_at:: 1609084800002"}]]
   ;; FIXME: Error: Insufficient bindings: #{?priority} not bound in [(contains? #{"A"} ?priority)]
   (pprint/pprint
    (d/q
-    '[:find (pull ?b [*])
-      :where
-      [?b :block/uuid]
-      (or (and [?b :block/priority ?priority] [(contains? #{"A"} ?priority)])
-          (not [?b :block/priority #{"A"}]
-               [(contains? #{"A"} ?priority)]))]
-    (frontend.db/get-conn test-db))))
+     '[:find (pull ?b [*])
+       :where
+       [?b :block/uuid]
+       (or (and [?b :block/priority ?priority] [(contains? #{"A"} ?priority)])
+           (not [?b :block/priority #{"A"}]
+                [(contains? #{"A"} ?priority)]))]
+     (frontend.db/get-conn test-db))))

+ 6 - 7
src/test/frontend/format/block_test.cljs

@@ -13,8 +13,7 @@
     [["year" "1000"] ["tags" "name/with space, another"]] {:year 1000, :tags #{"name/with space" "another"}}
     [["year" "1000"] ["alias" "name/with space, another"]] {:year 1000, :alias #{"name/with space" "another"}}
     [["year" "1000"] ["alias" "name/with space, [[another [[nested]]]]"]] {:year 1000, :alias #{"name/with space" "another [[nested]]"}}
-    ;; FIXME:
-    ;; [["year" "1000"] ["alias" "name/with space, [[[[nested]] another]]"]] {:year 1000, :alias #{"name/with space" "[[nested]] another"}}
+    [["year" "1000"] ["alias" "name/with space, [[[[nested]] another]]"]] {:year 1000, :alias #{"name/with space" "[[nested]] another"}}
     [["foo" "bar"]] {:foo "bar"}
     [["foo" "bar, baz"]] {:foo #{"bar" "baz"}}
     [["foo" "bar, [[baz]]"]] {:foo #{"bar" "baz"}}
@@ -27,10 +26,10 @@
   (are [x y] (= (vec (:page-refs (block/extract-properties x))) y)
     [["year" "1000"]] []
     [["year" "\"1000\""]] []
-    [["foo" "[[bar]] test"]] ["bar"]
-    [["foo" "[[bar]] test [[baz]]"]] ["bar" "baz"]
-    ;; FIXME:
-    ;; [["foo" "[[bar]] test [[baz]] [[nested [[baz]]]]"]] ["bar" "baz" "nested [[baz]]"]
-    ))
+    [["foo" "[[bar]] test"]] ["bar" "test"]
+    [["foo" "[[bar]] test [[baz]]"]] ["bar" "test" "baz"]
+    [["foo" "[[bar]] test [[baz]] [[nested [[baz]]]]"]] ["bar" "test" "baz" "nested [[baz]]"]
+    [["foo" "#bar, #baz"]] ["bar" "baz"]
+    [["foo" "[[nested [[page]]]], test"]] ["nested [[page]]" "test"]))
 
 #_(run-tests)

+ 4 - 0
templates/config.edn

@@ -24,6 +24,10 @@
  :default-templates
  {:journals ""}
 
+ ;; Whether to enable hover on tooltip preview feature
+ ;; Default is true, you can also toggle this via setting page
+ :ui/enable-tooltip? true
+
  ;; The app will show those queries in today's journal page,
  ;; the "NOW" query asks the tasks which need to be finished "now",
  ;; the "NEXT" query asks the future tasks.

+ 1 - 1
templates/tutorial-en.md

@@ -1,5 +1,5 @@
 ## Hi, welcome to Logseq!
-- Logseq is a _privacy-first_, _open-source_ platform for _knowledge_ sharing and management.
+- Logseq is a _privacy-first_, _open-source_ platform for _knowledge_ management and collaboration.
 - This is a 3 minute tutorial on how to use Logseq. Let's get started!
 - (Feel free to edit anything, no change will be saved at this moment. If you do want to persist your work, click the **top-right** corner of the screen to connect Logseq to either Github or local directory.)
 - Here are some tips might be useful.

+ 94 - 25
yarn.lock

@@ -242,6 +242,58 @@
   resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.2.tgz#adea7b6953cbb34651766b0548468e743c6a2353"
   integrity sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==
 
+"@sentry/browser@^6.4.1":
+  version "6.4.1"
+  resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.4.1.tgz#b6c62736caaade7fdf6638513d9aad138abde2ac"
+  integrity sha512-3cDud6GWutnJqcnheIq0lPNTsUJbrRLevQ+g1YfawVXFUxfmmY2bOsGd/Mxq17LxYeBHgKTitXv3DU1bsQ+WBQ==
+  dependencies:
+    "@sentry/core" "6.4.1"
+    "@sentry/types" "6.4.1"
+    "@sentry/utils" "6.4.1"
+    tslib "^1.9.3"
+
+"@sentry/[email protected]":
+  version "6.4.1"
+  resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.4.1.tgz#789b0071996de5c1a20673f879408926aa3b4fa6"
+  integrity sha512-Lx13oTiP+Tjvm5VxulcCszNVd2S1wY4viSnr+ygq62ySVERR+t7uOZDSARZ0rZ259GwW6nkbMh9dDmD0d6VCGQ==
+  dependencies:
+    "@sentry/hub" "6.4.1"
+    "@sentry/minimal" "6.4.1"
+    "@sentry/types" "6.4.1"
+    "@sentry/utils" "6.4.1"
+    tslib "^1.9.3"
+
+"@sentry/[email protected]":
+  version "6.4.1"
+  resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.4.1.tgz#fa9c05ca32674e2e8477120b71084a1c91a5e023"
+  integrity sha512-7IZRP5buDE6s/c3vWzzPR/ySE+8GUuHPgTEPiDCPOCWwUN11zXDafJDKkJqY3muJfebUKmC/JG67RyBx+XlnlQ==
+  dependencies:
+    "@sentry/types" "6.4.1"
+    "@sentry/utils" "6.4.1"
+    tslib "^1.9.3"
+
+"@sentry/[email protected]":
+  version "6.4.1"
+  resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.4.1.tgz#d3f968c060c3d3cc997071756659e24047b5dd97"
+  integrity sha512-4x/PRbDZACCKJqjta9EkhiIMyGMf7VgBX13EEWEDVWLP7ymFukBuTr4ap/Tz9429kB/yXZuDGGMIZp/G618H3g==
+  dependencies:
+    "@sentry/hub" "6.4.1"
+    "@sentry/types" "6.4.1"
+    tslib "^1.9.3"
+
+"@sentry/[email protected]":
+  version "6.4.1"
+  resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.4.1.tgz#7c0a4355a1d04321b901197723a8f55c263226e9"
+  integrity sha512-sTu/GaLsLYk1AkAqpkMT4+4q665LtZjhV0hkgiTD4N3zPl5uSf1pCIzxPRYjOpe7NEANmWv8U4PaGKGtc2eMfA==
+
+"@sentry/[email protected]":
+  version "6.4.1"
+  resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.4.1.tgz#55fa7da58898773cbd538e4895fc2e4ec695ecab"
+  integrity sha512-xJ1uVa5fvg23pXQfulvCIBb9pQ3p1awyd1PapK2AYi+wKjTuYl4B9edmhjRREEQEExznl/d2OVm78fRXLq7M9Q==
+  dependencies:
+    "@sentry/types" "6.4.1"
+    tslib "^1.9.3"
+
 "@sindresorhus/is@^0.14.0":
   version "0.14.0"
   resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz"
@@ -885,15 +937,15 @@ browserify-zlib@^0.2.0:
     pako "~1.0.5"
 
 browserslist@^4.0.0, browserslist@^4.12.0:
-  version "4.16.3"
-  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz"
-  integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==
+  version "4.16.6"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2"
+  integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==
   dependencies:
-    caniuse-lite "^1.0.30001181"
-    colorette "^1.2.1"
-    electron-to-chromium "^1.3.649"
+    caniuse-lite "^1.0.30001219"
+    colorette "^1.2.2"
+    electron-to-chromium "^1.3.723"
     escalade "^3.1.1"
-    node-releases "^1.1.70"
+    node-releases "^1.1.71"
 
 buffer-crc32@~0.2.3:
   version "0.2.13"
@@ -1028,10 +1080,10 @@ caniuse-api@^3.0.0:
     lodash.memoize "^4.1.2"
     lodash.uniq "^4.5.0"
 
-caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001181:
-  version "1.0.30001185"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001185.tgz"
-  integrity sha512-Fpi4kVNtNvJ15H0F6vwmXtb3tukv3Zg3qhKkOGUq7KJ1J6b9kf4dnNgtEAFXhRsJo0gNj9W60+wBvn0JcTvdTg==
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001219:
+  version "1.0.30001230"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz#8135c57459854b2240b57a4a6786044bdc5a9f71"
+  integrity sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==
 
 chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2:
   version "2.4.2"
@@ -1284,7 +1336,7 @@ color@^3.0.0, color@^3.1.3:
 
 colorette@^1.2.1, colorette@^1.2.2:
   version "1.2.2"
-  resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz"
+  resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
   integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
 
 commander@^6.0.0:
@@ -1856,10 +1908,10 @@ each-props@^1.3.0:
     is-plain-object "^2.0.1"
     object.defaults "^1.1.0"
 
-electron-to-chromium@^1.3.649:
-  version "1.3.657"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.657.tgz"
-  integrity sha512-/9ROOyvEflEbaZFUeGofD+Tqs/WynbSTbNgNF+/TJJxH1ePD/e6VjZlDJpW3FFFd3nj5l3Hd8ki2vRwy+gyRFw==
+electron-to-chromium@^1.3.723:
+  version "1.3.739"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.739.tgz#f07756aa92cabd5a6eec6f491525a64fe62f98b9"
+  integrity sha512-+LPJVRsN7hGZ9EIUUiWCpO7l4E3qBYHNadazlucBfsXBbccDFNKUBAgzE68FnkWGJPwD/AfKhSzL+G+Iqb8A4A==
 
 electron@^11.2.0:
   version "11.2.3"
@@ -2018,7 +2070,7 @@ es6-weak-map@^2.0.1:
 
 escalade@^3.1.1:
   version "3.1.1"
-  resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz"
+  resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
   integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
 
 escape-string-regexp@^1.0.5:
@@ -2203,6 +2255,11 @@ fd-slicer@~1.1.0:
   dependencies:
     pend "~1.2.0"
 
+fflate@^0.4.1:
+  version "0.4.8"
+  resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae"
+  integrity sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==
+
 file-entry-cache@^6.0.0:
   version "6.0.0"
   resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.0.tgz"
@@ -3860,10 +3917,10 @@ mkdirp@^0.5.4, mkdirp@~0.5.1:
   dependencies:
     minimist "^1.2.5"
 
[email protected].18:
-  version "0.6.18"
-  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-0.6.18.tgz#a1c0b76d193ef9cd98c2763de3e8414cd83b9368"
-  integrity sha512-Bg460Jdp4kBgmAYUdVtWG7AZSQ0EWVNsiWQ/7mljVLQp5yyERcySzw1nM4Hs5eZ5IKStzl6qz4m6XN/q11AUJw==
[email protected].22:
+  version "0.6.22"
+  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-0.6.22.tgz#78c11db4547d7df0e15ecdef51b0eb84248ed1a8"
+  integrity sha512-kUZ3iYmF05ztML0rhQPKd8x/4c/zQlCCZKZV6BxbDXSwEaTp137xTKeJ6tK6Z70wmNK3SWRRgrYDB+TFCEoDTw==
   dependencies:
     yargs "^12.0.2"
 
@@ -3960,10 +4017,10 @@ node-libs-browser@^2.2.1:
     util "^0.11.0"
     vm-browserify "^1.0.1"
 
-node-releases@^1.1.70:
-  version "1.1.70"
-  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.70.tgz"
-  integrity sha512-Slf2s69+2/uAD79pVVQo8uSiC34+g8GWY8UH2Qtqv34ZfhYrxpYpfzs9Js9d6O0mbDmALuxaTlplnBTnSELcrw==
+node-releases@^1.1.71:
+  version "1.1.72"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe"
+  integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==
 
 normalize-package-data@^2.3.2, normalize-package-data@^2.5.0:
   version "2.5.0"
@@ -4939,6 +4996,13 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.21
     source-map "^0.6.1"
     supports-color "^6.1.0"
 
+posthog-js@^1.10.2:
+  version "1.10.2"
+  resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.10.2.tgz#74d6c84f9675b65dfd4ff6f4051ed8d3cb974076"
+  integrity sha512-JNjWstHEexhj5CEKldSeYNyPJbtOvZQ3ZPL55fxU7+f+gTBL8RlOb8eFohCPYIk0VhMf2UM1rXxwVBOeMQQQFw==
+  dependencies:
+    fflate "^0.4.1"
+
 prepend-http@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz"
@@ -6236,6 +6300,11 @@ ts-essentials@^2.0.3:
   resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-2.0.12.tgz"
   integrity sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w==
 
+tslib@^1.9.3:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
+  integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
+
 [email protected]:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz"

部分文件因为文件数量过多而无法显示