Explorar o código

Merge branch 'master' into disable-webview-resize

llcc %!s(int64=3) %!d(string=hai) anos
pai
achega
628fd0f3e4
Modificáronse 79 ficheiros con 679 adicións e 370 borrados
  1. 5 1
      .clj-kondo/config.edn
  2. 10 1
      .github/workflows/build.yml
  3. 2 0
      .gitignore
  4. 7 5
      CODEBASE_OVERVIEW.md
  5. 5 0
      bb.edn
  6. 0 1
      deps.edn
  7. 7 0
      docs/dev-practices.md
  8. 6 0
      libs/src/LSPlugin.ts
  9. 1 0
      package.json
  10. 1 1
      resources/package.json
  11. 30 0
      scripts/src/logseq/tasks/nbb.clj
  12. 1 1
      src/electron/electron/file_sync_rsapi.cljs
  13. 9 7
      src/main/frontend/commands.cljs
  14. 13 11
      src/main/frontend/components/block.cljs
  15. 0 1
      src/main/frontend/components/command_palette.css
  16. 2 1
      src/main/frontend/components/content.cljs
  17. 4 3
      src/main/frontend/components/editor.cljs
  18. 3 2
      src/main/frontend/components/file.cljs
  19. 0 8
      src/main/frontend/components/onboarding/index.css
  20. 6 5
      src/main/frontend/components/onboarding/setups.cljs
  21. 4 3
      src/main/frontend/components/page.cljs
  22. 2 1
      src/main/frontend/components/page_menu.cljs
  23. 2 1
      src/main/frontend/components/reference.cljs
  24. 2 1
      src/main/frontend/components/search.cljs
  25. 4 13
      src/main/frontend/config.cljs
  26. 2 1
      src/main/frontend/db/conn.cljs
  27. 1 1
      src/main/frontend/db/default.cljs
  28. 4 3
      src/main/frontend/db/model.cljs
  29. 3 2
      src/main/frontend/db/query_dsl.cljs
  30. 3 2
      src/main/frontend/db/query_react.cljs
  31. 2 1
      src/main/frontend/db/react.cljs
  32. 1 1
      src/main/frontend/db/rules.cljc
  33. 3 2
      src/main/frontend/db/utils.cljs
  34. 1 1
      src/main/frontend/db_schema.cljs
  35. 2 1
      src/main/frontend/diff.cljs
  36. 2 2
      src/main/frontend/extensions/code.cljs
  37. 2 1
      src/main/frontend/extensions/html_parser.cljs
  38. 8 7
      src/main/frontend/extensions/pdf/assets.cljs
  39. 13 13
      src/main/frontend/extensions/pdf/highlights.cljs
  40. 2 2
      src/main/frontend/extensions/pdf/utils.cljs
  41. 0 2
      src/main/frontend/external.cljs
  42. 42 50
      src/main/frontend/external/roam.cljs
  43. 11 9
      src/main/frontend/format/block.cljs
  44. 10 10
      src/main/frontend/format/mldoc.cljs
  45. 3 3
      src/main/frontend/fs/watcher_handler.cljs
  46. 3 2
      src/main/frontend/handler/draw.cljs
  47. 19 16
      src/main/frontend/handler/editor.cljs
  48. 4 3
      src/main/frontend/handler/extract.cljs
  49. 3 2
      src/main/frontend/handler/file.cljs
  50. 3 2
      src/main/frontend/handler/graph.cljs
  51. 9 7
      src/main/frontend/handler/page.cljs
  52. 2 1
      src/main/frontend/handler/repo.cljs
  53. 2 1
      src/main/frontend/handler/route.cljs
  54. 2 2
      src/main/frontend/handler/shell.cljs
  55. 2 1
      src/main/frontend/handler/ui.cljs
  56. 6 4
      src/main/frontend/handler/web/nfs.cljs
  57. 2 2
      src/main/frontend/modules/instrumentation/posthog.cljs
  58. 5 5
      src/main/frontend/modules/instrumentation/sentry.cljs
  59. 6 6
      src/main/frontend/modules/layout/core.cljs
  60. 2 1
      src/main/frontend/modules/outliner/core.cljs
  61. 2 2
      src/main/frontend/modules/outliner/datascript.cljc
  62. 2 2
      src/main/frontend/modules/outliner/tree.cljs
  63. 2 2
      src/main/frontend/modules/shortcut/core.cljs
  64. 27 12
      src/main/frontend/modules/shortcut/data_helper.cljs
  65. 2 2
      src/main/frontend/security.cljs
  66. 2 1
      src/main/frontend/state.cljs
  67. 6 5
      src/main/frontend/text.cljs
  68. 0 1
      src/main/frontend/ui.cljs
  69. 20 83
      src/main/frontend/util.cljc
  70. 3 2
      src/main/frontend/util/drawer.cljs
  71. 4 3
      src/main/frontend/util/marker.cljs
  72. 3 2
      src/main/frontend/util/priority.cljs
  73. 10 9
      src/main/frontend/util/property.cljs
  74. 3 2
      src/main/logseq/api.cljs
  75. 16 0
      src/main/logseq/graph_parser/config.cljs
  76. 64 0
      src/main/logseq/graph_parser/util.cljs
  77. 166 0
      src/test/frontend/handler/repo_test.cljs
  78. 8 6
      src/test/frontend/modules/outliner/core_test.cljs
  79. 28 0
      yarn.lock

+ 5 - 1
.clj-kondo/config.edn

@@ -17,7 +17,11 @@
              medley.core medley
              frontend.db.query-dsl query-dsl
              frontend.db.react react
-             frontend.db.query-react query-react}}}
+             frontend.db.query-react query-react
+             frontend.util util
+             frontend.config config
+             logseq.graph-parser.util gp-util
+             logseq.graph-parser.config gp-config}}}
 
  :hooks {:analyze-call {rum.core/defc hooks.rum/defc
                          rum.core/defcs hooks.rum/defcs}}

+ 10 - 1
.github/workflows/build.yml

@@ -17,7 +17,7 @@ env:
   JAVA_VERSION: '8'
   # This is the latest node version we can run.
   NODE_VERSION: '16'
-  BABASHKA_VERSION: '0.7.7'
+  BABASHKA_VERSION: '0.8.1'
 
 jobs:
 
@@ -52,6 +52,11 @@ jobs:
         with:
           cli: ${{ env.CLOJURE_VERSION }}
 
+      - name: Setup Babashka
+        uses: turtlequeue/[email protected]
+        with:
+          babashka-version: ${{ env.BABASHKA_VERSION }}
+
       - name: Clojure cache
         uses: actions/cache@v2
         id: clojure-deps
@@ -74,6 +79,10 @@ jobs:
           yarn cljs:test
           node static/tests.js
 
+      # In this job because it depends on an npm package
+      - name: Load nbb compatible namespaces
+        run: bb test:load-nbb-compatible-namespaces
+
   lint:
     runs-on: ubuntu-latest
 

+ 2 - 0
.gitignore

@@ -42,3 +42,5 @@ android/app/src/main/assets/capacitor.plugin.json
 ios/App/App/capacitor.config.json
 
 startup.png
+
+/src/test/docs

+ 7 - 5
CODEBASE_OVERVIEW.md

@@ -36,15 +36,17 @@ After cloning the [Logseq repository](https://github.com/logseq/logseq), there a
 
 - Config files are located at the root directory. `package.json` contains the JavaScript dependencies while `deps.edn` contains their Clojure counterparts. `shadow-cljs.edn` and `gulpfile.js` contain all the build scripts.
 
-- `/public` and `/resources` contain all the static assets
+- `public/` and `resources/` contain all the static assets
 
-- `/src` is where most of the code locates.
+- `src/` is where most of the code is located.
 
-  - `/src/electron` and `/src/main/electron` contains code specific to the desktop app.
+  - `src/electron/` and `src/main/electron/` contains code specific to the desktop app.
 
-  - `/src/test` contains all the test and `/src/dev-cljs` contains some development utilities.
+  - `src/test/` contains all the tests and `src/dev-cljs/` contains some development utilities.
 
-  - `/src/main/frontend` contains code that powers the Logseq editor. Folders and files inside are organized by features or functions. For example, `components` contains all the UI components and `handler` contains all the event-handling code. You can explore on your own interest.
+  - `src/main/frontend/` contains code that powers the Logseq editor. Folders and files inside are organized by features or functions. For example, `components` contains all the UI components and `handler` contains all the event-handling code. You can explore on your own interest.
+
+  - `src/main/logseq/` contains the api used by plugins and the graph-parser.
 
 ## Data Flow
 

+ 5 - 0
bb.edn

@@ -4,6 +4,8 @@
   {:git/url "https://github.com/babashka/spec.alpha"
    :sha "1a841c4cc1d4f6dab7505a98ed2d532dd9d56b78"}
   medley/medley {:mvn/version "1.3.0"}}
+ :pods
+ {clj-kondo/clj-kondo {:version "2022.02.09"}}
  :tasks
  {dev:watch
   logseq.tasks.dev/watch
@@ -22,6 +24,9 @@
   dev:validate-local-storage
   logseq.tasks.spec/validate-local-storage
 
+  test:load-nbb-compatible-namespaces
+  logseq.tasks.nbb/load-compatible-namespaces
+
   lang:list
   logseq.tasks.lang/list-langs
 

+ 0 - 1
deps.edn

@@ -1,7 +1,6 @@
 {:paths ["src/main" "src/electron" "src/workspaces" "templates"]
  :deps
  {org.clojure/clojure                   {:mvn/version "1.10.0"}
-  cheshire/cheshire                     {:mvn/version "5.10.0"}
   rum/rum                               {:mvn/version "0.12.9"}
   datascript/datascript                 {:mvn/version "1.3.8"}
   datascript-transit/datascript-transit {:mvn/version "0.3.0"}

+ 7 - 0
docs/dev-practices.md

@@ -58,6 +58,13 @@ We use [datascript](https://github.com/tonsky/datascript)'s datalog to power our
 scripts/lint_rules.clj
 ```
 
+### Nbb compatible
+
+Namespaces have the metadata flag `^:nbb-compatible` indicate they are compatible with https://github.com/logseq/nbb-logseq. This compatibility is necessary in order for namespaces to be reused by the frontend and CLIs. To confirm these compatibilities, run:
+```
+bb test:load-nbb-compatible-namespaces
+```
+
 ## Testing
 
 We have unit and end to end tests.

+ 6 - 0
libs/src/LSPlugin.ts

@@ -308,6 +308,12 @@ export interface IAppProxy {
     action: SimpleCommandCallback
   ) => void
 
+  /**
+   * Supported key names
+   * @link https://gist.github.com/xyhp915/d1a6d151a99f31647a95e59cdfbf4ddc
+   * @param keybinding
+   * @param action
+   */
   registerCommandShortcut: (
     keybinding: SimpleCommandKeybinding,
     action: SimpleCommandCallback

+ 1 - 0
package.json

@@ -13,6 +13,7 @@
         "del": "^6.0.0",
         "gulp": "^4.0.2",
         "gulp-clean-css": "^4.3.0",
+        "@logseq/nbb-logseq": "^0.3.10",
         "npm-run-all": "^4.1.5",
         "playwright": "^1.19.2",
         "postcss": "8.2.13",

+ 1 - 1
resources/package.json

@@ -36,7 +36,7 @@
     "https-proxy-agent": "5.0.0",
     "@sentry/electron": "2.5.1",
     "posthog-js": "1.10.2",
-    "@andelf/rsapi": "0.0.7",
+    "@logseq/rsapi": "0.0.9",
     "electron-deeplink": "1.0.9"
   },
   "devDependencies": {

+ 30 - 0
scripts/src/logseq/tasks/nbb.clj

@@ -0,0 +1,30 @@
+(ns logseq.tasks.nbb
+  (:require [pod.borkdude.clj-kondo :as clj-kondo]
+            [babashka.tasks :refer [shell]]))
+
+(defn- fetch-meta-namespaces
+  "Return namespaces with metadata"
+  [paths]
+  (let [paths (or (seq paths) ["src"])
+        {{:keys [namespace-definitions]} :analysis}
+        (clj-kondo/run!
+         {:lint paths
+          :config {:output {:analysis {:namespace-definitions {:meta true}}}}})
+        matches (keep (fn [m]
+                        (when (:meta m)
+                          {:ns   (:name m)
+                           :meta (:meta m)}))
+                      namespace-definitions)]
+    matches))
+
+(defn load-compatible-namespaces
+  "Check nbb-compatible namespaces can be required by nbb-logseq"
+  []
+  (let [namespaces (map :ns
+                        (filter #(get-in % [:meta :nbb-compatible])
+                                (fetch-meta-namespaces ["src/main"])))]
+    (assert (seq namespaces) "There must be some nbb namespaces to check")
+    (doseq [n namespaces]
+      (println "Requiring" n "...")
+      (shell "yarn nbb-logseq -cp src/main -e" (format "(require '[%s])" n)))
+    (println "Success!")))

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

@@ -1,5 +1,5 @@
 (ns electron.file-sync-rsapi
-  (:require ["@andelf/rsapi" :as rsapi]))
+  (:require ["@logseq/rsapi" :as rsapi]))
 
 (defn set-env [env] (rsapi/setEnv env))
 

+ 9 - 7
src/main/frontend/commands.cljs

@@ -15,6 +15,8 @@
             [frontend.util.marker :as marker]
             [frontend.util.priority :as priority]
             [frontend.util.property :as property]
+            [logseq.graph-parser.util :as gp-util]
+            [logseq.graph-parser.config :as gp-config]
             [goog.dom :as gdom]
             [goog.object :as gobj]
             [promesa.core :as p]))
@@ -274,7 +276,7 @@
                     [:codemirror/focus]] "Insert a calculator"]
      ["Draw" (fn []
                (let [file (draw/file-name)
-                     path (str config/default-draw-directory "/" file)
+                     path (str gp-config/default-draw-directory "/" file)
                      text (util/format "[[%s]]" path)]
                  (p/let [_ (draw/create-draw-with-default-content path)]
                    (println "draw file created, " path))
@@ -333,13 +335,13 @@
           current-pos (cursor/pos input)
           current-pos (or
                        (when (and end-pattern (string? end-pattern))
-                         (when-let [i (string/index-of (util/safe-subs edit-content current-pos) end-pattern)]
+                         (when-let [i (string/index-of (gp-util/safe-subs edit-content current-pos) end-pattern)]
                            (+ current-pos i)))
                        current-pos)
           orig-prefix (subs edit-content 0 current-pos)
           space? (when (and last-pattern orig-prefix)
                    (let [s (when-let [last-index (string/last-index-of orig-prefix last-pattern)]
-                             (util/safe-subs orig-prefix 0 last-index))]
+                             (gp-util/safe-subs orig-prefix 0 last-index))]
                      (not
                       (or
                        (and s
@@ -352,7 +354,7 @@
                    space?)
           prefix (cond
                    (and backward-truncate-number (integer? backward-truncate-number))
-                   (str (util/safe-subs orig-prefix 0 (- (count orig-prefix) backward-truncate-number))
+                   (str (gp-util/safe-subs orig-prefix 0 (- (count orig-prefix) backward-truncate-number))
                         (when-not (zero? backward-truncate-number)
                           value))
 
@@ -515,7 +517,7 @@
 
 (defn compute-pos-delta-when-change-marker
   [edit-content marker pos]
-  (let [old-marker (some->> (first (util/safe-re-find marker/bare-marker-pattern edit-content))
+  (let [old-marker (some->> (first (gp-util/safe-re-find marker/bare-marker-pattern edit-content))
                             (string/trim))
         pos-delta (- (count marker)
                      (count old-marker))
@@ -540,7 +542,7 @@
                   (if-let [matches (seq (util/re-pos new-line-re-pattern prefix))]
                     (let [[start-pos content] (last matches)]
                       (+ start-pos (count content)))
-                    (count (util/safe-re-find re-pattern prefix))))
+                    (count (gp-util/safe-re-find re-pattern prefix))))
             new-value (str (subs edit-content 0 pos)
                            (string/replace-first (subs edit-content pos)
                                                  (marker/marker-pattern format)
@@ -581,7 +583,7 @@
       (let [edit-content (gobj/get current-input "value")
             heading-pattern #"^#+\s+"
             new-value (cond
-                        (util/safe-re-find heading-pattern edit-content)
+                        (gp-util/safe-re-find heading-pattern edit-content)
                         (string/replace-first edit-content
                                               heading-pattern
                                               (str heading " "))

+ 13 - 11
src/main/frontend/components/block.cljs

@@ -51,6 +51,8 @@
             [frontend.util.clock :as clock]
             [frontend.util.property :as property]
             [frontend.util.drawer :as drawer]
+            [logseq.graph-parser.config :as gp-config]
+            [logseq.graph-parser.util :as gp-util]
             [goog.dom :as gdom]
             [goog.object :as gobj]
             [lambdaisland.glogi :as log]
@@ -281,7 +283,7 @@
         title (second (first label))]
     (ui/catch-error
      [:span.warning full_text]
-     (if (and (config/local-asset? href)
+     (if (and (gp-config/local-asset? href)
               (config/local-db? (state/get-current-repo)))
        (asset-link config title href metadata full_text)
        (let [href (cond
@@ -647,7 +649,7 @@
   [config id label]
   (when (and
          (not (string/blank? id))
-         (util/uuid-string? id))
+         (gp-util/uuid-string? id))
     (let [block-id (uuid id)
           block (db/pull-block block-id)
           block-type (keyword (get-in block [:block/properties :ls-type]))
@@ -763,7 +765,7 @@
        (and
         (nil? metadata-show)
         (or
-         (config/local-asset? s)
+         (gp-config/local-asset? s)
          (text/media-link? media-formats s)))
        (true? (boolean metadata-show))))
 
@@ -785,7 +787,7 @@
 
 (rum/defc audio-link
   [config url href _label metadata full_text]
-  (if (and (config/local-asset? href)
+  (if (and (gp-config/local-asset? href)
            (config/local-db? (state/get-current-repo)))
     (asset-link config nil href metadata full_text)
     (let [href (cond
@@ -919,7 +921,7 @@
                (= "Complex" protocol)
                (= (string/lower-case (:protocol path)) "id")
                (string? (:link path))
-               (util/uuid-string? (:link path))) ; org mode id
+               (gp-util/uuid-string? (:link path))) ; org mode id
           (let [id (uuid (:link path))
                 block (db/entity [:block/uuid id])]
             (if (:block/pre-block? block)
@@ -1035,7 +1037,7 @@
                        string/trim)]
         (when-let [id (and s
                            (let [s (string/trim s)]
-                             (and (util/uuid-string? s)
+                             (and (gp-util/uuid-string? s)
                                   (uuid s))))]
           (block-embed (assoc config :link-depth (inc link-depth)) id)))
 
@@ -1046,7 +1048,7 @@
   [_config arguments]
   (when-let [url (first arguments)]
     (let [Vimeo-regex #"^((?:https?:)?//)?((?:www).)?((?:player.vimeo.com|vimeo.com)?)((?:/video/)?)([\w-]+)(\S+)?$"]
-      (when-let [vimeo-id (nth (util/safe-re-find Vimeo-regex url) 5)]
+      (when-let [vimeo-id (nth (gp-util/safe-re-find Vimeo-regex url) 5)]
         (when-not (string/blank? vimeo-id)
           (let [width (min (- (util/get-width) 96)
                            560)
@@ -1067,7 +1069,7 @@
       (when-let [id (cond
                       (<= (count url) 15) url
                       :else
-                      (last (util/safe-re-find id-regex url)))]
+                      (last (gp-util/safe-re-find id-regex url)))]
         (when-not (string/blank? id)
           (let [width (min (- (util/get-width) 96)
                            560)
@@ -1197,7 +1199,7 @@
           (when-let [youtube-id (cond
                                   (== 11 (count url)) url
                                   :else
-                                  (nth (util/safe-re-find YouTube-regex url) 5))]
+                                  (nth (gp-util/safe-re-find YouTube-regex url) 5))]
             (when-not (string/blank? youtube-id)
               (youtube/youtube-video youtube-id)))))
 
@@ -1228,7 +1230,7 @@
           (when-let [id (cond
                           (<= (count url) 15) url
                           :else
-                          (last (util/safe-re-find id-regex url)))]
+                          (last (gp-util/safe-re-find id-regex url)))]
             (ui/tweet-embed id))))
 
       (= name "embed")
@@ -2757,7 +2759,7 @@
 
         ["Paragraph" l]
              ;; TODO: speedup
-        (if (util/safe-re-find #"\"Export_Snippet\" \"embed\"" (str l))
+        (if (gp-util/safe-re-find #"\"Export_Snippet\" \"embed\"" (str l))
           (->elem :div (map-inline config l))
           (->elem :div.is-paragraph (map-inline config l)))
 

+ 0 - 1
src/main/frontend/components/command_palette.css

@@ -16,7 +16,6 @@
     }
 
     .menu-link {
-      background-color: transparent;
       transition: none;
       border: none;
       border-radius: unset !important;

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

@@ -21,6 +21,7 @@
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]
             [frontend.util.url :as url-util]
             [goog.dom :as gdom]
             [goog.object :as gobj]
@@ -364,7 +365,7 @@
                            e
                            (custom-context-menu-content))
 
-                          (and block-id (util/uuid-string? block-id))
+                          (and block-id (gp-util/uuid-string? block-id))
                           (let [block (.closest target ".ls-block")]
                             (when block
                               (util/select-highlight! [block]))

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

@@ -20,6 +20,7 @@
             [frontend.util :as util]
             [frontend.util.cursor :as cursor]
             [frontend.util.keycode :as keycode]
+            [logseq.graph-parser.util :as gp-util]
             [goog.dom :as gdom]
             [promesa.core :as p]
             [rum.core :as rum]
@@ -107,9 +108,9 @@
               q (or
                  @editor-handler/*selected-text
                  (when (state/sub :editor/show-page-search-hashtag?)
-                   (util/safe-subs edit-content pos current-pos))
+                   (gp-util/safe-subs edit-content pos current-pos))
                  (when (> (count edit-content) current-pos)
-                   (util/safe-subs edit-content pos current-pos))
+                   (gp-util/safe-subs edit-content pos current-pos))
                  "")
               matched-pages (when-not (string/blank? q)
                               (editor-handler/get-matched-pages q))
@@ -528,7 +529,7 @@
      (when (= (state/sub :editor/record-status) "RECORDING")
        [:div#audio-record-toolbar
         (footer/audio-record-cp)])
-     
+
      (ui/ls-textarea
       {:id                id
        :cacheMeasurements (editor-row-height-unchanged?) ;; check when content updated (as the content variable is binded)

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

@@ -13,6 +13,7 @@
             [frontend.handler.export :as export-handler]
             [frontend.state :as state]
             [frontend.util :as util]
+            [logseq.graph-parser.config :as gp-config]
             [goog.object :as gobj]
             [reitit.frontend.easy :as rfe]
             [rum.core :as rum]))
@@ -43,8 +44,8 @@
            (let [file-id file]
              [:tr {:key file-id}
               [:td
-               (let [href (if (config/draw? file)
-                            (rfe/href :draw nil {:file (string/replace file (str config/default-draw-directory "/") "")})
+               (let [href (if (gp-config/draw? file)
+                            (rfe/href :draw nil {:file (string/replace file (str gp-config/default-draw-directory "/") "")})
                             (rfe/href :file {:path file-id}))]
                  [:a {:href href}
                   file])]

+ 0 - 8
src/main/frontend/components/onboarding/index.css

@@ -174,7 +174,6 @@ body[data-page=import] {
 
                 &.importer {
                     background-color: var(--ls-tertiary-background-color);
-                    padding-top: 80px;
                     position: relative;
 
                     > section {
@@ -200,7 +199,6 @@ body[data-page=import] {
                                 width: unset;
                                 height: 80px;
                                 flex: 1;
-                                margin: 0 15px;
                                 margin-bottom: 10px;
 
                                 > span {
@@ -274,8 +272,6 @@ body[data-page=import] {
                     }
 
                     &.importer {
-                        padding-top: 150px;
-
                         > section {
                             padding: 0;
 
@@ -289,10 +285,6 @@ body[data-page=import] {
                                 padding: unset;
                             }
 
-                            &.d {
-                                padding: 40px 150px;
-                            }
-
                             &.e {
                                 position: absolute;
                                 bottom: -50px;

+ 6 - 5
src/main/frontend/components/onboarding/setups.cljs

@@ -140,18 +140,19 @@
 
     (setups-container
      :importer
-     [:article.flex.flex-col.items-center.importer
+     [:article.flex.flex-col.items-center.importer.py-16.px-8
       [:section.c.text-center
        [:h1 "Do you already have notes that you want to import?"]
        [:h2 "If they are in a JSON or Markdown format Logseq can work with them."]]
       [:section.d.md:flex
-       [:label.action-input.flex.items-center
+       [:label.action-input.flex.items-center.mx-2.my-2
         {:disabled (or roam-importing? opml-importing?)}
         [:span.as-flex-center [:i (svg/roam-research 28)]]
-        [:span.flex.flex-col
+        [:div.flex.flex-col
          (if roam-importing?
            (ui/loading "Importing ...")
-           [[:strong "RoamResearch"]
+           [
+            [:strong "RoamResearch"]
             [:small "Import a JSON Export of your Roam graph"]])]
         [:input.absolute.hidden
          {:id        "import-roam"
@@ -172,7 +173,7 @@
                            (notification/show! "Please choose a JSON file."
                                                :error))))}]]
 
-       [:label.action-input.flex.items-center
+       [:label.action-input.flex.items-center.mx-2.my-2
         {:disabled (or roam-importing? opml-importing?)}
         [:span.as-flex-center (ui/icon "sitemap" {:style {:fontSize "26px"}})]
         [:span.flex.flex-col

+ 4 - 3
src/main/frontend/components/page.cljs

@@ -34,6 +34,7 @@
             [reitit.frontend.easy :as rfe]
             [medley.core :as medley]
             [rum.core :as rum]
+            [logseq.graph-parser.util :as gp-util]
             [frontend.mobile.util :as mobile-util]))
 
 (defn- get-page-name
@@ -114,7 +115,7 @@
   (when page-e
     (let [page-name (or (:block/name page-e)
                         (str (:block/uuid page-e)))
-          block? (util/uuid-string? page-name)
+          block? (gp-util/uuid-string? page-name)
           block-id (and block? (uuid page-name))
           page-blocks (get-blocks repo page-name block-id)]
       (if (empty? page-blocks)
@@ -308,7 +309,7 @@
     (let [current-repo (state/sub :git/current-repo)
           repo (or repo current-repo)
           page-name (util/page-name-sanity-lc path-page-name)
-          block? (util/uuid-string? page-name)
+          block? (gp-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])))
@@ -631,7 +632,7 @@
               (date/today))
         theme (:ui/theme @state/state)
         dark? (= theme "dark")
-        graph (if (util/uuid-string? page)
+        graph (if (gp-util/uuid-string? page)
                 (graph-handler/build-block-graph (uuid page) theme)
                 (graph-handler/build-page-graph page theme))]
     (when (seq (:nodes graph))

+ 2 - 1
src/main/frontend/components/page_menu.cljs

@@ -14,6 +14,7 @@
             [frontend.handler.shell :as shell]
             [frontend.handler.plugin :as plugin-handler]
             [frontend.mobile.util :as mobile-util]
+            [logseq.graph-parser.util :as gp-util]
             [electron.ipc :as ipc]
             [frontend.config :as config]
             [frontend.handler.user :as user-handler]
@@ -63,7 +64,7 @@
           repo (state/sub :git/current-repo)
           page (db/entity repo [:block/name page-name])
           page-original-name (:block/original-name page)
-          block? (and page (util/uuid-string? page-name))
+          block? (and page (gp-util/uuid-string? page-name))
           contents? (= page-name "contents")
           properties (:block/properties page)
           public? (true? (:public properties))

+ 2 - 1
src/main/frontend/components/reference.cljs

@@ -12,6 +12,7 @@
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]
             [medley.core :as medley]
             [rum.core :as rum]))
 
@@ -84,7 +85,7 @@
           default-collapsed? (>= (count refed-blocks-ids) threshold)
           filters-atom (get state ::filters)
           filter-state (rum/react filters-atom)
-          block? (util/uuid-string? page-name)
+          block? (gp-util/uuid-string? page-name)
           block-id (and block? (uuid page-name))
           page-name (string/lower-case page-name)
           journal? (date/valid-journal-title? (string/capitalize page-name))

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

@@ -20,6 +20,7 @@
             [clojure.string :as string]
             [frontend.context.i18n :refer [t]]
             [frontend.date :as date]
+            [logseq.graph-parser.util :as gp-util]
             [reitit.frontend.easy :as rfe]
             [frontend.modules.shortcut.core :as shortcut]))
 
@@ -32,7 +33,7 @@
             lc-content (util/search-normalize content)
             lc-q (util/search-normalize q)]
         (if (and (string/includes? lc-content lc-q)
-                 (not (util/safe-re-find #" " q)))
+                 (not (gp-util/safe-re-find #" " q)))
           (let [i (string/index-of lc-content lc-q)
                 [before after] [(subs content 0 i) (subs content (+ i (count q)))]]
             [:div

+ 4 - 13
src/main/frontend/config.cljs

@@ -4,6 +4,7 @@
             [frontend.state :as state]
             [frontend.util :as util]
             [shadow.resource :as rc]
+            [logseq.graph-parser.util :as gp-util]
             [frontend.mobile.util :as mobile-util]))
 
 (goog-define DEV-RELEASE false)
@@ -26,7 +27,7 @@
 ;; dev env
 (goog-define FILE-SYNC-PROD? false)
 (goog-define LOGIN-URL
-             "https://logseq-test.auth.us-east-2.amazoncognito.com/login?client_id=4fi79en9aurclkb92e25hmu9ts&response_type=code&scope=email+openid+phone&redirect_uri=logseq%3A%2F%2Fauth-callback")
+             "https://logseq-test2.auth.us-east-2.amazoncognito.com/login?client_id=3ji1a0059hspovjq5fhed3uil8&response_type=code&scope=email+openid+phone&redirect_uri=logseq%3A%2F%2Fauth-callback")
 (goog-define API-DOMAIN "api.logseq.com")
 (goog-define WS-URL "wss://og96xf1si7.execute-api.us-east-2.amazonaws.com/production?graphuuid=%s")
 
@@ -105,7 +106,7 @@
 
 (def mobile?
   (when-not util/node-test?
-    (util/safe-re-find #"Mobi" js/navigator.userAgent)))
+    (gp-util/safe-re-find #"Mobi" js/navigator.userAgent)))
 
 ;; TODO: protocol design for future formats support
 
@@ -254,7 +255,6 @@
 
 (defonce default-journals-directory "journals")
 (defonce default-pages-directory "pages")
-(defonce default-draw-directory "draws")
 
 (defn get-pages-directory
   []
@@ -264,10 +264,6 @@
   []
   (or (state/get-journals-directory) default-journals-directory))
 
-(defn draw?
-  [path]
-  (util/starts-with? path default-draw-directory))
-
 (defonce local-repo "local")
 
 (defn demo-graph?
@@ -276,7 +272,6 @@
   ([graph]
    (= graph local-repo)))
 
-(defonce local-assets-dir "assets")
 (defonce recycle-dir ".recycle")
 (def config-file "config.edn")
 (def custom-css-file "custom.css")
@@ -299,10 +294,6 @@
   (and (string? s)
        (string/starts-with? s local-db-prefix)))
 
-(defn local-asset?
-  [s]
-  (util/safe-re-find (re-pattern (str "^[./]*" local-assets-dir)) s))
-
 (defn get-local-asset-absolute-path
   [s]
   (str "/" (string/replace s #"^[./]*" "")))
@@ -368,7 +359,7 @@
 
                  :else
                  relative-path)]
-      (util/path-normalize path))))
+      (gp-util/path-normalize path))))
 
 (defn get-config-path
   ([]

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

@@ -8,6 +8,7 @@
             [frontend.state :as state]
             [frontend.config :as config]
             [frontend.text :as text]
+            [logseq.graph-parser.util :as gp-util]
             [datascript.core :as d]))
 
 (defonce conns (atom {}))
@@ -69,7 +70,7 @@
 
 (defn me-tx
   [_db {:keys [name email avatar]}]
-  (util/remove-nils {:me/name name
+  (gp-util/remove-nils {:me/name name
                      :me/email email
                      :me/avatar avatar}))
 

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

@@ -1,4 +1,4 @@
-(ns frontend.db.default
+(ns ^:nbb-compatible frontend.db.default
   (:require [clojure.string :as string]))
 
 (defonce built-in-pages-names

+ 4 - 3
src/main/frontend/db/model.cljs

@@ -15,6 +15,7 @@
             [frontend.format :as format]
             [frontend.state :as state]
             [frontend.util :as util :refer [react]]
+            [logseq.graph-parser.util :as gp-util]
             [frontend.db.rules :refer [rules]]
             [frontend.db.default :as default-db]
             [frontend.util.drawer :as drawer]))
@@ -61,7 +62,7 @@
    (db-utils/transact! (state/get-current-repo) tx-data))
   ([repo-url tx-data]
    (when-not config/publishing?
-     (let [tx-data (->> (util/remove-nils tx-data)
+     (let [tx-data (->> (gp-util/remove-nils tx-data)
                         (remove nil?)
                         (map #(dissoc % :file/handle :file/type)))]
        (when (seq tx-data)
@@ -898,7 +899,7 @@
 
 (defn get-page
   [page-name]
-  (if (util/uuid-string? page-name)
+  (if (gp-util/uuid-string? page-name)
     (db-utils/entity [:block/uuid (uuid page-name)])
     (db-utils/entity [:block/name (util/page-name-sanity-lc page-name)])))
 
@@ -1256,7 +1257,7 @@
 
 (defn get-referenced-blocks-ids
   [page-name-or-block-uuid]
-  (if (util/uuid-string? (str page-name-or-block-uuid))
+  (if (gp-util/uuid-string? (str page-name-or-block-uuid))
     (let [id (uuid page-name-or-block-uuid)]
       (get-block-referenced-blocks-ids id))
     (get-page-referenced-blocks-ids page-name-or-block-uuid)))

+ 3 - 2
src/main/frontend/db/query_dsl.cljs

@@ -13,7 +13,8 @@
             [frontend.db.rules :as rules]
             [frontend.template :as template]
             [frontend.text :as text]
-            [frontend.util :as util]))
+            [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]))
 
 
 ;; Query fields:
@@ -447,7 +448,7 @@ Some bindings in this fn:
                                                  (remove string/blank?)
                                                  (map (fn [x]
                                                         (if (or (contains? #{"+" "-"} (first x))
-                                                                (and (util/safe-re-find #"\d" (first x))
+                                                                (and (gp-util/safe-re-find #"\d" (first x))
                                                                      (some #(string/ends-with? x %) ["y" "m" "d" "h" "min"])))
                                                           (keyword (name x))
                                                           x)))

+ 3 - 2
src/main/frontend/db/query_react.cljs

@@ -12,6 +12,7 @@
             [frontend.state :as state]
             [frontend.text :as text]
             [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]
             [lambdaisland.glogi :as log]))
 
 (defn resolve-input
@@ -31,12 +32,12 @@
     ;; This sometimes runs when there isn't a current page e.g. :home route
     (some-> (state/get-current-page) string/lower-case)
     (and (keyword? input)
-         (util/safe-re-find #"^\d+d(-before)?$" (name input)))
+         (gp-util/safe-re-find #"^\d+d(-before)?$" (name input)))
     (let [input (name input)
           days (util/parse-int (subs input 0 (dec (count input))))]
       (date->int (t/minus (t/today) (t/days days))))
     (and (keyword? input)
-         (util/safe-re-find #"^\d+d(-after)?$" (name input)))
+         (gp-util/safe-re-find #"^\d+d(-after)?$" (name input)))
     (let [input (name input)
           days (util/parse-int (subs input 0 (dec (count input))))]
       (date->int (t/plus (t/today) (t/days days))))

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

@@ -10,6 +10,7 @@
             [frontend.db.utils :as db-utils]
             [frontend.state :as state]
             [frontend.util :as util :refer [react]]
+            [logseq.graph-parser.util :as gp-util]
             [cljs.spec.alpha :as s]
             [clojure.core.async :as async]))
 
@@ -227,7 +228,7 @@
         affected-keys (concat
                        (mapcat
                         (fn [block-id]
-                          (let [block-id (if (and (string? block-id) (util/uuid-string? block-id))
+                          (let [block-id (if (and (string? block-id) (gp-util/uuid-string? block-id))
                                            [:block/uuid block-id]
                                            block-id)]
                             (when-let [block (db-utils/entity block-id)]

+ 1 - 1
src/main/frontend/db/rules.cljc

@@ -1,4 +1,4 @@
-(ns ^:bb-compatible frontend.db.rules)
+(ns ^:bb-compatible ^:nbb-compatible frontend.db.rules)
 
 (def rules
   ;; rule "parent" is optimized for child node -> parent node nesting queries

+ 3 - 2
src/main/frontend/db/utils.cljs

@@ -7,7 +7,8 @@
             [frontend.util :as util]
             [frontend.date :as date]
             [frontend.db.conn :as conn]
-            [frontend.config :as config]))
+            [frontend.config :as config]
+            [logseq.graph-parser.util :as gp-util]))
 
 ;; transit serialization
 
@@ -88,7 +89,7 @@
    (transact! repo-url tx-data nil))
   ([repo-url tx-data tx-meta]
    (when-not config/publishing?
-     (let [tx-data (->> (util/remove-nils tx-data)
+     (let [tx-data (->> (gp-util/remove-nils tx-data)
                         (remove nil?))]
        (when (seq tx-data)
          (when-let [conn (conn/get-db repo-url false)]

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

@@ -1,4 +1,4 @@
-(ns frontend.db-schema)
+(ns ^:nbb-compatible frontend.db-schema)
 
 (defonce version 1)
 (defonce ast-version 1)

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

@@ -5,6 +5,7 @@
             [lambdaisland.glogi :as log]
             [cljs-bean.core :as bean]
             [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]
             [frontend.text :as text]))
 
 (defn diff
@@ -54,7 +55,7 @@
           (+ pos 2)
 
           (contains? inline-special-chars (util/nth-safe markup pos))
-          (let [matched (->> (take-while inline-special-chars (util/safe-subs markup pos))
+          (let [matched (->> (take-while inline-special-chars (gp-util/safe-subs markup pos))
                              (apply str))
                 matched? (and current-line (string/includes? current-line (string/reverse matched)))]
             (if matched?

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

@@ -134,7 +134,7 @@
             [frontend.state :as state]
             [frontend.utf8 :as utf8]
             [frontend.util :as util]
-            [frontend.config :as ui-config]
+            [frontend.config :as config]
             [goog.dom :as gdom]
             [goog.object :as gobj]
             [rum.core :as rum]))
@@ -238,7 +238,7 @@
                                                    (when-let [block-id (:block/uuid config)]
                                                      (let [block (db/pull [:block/uuid block-id])]
                                                        (editor-handler/edit-block! block :max block-id))))}}
-                          (when ui-config/publishing?
+                          (when config/publishing?
                             {:readOnly true
                              :cursorBlinkRate -1}))
         editor (when textarea

+ 2 - 1
src/main/frontend/extensions/html_parser.cljs

@@ -4,6 +4,7 @@
             [clojure.walk :as walk]
             [frontend.config :as config]
             [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]
             [hickory.core :as hickory]))
 
 (defonce *inside-pre? (atom false))
@@ -74,7 +75,7 @@
                                 :h6 (block-transform 6 children)
                                 :a (let [href (:href attrs)
                                          label (map-join children)
-                                         has-img-tag? (util/safe-re-find #"\[:img" (str x))]
+                                         has-img-tag? (gp-util/safe-re-find #"\[:img" (str x))]
                                      (if has-img-tag?
                                        (export-hiccup x)
                                        (case format

+ 8 - 7
src/main/frontend/extensions/pdf/assets.cljs

@@ -9,6 +9,7 @@
             [frontend.handler.page :as page-handler]
             [frontend.state :as state]
             [frontend.util :as util]
+            [logseq.graph-parser.config :as gp-config]
             [medley.core :as medley]
             [promesa.core :as p]
             [reitit.frontend.easy :as rfe]
@@ -35,8 +36,8 @@
 
               :else
               (let [full-path (string/replace full-path #"^[.\/\\]+" "")
-                    full-path (if-not (string/starts-with? full-path config/local-assets-dir)
-                                (util/node-path.join config/local-assets-dir full-path)
+                    full-path (if-not (string/starts-with? full-path gp-config/local-assets-dir)
+                                (util/node-path.join gp-config/local-assets-dir full-path)
                                 full-path)]
                 (str "file://"  ;; TODO: bfs
                      (util/node-path.join
@@ -75,7 +76,7 @@
 (defn resolve-hls-data-by-key$
   [target-key]
   ;; TODO: fuzzy match
-  (when-let [hls-file (and target-key (str config/local-assets-dir "/" target-key ".edn"))]
+  (when-let [hls-file (and target-key (str gp-config/local-assets-dir "/" target-key ".edn"))]
     (load-hls-data$ {:hls-file hls-file})))
 
 (defn area-highlight?
@@ -113,7 +114,7 @@
                                    fstamp (get-in new-hl [:content :image])
                                    old-fstamp (and old-hl (get-in old-hl [:content :image]))
                                    fname (str (:page new-hl) "_" (:id new-hl))
-                                   fdir (str config/local-assets-dir "/" key)
+                                   fdir (str gp-config/local-assets-dir "/" key)
                                    _ (fs/mkdir-if-not-exists (str repo-dir "/" fdir))
                                    new-fpath (str fdir "/" fname "_" fstamp ".png")
                                    old-fpath (and old-fstamp (str fdir "/" fname "_" old-fstamp ".png"))
@@ -142,7 +143,7 @@
           repo-dir (config/get-repo-dir repo-cur)
           fstamp (get-in hl [:content :image])
           fname (str (:page hl) "_" (:id hl))
-          fdir (str config/local-assets-dir "/" fkey)
+          fdir (str gp-config/local-assets-dir "/" fkey)
           fpath (util/node-path.join repo-dir (str fdir "/" fname "_" fstamp ".png"))]
 
       (fs/unlink! repo-cur fpath {}))))
@@ -156,7 +157,7 @@
         format (state/get-preferred-format)]
     (if-not page
       (let [repo-dir (config/get-repo-dir (state/get-current-repo))
-            asset-dir (util/node-path.join repo-dir config/local-assets-dir)
+            asset-dir (util/node-path.join repo-dir gp-config/local-assets-dir)
             url (if (string/includes? url asset-dir)
                   (str ".." (last (string/split url repo-dir)))
                   url)
@@ -245,7 +246,7 @@
       (when-let [group-key (string/replace-first (:block/original-name page) #"^hls__" "")]
         (when-let [hl-page (:hl-page props)]
           (let [asset-path (editor-handler/make-asset-url
-                             (str "/" config/local-assets-dir "/" group-key "/" (str hl-page "_" id "_" stamp ".png")))]
+                             (str "/" gp-config/local-assets-dir "/" group-key "/" (str hl-page "_" id "_" stamp ".png")))]
             [:span.hl-area
              [:img {:src asset-path}]]))))))
 

+ 13 - 13
src/main/frontend/extensions/pdf/highlights.cljs

@@ -11,7 +11,7 @@
             [frontend.state :as state]
             [frontend.storage :as storage]
             [frontend.ui :as ui]
-            [frontend.util :as front-utils]
+            [frontend.util :as util]
             [medley.core :as medley]
             [promesa.core :as p]
             [rum.core :as rum]))
@@ -44,7 +44,7 @@
       (let [active-hl (:pdf/ref-highlight @state/state)
             page-key (:filename current)
             last-page (and page-key
-                           (front-utils/safe-parse-int (storage/get (str "ls-pdf-last-page-" page-key))))]
+                           (util/safe-parse-int (storage/get (str "ls-pdf-last-page-" page-key))))]
 
         (when (and last-page (nil? active-hl))
           (set! (.-currentPageNumber viewer) last-page)))))
@@ -54,7 +54,7 @@
   [^js viewer]
   (let [el-ref (rum/use-ref nil)
         adjust-main-size!
-        (front-utils/debounce
+        (util/debounce
           200 (fn [width]
                 (let [root-el js/document.documentElement]
                   (.setProperty (.-style root-el) "--ph-view-container-width" width)
@@ -119,7 +119,7 @@
 
                         "copy"
                         (do
-                          (front-utils/copy-to-clipboard!
+                          (util/copy-to-clipboard!
                             (or (:text content) (.toString range)))
                           (pdf-utils/clear-all-selection))
 
@@ -155,7 +155,7 @@
         (if (and @*highlight-mode? new?)
           (action-fn! @*highlight-last-color true)
           (let [^js el (rum/deref *el)
-                {:keys [x y]} (front-utils/calc-delta-rect-offset el (.closest el ".extensions__pdf-viewer"))]
+                {:keys [x y]} (util/calc-delta-rect-offset el (.closest el ".extensions__pdf-viewer"))]
             (set! (.. el -style -transform)
                   (str "translate3d(" (if (neg? x) (- x 5) 0) "px," (if (neg? y) (- y 5) 0) "px" ",0)"))))
         #())
@@ -341,7 +341,7 @@
                                       (.contains (.-classList target) "extensions__pdf-hls-area-region"))
                                     (.closest target ".page"))
                            (and e (or (.-metaKey e)
-                                      (and front-utils/win32? (.-shiftKey e))
+                                      (and util/win32? (.-shiftKey e))
                                       @*area-mode?)))))
 
         reset-coords #(do
@@ -688,7 +688,7 @@
         expanded? (boolean expanded)]
 
     [:div.extensions__pdf-outline-item
-     {:class (front-utils/classnames [{:has-children has-child? :is-expand expanded?}])}
+     {:class (util/classnames [{:has-children has-child? :is-expand expanded?}])}
      [:div.inner
       [:a
        {:href      "javascript:void(0);"
@@ -755,7 +755,7 @@
         [])
 
       [:div.extensions__pdf-outline-wrap.hls-popup-wrap
-       {:class    (front-utils/classnames [{:visible visible?}])
+       {:class    (util/classnames [{:visible visible?}])
         :on-click (fn [^js/MouseEvent e]
                     (let [target (.-target e)]
                       (when-not (.contains (rum/deref *el-outline) target)
@@ -790,7 +790,7 @@
                (fn []
                  (let [text (.-innerText (js/document.querySelector "#pdf-docinfo > .inner-text"))
                        text (string/replace text #"[\n\t]+" "\n")]
-                   (front-utils/copy-to-clipboard! text)
+                   (util/copy-to-clipboard! text)
                    (notification/show! "Copied!" :success)
                    (close-fn!))))]])
 
@@ -852,7 +852,7 @@
 
        ;; selection
        [:a.button
-        {:title    (str "Area highlight (" (if front-utils/mac? "⌘" "Shift") ")")
+        {:title    (str "Area highlight (" (if util/mac? "⌘" "Shift") ")")
          :class    (when area-mode? "is-active")
          :on-click #(set-area-mode! (not area-mode?))}
         (svg/icon-area 18)]
@@ -902,7 +902,7 @@
                   :on-mouse-enter #(.select ^js (.-target %))
                   :on-key-up      (fn [^js e]
                                     (let [^js input (.-target e)
-                                          value (front-utils/safe-parse-int (.-value input))]
+                                          value (util/safe-parse-int (.-value input))]
                                       (when (and (= (.-keyCode e) 13) value (> value 0))
                                         (set! (. viewer -currentPageNumber)
                                               (if (> value total-page-num) total-page-num value)))))}]
@@ -962,7 +962,7 @@
                ;;TODO: destroy
                (fn []
                  (when-let [last-page (.-currentPageNumber viewer)]
-                   (storage/set (str "ls-pdf-last-page-" (front-utils/node-path.basename url)) last-page))
+                   (storage/set (str "ls-pdf-last-page-" (util/node-path.basename url)) last-page))
 
                  (when pdf-document (.destroy pdf-document)))))
       [])
@@ -995,7 +995,7 @@
     (let [^js viewer (:viewer state)]
       [:div.extensions__pdf-viewer-cnt
        [:div.extensions__pdf-viewer
-        {:ref *el-ref :class (front-utils/classnames [{:is-area-dashed @*area-dashed?}])}
+        {:ref *el-ref :class (util/classnames [{:is-area-dashed @*area-dashed?}])}
         [:div.pdfViewer "viewer pdf"]
         [:div.pp-holder]
 

+ 2 - 2
src/main/frontend/extensions/pdf/utils.cljs

@@ -1,7 +1,7 @@
 (ns frontend.extensions.pdf.utils
   (:require [promesa.core :as p]
             [cljs-bean.core :as bean]
-            [frontend.util :as front-utils]
+            [frontend.util :as util]
             ["/frontend/extensions/pdf/utils" :as js-utils]
             [frontend.db :as front-db]
             [frontend.loader :refer [load]]))
@@ -105,7 +105,7 @@
   (.removeAllRanges (js/window.getSelection)))
 
 (def adjust-viewer-size!
-  (front-utils/debounce
+  (util/debounce
     200 (fn [^js viewer] (set! (. viewer -currentScaleValue) "auto"))))
 
 (defn fix-nested-js

+ 0 - 2
src/main/frontend/external.cljc → src/main/frontend/external.cljs

@@ -1,6 +1,4 @@
 (ns frontend.external
-  ;; Wonky cljs detection
-  #_:clj-kondo/ignore
   (:require [frontend.external.roam :refer [->Roam]]
             [frontend.external.protocol :as protocol]))
 

+ 42 - 50
src/main/frontend/external/roam.cljc → src/main/frontend/external/roam.cljs

@@ -1,15 +1,12 @@
 (ns frontend.external.roam
-  (:require #?(:cljs [cljs-bean.core :as bean]
-               :clj [cheshire.core :as json])
-            ;; TODO: clj-kondo incorrectly thinks these requires are unused
-            #_:clj-kondo/ignore
+  (:require [cljs-bean.core :as bean]
             [frontend.external.protocol :as protocol]
-            #_:clj-kondo/ignore
             [frontend.date :as date]
             [medley.core :as medley]
             [clojure.walk :as walk]
             [clojure.string :as string]
             [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]
             [frontend.text :as text]))
 
 (defonce all-refed-uids (atom #{}))
@@ -39,10 +36,8 @@
 (defn macro-transform
   [text]
   (string/replace text macro-pattern (fn [[original text]]
-                                       (let [[name arg] (util/split-first ":" text)]
+                                       (let [[name arg] (gp-util/split-first ":" text)]
                                          (if name
-                                           ;; TODO: Why unresolved var
-                                           #_:clj-kondo/ignore
                                            (let [name (text/page-ref-un-brackets! name)]
                                              (util/format "{{%s %s}}" name arg))
                                            original)))))
@@ -104,48 +99,45 @@
 
 (defn json->edn
   [raw-string]
-  #?(:cljs (-> raw-string js/JSON.parse bean/->clj)
-     :clj (-> raw-string json/parse-string clojure.walk/keywordize-keys)))
-
-#?(:cljs
-   (do
-     (defn ->file
-      [page-data]
-      (let [{:keys [create-time title children edit-time]} page-data
-            initial-level 1
-            text (when (seq children)
-                   (when-let [text (children->text children (dec initial-level))]
-                     (let [journal? (date/valid-journal-title? title)
-                           front-matter (if journal?
-                                          ""
-                                          (util/format "---\ntitle: %s\n---\n\n" title))]
-                       (str front-matter (transform text)))))]
-        (when (and (not (string/blank? title))
-                   text)
-          {:title title
-           :created-at create-time
-           :last-modified-at edit-time
-           :text text})))
-
-     (defn ->files
-       [edn-data]
-       (load-all-refed-uids! edn-data)
-       (let [files (map ->file edn-data)
-             files (remove #(nil? (:title %)) files)
-             files (group-by (fn [f] (string/lower-case (:title f)))
-                             files)]
-         (map
-          (fn [[_ [fst & others]]]
-            (assoc fst :text
-                   (->> (map :text (cons fst others))
-                        (interpose "\n")
-                        (apply str))))
-          files)))
-
-     (defrecord Roam []
-       protocol/External
-       (toMarkdownFiles [_this content _config]
-                        (-> content json->edn ->files)))))
+  (-> raw-string js/JSON.parse bean/->clj))
+
+(defn ->file
+  [page-data]
+  (let [{:keys [create-time title children edit-time]} page-data
+        initial-level 1
+        text (when (seq children)
+               (when-let [text (children->text children (dec initial-level))]
+                 (let [journal? (date/valid-journal-title? title)
+                       front-matter (if journal?
+                                      ""
+                                      (util/format "---\ntitle: %s\n---\n\n" title))]
+                   (str front-matter (transform text)))))]
+    (when (and (not (string/blank? title))
+               text)
+      {:title title
+       :created-at create-time
+       :last-modified-at edit-time
+       :text text})))
+
+(defn ->files
+  [edn-data]
+  (load-all-refed-uids! edn-data)
+  (let [files (map ->file edn-data)
+        files (remove #(nil? (:title %)) files)
+        files (group-by (fn [f] (string/lower-case (:title f)))
+                        files)]
+    (map
+     (fn [[_ [fst & others]]]
+       (assoc fst :text
+              (->> (map :text (cons fst others))
+                   (interpose "\n")
+                   (apply str))))
+     files)))
+
+(defrecord Roam []
+  protocol/External
+  (toMarkdownFiles [_this content _config]
+                   (-> content json->edn ->files)))
 
 (comment
   (defonce test-roam-json (frontend.db/get-file "same.json"))

+ 11 - 9
src/main/frontend/format/block.cljs

@@ -11,6 +11,8 @@
             [frontend.utf8 :as utf8]
             [frontend.util :as util]
             [frontend.util.property :as property]
+            [logseq.graph-parser.util :as gp-util]
+            [logseq.graph-parser.config :as gp-config]
             [lambdaisland.glogi :as log]
             [medley.core :as medley]
             [frontend.format.mldoc :as mldoc]))
@@ -53,8 +55,8 @@
                   (and
                    (= typ "Page_ref")
                    (and (string? value)
-                        (not (or (config/local-asset? value)
-                                 (config/draw? value))))
+                        (not (or (gp-config/local-asset? value)
+                                 (gp-config/draw? value))))
                    value)
 
                   (and
@@ -69,7 +71,7 @@
                      (when (and (not (util/starts-with? value "http:"))
                                 (not (util/starts-with? value "https:"))
                                 (not (util/starts-with? value "file:"))
-                                (not (config/local-asset? value))
+                                (not (gp-config/local-asset? value))
                                 (or (= ext :excalidraw)
                                     (not (contains? (config/supported-formats) ext))))
                        value)))
@@ -136,7 +138,7 @@
                         :else
                         nil)]
     (when (and block-id
-               (util/uuid-string? block-id))
+               (gp-util/uuid-string? block-id))
       block-id)))
 
 (defn paragraph-block?
@@ -265,7 +267,7 @@
             {}
             {:block/uuid (db/new-block-id)}))
         (when namespace?
-          (let [namespace (first (util/split-last "/" original-page-name))]
+          (let [namespace (first (gp-util/split-last "/" original-page-name))]
             (when-not (string/blank? namespace)
               {:block/namespace {:block/name (util/page-name-sanity-lc namespace)}})))
         (when (and with-timestamp? (not page-entity)) ;; Only assign timestamp on creating new entity
@@ -302,7 +304,7 @@
            (swap! refs conj page))
          (when-let [tag (get-tag form)]
            (let [tag (text/page-ref-un-brackets! tag)]
-             (when (util/tag-valid? tag)
+             (when (gp-util/tag-valid? tag)
                (swap! refs conj tag))))
          form))
      (concat title body))
@@ -333,7 +335,7 @@
        form)
      (concat title body))
     (let [ref-blocks (->> @ref-blocks
-                          (filter util/uuid-string?))
+                          (filter gp-util/uuid-string?))
           ref-blocks (map
                        (fn [id]
                          [:block/uuid (medley/uuid id)])
@@ -354,7 +356,7 @@
   [blocks]
   (map (fn [block]
          (if (map? block)
-           (block-keywordize (util/remove-nils block))
+           (block-keywordize (gp-util/remove-nils block))
            block))
        blocks))
 
@@ -437,7 +439,7 @@
                                (get-in properties [:properties :custom_id])
                                (get-in properties [:properties :id]))]
         (let [custom-id (and (string? custom-id) (string/trim custom-id))]
-          (when (and custom-id (util/uuid-string? custom-id))
+          (when (and custom-id (gp-util/uuid-string? custom-id))
             (uuid custom-id))))
       (db/new-block-id)))
 

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

@@ -3,13 +3,13 @@
             [clojure.string :as string]
             [frontend.format.protocol :as protocol]
             [frontend.utf8 :as utf8]
-            [frontend.util :as util]
             [goog.object :as gobj]
             [lambdaisland.glogi :as log]
             [medley.core :as medley]
             ["mldoc" :as mldoc :refer [Mldoc]]
             [linked.core :as linked]
-            [frontend.config :as config]))
+            [logseq.graph-parser.util :as gp-util]
+            [logseq.graph-parser.config :as gp-config]))
 
 (defonce parseJson (gobj/get Mldoc "parseJson"))
 (defonce parseInlineJson (gobj/get Mldoc "parseInlineJson"))
@@ -92,8 +92,8 @@
   (let [lines (string/split-lines s)
         [f & r] lines
         body (map (fn [line]
-                    (if (string/blank? (util/safe-subs line 0 level))
-                      (util/safe-subs line level)
+                    (if (string/blank? (gp-util/safe-subs line 0 level))
+                      (gp-util/safe-subs line level)
                       line))
                (if remove-first-line? lines r))
         content (if remove-first-line? body (cons f body))]
@@ -138,7 +138,7 @@
                    (->>
                     (map
                      (fn [[_ v]]
-                       (let [[k v] (util/split-first " " v)]
+                       (let [[k v] (gp-util/split-first " " v)]
                          (mapv
                           string/trim
                           [k v])))
@@ -202,7 +202,7 @@
         []
         (-> content
             (parse-json config)
-            (util/json->clj)
+            (gp-util/json->clj)
             (update-src-full-content content)
             (collect-page-properties parse-property)))
       (catch js/Error e
@@ -215,7 +215,7 @@
   (try
     (if (string/blank? content)
       {}
-      (let [[headers blocks] (-> content (parse-opml) (util/json->clj))]
+      (let [[headers blocks] (-> content (parse-opml) (gp-util/json->clj))]
         [headers (collect-page-properties blocks parse-property)]))
     (catch js/Error e
       (log/error :edn/convert-failed e)
@@ -228,7 +228,7 @@
       {}
       (-> text
           (inline-parse-json config)
-          (util/json->clj)))
+          (gp-util/json->clj)))
     (catch js/Error _e
       [])))
 
@@ -273,7 +273,7 @@
             (and (contains? #{"Page_ref"} ref-type)
                  (or
                   ;; 2. excalidraw link
-                  (config/draw? ref-value)
+                  (gp-config/draw? ref-value)
 
                   ;; 3. local asset link
-                  (boolean (config/local-asset? ref-value)))))))))
+                  (boolean (gp-config/local-asset? ref-value)))))))))

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

@@ -9,13 +9,13 @@
             [frontend.handler.page :as page-handler]
             [frontend.handler.repo :as repo-handler]
             [frontend.handler.ui :as ui-handler]
-            [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]
             [lambdaisland.glogi :as log]
             [electron.ipc :as ipc]
             [promesa.core :as p]
             [frontend.state :as state]))
 
-;; all IPC paths must be normalized! (via util/path-normalize)
+;; all IPC paths must be normalized! (via gp-util/path-normalize)
 
 (defn- set-missing-block-ids!
   [content]
@@ -42,7 +42,7 @@
 (defn handle-changed!
   [type {:keys [dir path content stat] :as payload}]
   (when dir
-    (let [path (util/path-normalize path)
+    (let [path (gp-util/path-normalize path)
           repo (config/get-local-repo dir)
           pages-metadata-path (config/get-pages-metadata-path)
           {:keys [mtime]} stat

+ 3 - 2
src/main/frontend/handler/draw.cljs

@@ -7,6 +7,7 @@
             [frontend.handler.file :as file-handler]
             [frontend.state :as state]
             [frontend.util :as util]
+            [logseq.graph-parser.config :as gp-config]
             [promesa.core :as p]))
 
 (defn create-draws-directory!
@@ -14,7 +15,7 @@
   (when repo
     (let [repo-dir (config/get-repo-dir repo)]
       (util/p-handle
-       (fs/mkdir! (str repo-dir (str "/" config/default-draw-directory)))
+       (fs/mkdir! (str repo-dir (str "/" gp-config/default-draw-directory)))
        (fn [_result] nil)
        (fn [_error] nil)))))
 
@@ -61,6 +62,6 @@
   [current-file]
   (when-let [repo (state/get-current-repo)]
     (p/let [exists? (fs/file-exists? (config/get-repo-dir repo)
-                                     (str config/default-draw-directory current-file))]
+                                     (str gp-config/default-draw-directory current-file))]
       (when-not exists?
         (save-excalidraw! current-file default-content)))))

+ 19 - 16
src/main/frontend/handler/editor.cljs

@@ -53,7 +53,10 @@
             [goog.object :as gobj]
             [lambdaisland.glogi :as log]
             [medley.core :as medley]
-            [promesa.core :as p]))
+            [promesa.core :as p]
+            [frontend.util.keycode :as keycode]
+            [logseq.graph-parser.util :as gp-util]
+            ["path" :as path]))
 
 ;; FIXME: should support multiple images concurrently uploading
 
@@ -256,7 +259,7 @@
 (defn- another-block-with-same-id-exists?
   [current-id block-id]
   (and (string? block-id)
-       (util/uuid-string? block-id)
+       (gp-util/uuid-string? block-id)
        (not= current-id (cljs.core/uuid block-id))
        (db/entity [:block/uuid (cljs.core/uuid block-id)])))
 
@@ -337,7 +340,7 @@
   (if (and (state/enable-timetracking?)
            (not= (:block/content block) value))
     (let [format (:block/format block)
-          new-marker (last (util/safe-re-find (marker/marker-pattern format) (or value "")))
+          new-marker (last (gp-util/safe-re-find (marker/marker-pattern format) (or value "")))
           new-value (with-marker-time value block format
                       new-marker
                       (:block/marker block))]
@@ -481,10 +484,10 @@
   (let [current-page (state/get-current-page)
         block-id (or
                   (and (:id config)
-                       (util/uuid-string? (:id config))
+                       (gp-util/uuid-string? (:id config))
                        (:id config))
                   (and current-page
-                       (util/uuid-string? current-page)
+                       (gp-util/uuid-string? current-page)
                        current-page))]
     (= uuid (and block-id (medley/uuid block-id)))))
 
@@ -1143,7 +1146,7 @@
   []
   (when-let [page (get-nearest-page)]
     (let [page-name (string/lower-case page)
-          block? (util/uuid-string? page-name)]
+          block? (gp-util/uuid-string? page-name)]
       (when-let [page (db/get-page page-name)]
         (if block?
           (state/sidebar-add-block!
@@ -1175,7 +1178,7 @@
     (let [page (state/get-current-page)
           block-id (and
                     (string? page)
-                    (util/uuid-string? page)
+                    (gp-util/uuid-string? page)
                     (medley/uuid page))]
       (when block-id
         (let [block-parent (db/get-block-parent block-id)]
@@ -1296,7 +1299,7 @@
                  elem (and input-id (gdom/getElement input-id))
                  db-content (:block/content db-block)
                  db-content-without-heading (and db-content
-                                                 (util/safe-subs db-content (:block/level db-block)))
+                                                 (gp-util/safe-subs db-content (:block/level db-block)))
                  value (and elem (gobj/get elem "value"))]
              (cond
                force?
@@ -1764,7 +1767,7 @@
             edit-content (or (state/sub [:editor/content id]) "")]
         (or
          @*selected-text
-         (util/safe-subs edit-content pos current-pos))))))
+         (gp-util/safe-subs edit-content pos current-pos))))))
 
 (defn close-autocomplete-if-outside
   [input]
@@ -1777,7 +1780,7 @@
       (let [value (gobj/get input "value")
             pos (state/get-editor-last-pos)
             current-pos (cursor/pos input)
-            between (util/safe-subs value (min pos current-pos) (max pos current-pos))]
+            between (gp-util/safe-subs value (min pos current-pos) (max pos current-pos))]
         (when (and between
                    (or
                     (string/includes? between "[")
@@ -2038,7 +2041,7 @@
 (defn- last-top-level-child?
   [{:keys [id]} current-node]
   (when id
-    (when-let [entity (if (util/uuid-string? (str id))
+    (when-let [entity (if (gp-util/uuid-string? (str id))
                         (db/entity [:block/uuid (uuid id)])
                         (db/entity [:block/name (util/page-name-sanity-lc id)]))]
       (= (:block/uuid entity) (tree/-get-parent-id current-node)))))
@@ -2852,7 +2855,7 @@
         (string/join "\n"
                      (mapv (fn [p] (->> (string/trim p)
                                         ((fn [p]
-                                           (if (util/safe-re-find (if (= format :org)
+                                           (if (gp-util/safe-re-find (if (= format :org)
                                                                     #"\s*\*+\s+"
                                                                     #"\s*-\s+") p)
                                              p
@@ -2915,9 +2918,9 @@
       ;; from external
       (let [format (or (db/get-page-format (state/get-current-page)) :markdown)]
         (match [format
-                (nil? (util/safe-re-find #"(?m)^\s*(?:[-+*]|#+)\s+" text))
-                (nil? (util/safe-re-find #"(?m)^\s*\*+\s+" text))
-                (nil? (util/safe-re-find #"(?:\r?\n){2,}" text))]
+                (nil? (gp-util/safe-re-find #"(?m)^\s*(?:[-+*]|#+)\s+" text))
+                (nil? (gp-util/safe-re-find #"(?m)^\s*\*+\s+" text))
+                (nil? (gp-util/safe-re-find #"(?:\r?\n){2,}" text))]
           [:markdown false _ _]
           (paste-text-parseable format text)
 
@@ -3202,7 +3205,7 @@
     :or {collapse? false expanded? false incremental? true root-block nil}}]
   (when-let [page (or (state/get-current-page)
                       (date/today))]
-    (let [block? (util/uuid-string? page)
+    (let [block? (gp-util/uuid-string? page)
           block-id (or root-block (and block? (uuid page)))
           blocks (if block-id
                    (db/get-block-and-children (state/get-current-repo) block-id)

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

@@ -11,6 +11,7 @@
             [frontend.state :as state]
             [frontend.text :as text]
             [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]
             [frontend.util.property :as property]
             [lambdaisland.glogi :as log]))
 
@@ -29,7 +30,7 @@
                                     (string? title)
                                     title))
             file-name (when-let [file-name (last (string/split file #"/"))]
-                        (let [result (first (util/split-last "." file-name))]
+                        (let [result (first (gp-util/split-last "." file-name))]
                           (if (config/mldoc-support? (string/lower-case (util/get-file-ext file)))
                             (string/replace result "." "/")
                             result)))]
@@ -90,10 +91,10 @@
                                        aliases)
                                      (remove nil?))]
                         (cond->
-                          (util/remove-nils
+                          (gp-util/remove-nils
                            (assoc
                             (block/page-name->map page false)
-                            :block/file {:file/path (util/path-normalize file)}))
+                            :block/file {:file/path (gp-util/path-normalize file)}))
                           (seq properties)
                           (assoc :block/properties properties)
 

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

@@ -16,6 +16,7 @@
             [frontend.handler.ui :as ui-handler]
             [frontend.state :as state]
             [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]  
             [lambdaisland.glogi :as log]
             [promesa.core :as p]
             [frontend.mobile.util :as mobile]
@@ -75,7 +76,7 @@
                                         (seq images)
                                         (merge (zipmap images (repeat (count images) ""))))
                         file-contents (for [[file content] file-contents]
-                                        {:file/path (util/path-normalize file)
+                                        {:file/path (gp-util/path-normalize file)
                                          :file/content content})]
                     (ok-handler file-contents))))
         (p/catch (fn [error]
@@ -115,7 +116,7 @@
 
                 :else
                 file)
-         file (util/path-normalize file)
+         file (gp-util/path-normalize file)
          new? (nil? (db/entity [:file/path file]))]
      (db/set-file-content! repo-url file content)
      (let [format (format/get-format file)

+ 3 - 2
src/main/frontend/handler/graph.cljs

@@ -4,7 +4,8 @@
             [frontend.db :as db]
             [frontend.db.default :as default-db]
             [frontend.state :as state]
-            [frontend.util :as util]))
+            [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]))
 
 (defn- build-links
   [links]
@@ -45,7 +46,7 @@
                   ;; slow
 (defn- uuid-or-asset?
   [id]
-  (or (util/uuid-string? id)
+  (or (gp-util/uuid-string? id)
       (string/starts-with? id "../assets/")
       (= id "..")
       (string/starts-with? id "assets/")

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

@@ -33,6 +33,8 @@
             [lambdaisland.glogi :as log]
             [promesa.core :as p]
             [frontend.mobile.util :as mobile-util]
+            [logseq.graph-parser.util :as gp-util]
+            [logseq.graph-parser.config :as gp-config]
             [goog.functions :refer [debounce]]))
 
 (defn- get-directory
@@ -47,7 +49,7 @@
                  (date/journal-title->default title)
                  (util/page-name-sanity (string/lower-case title)))]
     ;; Win10 file path has a length limit of 260 chars
-    (util/safe-subs s 0 200)))
+    (gp-util/safe-subs s 0 200)))
 
 (defn get-page-file-path
   ([] (get-page-file-path (state/get-current-page)))
@@ -625,8 +627,8 @@
   (->> (db/get-all-pages repo)
        (remove (fn [p]
                  (let [name (:block/name p)]
-                   (or (util/uuid-string? name)
-                       (config/draw? name)
+                   (or (gp-util/uuid-string? name)
+                       (gp-config/draw? name)
                        (db/built-in-pages-names (string/upper-case name))))))
        (common-handler/fix-pages-timestamps)))
 
@@ -669,17 +671,17 @@
         q (or
            @editor-handler/*selected-text
            (when (state/sub :editor/show-page-search-hashtag?)
-             (util/safe-subs edit-content pos current-pos))
+             (gp-util/safe-subs edit-content pos current-pos))
            (when (> (count edit-content) current-pos)
-             (util/safe-subs edit-content pos current-pos)))]
+             (gp-util/safe-subs edit-content pos current-pos)))]
     (if (state/sub :editor/show-page-search-hashtag?)
       (fn [chosen _click?]
         (state/set-editor-show-page-search! false)
-        (let [wrapped? (= "[[" (util/safe-subs edit-content (- pos 2) pos))
+        (let [wrapped? (= "[[" (gp-util/safe-subs edit-content (- pos 2) pos))
               chosen (if (string/starts-with? chosen "New page: ") ;; FIXME: What if a page named "New page: XXX"?
                        (subs chosen 10)
                        chosen)
-              chosen (if (and (util/safe-re-find #"\s+" chosen) (not wrapped?))
+              chosen (if (and (gp-util/safe-re-find #"\s+" chosen) (not wrapped?))
                        (util/format "[[%s]]" chosen)
                        chosen)
               q (if @editor-handler/*selected-text "" q)

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

@@ -22,6 +22,7 @@
             [promesa.core :as p]
             [shadow.resource :as rc]
             [frontend.db.persist :as db-persist]
+            [logseq.graph-parser.util :as gp-util]
             [electron.ipc :as ipc]
             [clojure.set :as set]
             [clojure.core.async :as async]))
@@ -300,7 +301,7 @@
                              [])
               add-or-modify-files (some->>
                                    (concat modify-files add-files)
-                                   (util/remove-nils))
+                                   (gp-util/remove-nils))
               options {:delete-files (concat delete-files delete-pages)
                        :delete-blocks delete-blocks
                        :re-render? true}]

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

@@ -8,6 +8,7 @@
             [frontend.state :as state]
             [frontend.text :as text]
             [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]
             [medley.core :as medley]
             [reitit.frontend.easy :as rfe]))
 
@@ -77,7 +78,7 @@
     "Create a new page"
     :page
     (let [name (:name path-params)
-          block? (util/uuid-string? name)]
+          block? (gp-util/uuid-string? name)]
       (if block?
         (if-let [block (db/entity [:block/uuid (medley/uuid name)])]
           (let [content (text/remove-level-spaces (:block/content block)

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

@@ -1,7 +1,7 @@
 (ns frontend.handler.shell
   (:require [electron.ipc :as ipc]
             [clojure.string :as string]
-            [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]
             [frontend.handler.notification :as notification]
             [promesa.core :as p]
             [frontend.db :as db]
@@ -34,7 +34,7 @@
 
 (defn run-command!
   [command]
-  (let [[command args] (util/split-first " " command)
+  (let [[command args] (gp-util/split-first " " command)
         command (and command (string/lower-case command))]
     (when (and (not (string/blank? command)) (not (string/blank? args)))
       (let [args (string/trim args)]

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

@@ -14,6 +14,7 @@
             [clojure.string :as string]
             [rum.core :as rum]
             [frontend.mobile.util :as mobile]
+            [logseq.graph-parser.util :as gp-util]
             [electron.ipc :as ipc]))
 
 (defn- get-css-var-value
@@ -111,7 +112,7 @@
   (let [id (and
             (> (count fragment) 36)
             (subs fragment (- (count fragment) 36)))]
-    (if (and id (util/uuid-string? id))
+    (if (and id (gp-util/uuid-string? id))
       (let [elements (array-seq (js/document.getElementsByClassName id))]
         (when (first elements)
           (util/scroll-to-element (gobj/get (first elements) "id")))

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

@@ -20,6 +20,8 @@
             [lambdaisland.glogi :as log]
             [promesa.core :as p]
             [frontend.mobile.util :as mobile-util]
+            [logseq.graph-parser.util :as gp-util]
+            [logseq.graph-parser.config :as gp-config]
             [clojure.core.async :as async]))
 
 (defn remove-ignore-files
@@ -48,7 +50,7 @@
    (cond
      mobile-native?
      (map (fn [{:keys [uri content size mtime]}]
-            {:file/path             (util/path-normalize uri)
+            {:file/path             (gp-util/path-normalize uri)
              :file/last-modified-at mtime
              :file/size             size
              :file/content content})
@@ -57,7 +59,7 @@
      electron?
      (map (fn [{:keys [path stat content]}]
             (let [{:keys [mtime size]} stat]
-              {:file/path             (util/path-normalize path)
+              {:file/path             (gp-util/path-normalize path)
                :file/last-modified-at mtime
                :file/size             size
                :file/content content}))
@@ -71,7 +73,7 @@
                     path (-> (get-attr "webkitRelativePath")
                              (string/replace-first (str dir-name "/") ""))]
                 {:file/name             (get-attr "name")
-                 :file/path             (util/path-normalize path)
+                 :file/path             (gp-util/path-normalize path)
                  :file/last-modified-at (get-attr "lastModified")
                  :file/size             (get-attr "size")
                  :file/type             (get-attr "type")
@@ -155,7 +157,7 @@
                                                                         (string/replace-first path (str dir-name "/") ""))
                                                              (let [last-part (last (string/split path "/"))]
                                                                (contains? #{config/app-name
-                                                                            config/default-draw-directory
+                                                                            gp-config/default-draw-directory
                                                                             (config/get-journals-directory)
                                                                             (config/get-pages-directory)}
                                                                           last-part)))))

+ 2 - 2
src/main/frontend/modules/instrumentation/posthog.cljs

@@ -1,5 +1,5 @@
 (ns frontend.modules.instrumentation.posthog
-  (:require [frontend.config :as cfg]
+  (:require [frontend.config :as config]
             [frontend.util :as util]
             [frontend.mobile.util :as mobile]
             [frontend.version :refer [version]]
@@ -22,7 +22,7 @@
 
                    :else
                    "web"))
-     :app_env (if cfg/dev? "development" "production")
+     :app_env (if config/dev? "development" "production")
      :app_ver version
      :schema_ver 0
      ;; hack, did not find ways to hack data on-the-fly with posthog-js

+ 5 - 5
src/main/frontend/modules/instrumentation/sentry.cljs

@@ -1,7 +1,7 @@
 (ns frontend.modules.instrumentation.sentry
   (:require [frontend.version :refer [version]]
             [frontend.util :as util]
-            [frontend.config :as cfg]
+            [frontend.config :as config]
             ["@sentry/react" :as Sentry]
             ["@sentry/tracing" :refer [BrowserTracing]]
             ["posthog-js" :as posthog]
@@ -14,16 +14,16 @@
                                          (mobile-util/native-ios?) "-ios"
                                          :else "")
                          version)
-   :environment (if cfg/dev? "development" "production")
+   :environment (if config/dev? "development" "production")
    :initialScope {:tags
                   {:platform (cond
                                (util/electron?) "electron"
                                (mobile-util/native-platform?) "mobile"
                                :else "web")
-                   :publishing cfg/publishing?}}
+                   :publishing config/publishing?}}
    :integrations [(new posthog/SentryIntegration posthog "logseq" 5311485)
                   (new BrowserTracing)]
-   :debug cfg/dev?
+   :debug config/dev?
    :tracesSampleRate 1.0
    :beforeSend (fn [^js event]
                  (try
@@ -43,6 +43,6 @@
                  event)})
 
 (defn init []
-  (when-not cfg/dev?
+  (when-not config/dev?
     (let [config (clj->js config)]
      (Sentry/init config))))

+ 6 - 6
src/main/frontend/modules/layout/core.cljs

@@ -1,6 +1,6 @@
 (ns frontend.modules.layout.core
   (:require [cljs-bean.core :as bean]
-            [frontend.util :as frontend-utils]))
+            [frontend.util :as util]))
 
 (defonce *movable-containers (atom {}))
 
@@ -24,7 +24,7 @@
                    (remove nil?))
           zdx (bean/->js zdx)
           zdx (and zdx (js/Math.max.apply nil zdx))
-          zdx' (frontend-utils/safe-parse-int (.. container -style -zIndex))]
+          zdx' (util/safe-parse-int (.. container -style -zIndex))]
 
       (when (or (nil? zdx') (not= zdx zdx'))
         (set! (.. container -style -zIndex) (inc zdx))))))
@@ -46,8 +46,8 @@
                         (let [^js dset (.-dataset el)
                               dx (.-dx e)
                               dy (.-dy e)
-                              dx' (frontend-utils/safe-parse-float (.-dx dset))
-                              dy' (frontend-utils/safe-parse-float (.-dy dset))
+                              dx' (util/safe-parse-float (.-dx dset))
+                              dy' (util/safe-parse-float (.-dy dset))
                               x (+ dx (if dx' dx' 0))
                               y (+ dy (if dy' dy' 0))]
 
@@ -92,8 +92,8 @@
                              dx (.. e -deltaRect -left)
                              dy (.. e -deltaRect -top)
 
-                             dx' (frontend-utils/safe-parse-float (.-dx dset))
-                             dy' (frontend-utils/safe-parse-float (.-dy dset))
+                             dx' (util/safe-parse-float (.-dx dset))
+                             dy' (util/safe-parse-float (.-dy dset))
 
                              x (+ dx (if dx' dx' 0))
                              y (+ dy (if dy' dy' 0))]

+ 2 - 1
src/main/frontend/modules/outliner/core.cljs

@@ -12,6 +12,7 @@
             [frontend.modules.outliner.utils :as outliner-u]
             [frontend.state :as state]
             [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]
             [cljs.spec.alpha :as s]))
 
 (s/def ::block-map (s/keys :req [:db/id :block/uuid]
@@ -135,7 +136,7 @@
     (let [m (-> (:data this)
                 (dissoc :block/children :block/meta :block/top? :block/bottom?
                         :block/title :block/body :block/level)
-                (util/remove-nils))
+                (gp-util/remove-nils))
           m (if (state/enable-block-timestamps?) (block-with-timestamps m) m)
           other-tx (:db/other-tx m)
           id (:db/id (:data this))

+ 2 - 2
src/main/frontend/modules/outliner/datascript.cljc

@@ -7,8 +7,8 @@
                      [frontend.modules.editor.undo-redo :as undo-redo]
                      [frontend.state :as state]
                      [frontend.config :as config]
+                     [logseq.graph-parser.util :as gp-util]
                      [lambdaisland.glogi :as log]
-                     [frontend.util :as util]
                      [medley.core :as medley])))
 
 #?(:cljs
@@ -30,7 +30,7 @@
 #?(:cljs
    (defn- remove-nil-from-transaction
      [txs]
-     (some->> (util/remove-nils txs)
+     (some->> (gp-util/remove-nils txs)
               (map (fn [x]
                      (if (map? x)
                        (medley/map-vals (fn [v] (if (vector? v)

+ 2 - 2
src/main/frontend/modules/outliner/tree.cljs

@@ -1,6 +1,6 @@
 (ns frontend.modules.outliner.tree
   (:require [frontend.db :as db]
-            [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]
             [clojure.string :as string]
             [frontend.state :as state]))
 
@@ -45,7 +45,7 @@
 (defn- get-root-and-page
   [repo root-id]
   (if (string? root-id)
-    (if (util/uuid-string? root-id)
+    (if (gp-util/uuid-string? root-id)
       [false (db/entity repo [:block/uuid (uuid root-id)])]
       [true (db/entity repo [:block/name (string/lower-case root-id)])])
     [false root-id]))

+ 2 - 2
src/main/frontend/modules/shortcut/core.cljs

@@ -66,7 +66,7 @@
          (doseq [k (dh/shortcut-binding id)]
            (try
              (log/debug :shortcut/register-shortcut {:id id :binding k})
-             (.registerShortcut handler (util/keyname id) k)
+             (.registerShortcut handler (util/keyname id) (dh/normalize-user-keyname k))
              (catch js/Object e
                (log/error :shortcut/register-shortcut {:id      id
                                                        :binding k
@@ -81,7 +81,7 @@
   (when-let [handler (get-handler-by-id handler-id)]
     (when-let [ks (dh/shortcut-binding shortcut-id)]
       (doseq [k ks]
-        (.unregisterShortcut ^js handler k)))
+        (.unregisterShortcut ^js handler (dh/normalize-user-keyname k))))
     (shortcut-config/remove-shortcut! handler-id shortcut-id)))
 
 (defn uninstall-shortcut!

+ 27 - 12
src/main/frontend/modules/shortcut/data_helper.cljs

@@ -2,10 +2,10 @@
   (:require [borkdude.rewrite-edn :as rewrite]
             [clojure.string :as str]
             [clojure.set :refer [rename-keys]]
-            [frontend.config :as cfg]
+            [frontend.config :as config]
             [frontend.db :as db]
             [frontend.handler.file :as file]
-            [frontend.modules.shortcut.config :as config]
+            [frontend.modules.shortcut.config :as shortcut-config]
             [frontend.state :as state]
             [frontend.util :as util]
             [lambdaisland.glogi :as log]
@@ -14,7 +14,7 @@
 
 (defn get-bindings
   []
-  (->> (vals @config/config)
+  (->> (vals @shortcut-config/config)
        (into {})
        (map (fn [[k {:keys [binding]}]]
               {k binding}))
@@ -44,21 +44,32 @@
          shortcut)
        (mapv mod-key)))))
 
+(defn normalize-user-keyname
+  [k]
+  (some-> k
+          (util/safe-lower-case)
+          (str/replace #";+" "semicolon")
+          (str/replace #"=+" "equals")
+          (str/replace #"~+" "dash")
+          (str/replace "[" "open-square-bracket")
+          (str/replace "]" "close-square-bracket")
+          (str/replace "'" "single-quote")))
+
 ;; returns a vector to preserve order
 (defn binding-by-category [name]
-  (let [dict (->> (vals @config/config)
+  (let [dict (->> (vals @shortcut-config/config)
                   (apply merge)
                   (map (fn [[k _]]
                          {k {:binding (shortcut-binding k)}}))
                   (into {}))]
-    (->> (config/category name)
+    (->> (shortcut-config/category name)
          (mapv (fn [k] [k (k dict)])))))
 
 (defn shortcut-map
   ([handler-id]
    (shortcut-map handler-id nil))
   ([handler-id state]
-   (let [raw       (get @config/config handler-id)
+   (let [raw       (get @shortcut-config/config handler-id)
          handler-m (->> raw
                         (map (fn [[k {:keys [fn]}]]
                                {k fn}))
@@ -125,7 +136,7 @@
 
 (defn remove-shortcut [k]
   (let [repo (state/get-current-repo)
-        path (cfg/get-config-path)]
+        path (config/get-config-path)]
     (when-let [content (db/get-file path)]
       (let [result (common-handler/parse-config content)
             new-result (rewrite/update
@@ -140,7 +151,7 @@
   "Given shortcut key, return handler group
   eg: :editor/new-line -> :shortcut.handler/block-editing-only"
   [k]
-  (->> @config/config
+  (->> @shortcut-config/config
        (filter (fn [[_ v]] (contains? v k)))
        (map key)
        (first)))
@@ -150,9 +161,13 @@
     false
     (let [handler-id    (get-group k)
           shortcut-m    (shortcut-map handler-id)
+          parse-shortcut #(try
+                           (KeyboardShortcutHandler/parseStringShortcut %)
+                           (catch js/Error e
+                             (js/console.error "[shortcut/parse-error]" (str % " - " (.-message e)))))
           bindings      (->> (shortcut-binding k)
                              (map mod-key)
-                             (map KeyboardShortcutHandler/parseStringShortcut)
+                             (map parse-shortcut)
                              (map js->clj))
           rest-bindings (->> (map key shortcut-m)
                              (remove #{k})
@@ -160,14 +175,14 @@
                              (filter vector?)
                              (mapcat identity)
                              (map mod-key)
-                             (map KeyboardShortcutHandler/parseStringShortcut)
+                             (map parse-shortcut)
                              (map js->clj))]
 
       (some? (some (fn [b] (some #{b} rest-bindings)) bindings)))))
 
 (defn shortcut-data-by-id [id]
   (let [binding (shortcut-binding id)
-        data    (->> (vals @config/config)
+        data    (->> (vals @shortcut-config/config)
                      (into  {})
                      id)]
     (assoc
@@ -176,7 +191,7 @@
       (binding-for-display id binding))))
 
 (defn shortcuts->commands [handler-id]
-  (let [m (get @config/config handler-id)]
+  (let [m (get @shortcut-config/config handler-id)]
     (->> m
          (map (fn [[id _]] (-> (shortcut-data-by-id id)
                                (assoc :id id :handler-id handler-id)

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

@@ -1,6 +1,6 @@
 (ns frontend.security
   (:require [clojure.walk :as walk]
-            [frontend.util :as util]))
+            [logseq.graph-parser.util :as gp-util]))
 
 ;; To prevent from cross-site scripting vulnerability, we should add security checks for both hiccup and raw html.
 ;; Hiccup: [:a {:href "javascript:alert('hei')"} "click me"]
@@ -12,7 +12,7 @@
    (= :a (first f))
    (:href (second f))
    (:href (second f))
-   (util/safe-re-find #"(?i)javascript" (:href (second f)))))
+   (gp-util/safe-re-find #"(?i)javascript" (:href (second f)))))
 
 (defn remove-javascript-links-in-href
   [hiccup]

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

@@ -13,6 +13,7 @@
             [goog.object :as gobj]
             [promesa.core :as p]
             [rum.core :as rum]
+            [logseq.graph-parser.util :as gp-util]
             [frontend.mobile.util :as mobile-util]))
 
 (defonce ^:large-vars/data-var state
@@ -448,7 +449,7 @@
     (or
       (when-let [workflow (:preferred-workflow (get-config))]
         (let [workflow (name workflow)]
-          (if (util/safe-re-find #"now|NOW" workflow)
+          (if (gp-util/safe-re-find #"now|NOW" workflow)
             :now
             :todo)))
       (get-in @state [:me :preferred_workflow] :now))))

+ 6 - 5
src/main/frontend/text.cljs

@@ -4,6 +4,7 @@
             [clojure.string :as string]
             [frontend.format.mldoc :as mldoc]
             [clojure.set :as set]
+            [logseq.graph-parser.util :as gp-util]
             [frontend.state :as state]))
 
 (def page-ref-re-0 #"\[\[(.*)\]\]")
@@ -136,8 +137,8 @@
 
      (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 #"[\,|,|#|\"]+" s)))
+            (or (gp-util/safe-re-find page-ref-re s)
+                (gp-util/safe-re-find #"[\,|,|#|\"]+" s)))
      (let [result (->> (sep-by-quotes s)
                        (mapcat
                         (fn [s]
@@ -192,7 +193,7 @@
     (let [pattern (util/format
                    "^[%s]+\\s?"
                    (config/get-block-pattern format))]
-      (util/safe-re-find (re-pattern pattern) text))
+      (gp-util/safe-re-find (re-pattern pattern) text))
     ""))
 
 (defn- remove-level-space-aux!
@@ -231,7 +232,7 @@
 
 (defn media-link?
   [media-formats s]
-  (some (fn [fmt] (util/safe-re-find (re-pattern (str "(?i)\\." fmt "(?:\\?([^#]*))?(?:#(.*))?$")) s)) media-formats))
+  (some (fn [fmt] (gp-util/safe-re-find (re-pattern (str "(?i)\\." fmt "(?:\\?([^#]*))?(?:#(.*))?$")) s)) media-formats))
 
 (defn namespace-page?
   [p]
@@ -356,7 +357,7 @@
        (= v "false")
        false
 
-       (and (not= k "alias") (util/safe-re-find #"^\d+$" v))
+       (and (not= k "alias") (gp-util/safe-re-find #"^\d+$" v))
        (util/safe-parse-int v)
 
        (util/wrapped-by-quotes? v) ; wrapped in ""

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

@@ -398,7 +398,6 @@
                     (menu-link
                      {:id            (str "ac-" idx)
                       :class         (when chosen? "chosen")
-                      :on-mouse-enter #(reset! current-idx idx)
                       :on-mouse-down (fn [e]
                                        (util/stop e)
                                        (if (and (gobj/get e "shiftKey") on-shift-chosen)

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

@@ -7,13 +7,12 @@
             ["@capacitor/status-bar" :refer [^js StatusBar Style]]
             ["grapheme-splitter" :as GraphemeSplitter]
             ["remove-accents" :as removeAccents]
-            [camel-snake-kebab.core :as csk]
-            [camel-snake-kebab.extras :as cske]
             [cljs-bean.core :as bean]
             [cljs-time.coerce :as tc]
             [cljs-time.core :as t]
             [dommy.core :as d]
             [frontend.mobile.util :refer [native-platform?]]
+            [logseq.graph-parser.util :as gp-util]
             [goog.dom :as gdom]
             [goog.object :as gobj]
             [goog.string :as gstring]
@@ -52,20 +51,11 @@
        (and (string/includes? ua "webkit")
             (not (string/includes? ua "chrome"))))))
 
-(defn safe-re-find
-  [pattern s]
-  #?(:cljs
-     (when-not (string? s)
-       ;; TODO: sentry
-       (js/console.trace)))
-  (when (string? s)
-    (re-find pattern s)))
-
 #?(:cljs
    (defn mobile?
      []
      (when-not node-test?
-       (safe-re-find #"Mobi" js/navigator.userAgent))))
+       (gp-util/safe-re-find #"Mobi" js/navigator.userAgent))))
 
 #?(:cljs
    (defn electron?
@@ -150,28 +140,6 @@
 ;;   [fmt & args]
 ;;   (apply gstring/format fmt args))
 
-#?(:cljs
-   (defn json->clj
-     ([json-string]
-      (json->clj json-string false))
-     ([json-string kebab?]
-      (let [m (-> json-string
-                  (js/JSON.parse)
-                  (js->clj :keywordize-keys true))]
-        (if kebab?
-          (cske/transform-keys csk/->kebab-case-keyword m)
-          m)))))
-
-(defn remove-nils
-  "remove pairs of key-value that has nil value from a (possibly nested) map."
-  [nm]
-  (walk/postwalk
-   (fn [el]
-     (if (map? el)
-       (into {} (remove (comp nil? second)) el)
-       el))
-   nm))
-
 (defn remove-nils-non-nested
   [nm]
   (into {} (remove (comp nil? second)) nm))
@@ -381,7 +349,7 @@
 #?(:cljs
    (defn scroll-to-element
      [elem-id]
-     (when-not (safe-re-find #"^/\d+$" elem-id)
+     (when-not (gp-util/safe-re-find #"^/\d+$" elem-id)
        (when elem-id
          (when-let [elem (gdom/getElement elem-id)]
            (.scroll (app-scroll-container-node)
@@ -525,16 +493,6 @@
   (if (string? s)
     (string/lower-case s) s))
 
-(defn split-first [pattern s]
-  (when-let [first-index (string/index-of s pattern)]
-    [(subs s 0 first-index)
-     (subs s (+ first-index (count pattern)) (count s))]))
-
-(defn split-last [pattern s]
-  (when-let [last-index (string/last-index-of s pattern)]
-    [(subs s 0 last-index)
-     (subs s (+ last-index (count pattern)) (count s))]))
-
 (defn trim-safe
   [s]
   (when s
@@ -698,14 +656,6 @@
     []
     (subvec xs start end)))
 
-(defn safe-subs
-  ([s start]
-   (let [c (count s)]
-     (safe-subs s start c)))
-  ([s start end]
-   (let [c (count s)]
-     (subs s (min c start) (min c end)))))
-
 #?(:cljs
    (defn get-nodes-between-two-nodes
      [id1 id2 class]
@@ -770,12 +720,6 @@
      ([s html?]
       (utils/writeClipboard s html?))))
 
-(def uuid-pattern "[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}")
-(defonce exactly-uuid-pattern (re-pattern (str "(?i)^" uuid-pattern "$")))
-(defn uuid-string?
-  [s]
-  (safe-re-find exactly-uuid-pattern s))
-
 (defn drop-nth [n coll]
   (keep-indexed #(when (not= %1 n) %2) coll))
 
@@ -882,11 +826,6 @@
   []
   (str (rand-str 6) (rand-str 3)))
 
-(defn tag-valid?
-  [tag-name]
-  (when (string? tag-name)
-    (not (safe-re-find #"[# \t\r\n]+" tag-name))))
-
 (defn pp-str [x]
   #_:clj-kondo/ignore
   (with-out-str (clojure.pprint/pprint x)))
@@ -905,8 +844,8 @@
      []
      (let [user-agent js/navigator.userAgent
            vendor js/navigator.vendor]
-       (and (safe-re-find #"Chrome" user-agent)
-            (safe-re-find #"Google Inc" vendor)))))
+       (and (gp-util/safe-re-find #"Chrome" user-agent)
+            (gp-util/safe-re-find #"Google Inc" vendor)))))
 
 #?(:cljs
    (defn indexeddb-check?
@@ -947,24 +886,26 @@
      [block-id]
      (when block-id
        (let [block-id (str block-id)]
-         (when (uuid-string? block-id)
+         (when (gp-util/uuid-string? block-id)
            (first (array-seq (js/document.getElementsByClassName block-id))))))))
 
 (def windows-reserved-chars #"[:\\*\\?\"<>|]+")
 
-(defn include-windows-reserved-chars?
-  [s]
-  (safe-re-find windows-reserved-chars s))
+#?(:cljs
+   (do
+     (defn include-windows-reserved-chars?
+      [s]
+       (gp-util/safe-re-find windows-reserved-chars s))
 
-(defn create-title-property?
-  [s]
-  (and (string? s)
-       (or (include-windows-reserved-chars? s)
-           (string/includes? s "_")
-           (string/includes? s "/")
-           (string/includes? s ".")
-           (string/includes? s "%")
-           (string/includes? s "#"))))
+     (defn create-title-property?
+       [s]
+       (and (string? s)
+            (or (include-windows-reserved-chars? s)
+                (string/includes? s "_")
+                (string/includes? s "/")
+                (string/includes? s ".")
+                (string/includes? s "%")
+                (string/includes? s "#"))))))
 
 (defn remove-boundary-slashes
   [s]
@@ -979,10 +920,6 @@
 (defn normalize
   [s]
   (.normalize s "NFC"))
-(defn path-normalize
-  "Normalize file path (for reading paths from FS, not required by writting)"
-  [s]
-  (.normalize s "NFC"))
 
 #?(:cljs
    (defn search-normalize

+ 3 - 2
src/main/frontend/util/drawer.cljs

@@ -1,6 +1,7 @@
 (ns frontend.util.drawer
   (:require [clojure.string :as string]
             [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]
             [frontend.util.property :as property]
             [frontend.format.mldoc :as mldoc]))
 
@@ -86,8 +87,8 @@
 
 (defn contains-logbook?
   [content]
-  (and (util/safe-re-find (re-pattern (str "(?i)" logbook-start)) content)
-       (util/safe-re-find (re-pattern (str "(?i)" drawer-end)) content)))
+  (and (gp-util/safe-re-find (re-pattern (str "(?i)" logbook-start)) content)
+       (gp-util/safe-re-find (re-pattern (str "(?i)" drawer-end)) content)))
 
 ;; TODO: DRY
 (defn remove-logbook

+ 4 - 3
src/main/frontend/util/marker.cljs

@@ -1,6 +1,7 @@
 (ns frontend.util.marker
   (:require [clojure.string :as string]
-            [frontend.util :as util]))
+            [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]))
 
 (defn marker-pattern [format]
   (re-pattern
@@ -20,7 +21,7 @@
         (if-let [matches (seq (util/re-pos new-line-re-pattern content))]
           (let [[start-pos content] (last matches)]
             (+ start-pos (count content)))
-          (count (util/safe-re-find re-pattern content)))
+          (count (gp-util/safe-re-find re-pattern content)))
         new-content
         (str (subs content 0 pos)
              (string/replace-first (subs content pos)
@@ -59,6 +60,6 @@
   (let [content    (string/triml content)
         new-marker (or new-marker
                        (cycle-marker-state (or marker
-                                               (last (util/safe-re-find (marker-pattern format) content))) ; Returns the last matching group (last vec)
+                                               (last (gp-util/safe-re-find (marker-pattern format) content))) ; Returns the last matching group (last vec)
                                            preferred-workflow))]
     [(add-or-update-marker content format new-marker) new-marker]))

+ 3 - 2
src/main/frontend/util/priority.cljs

@@ -1,12 +1,13 @@
 (ns frontend.util.priority
   (:require [clojure.string :as string]
             [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]
             [frontend.util.marker :as marker]))
 
 (defn cycle-priority-state
   [content]
   (let [priority-reg #"\[#([ABC]{1})\]\s{1}"
-        priority (last (util/safe-re-find priority-reg content))
+        priority (last (gp-util/safe-re-find priority-reg content))
         next-priority (case priority
                         "A" "B"
 
@@ -28,7 +29,7 @@
         (if-let [matches (seq (util/re-pos new-line-re-pattern content))]
           (let [[start-pos content] (last matches)]
             (+ start-pos (count content)))
-          (count (util/safe-re-find re-pattern content)))
+          (count (gp-util/safe-re-find re-pattern content)))
         skip-marker-pos
         (if-let [matches (seq (util/re-pos marker/bare-marker-pattern (subs content skip-hash-pos)))]
           (let [[start-pos content] (last matches)]

+ 10 - 9
src/main/frontend/util/property.cljs

@@ -4,6 +4,7 @@
             [clojure.set :as set]
             [frontend.config :as config]
             [medley.core :as medley]
+            [logseq.graph-parser.util :as gp-util]
             [frontend.format.mldoc :as mldoc]
             [frontend.text :as text]
             [frontend.util.cursor :as cursor]))
@@ -38,7 +39,7 @@
   [content]
   (when content
     (and (string/includes? content properties-start)
-         (util/safe-re-find properties-end-pattern content))))
+         (gp-util/safe-re-find properties-end-pattern content))))
 
 (defn remove-empty-properties
   [content]
@@ -52,28 +53,28 @@
   [line]
   (boolean
    (and (string? line)
-        (util/safe-re-find #"^\s?[^ ]+:: " line))))
+        (gp-util/safe-re-find #"^\s?[^ ]+:: " line))))
 
 (defn front-matter-property?
   [line]
   (boolean
    (and (string? line)
-        (util/safe-re-find #"^\s*[^ ]+: " line))))
+        (gp-util/safe-re-find #"^\s*[^ ]+: " line))))
 
 (defn get-property-key
   [line format]
   (and (string? line)
        (when-let [key (last
                        (if (= format :org)
-                         (util/safe-re-find #"^\s*:([^: ]+): " line)
-                         (util/safe-re-find #"^\s*([^ ]+):: " line)))]
+                         (gp-util/safe-re-find #"^\s*:([^: ]+): " line)
+                         (gp-util/safe-re-find #"^\s*([^ ]+):: " line)))]
          (keyword key))))
 
 (defn org-property?
   [line]
   (boolean
    (and (string? line)
-        (util/safe-re-find #"^\s*:[^: ]+: " line)
+        (gp-util/safe-re-find #"^\s*:[^: ]+: " line)
         (when-let [key (get-property-key line :org)]
           (not (contains? #{:PROPERTIES :END} key))))))
 
@@ -265,7 +266,7 @@
                           middle (doall
                                   (->> (subvec lines (inc start-idx) end-idx)
                                        (mapv (fn [text]
-                                               (let [[k v] (util/split-first ":" (subs text 1))]
+                                               (let [[k v] (gp-util/split-first ":" (subs text 1))]
                                                  (if (and k v)
                                                    (let [key-exists? (= k key)
                                                          _ (when key-exists? (reset! exists? true))
@@ -288,7 +289,7 @@
                                                     (if (property-f (first lines))
                                                       (let [lines (doall
                                                                    (mapv (fn [text]
-                                                                           (let [[k v] (util/split-first sym text)]
+                                                                           (let [[k v] (gp-util/split-first sym text)]
                                                                              (if (and k v)
                                                                                (let [key-exists? (= k key)
                                                                                      _ (when key-exists? (reset! exists? true))
@@ -375,7 +376,7 @@
         (let [before (subvec lines 0 start-idx)
               middle (->> (subvec lines (inc start-idx) end-idx)
                           (map (fn [text]
-                                 (let [[k v] (util/split-first ":" (subs text 1))]
+                                 (let [[k v] (gp-util/split-first ":" (subs text 1))]
                                    (if (and k v)
                                      (let [k (string/replace k "_" "-")
                                            compare-k (keyword (string/lower-case k))

+ 3 - 2
src/main/logseq/api.cljs

@@ -36,6 +36,7 @@
             [promesa.core :as p]
             [reitit.frontend.easy :as rfe]
             [sci.core :as sci]
+            [logseq.graph-parser.util :as gp-util]
             [frontend.version :as fv]
             [frontend.handler.shell :as shell]
             [frontend.modules.layout.core]))
@@ -636,7 +637,7 @@
 
 (defn ^:export prepend_block_in_page
   [uuid-or-page-name content ^js opts]
-  (let [page? (not (util/uuid-string? uuid-or-page-name))
+  (let [page? (not (gp-util/uuid-string? uuid-or-page-name))
         page-not-exist? (and page? (nil? (db-model/get-page uuid-or-page-name)))
         _ (and page-not-exist? (page-handler/create! uuid-or-page-name
                                  {:redirect? false
@@ -652,7 +653,7 @@
 
 (defn ^:export append_block_in_page
   [uuid-or-page-name content ^js opts]
-  (let [page? (not (util/uuid-string? uuid-or-page-name))
+  (let [page? (not (gp-util/uuid-string? uuid-or-page-name))
         page-not-exist? (and page? (nil? (db-model/get-page uuid-or-page-name)))
         _ (and page-not-exist? (page-handler/create! uuid-or-page-name
                                  {:redirect? false

+ 16 - 0
src/main/logseq/graph_parser/config.cljs

@@ -0,0 +1,16 @@
+(ns ^:nbb-compatible logseq.graph-parser.config
+  "Config that is shared between graph-parser and rest of app"
+  (:require [logseq.graph-parser.util :as gp-util]
+            [clojure.string :as string]))
+
+(defonce local-assets-dir "assets")
+
+(defn local-asset?
+  [s]
+  (gp-util/safe-re-find (re-pattern (str "^[./]*" local-assets-dir)) s))
+
+(defonce default-draw-directory "draws")
+
+(defn draw?
+  [path]
+  (string/starts-with? path default-draw-directory))

+ 64 - 0
src/main/logseq/graph_parser/util.cljs

@@ -0,0 +1,64 @@
+(ns ^:nbb-compatible logseq.graph-parser.util
+  "Util fns shared between graph-parser and rest of app. Util fns only rely on
+  clojure standard libraries."
+  (:require [clojure.walk :as walk]
+            [clojure.string :as string]))
+
+(def uuid-pattern "[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}")
+(defonce exactly-uuid-pattern (re-pattern (str "(?i)^" uuid-pattern "$")))
+
+(defn safe-re-find
+  [pattern s]
+  (when-not (string? s)
+    ;; TODO: sentry
+    (js/console.trace))
+  (when (string? s)
+    (re-find pattern s)))
+
+(defn uuid-string?
+  [s]
+  (safe-re-find exactly-uuid-pattern s))
+
+(defn path-normalize
+  "Normalize file path (for reading paths from FS, not required by writting)"
+  [s]
+  (.normalize s "NFC"))
+
+(defn remove-nils
+  "remove pairs of key-value that has nil value from a (possibly nested) map."
+  [nm]
+  (walk/postwalk
+   (fn [el]
+     (if (map? el)
+       (into {} (remove (comp nil? second)) el)
+       el))
+   nm))
+
+(defn split-first [pattern s]
+  (when-let [first-index (string/index-of s pattern)]
+    [(subs s 0 first-index)
+     (subs s (+ first-index (count pattern)) (count s))]))
+
+(defn split-last [pattern s]
+  (when-let [last-index (string/last-index-of s pattern)]
+    [(subs s 0 last-index)
+     (subs s (+ last-index (count pattern)) (count s))]))
+
+(defn tag-valid?
+  [tag-name]
+  (when (string? tag-name)
+    (not (safe-re-find #"[# \t\r\n]+" tag-name))))
+
+(defn safe-subs
+  ([s start]
+   (let [c (count s)]
+     (safe-subs s start c)))
+  ([s start end]
+   (let [c (count s)]
+     (subs s (min c start) (min c end)))))
+
+(defn json->clj
+  [json-string]
+  (-> json-string
+      (js/JSON.parse)
+      (js->clj :keywordize-keys true)))

+ 166 - 0
src/test/frontend/handler/repo_test.cljs

@@ -0,0 +1,166 @@
+(ns frontend.handler.repo-test
+  (:require [cljs.test :refer [deftest use-fixtures is testing]]
+            [clojure.string :as string]
+            ["fs" :as fs]
+            ["child_process" :as child-process]
+            [frontend.handler.repo :as repo-handler]
+            [frontend.test.helper :as test-helper]
+            [datascript.core :as d]
+            [frontend.db.conn :as conn]))
+
+(use-fixtures :each {:before test-helper/start-test-db!
+                     :after test-helper/destroy-test-db!})
+
+(defn- slurp
+  "Like clojure.core/slurp"
+  [file]
+  (str (fs/readFileSync file)))
+
+(defn- sh
+  "Run shell cmd synchronously and print to inherited streams by default. Aims
+    to be similar to babashka.tasks/shell"
+  [cmd opts]
+  (child-process/spawnSync (first cmd)
+                           (clj->js (rest cmd))
+                           (clj->js (merge {:stdio "inherit"} opts))))
+
+(defn- build-graph-files
+  [dir]
+  (let [files (->> (str (.-stdout (sh ["git" "ls-files"]
+                                      {:cwd dir :stdio nil})))
+                   string/split-lines
+                   (filter #(re-find #"^(pages|journals)" %))
+                   (map #(str dir "/" %)))]
+    (mapv #(hash-map :file/path % :file/content (slurp %)) files)))
+
+(defn- clone-docs-repo-if-not-exists
+  [dir]
+  (when-not (.existsSync fs dir)
+    (sh ["git" "clone" "--depth" "1" "-b" "v0.6.7" "-c" "advice.detachedHead=false"
+         "https://github.com/logseq/docs" dir] {})))
+
+(defn- get-top-block-properties
+  [db]
+  (->> (d/q '[:find (pull ?b [*])
+              :where
+              [?b :block/properties]
+              [(missing? $ ?b :block/name)]]
+            db)
+       (map first)
+       (map (fn [m] (zipmap (keys (:block/properties m)) (repeat 1))))
+       (apply merge-with +)
+       (filter #(>= (val %) 5))
+       (into {})))
+
+(defn- get-all-page-properties
+  [db]
+  (->> (d/q '[:find (pull ?b [*])
+              :where
+              [?b :block/properties]
+              [?b :block/name]]
+            db)
+       (map first)
+       (map (fn [m] (zipmap (keys (:block/properties m)) (repeat 1))))
+       (apply merge-with +)
+       (into {})))
+
+;; Integration test that test parsing a large graph like docs
+(deftest ^:integration parse-and-load-files-to-db
+  (let [graph-dir "src/test/docs"
+        _ (clone-docs-repo-if-not-exists graph-dir)
+        files (build-graph-files graph-dir)
+        _ (repo-handler/parse-files-and-load-to-db! test-helper/test-db files {:re-render? false})
+        db (conn/get-db test-helper/test-db)]
+
+    ;; Counts assertions help check for no major regressions. These counts should
+    ;; only increase over time as the docs graph rarely has deletions
+    (testing "Counts"
+      (is (= 206 (count files)) "Correct file count")
+      (is (= 40888 (count (d/datoms db :eavt))) "Correct datoms count")
+
+      (is (= 3597
+             (ffirst
+              (d/q '[:find (count ?b)
+                     :where [?b :block/path-refs ?bp] [?bp :block/name]] db)))
+          "Correct referenced blocks count")
+      (is (= 21
+             (ffirst
+              (d/q '[:find (count ?b)
+                     :where [?b :block/content ?content]
+                     [(clojure.string/includes? ?content "+BEGIN_QUERY")]]
+                   db)))
+          "Advanced query count"))
+
+    (testing "Query based stats"
+      (is (= (set (map :file/path files))
+             (->> (d/q '[:find (pull ?b [* {:block/file [:file/path]}])
+                         :where [?b :block/name] [?b :block/file]]
+                       db)
+                  (map (comp #(get-in % [:block/file :file/path]) first))
+                  set))
+          "Journal and pages files on disk should equal ones in db")
+
+      (is (= (count (filter #(re-find #"journals/" (:file/path %))
+                            files))
+             (->> (d/q '[:find (count ?b)
+                         :where
+                         [?b :block/journal? true]
+                         [?b :block/name]
+                         [?b :block/file]]
+                       db)
+                  ffirst))
+          "Journal page count on disk equals count in db")
+
+      (is (= {"CANCELED" 2 "DONE" 6 "LATER" 4 "NOW" 5}
+             (->> (d/q '[:find (pull ?b [*]) :where [?b :block/marker] ]
+                       db)
+                  (map first)
+                  (group-by :block/marker)
+                  (map (fn [[k v]] [k (count v)]))
+                  (into {})))
+          "Task marker counts")
+
+      (is (= {:markdown 3140 :org 460}
+             (->> (d/q '[:find (pull ?b [*]) :where [?b :block/format]] db)
+                  (map first)
+                  (group-by :block/format)
+                  (map (fn [[k v]] [k (count v)]))
+                  (into {})))
+          "Block format counts")
+
+      (is (= {:title 98 :id 98
+              :updated-at 47 :created-at 47
+              :collapsed 22
+              :card-last-score 6 :card-repeats 6 :card-next-schedule 6
+              :card-last-interval 6 :card-ease-factor 6 :card-last-reviewed 6
+              :alias 6}
+             (get-top-block-properties db))
+          "Counts for top block properties")
+
+      (is (= {:title 98
+              :alias 6
+              :tags 2 :permalink 2
+              :name 1 :type 1 :related 1 :sample 1 :click 1 :id 1 :example 1}
+             (get-all-page-properties db))
+          "Counts for all page properties")
+
+      (is (= {:block/scheduled 2
+              :block/priority 4
+              :block/deadline 1
+              :block/collapsed? 22
+              :block/heading-level 57
+              :block/repeated? 1}
+             (->> [:block/scheduled :block/priority :block/deadline :block/collapsed?
+                   :block/heading-level :block/repeated?]
+                  (map (fn [attr]
+                         [attr
+                          (ffirst (d/q [:find (list 'count '?b) :where ['?b attr]]
+                                       db))]))
+                  (into {})))
+          "Counts for blocks with common block attributes")
+
+      (is (= #{"term" "setting" "book" "templates" "Query" "Query/table" "page"}
+             (->> (d/q '[:find (pull ?n [*]) :where [?b :block/namespace ?n]] db)
+                  (map (comp :block/original-name first))
+                  set))
+          "Has correct namespaces"))))

+ 8 - 6
src/test/frontend/modules/outliner/core_test.cljs

@@ -390,7 +390,7 @@
               (recur (conj result next) next)))
           result)))))
 
-(deftest random-inserts
+(deftest ^:long random-inserts
   (testing "Random inserts"
     (transact-random-tree!)
     (let [c1 (get-blocks-count)
@@ -402,7 +402,7 @@
       (let [total (get-blocks-count)]
         (is (= total (+ c1 @*random-count)))))))
 
-(deftest random-deletes
+(deftest ^:long random-deletes
   (testing "Random deletes"
     (transact-random-tree!)
     (dotimes [_i 100]
@@ -412,7 +412,7 @@
           (outliner-tx/transact! {:graph test-db}
             (outliner-core/delete-blocks! blocks {})))))))
 
-(deftest random-moves
+(deftest ^:long random-moves
   (testing "Random moves"
     (transact-random-tree!)
     (let [c1 (get-blocks-count)
@@ -429,7 +429,8 @@
               (let [total (get-blocks-count)]
                 (is (= total (+ c1 @*random-count)))))))))))
 
-(deftest random-move-up-down
+;; TODO: Enable when not failing as intermittently
+#_(deftest ^:long random-move-up-down
   (testing "Random move up down"
     (transact-random-tree!)
     (let [c1 (get-blocks-count)
@@ -445,7 +446,8 @@
             (let [total (get-blocks-count)]
               (is (= total (+ c1 @*random-count))))))))))
 
-(deftest random-indent-outdent
+;; TODO: Enable when not failing as intermittently
+#_(deftest ^:long random-indent-outdent
   (testing "Random indent and outdent"
     (transact-random-tree!)
     (let [c1 (get-blocks-count)
@@ -461,7 +463,7 @@
             (let [total (get-blocks-count)]
               (is (= total (+ c1 @*random-count))))))))))
 
-(deftest random-mixed-ops
+(deftest ^:long random-mixed-ops
   (testing "Random mixed operations"
     (transact-random-tree!)
     (let [c1 (get-blocks-count)

+ 28 - 0
yarn.lock

@@ -716,6 +716,13 @@
   resolved "https://registry.yarnpkg.com/@kanru/rage-wasm/-/rage-wasm-0.2.1.tgz#dd8fdd3133992c42bf68c0086d8cad40a13bc329"
   integrity sha512-sYi4F2mL6Mpcz7zbS4myasw11xLBEbgZkDMRVg9jNxTKt6Ct/LT7/vCHDmEzAFcPcPqixD5De6Ql3bJijAX0/w==
 
+"@logseq/nbb-logseq@^0.3.10":
+  version "0.3.10"
+  resolved "https://registry.yarnpkg.com/@logseq/nbb-logseq/-/nbb-logseq-0.3.10.tgz#74534f9d263eb2105a41143a55425d0910d43eb8"
+  integrity sha512-y7/VJ99WCoNpQMqQOOBobKzNrRz4IcG85HggAkIaUtNwJ/Adb5fA28ZKP5ttEpqWLXTbAyqbBObD5q/xJLLD8w==
+  dependencies:
+    import-meta-resolve "^1.1.1"
+
 "@logseq/[email protected]":
   version "1.3.1-1"
   resolved "https://registry.yarnpkg.com/@logseq/react-tweet-embed/-/react-tweet-embed-1.3.1-1.tgz#119d22be8234de006fc35c3fa2a36f85634c5be6"
@@ -1833,6 +1840,13 @@ builtin-status-codes@^3.0.0:
   resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
   integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
 
+builtins@^4.0.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/builtins/-/builtins-4.1.0.tgz#1edd016dd91ce771a1ed6fc3b2b71fb918953250"
+  integrity sha512-1bPRZQtmKaO6h7qV1YHXNtr6nCK28k0Zo95KM4dXfILcZZwoHJBN1m3lfLv9LPkcOZlrSr+J1bzMaZFO98Yq0w==
+  dependencies:
+    semver "^7.0.0"
+
 bytes@^3.0.0:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
@@ -4111,6 +4125,13 @@ import-lazy@^4.0.0:
   resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153"
   integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==
 
+import-meta-resolve@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-1.1.1.tgz#244fd542fd1fae73550d4f8b3cde3bba1d7b2b18"
+  integrity sha512-JiTuIvVyPaUg11eTrNDx5bgQ/yMKMZffc7YSjvQeSMXy58DO2SQ8BtAf3xteZvmzvjYh14wnqNjL8XVeDy2o9A==
+  dependencies:
+    builtins "^4.0.0"
+
 imurmurhash@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
@@ -7285,6 +7306,13 @@ semver@^6.2.0, semver@^6.3.0:
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
   integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
 
+semver@^7.0.0:
+  version "7.3.7"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
+  integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
+  dependencies:
+    lru-cache "^6.0.0"
+
 semver@^7.3.2, semver@^7.3.4:
   version "7.3.5"
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"