Browse Source

Merge remote-tracking branch 'upstream/master' into whiteboards

Peng Xiao 3 years ago
parent
commit
6b5b1940d3
100 changed files with 1258 additions and 817 deletions
  1. 2 14
      .github/workflows/build.yml
  2. 93 0
      .github/workflows/db.yml
  3. 27 18
      .github/workflows/graph-parser.yml
  4. 2 2
      android/app/build.gradle
  5. 17 8
      bb.edn
  6. 1 1
      deps.edn
  7. 6 0
      deps/db/.carve/config.edn
  8. 6 0
      deps/db/.carve/ignore
  9. 52 0
      deps/db/README.md
  10. 35 0
      deps/db/bb.edn
  11. 7 0
      deps/db/deps.edn
  12. 8 0
      deps/db/package.json
  13. 3 3
      deps/db/src/logseq/db.cljs
  14. 1 1
      deps/db/src/logseq/db/default.cljs
  15. 4 3
      deps/db/src/logseq/db/rules.cljc
  16. 1 1
      deps/db/src/logseq/db/schema.cljs
  17. 43 0
      deps/db/yarn.lock
  18. 0 2
      deps/graph-parser/.carve/config.edn
  19. 23 0
      deps/graph-parser/bb.edn
  20. 5 4
      deps/graph-parser/deps.edn
  21. 1 1
      deps/graph-parser/package.json
  22. 37 45
      deps/graph-parser/src/logseq/graph_parser/block.cljs
  23. 2 2
      deps/graph-parser/src/logseq/graph_parser/cli.cljs
  24. 4 2
      deps/graph-parser/test/logseq/graph_parser/nbb_test_runner.cljs
  25. 43 0
      deps/graph-parser/test/logseq/graph_parser_test.cljs
  26. 4 4
      deps/graph-parser/yarn.lock
  27. 9 14
      docs/dev-practices.md
  28. 4 4
      ios/App/App.xcodeproj/project.pbxproj
  29. 1 2
      package.json
  30. 0 1
      public/index.html
  31. 8 0
      resources/css/common.css
  32. 1 1
      resources/package.json
  33. 0 43
      scripts/carve.clj
  34. 0 54
      scripts/large_vars.clj
  35. 0 52
      scripts/lint_rules.clj
  36. 4 6
      scripts/src/logseq/tasks/dev.clj
  37. 0 47
      scripts/src/logseq/tasks/nbb.clj
  38. 5 4
      src/electron/electron/core.cljs
  39. 63 63
      src/electron/electron/fs_watcher.cljs
  40. 27 15
      src/electron/electron/handler.cljs
  41. 2 2
      src/electron/electron/state.cljs
  42. 6 0
      src/electron/electron/utils.cljs
  43. 20 15
      src/main/frontend/components/block.cljs
  44. 9 1
      src/main/frontend/components/block.css
  45. 16 13
      src/main/frontend/components/export.cljs
  46. 15 17
      src/main/frontend/components/header.cljs
  47. 14 22
      src/main/frontend/components/header.css
  48. 1 1
      src/main/frontend/components/onboarding/index.css
  49. 1 1
      src/main/frontend/components/page.cljs
  50. 5 2
      src/main/frontend/components/page.css
  51. 0 1
      src/main/frontend/components/plugins.cljs
  52. 0 1
      src/main/frontend/components/plugins.css
  53. 1 1
      src/main/frontend/components/repo.cljs
  54. 11 12
      src/main/frontend/components/right_sidebar.cljs
  55. 20 5
      src/main/frontend/components/settings.cljs
  56. 9 8
      src/main/frontend/components/sidebar.cljs
  57. 8 14
      src/main/frontend/components/sidebar.css
  58. 7 4
      src/main/frontend/date.cljs
  59. 3 3
      src/main/frontend/db.cljs
  60. 2 2
      src/main/frontend/db/conn.cljs
  61. 10 15
      src/main/frontend/db/model.cljs
  62. 1 1
      src/main/frontend/db/query_custom.cljs
  63. 1 1
      src/main/frontend/db/query_dsl.cljs
  64. 6 1
      src/main/frontend/db/query_react.cljs
  65. 33 12
      src/main/frontend/dicts.cljc
  66. 36 20
      src/main/frontend/extensions/code.css
  67. 11 1
      src/main/frontend/extensions/excalidraw.cljs
  68. 7 6
      src/main/frontend/extensions/html_parser.cljs
  69. 15 7
      src/main/frontend/format/block.cljs
  70. 21 2
      src/main/frontend/fs.cljs
  71. 2 0
      src/main/frontend/fs/bfs.cljs
  72. 3 1
      src/main/frontend/fs/capacitor_fs.cljs
  73. 4 2
      src/main/frontend/fs/node.cljs
  74. 2 1
      src/main/frontend/fs/protocol.cljs
  75. 10 12
      src/main/frontend/fs/watcher_handler.cljs
  76. 1 1
      src/main/frontend/handler.cljs
  77. 2 1
      src/main/frontend/handler/block.cljs
  78. 34 31
      src/main/frontend/handler/editor.cljs
  79. 24 1
      src/main/frontend/handler/events.cljs
  80. 40 16
      src/main/frontend/handler/export.cljs
  81. 8 5
      src/main/frontend/handler/file.cljs
  82. 1 1
      src/main/frontend/handler/graph.cljs
  83. 15 14
      src/main/frontend/handler/metadata.cljs
  84. 4 0
      src/main/frontend/handler/notification.cljs
  85. 12 10
      src/main/frontend/handler/page.cljs
  86. 22 19
      src/main/frontend/handler/plugin.cljs
  87. 20 17
      src/main/frontend/handler/repo.cljs
  88. 5 2
      src/main/frontend/handler/web/nfs.cljs
  89. 10 0
      src/main/frontend/mobile/mobile_bar.cljs
  90. 6 4
      src/main/frontend/modules/file/core.cljs
  91. 1 1
      src/main/frontend/modules/instrumentation/sentry.cljs
  92. 66 46
      src/main/frontend/modules/outliner/core.cljs
  93. 2 1
      src/main/frontend/modules/outliner/pipeline.cljs
  94. 5 7
      src/main/frontend/modules/shortcut/config.cljs
  95. 62 9
      src/main/frontend/modules/shortcut/dicts.cljc
  96. 1 1
      src/main/frontend/publishing.cljs
  97. 12 2
      src/main/frontend/state.cljs
  98. 6 2
      src/main/frontend/ui.cljs
  99. 1 1
      src/main/frontend/version.cljs
  100. 37 1
      src/test/frontend/modules/outliner/core_test.cljs

+ 2 - 14
.github/workflows/build.yml

@@ -52,11 +52,6 @@ jobs:
         with:
         with:
           cli: ${{ env.CLOJURE_VERSION }}
           cli: ${{ env.CLOJURE_VERSION }}
 
 
-      - name: Setup Babashka
-        uses: turtlequeue/[email protected]
-        with:
-          babashka-version: ${{ env.BABASHKA_VERSION }}
-
       - name: Clojure cache
       - name: Clojure cache
         uses: actions/cache@v2
         uses: actions/cache@v2
         id: clojure-deps
         id: clojure-deps
@@ -79,10 +74,6 @@ jobs:
           yarn cljs:test
           yarn cljs:test
           node static/tests.js
           node static/tests.js
 
 
-      # In this job because it depends on an npm package
-      - name: Load nbb compatible namespaces
-        run: bb test:load-namespaces-with-nbb
-
   lint:
   lint:
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
 
 
@@ -110,17 +101,14 @@ jobs:
         run: clojure -M:clj-kondo --parallel --lint src
         run: clojure -M:clj-kondo --parallel --lint src
 
 
       - name: Carve lint for unused vars
       - name: Carve lint for unused vars
-        run: scripts/carve.clj
+        run: bb lint:carve
 
 
       - name: Lint for vars that are too large
       - name: Lint for vars that are too large
-        run: scripts/large_vars.clj
+        run: bb lint:large-vars
 
 
       - name: Lint invalid translation entries
       - name: Lint invalid translation entries
         run: bb lang:invalid-translations
         run: bb lang:invalid-translations
 
 
-      - name: Lint datalog rules
-        run: scripts/lint_rules.clj
-
   e2e-test:
   e2e-test:
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
 
 

+ 93 - 0
.github/workflows/db.yml

@@ -0,0 +1,93 @@
+name: logseq/db CI
+
+on:
+  # Path filters ensure jobs only kick off if a change is made to db
+  push:
+    branches: [master]
+    paths:
+      - 'deps/db/**'
+      - '.github/workflows/db.yml'
+      - '!deps/db/**.md'
+  pull_request:
+    branches: [master]
+    paths:
+      - 'deps/db/**'
+      - '.github/workflows/db.yml'
+      - '!deps/db/**.md'
+
+defaults:
+  run:
+    working-directory: deps/db
+
+env:
+  CLOJURE_VERSION: '1.10.1.727'
+  # This is the same as 1.8.
+  JAVA_VERSION: '8'
+  # This is the latest node version we can run.
+  NODE_VERSION: '16'
+  BABASHKA_VERSION: '0.8.156'
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v3
+
+      - name: Set up Node
+        uses: actions/setup-node@v3
+        with:
+          node-version: ${{ env.NODE_VERSION }}
+          cache: 'yarn'
+          cache-dependency-path: deps/db/yarn.lock
+
+      - name: Set up Java
+        uses: actions/setup-java@v3
+        with:
+          distribution: 'zulu'
+          java-version: ${{ env.JAVA_VERSION }}
+
+      - name: Set up Clojure
+        uses: DeLaGuardo/setup-clojure@master
+        with:
+          cli: ${{ env.CLOJURE_VERSION }}
+          bb: ${{ env.BABASHKA_VERSION }}
+
+      - name: Fetch yarn deps
+        run: yarn install --frozen-lockfile
+
+      # In this job because it depends on an npm package
+      - name: Load namespaces into nbb-logseq
+        run: bb test:load-all-namespaces-with-nbb .
+
+  lint:
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v3
+
+      - name: Set up Java
+        uses: actions/setup-java@v3
+        with:
+          distribution: 'zulu'
+          java-version: ${{ env.JAVA_VERSION }}
+
+      - name: Set up Clojure
+        uses: DeLaGuardo/setup-clojure@master
+        with:
+          cli: ${{ env.CLOJURE_VERSION }}
+          bb: ${{ env.BABASHKA_VERSION }}
+
+      - name: Run clj-kondo lint
+        run: clojure -M:clj-kondo --parallel --lint src
+
+      - name: Carve lint for unused vars
+        run: bb lint:carve
+
+      - name: Lint for vars that are too large
+        run: bb lint:large-vars
+
+      - name: Lint datalog rules
+        run: bb lint:rules

+ 27 - 18
.github/workflows/graph-parser.yml

@@ -1,26 +1,35 @@
-name: logseq graph-parser CI
+name: logseq/graph-parser CI
 
 
 on:
 on:
-  # Path filters ensure jobs only kick off if a change is made to graph-parser
+  # Path filters ensure jobs only kick off if a change is made to graph-parser or
+  # its local dependencies
   push:
   push:
     branches: [master]
     branches: [master]
     paths:
     paths:
       - 'deps/graph-parser/**'
       - 'deps/graph-parser/**'
+      # db is a local dep that could break functionality in this lib and should trigger this
+      - 'deps/db/**'
+      - '.github/workflows/graph-parser.yml'
       - '!deps/graph-parser/**.md'
       - '!deps/graph-parser/**.md'
   pull_request:
   pull_request:
     branches: [master]
     branches: [master]
     paths:
     paths:
       - 'deps/graph-parser/**'
       - 'deps/graph-parser/**'
+      - 'deps/db/**'
+      - '.github/workflows/graph-parser.yml'
       - '!deps/graph-parser/**.md'
       - '!deps/graph-parser/**.md'
 
 
+defaults:
+  run:
+    working-directory: deps/graph-parser
+
 env:
 env:
   CLOJURE_VERSION: '1.10.1.727'
   CLOJURE_VERSION: '1.10.1.727'
-  # setup-java@v2 dropped support for legacy Java version syntax.
   # This is the same as 1.8.
   # This is the same as 1.8.
   JAVA_VERSION: '8'
   JAVA_VERSION: '8'
   # This is the latest node version we can run.
   # This is the latest node version we can run.
   NODE_VERSION: '16'
   NODE_VERSION: '16'
-  BABASHKA_VERSION: '0.8.2'
+  BABASHKA_VERSION: '0.8.156'
 
 
 jobs:
 jobs:
   test:
   test:
@@ -28,17 +37,17 @@ jobs:
 
 
     steps:
     steps:
       - name: Checkout
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
 
       - name: Set up Node
       - name: Set up Node
-        uses: actions/setup-node@v2
+        uses: actions/setup-node@v3
         with:
         with:
           node-version: ${{ env.NODE_VERSION }}
           node-version: ${{ env.NODE_VERSION }}
           cache: 'yarn'
           cache: 'yarn'
           cache-dependency-path: deps/graph-parser/yarn.lock
           cache-dependency-path: deps/graph-parser/yarn.lock
 
 
       - name: Set up Java
       - name: Set up Java
-        uses: actions/setup-java@v2
+        uses: actions/setup-java@v3
         with:
         with:
           distribution: 'zulu'
           distribution: 'zulu'
           java-version: ${{ env.JAVA_VERSION }}
           java-version: ${{ env.JAVA_VERSION }}
@@ -54,7 +63,7 @@ jobs:
           babashka-version: ${{ env.BABASHKA_VERSION }}
           babashka-version: ${{ env.BABASHKA_VERSION }}
 
 
       - name: Clojure cache
       - name: Clojure cache
-        uses: actions/cache@v2
+        uses: actions/cache@v3
         id: clojure-deps
         id: clojure-deps
         with:
         with:
           path: |
           path: |
@@ -65,30 +74,30 @@ jobs:
 
 
       - name: Fetch Clojure deps
       - name: Fetch Clojure deps
         if: steps.clojure-deps.outputs.cache-hit != 'true'
         if: steps.clojure-deps.outputs.cache-hit != 'true'
-        run: cd deps/graph-parser && clojure -A:test -P
+        run: clojure -A:test -P
 
 
       - name: Fetch yarn deps
       - name: Fetch yarn deps
-        run: cd deps/graph-parser && yarn install --frozen-lockfile
+        run: yarn install --frozen-lockfile
 
 
       - name: Run ClojureScript tests
       - name: Run ClojureScript tests
-        run: cd deps/graph-parser && clojure -M:test
+        run: clojure -M:test
 
 
       - name: Run nbb-logseq tests
       - name: Run nbb-logseq tests
-        run: cd deps/graph-parser && yarn nbb-logseq -cp src:test -m logseq.graph-parser.nbb-test-runner/run-tests
+        run: yarn nbb-logseq -cp src:test:../db/src -m logseq.graph-parser.nbb-test-runner/run-tests
 
 
       # In this job because it depends on an npm package
       # In this job because it depends on an npm package
       - name: Load namespaces into nbb-logseq
       - name: Load namespaces into nbb-logseq
-        run: bb test:load-all-namespaces-with-nbb deps/graph-parser src
+        run: bb test:load-all-namespaces-with-nbb .
 
 
   lint:
   lint:
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
 
 
     steps:
     steps:
       - name: Checkout
       - name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
 
       - name: Set up Java
       - name: Set up Java
-        uses: actions/setup-java@v2
+        uses: actions/setup-java@v3
         with:
         with:
           distribution: 'zulu'
           distribution: 'zulu'
           java-version: ${{ env.JAVA_VERSION }}
           java-version: ${{ env.JAVA_VERSION }}
@@ -104,10 +113,10 @@ jobs:
           babashka-version: ${{ env.BABASHKA_VERSION }}
           babashka-version: ${{ env.BABASHKA_VERSION }}
 
 
       - name: Run clj-kondo lint
       - name: Run clj-kondo lint
-        run: cd deps/graph-parser && clojure -M:clj-kondo --parallel --lint src test
+        run: clojure -M:clj-kondo --parallel --lint src test
 
 
       - name: Carve lint for unused vars
       - name: Carve lint for unused vars
-        run: cd deps/graph-parser && ../../scripts/carve.clj
+        run: bb lint:carve
 
 
       - name: Lint for vars that are too large
       - name: Lint for vars that are too large
-        run: scripts/large_vars.clj deps/graph-parser/src '{:max-lines-count 75}'
+        run: bb lint:large-vars

+ 2 - 2
android/app/build.gradle

@@ -6,8 +6,8 @@ android {
         applicationId "com.logseq.app"
         applicationId "com.logseq.app"
         minSdkVersion rootProject.ext.minSdkVersion
         minSdkVersion rootProject.ext.minSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
-        versionCode 25
-        versionName "0.7.1"
+        versionCode 28
+        versionName "0.7.4"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         aaptOptions {
         aaptOptions {
              // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
              // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.

+ 17 - 8
bb.edn

@@ -6,7 +6,7 @@
   logseq/bb-tasks
   logseq/bb-tasks
   #_{:local/root "../bb-tasks"}
   #_{:local/root "../bb-tasks"}
   {:git/url "https://github.com/logseq/bb-tasks"
   {:git/url "https://github.com/logseq/bb-tasks"
-   :git/sha "4b3e623fb475cacb992425aa9dac770d6dd63e38"}
+   :git/sha "abb32ccd26405d56fd28a29d56f3cb902b8c4334"}
   logseq/graph-parser
   logseq/graph-parser
   {:local/root "deps/graph-parser"}}
   {:local/root "deps/graph-parser"}}
  :pods
  :pods
@@ -33,18 +33,18 @@
   dev:lint
   dev:lint
   logseq.tasks.dev/lint
   logseq.tasks.dev/lint
 
 
+  lint:large-vars
+  logseq.bb-tasks.lint.large-vars/-main
+
+  lint:carve
+  logseq.bb-tasks.lint.carve/-main
+
   nbb:watch
   nbb:watch
   logseq.bb-tasks.nbb.watch/watch
   logseq.bb-tasks.nbb.watch/watch
 
 
   nbb:portal-watch
   nbb:portal-watch
   logseq.bb-tasks.nbb.watch/portal-watch
   logseq.bb-tasks.nbb.watch/portal-watch
 
 
-  test:load-namespaces-with-nbb
-  logseq.tasks.nbb/load-compatible-namespaces
-
-  test:load-all-namespaces-with-nbb
-  logseq.tasks.nbb/load-all-namespaces
-
   lang:list
   lang:list
   logseq.tasks.lang/list-langs
   logseq.tasks.lang/list-langs
 
 
@@ -58,4 +58,13 @@
   logseq.tasks.lang/invalid-translations
   logseq.tasks.lang/invalid-translations
 
 
   file-sync:integration-tests
   file-sync:integration-tests
-  logseq.tasks.file-sync/integration-tests}}
+  logseq.tasks.file-sync/integration-tests}
+
+ :tasks/config
+ {:large-vars
+  ;; TODO: Get to a smaller max-lines-count
+  {:max-lines-count 100
+   ;; TODO: Address vars tagged with cleanup-todo. These
+   ;; are left mostly because they are not high priority
+   ;; or not well understood
+   :metadata-exceptions #{:large-vars/cleanup-todo}}}}

+ 1 - 1
deps.edn

@@ -47,5 +47,5 @@
                   :main-opts   ["-m" "shadow.cljs.devtools.cli"]}
                   :main-opts   ["-m" "shadow.cljs.devtools.cli"]}
 
 
            ;; Use :replace-deps for tools. See https://github.com/clj-kondo/clj-kondo/issues/1536#issuecomment-1013006889
            ;; Use :replace-deps for tools. See https://github.com/clj-kondo/clj-kondo/issues/1536#issuecomment-1013006889
-           :clj-kondo {:replace-deps {clj-kondo/clj-kondo {:mvn/version "2022.05.28"}}
+           :clj-kondo {:replace-deps {clj-kondo/clj-kondo {:mvn/version "2022.05.31"}}
                        :main-opts  ["-m" "clj-kondo.main"]}}}
                        :main-opts  ["-m" "clj-kondo.main"]}}}

+ 6 - 0
deps/db/.carve/config.edn

@@ -0,0 +1,6 @@
+{:paths ["src"]
+ :api-namespaces [
+                  ;; Some fns are used by frontend but not worth moving over yet
+                  logseq.db.schema
+                  ]
+ :report {:format :ignore}}

+ 6 - 0
deps/db/.carve/ignore

@@ -0,0 +1,6 @@
+;; API
+logseq.db/start-conn
+;; API
+logseq.db.rules/query-dsl-rules
+;; Internal API
+logseq.db.rules/rules

+ 52 - 0
deps/db/README.md

@@ -0,0 +1,52 @@
+## Description
+
+This library provides a minimal API for using a
+[datascript](https://github.com/tonsky/datascript) database from the Logseq app
+and the CLI. This library is compatible with ClojureScript and with
+[nbb-logseq](https://github.com/logseq/nbb-logseq) to respectively provide
+frontend and commandline functionality.
+
+## API
+
+This library is under the parent namespace `logseq.db`. This library provides
+two main namespaces, `logseq.db` and `logseq.db.rules`.
+
+## Usage
+
+See usage in `deps/graph-parser` and in the Logseq app.
+
+## Dev
+
+This follows the practices that [the Logseq frontend
+follows](/docs/dev-practices.md). Most of the same linters are used, with
+configurations that are specific to this library. See [this library's CI
+file](/.github/workflows/db.yml) for linting examples.
+
+### Setup
+
+To run linters, you'll want to install yarn dependencies once:
+```
+yarn install
+```
+
+This step is not needed if you're just running the application.
+
+## Linting
+
+### Datalog linting
+
+Our rules are linted through a script that also uses the datalog-parser. To run this linter:
+```
+bb lint:rules
+```
+
+
+### Managing dependencies
+
+The package.json dependencies are just for testing and should be updated if there is
+new behavior to test.
+
+The deps.edn dependecies are used by both ClojureScript and nbb-logseq. Their
+versions should be backwards compatible with each other with priority given to
+the frontend. _No new dependency_ should be introduced to this library without
+an understanding of the tradeoffs of adding this to nbb-logseq.

+ 35 - 0
deps/db/bb.edn

@@ -0,0 +1,35 @@
+{:paths ["src"]
+ :min-bb-version "0.8.156"
+ :deps
+ {logseq/bb-tasks
+  #_{:local/root "../../../bb-tasks"}
+  {:git/url "https://github.com/logseq/bb-tasks"
+   :git/sha "abb32ccd26405d56fd28a29d56f3cb902b8c4334"}}
+
+ :pods
+ {clj-kondo/clj-kondo {:version "2022.02.09"}}
+
+ :tasks
+ {test:load-all-namespaces-with-nbb
+  logseq.bb-tasks.nbb.test/load-all-namespaces
+
+  lint:large-vars
+  logseq.bb-tasks.lint.large-vars/-main
+
+  lint:carve
+  logseq.bb-tasks.lint.carve/-main
+
+  lint:rules
+  {:requires ([logseq.bb-tasks.lint.datalog :as datalog]
+              [logseq.db.rules :as rules])
+   :doc "Lint datalog rules for parsability and unbound variables"
+   :task (datalog/lint-rules
+          (into rules/rules
+                (-> rules/query-dsl-rules
+                    ;; TODO: Update linter to handle false positive on ?str-val
+                    (dissoc :property)
+                    vals)))}}
+
+ :tasks/config
+ {:large-vars
+  {:max-lines-count 30}}}

+ 7 - 0
deps/db/deps.edn

@@ -0,0 +1,7 @@
+{:deps
+ ;; External deps should be kept in sync with https://github.com/logseq/nbb-logseq/blob/main/bb.edn
+ {datascript/datascript {:mvn/version "1.3.8"}}
+ :aliases
+ {:clj-kondo
+  {:replace-deps {clj-kondo/clj-kondo {:mvn/version "2022.05.31"}}
+   :main-opts  ["-m" "clj-kondo.main"]}}}

+ 8 - 0
deps/db/package.json

@@ -0,0 +1,8 @@
+{
+  "name": "@logseq/db",
+  "version": "1.0.0",
+  "private": true,
+  "devDependencies": {
+    "@logseq/nbb-logseq": "^0.5.103"
+  }
+}

+ 3 - 3
deps/graph-parser/src/logseq/graph_parser/db.cljs → deps/db/src/logseq/db.cljs

@@ -1,6 +1,6 @@
-(ns logseq.graph-parser.db
-  (:require [logseq.graph-parser.db.default :as default-db]
-            [logseq.graph-parser.db.schema :as db-schema]
+(ns logseq.db
+  (:require [logseq.db.default :as default-db]
+            [logseq.db.schema :as db-schema]
             [datascript.core :as d]))
             [datascript.core :as d]))
 
 
 (defn start-conn
 (defn start-conn

+ 1 - 1
deps/graph-parser/src/logseq/graph_parser/db/default.cljs → deps/db/src/logseq/db/default.cljs

@@ -1,4 +1,4 @@
-(ns logseq.graph-parser.db.default
+(ns logseq.db.default
   (:require [clojure.string :as string]))
   (:require [clojure.string :as string]))
 
 
 (defonce built-in-pages-names
 (defonce built-in-pages-names

+ 4 - 3
src/main/frontend/db/rules.cljc → deps/db/src/logseq/db/rules.cljc

@@ -1,6 +1,7 @@
-(ns ^:bb-compatible ^:nbb-compatible frontend.db.rules)
+(ns ^:bb-compatible logseq.db.rules
+  "Datalog rules for use with logseq.db.schema")
 
 
-(def rules
+(def ^:large-vars/data-var rules
   ;; rule "parent" is optimized for child node -> parent node nesting queries
   ;; rule "parent" is optimized for child node -> parent node nesting queries
   '[[(parent ?p ?c)
   '[[(parent ?p ?c)
      [?c :block/parent ?p]]
      [?c :block/parent ?p]]
@@ -58,7 +59,7 @@
     ;;                      [?e ?a ?v]))]
     ;;                      [?e ?a ?v]))]
     ])
     ])
 
 
-(def query-dsl-rules
+(def ^:large-vars/data-var query-dsl-rules
   "Rules used by frontend.db.query-dsl. The symbols ?b and ?p respectively refer
   "Rules used by frontend.db.query-dsl. The symbols ?b and ?p respectively refer
   to block and page. Do not alter them as they are programatically built by the
   to block and page. Do not alter them as they are programatically built by the
   query-dsl ns"
   query-dsl ns"

+ 1 - 1
deps/graph-parser/src/logseq/graph_parser/db/schema.cljs → deps/db/src/logseq/db/schema.cljs

@@ -1,4 +1,4 @@
-(ns logseq.graph-parser.db.schema)
+(ns logseq.db.schema)
 
 
 (defonce version 1)
 (defonce version 1)
 (defonce ast-version 1)
 (defonce ast-version 1)

+ 43 - 0
deps/db/yarn.lock

@@ -0,0 +1,43 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@logseq/nbb-logseq@^0.5.103":
+  version "0.5.103"
+  resolved "https://registry.yarnpkg.com/@logseq/nbb-logseq/-/nbb-logseq-0.5.103.tgz#1084380cd54c92ca8cc94a8934cc777206e45cc0"
+  integrity sha512-V9UW0XrCaaadHUc6/Hp9wfGpQqkzqzoqnDGeSVZkWR6l3QwyqGi9mkhnhVcfTwAvxIfOgrfz93GcaeepV4pYNA==
+  dependencies:
+    import-meta-resolve "^1.1.1"
+
+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"
+
+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"
+
+lru-cache@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+  integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+  dependencies:
+    yallist "^4.0.0"
+
+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"
+
+yallist@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+  integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==

+ 0 - 2
deps/graph-parser/.carve/config.edn

@@ -2,8 +2,6 @@
  :api-namespaces [
  :api-namespaces [
                   ;; carve doesn't detect nbb only usage
                   ;; carve doesn't detect nbb only usage
                   logseq.graph-parser.log
                   logseq.graph-parser.log
-                  ;; Used by logseq but not worth splitting up
-                  logseq.graph-parser.db.schema
                   ;; Used in tests
                   ;; Used in tests
                   logseq.graph-parser.test.docs-graph-helper]
                   logseq.graph-parser.test.docs-graph-helper]
  :report {:format :ignore}}
  :report {:format :ignore}}

+ 23 - 0
deps/graph-parser/bb.edn

@@ -0,0 +1,23 @@
+{:min-bb-version "0.8.156"
+ :deps
+ {logseq/bb-tasks
+  #_{:local/root "../../../bb-tasks"}
+  {:git/url "https://github.com/logseq/bb-tasks"
+   :git/sha "abb32ccd26405d56fd28a29d56f3cb902b8c4334"}}
+
+ :pods
+ {clj-kondo/clj-kondo {:version "2022.02.09"}}
+
+ :tasks
+ {test:load-all-namespaces-with-nbb
+  logseq.bb-tasks.nbb.test/load-all-namespaces
+
+  lint:large-vars
+  logseq.bb-tasks.lint.large-vars/-main
+
+  lint:carve
+  logseq.bb-tasks.lint.carve/-main}
+
+ :tasks/config
+ {:large-vars
+  {:max-lines-count 75}}}

+ 5 - 4
deps/graph-parser/deps.edn

@@ -1,10 +1,11 @@
 {:paths ["src"]
 {:paths ["src"]
  :deps
  :deps
- ;; Deps should be kept in sync with https://github.com/logseq/nbb-logseq/blob/main/bb.edn
- {datascript/datascript {:mvn/version "1.3.8"}
-  frankiesardo/linked {:mvn/version "1.3.0"}
+ ;; External deps should be kept in sync with https://github.com/logseq/nbb-logseq/blob/main/bb.edn
+ {frankiesardo/linked {:mvn/version "1.3.0"}
   com.andrewmcveigh/cljs-time {:git/url "https://github.com/logseq/cljs-time" ;; fork
   com.andrewmcveigh/cljs-time {:git/url "https://github.com/logseq/cljs-time" ;; fork
                                :sha     "5704fbf48d3478eedcf24d458c8964b3c2fd59a9"}
                                :sha     "5704fbf48d3478eedcf24d458c8964b3c2fd59a9"}
+  ;; local dep
+  logseq/db {:local/root "../db"}
   ;; stubbed in nbb
   ;; stubbed in nbb
   com.lambdaisland/glogi {:mvn/version "1.1.144"}
   com.lambdaisland/glogi {:mvn/version "1.1.144"}
   ;; built in to nbb
   ;; built in to nbb
@@ -19,5 +20,5 @@
                       org.clojure/clojurescript {:mvn/version "1.11.54"}}
                       org.clojure/clojurescript {:mvn/version "1.11.54"}}
          :main-opts ["-m" "cljs-test-runner.main"]}
          :main-opts ["-m" "cljs-test-runner.main"]}
 
 
-  :clj-kondo {:replace-deps {clj-kondo/clj-kondo {:mvn/version "2022.05.28"}}
+  :clj-kondo {:replace-deps {clj-kondo/clj-kondo {:mvn/version "2022.05.31"}}
               :main-opts  ["-m" "clj-kondo.main"]}}}
               :main-opts  ["-m" "clj-kondo.main"]}}}

+ 1 - 1
deps/graph-parser/package.json

@@ -6,6 +6,6 @@
     "@logseq/nbb-logseq": "^0.5.103"
     "@logseq/nbb-logseq": "^0.5.103"
   },
   },
   "dependencies": {
   "dependencies": {
-    "mldoc": "^1.3.3"
+    "mldoc": "^1.3.9"
   }
   }
 }
 }

+ 37 - 45
deps/graph-parser/src/logseq/graph_parser/block.cljc → deps/graph-parser/src/logseq/graph_parser/block.cljs

@@ -1,7 +1,4 @@
 (ns logseq.graph-parser.block
 (ns logseq.graph-parser.block
-  ;; Disable clj linters since we don't support clj
-  #?(:clj {:clj-kondo/config {:linters {:unresolved-namespace {:level :off}
-                                        :unresolved-symbol {:level :off}}}})
   "Block related code needed for graph-parser"
   "Block related code needed for graph-parser"
   (:require [clojure.string :as string]
   (:require [clojure.string :as string]
             [clojure.walk :as walk]
             [clojure.walk :as walk]
@@ -12,9 +9,7 @@
             [logseq.graph-parser.util :as gp-util]
             [logseq.graph-parser.util :as gp-util]
             [logseq.graph-parser.config :as gp-config]
             [logseq.graph-parser.config :as gp-config]
             [logseq.graph-parser.mldoc :as gp-mldoc]
             [logseq.graph-parser.mldoc :as gp-mldoc]
-            [logseq.graph-parser.date-time-util :as date-time-util]
-            #?(:org.babashka/nbb [logseq.graph-parser.log :as log]
-               :default [lambdaisland.glogi :as log])))
+            [logseq.graph-parser.date-time-util :as date-time-util]))
 
 
 (defn heading-block?
 (defn heading-block?
   [block]
   [block]
@@ -546,45 +541,42 @@
      :date-formatter and :db"
      :date-formatter and :db"
   [blocks content with-id? format {:keys [user-config] :as options}]
   [blocks content with-id? format {:keys [user-config] :as options}]
   {:pre [(seq blocks) (string? content) (boolean? with-id?) (contains? #{:markdown :org} format)]}
   {:pre [(seq blocks) (string? content) (boolean? with-id?) (contains? #{:markdown :org} format)]}
-  (try
-    (let [encoded-content (utf8/encode content)
-          [blocks body pre-block-properties]
-          (loop [headings []
-                 blocks (reverse blocks)
-                 timestamps {}
-                 properties {}
-                 body []]
-            (if (seq blocks)
-              (let [[block pos-meta] (first blocks)
-                    ;; fix start_pos
-                    pos-meta (assoc pos-meta :end_pos
-                                    (if (seq headings)
-                                      (get-in (last headings) [:meta :start_pos])
-                                      nil))]
-                (cond
-                  (paragraph-timestamp-block? block)
-                  (let [timestamps (extract-timestamps block)
-                        timestamps' (merge timestamps timestamps)]
-                    (recur headings (rest blocks) timestamps' properties body))
-
-                  (gp-property/properties-ast? block)
-                  (let [properties (extract-properties format (second block) user-config)]
-                    (recur headings (rest blocks) timestamps properties body))
-
-                  (heading-block? block)
-                  (let [block (construct-block block properties timestamps body encoded-content format pos-meta with-id? options)]
-                    (recur (conj headings block) (rest blocks) {} {} []))
-
-                  :else
-                  (recur headings (rest blocks) timestamps properties (conj body block))))
-              [(-> (reverse headings)
-                   sanity-blocks-data)
-               body
-               properties]))
-          result (with-pre-block-if-exists blocks body pre-block-properties encoded-content options)]
-      (map #(dissoc % :block/meta) result))
-    (catch :default e
-      (log/error :extract-blocks-failure e))))
+  (let [encoded-content (utf8/encode content)
+        [blocks body pre-block-properties]
+        (loop [headings []
+               blocks (reverse blocks)
+               timestamps {}
+               properties {}
+               body []]
+          (if (seq blocks)
+            (let [[block pos-meta] (first blocks)
+                  ;; fix start_pos
+                  pos-meta (assoc pos-meta :end_pos
+                                  (if (seq headings)
+                                    (get-in (last headings) [:meta :start_pos])
+                                    nil))]
+              (cond
+                (paragraph-timestamp-block? block)
+                (let [timestamps (extract-timestamps block)
+                      timestamps' (merge timestamps timestamps)]
+                  (recur headings (rest blocks) timestamps' properties body))
+
+                (gp-property/properties-ast? block)
+                (let [properties (extract-properties format (second block) user-config)]
+                  (recur headings (rest blocks) timestamps properties body))
+
+                (heading-block? block)
+                (let [block (construct-block block properties timestamps body encoded-content format pos-meta with-id? options)]
+                  (recur (conj headings block) (rest blocks) {} {} []))
+
+                :else
+                (recur headings (rest blocks) timestamps properties (conj body block))))
+            [(-> (reverse headings)
+                 sanity-blocks-data)
+             body
+             properties]))
+        result (with-pre-block-if-exists blocks body pre-block-properties encoded-content options)]
+    (map #(dissoc % :block/meta) result)))
 
 
 (defn with-parent-and-left
 (defn with-parent-and-left
   [page-id blocks]
   [page-id blocks]

+ 2 - 2
deps/graph-parser/src/logseq/graph_parser/cli.cljs

@@ -6,7 +6,7 @@
             [clojure.string :as string]
             [clojure.string :as string]
             [logseq.graph-parser :as graph-parser]
             [logseq.graph-parser :as graph-parser]
             [logseq.graph-parser.config :as gp-config]
             [logseq.graph-parser.config :as gp-config]
-            [logseq.graph-parser.db :as gp-db]))
+            [logseq.db :as ldb]))
 
 
 (defn slurp
 (defn slurp
   "Return file contents like clojure.core/slurp"
   "Return file contents like clojure.core/slurp"
@@ -60,7 +60,7 @@ TODO: Fail fast when process exits 1"
    (parse-graph dir {}))
    (parse-graph dir {}))
   ([dir options]
   ([dir options]
    (let [files (or (:files options) (build-graph-files dir))
    (let [files (or (:files options) (build-graph-files dir))
-         conn (gp-db/start-conn)
+         conn (ldb/start-conn)
          config (read-config dir)]
          config (read-config dir)]
      (when-not (:files options) (println "Parsing" (count files) "files..."))
      (when-not (:files options) (println "Parsing" (count files) "files..."))
      (parse-files conn files (merge options {:config config}))
      (parse-files conn files (merge options {:config config}))

+ 4 - 2
deps/graph-parser/test/logseq/graph_parser/nbb_test_runner.cljs

@@ -6,7 +6,8 @@
             [logseq.graph-parser.block-test]
             [logseq.graph-parser.block-test]
             [logseq.graph-parser.property-test]
             [logseq.graph-parser.property-test]
             [logseq.graph-parser.extract-test]
             [logseq.graph-parser.extract-test]
-            [logseq.graph-parser.cli-test]))
+            [logseq.graph-parser.cli-test]
+            [logseq.graph-parser-test]))
 
 
 (defmethod cljs.test/report [:cljs.test/default :end-run-tests] [m]
 (defmethod cljs.test/report [:cljs.test/default :end-run-tests] [m]
   (when-not (cljs.test/successful? m)
   (when-not (cljs.test/successful? m)
@@ -19,4 +20,5 @@
                'logseq.graph-parser.property-test
                'logseq.graph-parser.property-test
                'logseq.graph-parser.block-test
                'logseq.graph-parser.block-test
                'logseq.graph-parser.extract-test
                'logseq.graph-parser.extract-test
-               'logseq.graph-parser.cli-test))
+               'logseq.graph-parser.cli-test
+               'logseq.graph-parser-test))

+ 43 - 0
deps/graph-parser/test/logseq/graph_parser_test.cljs

@@ -0,0 +1,43 @@
+(ns logseq.graph-parser-test
+  (:require [cljs.test :refer [deftest testing is]]
+            [logseq.graph-parser :as graph-parser]
+            [logseq.db :as ldb]
+            [logseq.graph-parser.block :as gp-block]
+            [datascript.core :as d]))
+
+(deftest parse-file
+  (testing "id properties"
+    (let [conn (ldb/start-conn)]
+      (graph-parser/parse-file conn "foo.md" "- id:: 628953c1-8d75-49fe-a648-f4c612109098" {})
+      (is (= [{:id "628953c1-8d75-49fe-a648-f4c612109098"}]
+             (->> (d/q '[:find (pull ?b [*])
+                         :in $
+                         :where [?b :block/content] [(missing? $ ?b :block/name)]]
+                       @conn)
+                  (map first)
+                  (map :block/properties)))
+          "id as text has correct :block/properties"))
+
+    (let [conn (ldb/start-conn)]
+      (graph-parser/parse-file conn "foo.md" "- id:: [[628953c1-8d75-49fe-a648-f4c612109098]]" {})
+      (is (= [{:id #{"628953c1-8d75-49fe-a648-f4c612109098"}}]
+             (->> (d/q '[:find (pull ?b [*])
+                         :in $
+                         :where [?b :block/content] [(missing? $ ?b :block/name)]]
+                       @conn)
+                  (map first)
+                  (map :block/properties)))
+          "id as linked ref has correct :block/properties")))
+
+  (testing "unexpected failure during block extraction"
+    (let [conn (ldb/start-conn)
+          deleted-page (atom nil)]
+      (with-redefs [gp-block/with-pre-block-if-exists (fn stub-failure [& _args]
+                                              (throw (js/Error "Testing unexpected failure")))]
+        (try
+          (graph-parser/parse-file conn "foo.md" "- id:: 628953c1-8d75-49fe-a648-f4c612109098"
+                                  {:delete-blocks-fn (fn [page _file]
+                                                       (reset! deleted-page page))})
+          (catch :default _)))
+      (is (= nil @deleted-page)
+          "Page should not be deleted when there is unexpected failure"))))

+ 4 - 4
deps/graph-parser/yarn.lock

@@ -177,10 +177,10 @@ mimic-fn@^2.0.0:
   resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
   resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
   integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
   integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
 
 
-mldoc@^1.3.3:
-  version "1.3.3"
-  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-1.3.3.tgz#b7f39b48eb0ef3558619d3e3522265977bd78fe3"
-  integrity sha512-TzW06GBltdKxwWAxOvflPmIVedu6bzl9T4YoYqnDUyZ3kELFMllEgiYCh65PPW3xsRMA/5OcRQqqGZGiKEJEug==
+mldoc@^1.3.9:
+  version "1.3.9"
+  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-1.3.9.tgz#9e45a25ba79596f3b0b0eace65f651a4c5a0c30a"
+  integrity sha512-UfqNuBphOj7paSBvozTdin5BLB5+W2tr2SGKTfp5eae6VQPx23oICx6RPQprft7KGFtn8T3rpM1YMUN8FaJLhg==
   dependencies:
   dependencies:
     yargs "^12.0.2"
     yargs "^12.0.2"
 
 

+ 9 - 14
docs/dev-practices.md

@@ -27,7 +27,7 @@ We use https://github.com/borkdude/carve to detect unused vars in our codebase.
 
 
 To run this linter:
 To run this linter:
 ```
 ```
-scripts/carve.clj
+bb lint:carve
 ```
 ```
 
 
 By default, the script runs in CI mode which prints unused vars if they are
 By default, the script runs in CI mode which prints unused vars if they are
@@ -35,7 +35,7 @@ found. The script can be run in an interactive mode which prompts for keeping
 (ignoring) an unused var or removing it. Run this mode with:
 (ignoring) an unused var or removing it. Run this mode with:
 
 
 ```
 ```
-scripts/carve.clj '{:interactive true}'
+bb lint:carve '{:interactive true}'
 ```
 ```
 
 
 When a var is ignored, it is added to `.carve/ignore`. Please add a comment for
 When a var is ignored, it is added to `.carve/ignore`. Please add a comment for
@@ -46,24 +46,19 @@ why a var is ignored to help others understand why it's unused.
 Large vars have a lot of complexity and make it hard for the team to maintain
 Large vars have a lot of complexity and make it hard for the team to maintain
 and understand them. To run this linter:
 and understand them. To run this linter:
 ```
 ```
-scripts/large_vars.clj
+bb lint:large-vars
 ```
 ```
 
 
 To configure the linter, see its `config` var.
 To configure the linter, see its `config` var.
 
 
 ### Datalog linting
 ### Datalog linting
 
 
-We use [datascript](https://github.com/tonsky/datascript)'s datalog to power our modeling and querying layer. Since datalog is concise, it is easy to write something invalid. To avoid typos and other preventable mistakes, we lint our queries and rules. Our queries are linted through clj-kondo and [datalog-parser](https://github.com/lambdaforge/datalog-parser). clj-kondo will error if it detects an invalid query. Our rules are linted through a script that also uses the datalog-parser. To run this linter:
-```
-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-namespaces-with-nbb
-```
+We use [datascript](https://github.com/tonsky/datascript)'s datalog to power our
+modeling and querying layer. Since datalog is concise, it is easy to write
+something invalid. To avoid typos and other preventable mistakes, we lint our
+queries and rules. Our queries are linted through clj-kondo and
+[datalog-parser](https://github.com/lambdaforge/datalog-parser). clj-kondo will
+error if it detects an invalid query.
 
 
 ## Testing
 ## Testing
 
 

+ 4 - 4
ios/App/App.xcodeproj/project.pbxproj

@@ -550,7 +550,7 @@
 				INFOPLIST_FILE = App/Info.plist;
 				INFOPLIST_FILE = App/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
-				MARKETING_VERSION = 0.7.1;
+				MARKETING_VERSION = 0.7.4;
 				OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
 				OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -576,7 +576,7 @@
 				INFOPLIST_FILE = App/Info.plist;
 				INFOPLIST_FILE = App/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
-				MARKETING_VERSION = 0.7.1;
+				MARKETING_VERSION = 0.7.4;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
@@ -601,7 +601,7 @@
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
-				MARKETING_VERSION = 0.7.1;
+				MARKETING_VERSION = 0.7.4;
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				MTL_FAST_MATH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq.ShareViewController;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq.ShareViewController;
@@ -628,7 +628,7 @@
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
-				MARKETING_VERSION = 0.7.1;
+				MARKETING_VERSION = 0.7.4;
 				MTL_FAST_MATH = YES;
 				MTL_FAST_MATH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq.ShareViewController;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq.ShareViewController;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PRODUCT_NAME = "$(TARGET_NAME)";

+ 1 - 2
package.json

@@ -5,7 +5,6 @@
     "main": "static/electron.js",
     "main": "static/electron.js",
     "devDependencies": {
     "devDependencies": {
         "@capacitor/cli": "3.2.2",
         "@capacitor/cli": "3.2.2",
-        "@logseq/nbb-logseq": "^0.5.103",
         "@playwright/test": "^1.19.2",
         "@playwright/test": "^1.19.2",
         "@tailwindcss/ui": "0.7.2",
         "@tailwindcss/ui": "0.7.2",
         "@types/gulp": "^4.0.7",
         "@types/gulp": "^4.0.7",
@@ -55,7 +54,7 @@
         "cljs:electron-watch": "clojure -M:cljs watch app electron --config-merge '{:asset-path \"./js\"}'",
         "cljs:electron-watch": "clojure -M:cljs watch app electron --config-merge '{:asset-path \"./js\"}'",
         "cljs:release": "clojure -M:cljs release app publishing electron",
         "cljs:release": "clojure -M:cljs release app publishing electron",
         "cljs:release-electron": "clojure -M:cljs release app electron --debug && clojure -M:cljs release publishing",
         "cljs:release-electron": "clojure -M:cljs release app electron --debug && clojure -M:cljs release publishing",
-        "cljs:release-app": "clojure -M:cljs release app",
+        "cljs:release-app": "clojure -M:cljs release app --config-merge '{:compiler-options {:output-feature-set :es6}}'",
         "cljs:release-android-app": "clojure -M:cljs release app --config-merge '{:compiler-options {:output-feature-set :es6}}'",
         "cljs:release-android-app": "clojure -M:cljs release app --config-merge '{:compiler-options {:output-feature-set :es6}}'",
         "cljs:test": "clojure -M:test compile test",
         "cljs:test": "clojure -M:test compile test",
         "cljs:run-test": "node static/tests.js",
         "cljs:run-test": "node static/tests.js",

+ 0 - 1
public/index.html

@@ -46,7 +46,6 @@
 </script>
 </script>
 <script defer src="/static/js/highlight.min.js"></script>
 <script defer src="/static/js/highlight.min.js"></script>
 <script defer src="/static/js/interact.min.js"></script>
 <script defer src="/static/js/interact.min.js"></script>
-<script defer src="/static/js/lsplugin.core.js"></script>
 <script defer src="/static/js/main.js"></script>
 <script defer src="/static/js/main.js"></script>
 <script defer src="/static/js/code-editor.js"></script>
 <script defer src="/static/js/code-editor.js"></script>
 <script defer src="/static/js/age-encryption.js"></script>
 <script defer src="/static/js/age-encryption.js"></script>

+ 8 - 0
resources/css/common.css

@@ -808,6 +808,14 @@ li p:last-child,
   border-right-color: var(--ls-border-color, #ccc);
   border-right-color: var(--ls-border-color, #ccc);
 }
 }
 
 
+i.ti {
+  /*
+  compensates the wrong top spacing in the iconfont.
+  See https://github.com/tabler/tabler-icons/issues/118/
+  */
+  transform: translateY(-1px);
+}
+
 .dnd-separator {
 .dnd-separator {
   border-bottom: 3px solid #ccc;
   border-bottom: 3px solid #ccc;
 }
 }

+ 1 - 1
resources/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "Logseq",
   "name": "Logseq",
-  "version": "0.7.1",
+  "version": "0.7.4",
   "main": "electron.js",
   "main": "electron.js",
   "author": "Logseq",
   "author": "Logseq",
   "license": "AGPL-3.0",
   "license": "AGPL-3.0",

+ 0 - 43
scripts/carve.clj

@@ -1,43 +0,0 @@
-#!/usr/bin/env bb
-;; This file is copied from
-;; https://github.com/borkdude/carve/blob/df552797a198b6701fb2d92390fce7c59205ea77/carve.clj
-;; and thus this file is under the same EPL license.
-;; The script is modified to run latest clj-kondo and carve versions and to add
-;; a more friendly commandline interface through -main
-
-(require '[babashka.pods :as pods])
-
-(pods/load-pod 'clj-kondo/clj-kondo "2022.02.09")
-(require '[pod.borkdude.clj-kondo :as clj-kondo])
-;; define clj-kondo.core ns which is used by carve
-(intern (create-ns 'clj-kondo.core) 'run! clj-kondo/run!)
-
-(require '[babashka.deps :as deps])
-(deps/add-deps '{:deps {borkdude/carve ;; {:local/root "."}
-                        {:git/url "https://github.com/borkdude/carve"
-                         :git/sha "df552797a198b6701fb2d92390fce7c59205ea77"}
-                        borkdude/spartan.spec {:git/url "https://github.com/borkdude/spartan.spec"
-                                               :sha "12947185b4f8b8ff8ee3bc0f19c98dbde54d4c90"}}})
-
-(require '[spartan.spec]) ;; defines clojure.spec
-
-(with-out-str ;; silence warnings about spartan.spec + with-gen
-  (binding [*err* *out*]
-    (require '[carve.api :as carve])))
-
-;; again to make clj-kondo happy
-(require '[carve.main])
-(require '[clojure.edn :as edn])
-
-(defn -main
-  "Wrapper around carve.main that defaults to .carve/config.edn and merges
-in an optional string of options"
-  [args]
-  (let [default-opts (slurp ".carve/config.edn")
-         opts (if-let [more-opts (first args)]
-                (pr-str (merge (select-keys (edn/read-string default-opts) [:paths :api-namespaces])
-                               (edn/read-string more-opts)))
-                default-opts)]
-    (apply carve.main/-main ["--opts" opts])))
-
-(-main *command-line-args*)

+ 0 - 54
scripts/large_vars.clj

@@ -1,54 +0,0 @@
-#!/usr/bin/env bb
-
-(ns large-vars
-  "This script detects vars that are too large and that make it difficult for
-  the team to maintain and understand them."
-  (:require [babashka.pods :as pods]
-            [clojure.pprint :as pprint]
-            [clojure.edn :as edn]
-            [clojure.set :as set]))
-
-(pods/load-pod 'clj-kondo/clj-kondo "2022.02.09")
-(require '[pod.borkdude.clj-kondo :as clj-kondo])
-
-(def default-config
-  ;; TODO: Discuss with team and agree on lower number
-  {:max-lines-count 100
-   ;; Vars with these metadata flags are allowed. Name should indicate the reason
-   ;; it is allowed
-   :metadata-exceptions #{::data-var
-                          ;; TODO: Address vars tagged with cleanup-todo. These
-                          ;; are left mostly because they are not high priority
-                          ;; or not well understood
-                          ::cleanup-todo}})
-
-(defn -main
-  [args]
-  (let [paths [(or (first args) "src")]
-        config (or (some->> (second args) edn/read-string (merge default-config))
-                   default-config)
-        {{:keys [var-definitions]} :analysis}
-        (clj-kondo/run!
-         {:lint paths
-          :config {:output {:analysis {:var-definitions {:meta true
-                                                         :lang :cljs}}}}})
-        vars (->> var-definitions
-                  (keep (fn [m]
-                          (let [lines-count (inc (- (:end-row m) (:row m)))]
-                            (when (and (> lines-count (:max-lines-count config))
-                                       (empty? (set/intersection (set (keys (:meta m)))
-                                                                 (:metadata-exceptions config))))
-                              {:var (:name m)
-                               :lines-count lines-count
-                               :filename (:filename m)}))))
-                  (sort-by :lines-count (fn [x y] (compare y x))))]
-    (if (seq vars)
-      (do
-        (println (format "\nThe following vars exceed the line count max of %s:"
-                         (:max-lines-count config)))
-        (pprint/print-table vars)
-        (System/exit 1))
-      (println "All vars are below the max size!"))))
-
-(when (= *file* (System/getProperty "babashka.file"))
-  (-main *command-line-args*))

+ 0 - 52
scripts/lint_rules.clj

@@ -1,52 +0,0 @@
-#!/usr/bin/env bb
-
-(require '[babashka.deps :as deps])
-(deps/add-deps '{:deps {me.tagaholic/dlint {:mvn/version "0.1.0"}
-                        io.lambdaforge/datalog-parser {:mvn/version "0.1.11"}}
-                 :paths ["src/main"]})
-
-(ns lint-rules
-  "Lint datalog rules for parse-ability and unbound variables"
-  (:require [datalog.parser.impl :as parser-impl]
-            [dlint.core :as dlint]
-            [frontend.db.rules :as rules]))
-
-(defn- lint-unbound-rule [rule]
-  (->> (dlint/lint [rule])
-       (keep
-        (fn [[k v]]
-          (when (seq v)
-            {:success false :name k :rule rule :unbound-vars v})))))
-
-(defn- lint-rule [rule]
-  (try (parser-impl/parse-rule rule)
-    {:success true :rule rule}
-    (catch Exception e
-      {:success false :rule rule :error (.getMessage e)})))
-
-(defn- collect-logseq-rules
-  "Collects logseq rules and prepares them for linting"
-  []
-  (into rules/rules
-        (-> rules/query-dsl-rules
-            ;; TODO: Update linter to handle false positive on ?str-val
-            (dissoc :property)
-            vals)))
-
-(defn -main [rules]
-  (let [invalid-unbound-rules (->> rules
-                                   (mapcat lint-unbound-rule)
-                                   (remove :success))
-        invalid-rules (->> rules
-                           (map lint-rule)
-                           (remove :success))
-        lint-results (concat invalid-unbound-rules invalid-rules)]
-    (if (seq lint-results)
-      (do
-        (println (count lint-results) "rules failed to lint:")
-        (println lint-results)
-        (System/exit 1))
-      (println (count rules) "datalog rules linted fine!"))))
-
-(when (= *file* (System/getProperty "babashka.file"))
-  (-main (collect-logseq-rules)))

+ 4 - 6
scripts/src/logseq/tasks/dev.clj

@@ -31,13 +31,11 @@
   - clj-kondo lint
   - clj-kondo lint
   - carve lint for unused vars
   - carve lint for unused vars
   - lint for vars that are too large
   - lint for vars that are too large
-  - lint invalid translation entries
-  - Lint datalog rules"
+  - lint invalid translation entries"
   []
   []
   (doseq [cmd ["clojure -M:clj-kondo --parallel --lint src --cache false"
   (doseq [cmd ["clojure -M:clj-kondo --parallel --lint src --cache false"
-               "scripts/carve.clj"
-               "scripts/large_vars.clj"
-               "bb lang:invalid-translations"
-               "scripts/lint_rules.clj"]]
+               "bb lint:carve"
+               "bb lint:large-vars"
+               "bb lang:invalid-translations"]]
     (println cmd)
     (println cmd)
     (shell cmd)))
     (shell cmd)))

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

@@ -1,47 +0,0 @@
-(ns logseq.tasks.nbb
-  (:require [pod.borkdude.clj-kondo :as clj-kondo]
-            [clojure.string :as str]
-            [babashka.tasks :refer [shell]]))
-
-(defn- fetch-meta-namespaces
-  "Return namespaces with metadata"
-  [paths]
-  (let [{{:keys [namespace-definitions]} :analysis}
-        (clj-kondo/run!
-         {:lint paths
-          :config {:output {:analysis {:namespace-definitions {:meta true
-                                                               :lang :cljs}}}}})
-        matches (keep (fn [m]
-                        (when (:meta m)
-                          {:ns   (:name m)
-                           :meta (:meta m)}))
-                      namespace-definitions)]
-    matches))
-
-(defn- validate-namespaces
-  [namespaces classpath dir]
-  (assert (seq namespaces) "There must be some namespaces to check")
-  ;; distinct b/c sometimes namespaces are duplicated with .cljc analysis
-  (doseq [n (distinct namespaces)]
-    (println "Requiring" n "...")
-    (shell {:dir dir} "yarn nbb-logseq -cp" classpath "-e" (format "(require '[%s])" n)))
-  (println "Success!"))
-
-(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"])))]
-    (validate-namespaces namespaces "src/main" ".")))
-
-(defn load-all-namespaces
-  "Check all namespaces in source path(s) can be required by nbb-logseq"
-  [dir & paths]
-  (let [{{:keys [namespace-definitions]} :analysis}
-        (clj-kondo/run!
-         {:lint (map #(str dir "/" %) paths)
-          :config {:output {:analysis {:namespace-definitions {:lang :cljs}}}}})]
-    (validate-namespaces (map :name namespace-definitions)
-                         (str/join ":" paths)
-                         dir)))

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

@@ -2,7 +2,7 @@
   (:require [electron.handler :as handler]
   (:require [electron.handler :as handler]
             [electron.search :as search]
             [electron.search :as search]
             [electron.updater :refer [init-updater] :as updater]
             [electron.updater :refer [init-updater] :as updater]
-            [electron.utils :refer [*win mac? linux? dev? logger get-win-from-sender restore-user-fetch-agent]]
+            [electron.utils :refer [*win mac? linux? dev? logger get-win-from-sender restore-user-fetch-agent get-graph-name]]
             [electron.url :refer [logseq-url-handler]]
             [electron.url :refer [logseq-url-handler]]
             [clojure.string :as string]
             [clojure.string :as string]
             [promesa.core :as p]
             [promesa.core :as p]
@@ -201,9 +201,10 @@
                        {:role "fileMenu"
                        {:role "fileMenu"
                         :submenu [{:label "New Window"
                         :submenu [{:label "New Window"
                                    :click (fn []
                                    :click (fn []
-                                            (handler/open-new-window!))
-                                   :accelerator "CommandOrControl+N"
-                                   :acceleratorWorksWhenHidden false}
+                                            (p/let [graph-name (get-graph-name (state/get-graph-path))
+                                                    _ (handler/broadcast-persist-graph! graph-name)]
+                                              (handler/open-new-window!)))
+                                   :accelerator "CommandOrControl+N"}
                                   (if mac?
                                   (if mac?
                                     {:role "close"}
                                     {:role "close"}
                                     {:role "quit"})]}
                                     {:role "quit"})]}

+ 63 - 63
src/electron/electron/fs_watcher.cljs

@@ -4,8 +4,7 @@
             ["chokidar" :as watcher]
             ["chokidar" :as watcher]
             [electron.utils :as utils]
             [electron.utils :as utils]
             ["electron" :refer [app]]
             ["electron" :refer [app]]
-            [electron.window :as window]
-            ["path" :as path]))
+            [electron.window :as window]))
 
 
 ;; TODO: explore different solutions for different platforms
 ;; TODO: explore different solutions for different platforms
 ;; 1. https://github.com/Axosoft/nsfw
 ;; 1. https://github.com/Axosoft/nsfw
@@ -16,17 +15,22 @@
 
 
 (defonce file-watcher-chan "file-watcher")
 (defonce file-watcher-chan "file-watcher")
 (defn- send-file-watcher! [dir type payload]
 (defn- send-file-watcher! [dir type payload]
-  ;; Should only send to one window; then dbsync will do his job
-  ;; If no window is on this graph, just ignore
-  (let [sent? (some (fn [^js win]
-                      (when-not (.isDestroyed win)
-                        (.. win -webContents
-                            (send file-watcher-chan
-                                  (bean/->js {:type type :payload payload})))
-                        true)) ;; break some loop on success
-                    (window/get-graph-all-windows dir))]
-    (when-not sent? (prn "unhandled file event will cause uncatched file modifications!.
-                          target:" dir))))
+  (let [send-fn (fn [^js win]
+                  (when-not (.isDestroyed win)
+                    (.. win -webContents
+                        (send file-watcher-chan
+                              (bean/->js {:type type :payload payload})))
+                    true))
+        wins (window/get-graph-all-windows dir)]
+    (if (contains? #{"unlinkDir" "addDir"} type)
+      ;; notify every windows
+      (doseq [win wins] (send-fn win))
+
+      ;; Should only send to one window; then dbsync will do his job
+      ;; If no window is on this graph, just ignore
+      (let [sent? (some send-fn wins)]
+        (when-not sent? (prn "unhandled file event will cause uncatched file modifications!.
+                          target:" dir))))))
 
 
 (defn- publish-file-event!
 (defn- publish-file-event!
   [dir path event]
   [dir path event]
@@ -45,58 +49,54 @@
 
 
 (defn watch-dir!
 (defn watch-dir!
   "Watch a directory if no such file watcher exists"
   "Watch a directory if no such file watcher exists"
-  [_win dir]
-  (when (and (fs/existsSync dir)
-             (not (get @*file-watcher dir)))
-    (let [watcher-opts (clj->js
-                        {:ignored (fn [path]
-                                    (utils/ignored-path? dir path))
-                         :ignoreInitial false
-                         :ignorePermissionErrors true
-                         :interval polling-interval
-                         :binaryInterval polling-interval
-                         :persistent true
-                         :disableGlobbing true
-                         :usePolling false
-                         :awaitWriteFinish true})
-          dir-watcher (.watch watcher dir watcher-opts)
-          watcher-del-f #(.close dir-watcher)
-          parent-dir (path/join dir "..")
-          parent-watcher (.watch watcher parent-dir watcher-opts)
-          parent-watcher-del-f #(.close parent-watcher)]
-      (swap! *file-watcher assoc dir
-             [dir-watcher watcher-del-f]
-             [parent-watcher parent-watcher-del-f])
-      ;; TODO: batch sender
-      (.on parent-watcher "unlinkDir"
-           (fn [path]
-             (when (= dir path)
-               (publish-file-event! dir dir "unlinkDir"))))
-      (.on parent-watcher "addDir"
-           (fn [path]
-             (when (= dir path)
-               (publish-file-event! dir dir "addDir"))))
-      (.on dir-watcher "add"
-           (fn [path]
-             (publish-file-event! dir path "add")))
-      (.on dir-watcher "change"
-           (fn [path]
-             (publish-file-event! dir path "change")))
-      (.on dir-watcher "unlink"
-           (fn [path]
-             (publish-file-event! dir path "unlink")))
-      (.on dir-watcher "error"
-           (fn [path]
-             (println "Watch error happened: "
-                      {:path path})))
+  [dir]
+  (when-not (get @*file-watcher dir)
+    (if (fs/existsSync dir)
+      (let [watcher-opts (clj->js
+                          {:ignored (fn [path]
+                                      (utils/ignored-path? dir path))
+                           :ignoreInitial false
+                           :ignorePermissionErrors true
+                           :interval polling-interval
+                           :binaryInterval polling-interval
+                           :persistent true
+                           :disableGlobbing true
+                           :usePolling false
+                           :awaitWriteFinish true})
+            dir-watcher (.watch watcher dir watcher-opts)
+            watcher-del-f #(.close dir-watcher)]
+        (swap! *file-watcher assoc dir [dir-watcher watcher-del-f])
+        ;; TODO: batch sender
+        (.on dir-watcher "unlinkDir"
+             (fn [path]
+               (when (= dir path)
+                 (publish-file-event! dir dir "unlinkDir"))))
+        (.on dir-watcher "addDir"
+             (fn [path]
+               (when (= dir path)
+                 (publish-file-event! dir dir "addDir"))))
+        (.on dir-watcher "add"
+             (fn [path]
+               (publish-file-event! dir path "add")))
+        (.on dir-watcher "change"
+             (fn [path]
+               (publish-file-event! dir path "change")))
+        (.on dir-watcher "unlink"
+             (fn [path]
+               (publish-file-event! dir path "unlink")))
+        (.on dir-watcher "error"
+             (fn [path]
+               (println "Watch error happened: "
+                        {:path path})))
 
 
-      ;; electron app extends `EventEmitter`
-      ;; TODO check: duplicated with the logic in "window-all-closed" ?
-      (.on app "quit" (fn []
-                        (watcher-del-f)
-                        (parent-watcher-del-f)))
+        ;; electron app extends `EventEmitter`
+        ;; TODO check: duplicated with the logic in "window-all-closed" ?
+        (.on app "quit" watcher-del-f)
 
 
-      true)))
+        true)
+      ;; retry if the `dir` not exists, which is useful when a graph's folder is
+      ;; back after refreshing the window
+      (js/setTimeout #(watch-dir! dir) 5000))))
 
 
 (defn close-watcher!
 (defn close-watcher!
   "If no `dir` provided, close all watchers;
   "If no `dir` provided, close all watchers;

+ 27 - 15
src/electron/electron/handler.cljs

@@ -52,13 +52,16 @@
 (defmethod handle :unlink [_window [_ repo path]]
 (defmethod handle :unlink [_window [_ repo path]]
   (if (plugin/dotdir-file? path)
   (if (plugin/dotdir-file? path)
     (fs/unlinkSync path)
     (fs/unlinkSync path)
-    (let [file-name   (-> (string/replace path (str repo "/") "")
-                          (string/replace "/" "_")
-                          (string/replace "\\" "_"))
-          recycle-dir (str repo "/logseq/.recycle")
-          _           (fs-extra/ensureDirSync recycle-dir)
-          new-path    (str recycle-dir "/" file-name)]
-      (fs/renameSync path new-path))))
+    (try
+      (let [file-name   (-> (string/replace path (str repo "/") "")
+                           (string/replace "/" "_")
+                           (string/replace "\\" "_"))
+           recycle-dir (str repo "/logseq/.recycle")
+           _           (fs-extra/ensureDirSync recycle-dir)
+           new-path    (str recycle-dir "/" file-name)]
+        (fs/renameSync path new-path))
+      (catch :default _e
+        nil))))
 
 
 (defonce Diff (google-diff.))
 (defonce Diff (google-diff.))
 (defn string-some-deleted?
 (defn string-some-deleted?
@@ -292,6 +295,11 @@
   (p/let [_ (utils/fetch url)]
   (p/let [_ (utils/fetch url)]
     (utils/send-to-renderer win :notification {:type "success" :payload (str "Successfully: " url)})))
     (utils/send-to-renderer win :notification {:type "success" :payload (str "Successfully: " url)})))
 
 
+(defmethod handle :httpFetchJSON [_win [_ url options]]
+  (p/let [res (utils/fetch url options)
+          json (.json res)]
+         json))
+
 (defmethod handle :getUserDefaultPlugins []
 (defmethod handle :getUserDefaultPlugins []
   (utils/get-ls-default-plugins))
   (utils/get-ls-default-plugins))
 
 
@@ -329,7 +337,8 @@
 (defn set-current-graph!
 (defn set-current-graph!
   [window graph-path]
   [window graph-path]
   (let [old-path (state/get-window-graph-path window)]
   (let [old-path (state/get-window-graph-path window)]
-    (when old-path (close-watcher-when-orphaned! window old-path))
+    (when (and old-path graph-path (not= old-path graph-path))
+      (close-watcher-when-orphaned! window old-path))
     (swap! state/state assoc :graph/current graph-path)
     (swap! state/state assoc :graph/current graph-path)
     (swap! state/state assoc-in [:window/graph window] graph-path)
     (swap! state/state assoc-in [:window/graph window] graph-path)
     nil))
     nil))
@@ -382,19 +391,21 @@
         windows (win/get-graph-all-windows dir)]
         windows (win/get-graph-all-windows dir)]
     (> (count windows) 1)))
     (> (count windows) 1)))
 
 
-(defmethod handle :addDirWatcher [^js window [_ dir]]
+(defmethod handle :addDirWatcher [^js _window [_ dir]]
   ;; receive dir path (not repo / graph) from frontend
   ;; receive dir path (not repo / graph) from frontend
   ;; Windows on same dir share the same watcher
   ;; Windows on same dir share the same watcher
   ;; Only close file watcher when:
   ;; Only close file watcher when:
   ;;    1. there is no one window on the same dir
   ;;    1. there is no one window on the same dir
   ;;    2. reset file watcher to resend `add` event on window refreshing
   ;;    2. reset file watcher to resend `add` event on window refreshing
   (when dir
   (when dir
-    ;; adding dir watcher when the window has watcher already - must be cmd + r refreshing
-    ;; maintain only one file watcher when multiple windows on the same dir
-    (close-watcher-when-orphaned! window dir)
-    (watcher/watch-dir! window dir)))
+    (watcher/watch-dir! dir)))
+
+(defmethod handle :unwatchDir [^js _window [_ dir]]
+  (when dir
+    (watcher/close-watcher! dir)))
 
 
 (defn open-new-window!
 (defn open-new-window!
+  "Persist db first before calling! Or may break db persistency"
   []
   []
   (let [win (win/create-main-window)]
   (let [win (win/create-main-window)]
     (win/on-close-actions! win close-watcher-when-orphaned!)
     (win/on-close-actions! win close-watcher-when-orphaned!)
@@ -458,8 +469,9 @@
   (println "Error: no ipc handler for: " (bean/->js args)))
   (println "Error: no ipc handler for: " (bean/->js args)))
 
 
 (defn broadcast-persist-graph!
 (defn broadcast-persist-graph!
-  "Sends persist graph event to the renderer contains the target graph.
-   Returns a promise."
+  "Receive graph-name (not graph path)
+   Sends persist graph event to the renderer contains the target graph.
+   Returns a promise<void>."
   [graph-name]
   [graph-name]
   (p/create (fn [resolve _reject]
   (p/create (fn [resolve _reject]
               (let [graph-path (utils/get-graph-dir graph-name)
               (let [graph-path (utils/get-graph-dir graph-name)

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

@@ -15,10 +15,10 @@
 
 
          ;; window -> current graph
          ;; window -> current graph
          :window/graph {}
          :window/graph {}
-         
+
          ;; job to do when persistGraph is done on renderer
          ;; job to do when persistGraph is done on renderer
          :window/once-persist-done nil
          :window/once-persist-done nil
-         
+
          ;; job to do when graph is loaded on renderer
          ;; job to do when graph is loaded on renderer
          :window/once-graph-ready nil}))
          :window/once-graph-ready nil}))
 
 

+ 6 - 0
src/electron/electron/utils.cljs

@@ -121,9 +121,15 @@
          (send (name kind) (bean/->js payload))))))
          (send (name kind) (bean/->js payload))))))
 
 
 (defn get-graph-dir
 (defn get-graph-dir
+  "required by all internal state in the electron section"
   [graph-name]
   [graph-name]
   (string/replace graph-name "logseq_local_" ""))
   (string/replace graph-name "logseq_local_" ""))
 
 
+(defn get-graph-name
+  "reversing `get-graph-dir`"
+  [graph-dir]
+  (str "logseq_local_" graph-dir))
+
 ;; Keep update with the normalization in main
 ;; Keep update with the normalization in main
 (defn normalize
 (defn normalize
   [s]
   [s]

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

@@ -1563,7 +1563,9 @@
                      (if collapsed?
                      (if collapsed?
                        (editor-handler/expand-block! uuid)
                        (editor-handler/expand-block! uuid)
                        (editor-handler/collapse-block! uuid))))}
                        (editor-handler/collapse-block! uuid))))}
-      [:span {:class (if control-show? "control-show cursor-pointer" "control-hide")}
+      [:span {:class (if (and control-show?
+                              (or collapsed?
+                                  (editor-handler/collapsable? uuid {:semantic? true}))) "control-show cursor-pointer" "control-hide")}
        (ui/rotating-arrow collapsed?)]]
        (ui/rotating-arrow collapsed?)]]
      (let [bullet [:a {:on-click (fn [event]
      (let [bullet [:a {:on-click (fn [event]
                                    (bullet-on-click event block uuid))}
                                    (bullet-on-click event block uuid))}
@@ -1925,8 +1927,14 @@
         (state/conj-selection-block! (gdom/getElement block-id) :down))
         (state/conj-selection-block! (gdom/getElement block-id) :down))
       (when (contains? #{1 0} button)
       (when (contains? #{1 0} button)
         (when-not (target-forbidden-edit? target)
         (when-not (target-forbidden-edit? target)
-          (if (and shift? (state/get-selection-start-block))
+          (cond
+            (and shift? (state/get-selection-start-block))
             (editor-handler/highlight-selection-area! block-id)
             (editor-handler/highlight-selection-area! block-id)
+
+            shift?
+            (util/clear-selection!)
+
+            :else
             (do
             (do
               (editor-handler/clear-selection!)
               (editor-handler/clear-selection!)
               (editor-handler/unhighlight-blocks!)
               (editor-handler/unhighlight-blocks!)
@@ -2032,7 +2040,7 @@
        (merge attrs))
        (merge attrs))
 
 
      [:<>
      [:<>
-      [:div.flex.flex-row.justify-between
+      [:div.flex.flex-row.justify-between.block-content-inner
        [:div.flex-1
        [:div.flex-1
         (cond
         (cond
           (seq title)
           (seq title)
@@ -2309,13 +2317,10 @@
   (editor-handler/unhighlight-blocks!))
   (editor-handler/unhighlight-blocks!))
 
 
 (defn- block-mouse-over
 (defn- block-mouse-over
-  [uuid e *control-show? block-id doc-mode?]
+  [e *control-show? block-id doc-mode?]
   (when-not @*dragging?
   (when-not @*dragging?
     (util/stop e)
     (util/stop e)
-    (when (or
-           (model/block-collapsed? uuid)
-           (editor-handler/collapsable? uuid {:semantic? true}))
-      (reset! *control-show? true))
+    (reset! *control-show? true)
     (when-let [parent (gdom/getElement block-id)]
     (when-let [parent (gdom/getElement block-id)]
       (let [node (.querySelector parent ".bullet-container")]
       (let [node (.querySelector parent ".bullet-container")]
         (when doc-mode?
         (when doc-mode?
@@ -2477,7 +2482,7 @@
                        (block-handler/on-touch-end event block uuid *show-left-menu? *show-right-menu?))
                        (block-handler/on-touch-end event block uuid *show-left-menu? *show-right-menu?))
        :on-touch-cancel block-handler/on-touch-cancel
        :on-touch-cancel block-handler/on-touch-cancel
        :on-mouse-over (fn [e]
        :on-mouse-over (fn [e]
-                        (block-mouse-over uuid e *control-show? block-id doc-mode?))
+                        (block-mouse-over e *control-show? block-id doc-mode?))
        :on-mouse-leave (fn [e]
        :on-mouse-leave (fn [e]
                          (block-mouse-leave e *control-show? block-id doc-mode?))}
                          (block-mouse-leave e *control-show? block-id doc-mode?))}
       (when (not slide?)
       (when (not slide?)
@@ -2794,12 +2799,12 @@
            [:span.opacity-60.text-sm.ml-2.results-count
            [:span.opacity-60.text-sm.ml-2.results-count
             (str (count transformed-query-result) " results")]]
             (str (count transformed-query-result) " results")]]
            ;;insert an "edit" button in the query view
            ;;insert an "edit" button in the query view
-           [:a.opacity-70.hover:opacity-100.svg-small.inline 
-            {:on-mouse-down (fn [e]
-                              (util/stop e)
-                              (editor-handler/edit-block! current-block :max (:block/uuid current-block)))}
-            svg/edit]]
-          
+           (when-not built-in?
+            [:a.opacity-70.hover:opacity-100.svg-small.inline
+                      {:on-mouse-down (fn [e]
+                                        (util/stop e)
+                                        (editor-handler/edit-block! current-block :max (:block/uuid current-block)))}
+                      svg/edit])]
           (fn []
           (fn []
             [:div
             [:div
              (when (and current-block (not view-f) (nil? table-view?))
              (when (and current-block (not view-f) (nil? table-view?))

+ 9 - 1
src/main/frontend/components/block.css

@@ -227,7 +227,7 @@
   border-bottom-color: var(--ls-block-ref-link-text-color);
   border-bottom-color: var(--ls-block-ref-link-text-color);
   cursor: alias;
   cursor: alias;
   padding: 2px 0;
   padding: 2px 0;
-  display: inline-block;
+  display: inherit;
 
 
   &:hover {
   &:hover {
     color: var(--ls-link-text-hover-color);
     color: var(--ls-link-text-hover-color);
@@ -235,6 +235,14 @@
 
 
   .block-content {
   .block-content {
     cursor: inherit;
     cursor: inherit;
+
+    &-inner {
+      display: inherit;
+
+      > * {
+        display: inherit;
+      }
+    }
   }
   }
 }
 }
 
 

+ 16 - 13
src/main/frontend/components/export.cljs

@@ -1,10 +1,11 @@
 (ns frontend.components.export
 (ns frontend.components.export
-  (:require [rum.core :as rum]
-            [frontend.ui :as ui]
-            [frontend.util :as util]
+  (:require [frontend.context.i18n :refer [t]]
             [frontend.handler.export :as export]
             [frontend.handler.export :as export]
+            [frontend.mobile.util :as mobile-util]
             [frontend.state :as state]
             [frontend.state :as state]
-            [frontend.context.i18n :refer [t]]))
+            [frontend.ui :as ui]
+            [frontend.util :as util]
+            [rum.core :as rum]))
 
 
 (rum/defc export
 (rum/defc export
   []
   []
@@ -17,21 +18,23 @@
         [:li.mb-4
         [:li.mb-4
          [:a.font-medium {:on-click #(export/export-repo-as-html! current-repo)}
          [:a.font-medium {:on-click #(export/export-repo-as-html! current-repo)}
           (t :export-public-pages)]])
           (t :export-public-pages)]])
-      [:li.mb-4
-       [:a.font-medium {:on-click #(export/export-repo-as-markdown! current-repo)}
-        (t :export-markdown)]]
-      [:li.mb-4
-       [:a.font-medium {:on-click #(export/export-repo-as-opml! current-repo)}
-        (t :export-opml)]]
+      (when-not (mobile-util/native-platform?)
+        [:li.mb-4
+         [:a.font-medium {:on-click #(export/export-repo-as-markdown! current-repo)}
+          (t :export-markdown)]]
+        [:li.mb-4
+         [:a.font-medium {:on-click #(export/export-repo-as-opml! current-repo)}
+          (t :export-opml)]])
       [:li.mb-4
       [:li.mb-4
        [:a.font-medium {:on-click #(export/export-repo-as-edn-v2! current-repo)}
        [:a.font-medium {:on-click #(export/export-repo-as-edn-v2! current-repo)}
         (t :export-edn)]]
         (t :export-edn)]]
       [:li.mb-4
       [:li.mb-4
        [:a.font-medium {:on-click #(export/export-repo-as-json-v2! current-repo)}
        [:a.font-medium {:on-click #(export/export-repo-as-json-v2! current-repo)}
         (t :export-json)]]
         (t :export-json)]]
-      [:li.mb-4
-       [:a.font-medium {:on-click #(export/export-repo-as-roam-json! current-repo)}
-        (t :export-roam-json)]]]
+      (when-not (mobile-util/native-platform?)
+       [:li.mb-4
+        [:a.font-medium {:on-click #(export/export-repo-as-roam-json! current-repo)}
+         (t :export-roam-json)]])]
      [:a#download-as-edn-v2.hidden]
      [:a#download-as-edn-v2.hidden]
      [:a#download-as-json-v2.hidden]
      [:a#download-as-json-v2.hidden]
      [:a#download-as-roam-json.hidden]
      [:a#download-as-roam-json.hidden]

+ 15 - 17
src/main/frontend/components/header.cljs

@@ -25,7 +25,7 @@
 
 
 (rum/defc home-button []
 (rum/defc home-button []
   (ui/with-shortcut :go/home "left"
   (ui/with-shortcut :go/home "left"
-    [:a.button
+    [:button.button.icon.inline
      {:href     (rfe/href :home)
      {:href     (rfe/href :home)
       :on-click #(do
       :on-click #(do
                    (when (mobile-util/native-iphone?)
                    (when (mobile-util/native-iphone?)
@@ -40,13 +40,13 @@
       (if (user-handler/logged-in?)
       (if (user-handler/logged-in?)
         (ui/dropdown-with-links
         (ui/dropdown-with-links
          (fn [{:keys [toggle-fn]}]
          (fn [{:keys [toggle-fn]}]
-           [:a.button
+           [:button.button
             {:on-click toggle-fn}
             {:on-click toggle-fn}
             [:span.text-sm.font-medium (user-handler/email)]])
             [:span.text-sm.font-medium (user-handler/email)]])
          [{:title (t :logout)
          [{:title (t :logout)
            :options {:on-click user-handler/logout}}]
            :options {:on-click user-handler/logout}}]
          {})
          {})
-        [:a.button.text-sm.font-medium.block {:on-click #(js/window.open config/LOGIN-URL)}
+        [:button.button.text-sm.font-medium.block {:on-click #(js/window.open config/LOGIN-URL)}
          [:span (t :login)]]))))
          [:span (t :login)]]))))
 
 
 (rum/defcs file-sync-remote-graphs <
 (rum/defcs file-sync-remote-graphs <
@@ -90,10 +90,10 @@
         (ui/dropdown-with-links
         (ui/dropdown-with-links
          (fn [{:keys [toggle-fn]}]
          (fn [{:keys [toggle-fn]}]
            (if not-syncing?
            (if not-syncing?
-             [:a.button
+             [:button.button.icon.inline
               {:on-click toggle-fn}
               {:on-click toggle-fn}
               (ui/icon "cloud-off" {:style {:fontSize ui/icon-size}})]
               (ui/icon "cloud-off" {:style {:fontSize ui/icon-size}})]
-             [:a.button
+             [:button.button.icon.inline
               {:on-click toggle-fn}
               {:on-click toggle-fn}
               (ui/icon "cloud" {:style {:fontSize ui/icon-size}})]))
               (ui/icon "cloud" {:style {:fontSize ui/icon-size}})]))
          (cond-> []
          (cond-> []
@@ -131,11 +131,9 @@
 (rum/defc left-menu-button < rum/reactive
 (rum/defc left-menu-button < rum/reactive
   [{:keys [on-click]}]
   [{:keys [on-click]}]
   (ui/with-shortcut :ui/toggle-left-sidebar "bottom"
   (ui/with-shortcut :ui/toggle-left-sidebar "bottom"
-    [:a#left-menu.cp__header-left-menu.button
-     {:on-click on-click
-      :style    {:margin-left 12}}
-     [:span.inner
-      (ui/icon "menu-2" {:style {:fontSize ui/icon-size}})]]))
+    [:button.#left-menu.cp__header-left-menu.button.icon
+     {:on-click on-click}
+      (ui/icon "menu-2" {:style {:fontSize ui/icon-size}})]))
 
 
 (rum/defc dropdown-menu < rum/reactive
 (rum/defc dropdown-menu < rum/reactive
   [{:keys [current-repo t]}]
   [{:keys [current-repo t]}]
@@ -144,7 +142,7 @@
                            (concat page-menu [{:hr true}]))]
                            (concat page-menu [{:hr true}]))]
     (ui/dropdown-with-links
     (ui/dropdown-with-links
      (fn [{:keys [toggle-fn]}]
      (fn [{:keys [toggle-fn]}]
-       [:a.button
+       [:button.button.icon
         {:on-click toggle-fn}
         {:on-click toggle-fn}
         (ui/icon "dots" {:style {:fontSize ui/icon-size}})])
         (ui/icon "dots" {:style {:fontSize ui/icon-size}})])
      (->>
      (->>
@@ -188,12 +186,12 @@
   [:div.flex.flex-row
   [:div.flex.flex-row
 
 
    (ui/with-shortcut :go/backward "bottom"
    (ui/with-shortcut :go/backward "bottom"
-     [:a.it.navigation.nav-left.button
+     [:button.it.navigation.nav-left.button.icon
       {:title "Go back" :on-click #(js/window.history.back)}
       {:title "Go back" :on-click #(js/window.history.back)}
       (ui/icon "arrow-left" {:style {:fontSize ui/icon-size}})])
       (ui/icon "arrow-left" {:style {:fontSize ui/icon-size}})])
 
 
    (ui/with-shortcut :go/forward "bottom"
    (ui/with-shortcut :go/forward "bottom"
-     [:a.it.navigation.nav-right.button
+     [:button.it.navigation.nav-right.button.icon
       {:title "Go forward" :on-click #(js/window.history.forward)}
       {:title "Go forward" :on-click #(js/window.history.forward)}
       (ui/icon "arrow-right" {:style {:fontSize ui/icon-size}})])])
       (ui/icon "arrow-right" {:style {:fontSize ui/icon-size}})])])
 
 
@@ -249,7 +247,7 @@
         [left-menu
         [left-menu
          (when current-repo ;; this is for the Search button
          (when current-repo ;; this is for the Search button
            (ui/with-shortcut :go/search "right"
            (ui/with-shortcut :go/search "right"
-             [:a.button#search-button
+             [:button.button.icon#search-button
               {:on-click #(do (when (or (mobile-util/native-android?)
               {:on-click #(do (when (or (mobile-util/native-android?)
                                         (mobile-util/native-iphone?))
                                         (mobile-util/native-iphone?))
                                 (state/set-left-sidebar-open! false))
                                 (state/set-left-sidebar-open! false))
@@ -259,7 +257,7 @@
         (if (state/home?)
         (if (state/home?)
           left-menu
           left-menu
           (ui/with-shortcut :go/backward "bottom"
           (ui/with-shortcut :go/backward "bottom"
-            [:a.it.navigation.nav-left.button
+            [:button.it.navigation.nav-left.button.icon
              {:title "Go back" :on-click #(js/window.history.back)}
              {:title "Go back" :on-click #(js/window.history.back)}
              (ui/icon "chevron-left" {:style {:fontSize 25}})])))]
              (ui/icon "chevron-left" {:style {:fontSize 25}})])))]
 
 
@@ -281,7 +279,7 @@
         (new-block-mode))
         (new-block-mode))
 
 
       (when show-open-folder?
       (when show-open-folder?
-        [:a.text-sm.font-medium.button.add-graph-btn.flex.items-center
+        [:a.text-sm.font-medium.button.icon.add-graph-btn.flex.items-center
          {:on-click #(route-handler/redirect! {:to :repo-add})}
          {:on-click #(route-handler/redirect! {:to :repo-add})}
          (ui/icon "folder-plus")
          (ui/icon "folder-plus")
          (when-not config/mobile?
          (when-not config/mobile?
@@ -289,7 +287,7 @@
             (t :on-boarding/add-graph)])])
             (t :on-boarding/add-graph)])])
 
 
       (when config/publishing?
       (when config/publishing?
-        [:a.text-sm.font-medium.button {:href (rfe/href :graph)}
+        [:button.text-sm.font-medium.button {:href (rfe/href :graph)}
          (t :graph)])
          (t :graph)])
 
 
       (dropdown-menu {:t            t
       (dropdown-menu {:t            t

+ 14 - 22
src/main/frontend/components/header.css

@@ -17,6 +17,7 @@
   white-space: nowrap;
   white-space: nowrap;
 
 
   > .l {
   > .l {
+    @apply pl-4;
     width: var(--ls-left-sidebar-width);
     width: var(--ls-left-sidebar-width);
     height: 100%;
     height: 100%;
     align-items: center;
     align-items: center;
@@ -24,8 +25,8 @@
   }
   }
 
 
   > .r {
   > .r {
+    @apply pr-4;
     align-items: center;
     align-items: center;
-    padding-right: 0.5rem;
     flex: 1;
     flex: 1;
     justify-content: flex-end;
     justify-content: flex-end;
   }
   }
@@ -41,10 +42,7 @@
     transform: scale(0.8);
     transform: scale(0.8);
   }
   }
 
 
-  a.button {
-    margin: 0 4px;
-    height: 30px;
-    min-width: 30px;
+  .button {
     display: flex;
     display: flex;
     align-items: center;
     align-items: center;
     justify-content: center;
     justify-content: center;
@@ -55,6 +53,10 @@
     }
     }
   }
   }
 
 
+  .ui-items-container .button {
+    width: 2rem;
+  }
+
   svg.warning {
   svg.warning {
     transform: scale(0.6);
     transform: scale(0.6);
     color: red;
     color: red;
@@ -119,18 +121,6 @@
       top: 1px;
       top: 1px;
     }
     }
   }
   }
-
-  &-left-menu {
-    &.button {
-      margin: 0;
-      padding: 0;
-    }
-
-    > .inner {
-      line-height: 0;
-      padding: 3px;
-    }
-  }
 }
 }
 
 
 .is-electron.is-mac .cp__header {
 .is-electron.is-mac .cp__header {
@@ -191,11 +181,9 @@
   height: 14px;
   height: 14px;
 }
 }
 
 
-a.button {
-  padding: 0.25rem;
-  opacity: 0.6;
+.button {
+  @apply h-8 px-2.5 py-1 rounded-md opacity-60;
   display: block;
   display: block;
-  border-radius: 4px;
   user-select: none;
   user-select: none;
 
 
   &:hover, &.active {
   &:hover, &.active {
@@ -212,6 +200,10 @@ a.button {
   }
   }
 }
 }
 
 
+.button.icon {
+  @apply w-8 h-8 text-lg p-1;
+}
+
 .is-mac.is-electron :is(.cp__header, .cp__right-sidebar-topbar) :is(button, .button, a) {
 .is-mac.is-electron :is(.cp__header, .cp__right-sidebar-topbar) :is(button, .button, a) {
   cursor: default !important;
   cursor: default !important;
 }
 }
@@ -257,7 +249,7 @@ html.is-native-ipad {
       display: flex;
       display: flex;
     }
     }
 
 
-    a.button {
+    .button {
       opacity: 1;
       opacity: 1;
     }
     }
   }
   }

+ 1 - 1
src/main/frontend/components/onboarding/index.css

@@ -487,7 +487,7 @@ body[data-page=import] {
 
 
     .bd {
     .bd {
       ul a {
       ul a {
-        padding: 2px 4px !important;
+        padding: 2px 8px !important;
       }
       }
     }
     }
   }
   }

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

@@ -892,7 +892,7 @@
              [:a.button.journal
              [:a.button.journal
               {:class    (util/classnames [{:active (boolean @*journal?)}])
               {:class    (util/classnames [{:active (boolean @*journal?)}])
                :on-click #(reset! *journal? (not @*journal?))}
                :on-click #(reset! *journal? (not @*journal?))}
-              (ui/icon "calendar")])]
+              (ui/icon "calendar" {:style {:fontSize ui/icon-size}})])]
 
 
            [:div.paginates
            [:div.paginates
             [:span.flex.items-center
             [:span.flex.items-center

+ 5 - 2
src/main/frontend/components/page.css

@@ -119,6 +119,7 @@
       font-size: 16px;
       font-size: 16px;
       display: inline-block;
       display: inline-block;
       position: relative;
       position: relative;
+      transform: none;
     }
     }
 
 
     .l {
     .l {
@@ -133,9 +134,11 @@
     .r {
     .r {
       font-size: 14px;
       font-size: 14px;
 
 
-      a.journal {
+      a.button {
         color: var(--ls-primary-text-color);
         color: var(--ls-primary-text-color);
         margin-top: 1px;
         margin-top: 1px;
+        height: unset;
+        padding: 4px;
 
 
         &.active {
         &.active {
           opacity: 1;
           opacity: 1;
@@ -194,7 +197,7 @@
     display: flex;
     display: flex;
     align-items: center;
     align-items: center;
     justify-content: space-between;
     justify-content: space-between;
-    padding: 0 5px 0 0;
+    padding: 0 4px;
 
 
     > span {
     > span {
       color: var(--ls-primary-text-color);
       color: var(--ls-primary-text-color);

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

@@ -372,7 +372,6 @@
       [:p [:label [:strong (t :type)]
       [:p [:label [:strong (t :type)]
            (ui/select [{:label "Disabled" :value "" :selected disabled?}
            (ui/select [{:label "Disabled" :value "" :selected disabled?}
                        {:label "http" :value "http" :selected (= protocol "http")}
                        {:label "http" :value "http" :selected (= protocol "http")}
-                       {:label "https" :value "https" :selected (= protocol "https")}
                        {:label "socks5" :value "socks5" :selected (= protocol "socks5")}]
                        {:label "socks5" :value "socks5" :selected (= protocol "socks5")}]
                       #(set-opts!
                       #(set-opts!
                          (assoc opts :protocol (if (= "disabled" (util/safe-lower-case %)) nil %))) nil)]]
                          (assoc opts :protocol (if (= "disabled" (util/safe-lower-case %)) nil %))) nil)]]

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

@@ -70,7 +70,6 @@
       .ti {
       .ti {
         margin-right: 4px;
         margin-right: 4px;
       }
       }
-
       .ui__dropdown-trigger {
       .ui__dropdown-trigger {
         .ti-circle {
         .ti-circle {
           visibility: hidden;
           visibility: hidden;

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

@@ -148,7 +148,7 @@
             render-content (fn [{:keys [toggle-fn]}]
             render-content (fn [{:keys [toggle-fn]}]
                              (let [repo-path (db/get-repo-name current-repo)
                              (let [repo-path (db/get-repo-name current-repo)
                                    short-repo-name (db/get-short-repo-name repo-path)]
                                    short-repo-name (db/get-short-repo-name repo-path)]
-                               [:a.item.group.flex.items-center.px-1.py-2.text-sm.font-medium.rounded-md
+                               [:a.item.group.flex.items-center.p-2.text-sm.font-medium.rounded-md
                                 {:on-click (fn []
                                 {:on-click (fn []
                                              (check-multiple-windows? state)
                                              (check-multiple-windows? state)
                                              (toggle-fn))
                                              (toggle-fn))

+ 11 - 12
src/main/frontend/components/right_sidebar.cljs

@@ -24,7 +24,7 @@
   []
   []
   (when-not (util/mobile?)
   (when-not (util/mobile?)
     (ui/with-shortcut :ui/toggle-right-sidebar "left"
     (ui/with-shortcut :ui/toggle-right-sidebar "left"
-      [:a.button.fade-link.toggle-right-sidebar
+      [:button.button.icon.fade-link.toggle-right-sidebar
        {:on-click ui-handler/toggle-right-sidebar!}
        {:on-click ui-handler/toggle-right-sidebar!}
        (ui/icon "layout-sidebar-right" {:style {:fontSize "20px"}})])))
        (ui/icon "layout-sidebar-right" {:style {:fontSize "20px"}})])))
 
 
@@ -197,15 +197,15 @@
 
 
      (sidebar-resizer)
      (sidebar-resizer)
      [:div.cp__right-sidebar-scrollable
      [:div.cp__right-sidebar-scrollable
-      [:div.cp__right-sidebar-topbar.flex.flex-row.justify-between.items-center.pl-4.pr-2.h-12
-       [:div.cp__right-sidebar-settings.hide-scrollbar {:key "right-sidebar-settings"}
-        [:div.ml-4.text-sm
-         [:a.cp__right-sidebar-settings-btn {:on-click (fn [_e]
+      [:div.cp__right-sidebar-topbar.flex.flex-row.justify-between.items-center.px-2.h-12
+       [:div.cp__right-sidebar-settings.hide-scrollbar.gap-1 {:key "right-sidebar-settings"}
+        [:div.text-sm
+         [:button.button.cp__right-sidebar-settings-btn {:on-click (fn [_e]
                                                          (state/sidebar-add-block! repo "contents" :contents))}
                                                          (state/sidebar-add-block! repo "contents" :contents))}
           (t :right-side-bar/contents)]]
           (t :right-side-bar/contents)]]
 
 
-        [:div.ml-4.text-sm
-         [:a.cp__right-sidebar-settings-btn {:on-click (fn []
+        [:div.text-sm
+         [:button.button.cp__right-sidebar-settings-btn {:on-click (fn []
                                                          (when-let [page (get-current-page)]
                                                          (when-let [page (get-current-page)]
                                                            (state/sidebar-add-block!
                                                            (state/sidebar-add-block!
                                                             repo
                                                             repo
@@ -213,16 +213,15 @@
                                                             :page-graph)))}
                                                             :page-graph)))}
           (t :right-side-bar/page)]]
           (t :right-side-bar/page)]]
 
 
-        [:div.ml-4.text-sm
-         [:a.cp__right-sidebar-settings-btn {:on-click (fn [_e]
+        [:div.text-sm
+         [:button.button.cp__right-sidebar-settings-btn {:on-click (fn [_e]
                                                          (state/sidebar-add-block! repo "help" :help))}
                                                          (state/sidebar-add-block! repo "help" :help))}
           (t :right-side-bar/help)]]]
           (t :right-side-bar/help)]]]
 
 
-       [:div.flex.align-items {:style {:z-index 999
-                                       :margin-right 2}}
+       [:div
         (toggle)]]
         (toggle)]]
 
 
-      [:.sidebar-item-list.flex-1.scrollbar-spacing
+      [:.sidebar-item-list.flex-1.scrollbar-spacing.flex.flex-col.gap-2
        (if @*anim-finished?
        (if @*anim-finished?
          (for [[idx [repo db-id block-type]] (medley/indexed blocks)]
          (for [[idx [repo db-id block-type]] (medley/indexed blocks)]
            (rum/with-key
            (rum/with-key

+ 20 - 5
src/main/frontend/components/settings.cljs

@@ -491,7 +491,15 @@
          {:style {:top -18 :left 10}}
          {:style {:top -18 :left 10}}
          (ui/button (t :plugin/restart)
          (ui/button (t :plugin/restart)
                     :on-click #(js/logseq.api.relaunch)
                     :on-click #(js/logseq.api.relaunch)
-                    :small? true :intent "logseq")]])]))
+           :small? true :intent "logseq")]])]))
+
+(rum/defc flashcards-enabled-switcher
+  [enable-flashcards?]
+  (ui/toggle enable-flashcards?
+             (fn []
+               (let [value (not enable-flashcards?)]
+                 (config-handler/set-config! :feature/enable-flashcards? value)))
+             true))
 
 
 (rum/defc user-proxy-settings
 (rum/defc user-proxy-settings
   [{:keys [protocol host port] :as agent-opts}]
   [{:keys [protocol host port] :as agent-opts}]
@@ -508,6 +516,11 @@
    {:left-label (t :settings-page/plugin-system)
    {:left-label (t :settings-page/plugin-system)
     :action (plugin-enabled-switcher t)}))
     :action (plugin-enabled-switcher t)}))
 
 
+(defn flashcards-switcher-row [enable-flashcards?]
+  (row-with-button-action
+   {:left-label (t :settings-page/enable-flashcards)
+    :action (flashcards-enabled-switcher enable-flashcards?)}))
+
 (defn https-user-agent-row [agent-opts]
 (defn https-user-agent-row [agent-opts]
   (row-with-button-action
   (row-with-button-action
    {:left-label (t :settings-page/network-proxy)
    {:left-label (t :settings-page/network-proxy)
@@ -596,16 +609,18 @@
      :warning
      :warning
      [:p (t :settings-page/git-confirm)])])
      [:p (t :settings-page/git-confirm)])])
 
 
-(rum/defcs settings-advanced < rum/reactive
-  [_state]
+(rum/defc settings-advanced < rum/reactive
+  [current-repo]
   (let [instrument-disabled? (state/sub :instrument/disabled?)
   (let [instrument-disabled? (state/sub :instrument/disabled?)
         developer-mode? (state/sub [:ui/developer-mode?])
         developer-mode? (state/sub [:ui/developer-mode?])
-        https-agent-opts (state/sub [:electron/user-cfgs :settings/agent])]
+        https-agent-opts (state/sub [:electron/user-cfgs :settings/agent])
+        enable-flashcards? (state/enable-flashcards? current-repo)]
     [:div.panel-wrap.is-advanced
     [:div.panel-wrap.is-advanced
      (when (and util/mac? (util/electron?)) (app-auto-update-row t))
      (when (and util/mac? (util/electron?)) (app-auto-update-row t))
      (usage-diagnostics-row t instrument-disabled?)
      (usage-diagnostics-row t instrument-disabled?)
      (when-not (mobile-util/native-platform?) (developer-mode-row t developer-mode?))
      (when-not (mobile-util/native-platform?) (developer-mode-row t developer-mode?))
      (when (util/electron?) (plugin-system-switcher-row))
      (when (util/electron?) (plugin-system-switcher-row))
+     (flashcards-switcher-row enable-flashcards?)
      (when (util/electron?) (https-user-agent-row https-agent-opts))
      (when (util/electron?) (https-user-agent-row https-agent-opts))
      (clear-cache-row t)
      (clear-cache-row t)
 
 
@@ -678,6 +693,6 @@
          (settings-git)
          (settings-git)
 
 
          :advanced
          :advanced
-         (settings-advanced)
+         (settings-advanced current-repo)
 
 
          nil)]]]))
          nil)]]]))

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

@@ -223,11 +223,11 @@
                    (when (some (fn [sel] (boolean (.closest target sel)))
                    (when (some (fn [sel] (boolean (.closest target sel)))
                                [".favorites .bd" ".recent .bd" ".dropdown-wrapper" ".nav-header"])
                                [".favorites .bd" ".recent .bd" ".dropdown-wrapper" ".nav-header"])
                      (close-modal-fn)))}
                      (close-modal-fn)))}
-     [:div.flex.flex-col.pb-4.wrap
-      [:nav.px-4.pt-1.space-y-1 {:aria-label "Sidebar"}
+     [:div.flex.flex-col.pb-4.wrap.gap-4
+      [:nav.px-4.flex.flex-col.gap-1 {:aria-label "Sidebar"}
        (repo/repos-dropdown)
        (repo/repos-dropdown)
 
 
-       [:div.nav-header
+       [:div.nav-header.flex.gap-1.flex-col
         (if-let [page (:page default-home)]
         (if-let [page (:page default-home)]
           (sidebar-item
           (sidebar-item
             {:class            "home-nav"
             {:class            "home-nav"
@@ -245,8 +245,9 @@
              :on-click-handler route-handler/go-to-journals!
              :on-click-handler route-handler/go-to-journals!
              :icon             "calendar"}))
              :icon             "calendar"}))
 
 
-        [:div.flashcards-nav
-         (flashcards srs-open?)]
+        (when (state/enable-flashcards? (state/get-current-repo))
+          [:div.flashcards-nav
+           (flashcards srs-open?)])
 
 
         (sidebar-item
         (sidebar-item
           {:class  "graph-view-nav"
           {:class  "graph-view-nav"
@@ -340,7 +341,7 @@
 
 
       (when show-action-bar?
       (when show-action-bar?
         (action-bar/action-bar))
         (action-bar/action-bar))
-      
+
       [:div.cp__sidebar-main-content
       [:div.cp__sidebar-main-content
        {:data-is-margin-less-pages margin-less-pages?
        {:data-is-margin-less-pages margin-less-pages?
         :data-is-full-width        (or margin-less-pages?
         :data-is-full-width        (or margin-less-pages?
@@ -351,11 +352,11 @@
 
 
        (mobile-bar)
        (mobile-bar)
        (footer/footer)
        (footer/footer)
-       
+
        (when (and (not (mobile-util/native-platform?))
        (when (and (not (mobile-util/native-platform?))
                   (contains? #{:page :home} route-name))
                   (contains? #{:page :home} route-name))
          (widgets/demo-graph-alert))
          (widgets/demo-graph-alert))
-       
+
        (cond
        (cond
          (not indexeddb-support?)
          (not indexeddb-support?)
          nil
          nil

+ 8 - 14
src/main/frontend/components/sidebar.css

@@ -87,24 +87,24 @@
   }
   }
 
 
   .page-icon {
   .page-icon {
+    @apply mr-1 align-baseline;
+    width: 16px;
+    height: 16px;
     text-align: center;
     text-align: center;
     display: inline-block;
     display: inline-block;
     line-height: 1em;
     line-height: 1em;
-    color: #aaa;
-    padding: 0 4px 0 8px;
+    color: var(--ls-icon-color);
   }
   }
 
 
   a.item {
   a.item {
     user-select: none;
     user-select: none;
     transition: background-color .3s;
     transition: background-color .3s;
-    margin-bottom: 2px;
 
 
     > .ti {
     > .ti {
       font-size: 16px;
       font-size: 16px;
       margin-right: 8px;
       margin-right: 8px;
       opacity: .6;
       opacity: .6;
       position: relative;
       position: relative;
-      top: -1px;
     }
     }
 
 
     &.active, &:active {
     &.active, &:active {
@@ -121,19 +121,17 @@
   }
   }
 
 
   .nav-content-item {
   .nav-content-item {
-    margin-top: 14px;
-
     &-inner {
     &-inner {
       border-radius: 8px;
       border-radius: 8px;
     }
     }
 
 
     .header {
     .header {
+      @apply px-6 py-1;
       display: flex;
       display: flex;
       justify-content: space-between;
       justify-content: space-between;
       align-items: center;
       align-items: center;
       user-select: none;
       user-select: none;
       cursor: pointer;
       cursor: pointer;
-      padding: 4px 25px;
 
 
       > span {
       > span {
         > a {
         > a {
@@ -181,7 +179,6 @@
         > span {
         > span {
           font-size: 11px;
           font-size: 11px;
           font-weight: 600;
           font-weight: 600;
-          padding-top: 2px;
         }
         }
       }
       }
     }
     }
@@ -196,7 +193,7 @@
 
 
         a {
         a {
           width: 100%;
           width: 100%;
-          padding: 2px 18px;
+          padding: 2px 24px;
           display: block;
           display: block;
           text-overflow: ellipsis;
           text-overflow: ellipsis;
           overflow: hidden;
           overflow: hidden;
@@ -247,7 +244,7 @@
     background-color: var(--ls-secondary-background-color);
     background-color: var(--ls-secondary-background-color);
 
 
     > .wrap {
     > .wrap {
-      margin-top: 50px;
+      margin-top: 52px;
     }
     }
 
 
     .new-page {
     .new-page {
@@ -433,7 +430,6 @@ html[data-theme='dark'] {
 
 
   &-settings {
   &-settings {
     @apply flex flex-row;
     @apply flex flex-row;
-    margin: -15px;
     margin-bottom: 0;
     margin-bottom: 0;
     margin-top: 0;
     margin-top: 0;
     overflow: auto;
     overflow: auto;
@@ -478,9 +474,7 @@ html[data-theme='dark'] {
   }
   }
 
 
   .sidebar-item {
   .sidebar-item {
-    padding-top: 24px;
-    padding-bottom: 24px;
-    margin-bottom: 8px;
+    @apply p-4;
 
 
     .close {
     .close {
       transform: scale(0.8);
       transform: scale(0.8);

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

@@ -21,8 +21,11 @@
 
 
 (defn journal-title-formatters
 (defn journal-title-formatters
   []
   []
-  (conj
-   #{"do MMM yyyy"
+  (->
+   (cons
+    (state/get-date-formatter)
+    (list
+     "do MMM yyyy"
      "do MMMM yyyy"
      "do MMMM yyyy"
      "MMM do, yyyy"
      "MMM do, yyyy"
      "MMMM do, yyyy"
      "MMMM do, yyyy"
@@ -48,8 +51,8 @@
      "yyyy-MM-dd EEEE"
      "yyyy-MM-dd EEEE"
      "yyyy_MM_dd"
      "yyyy_MM_dd"
      "yyyyMMdd"
      "yyyyMMdd"
-     "yyyy年MM月dd日"}
-   (state/get-date-formatter)))
+     "yyyy年MM月dd日"))
+   (distinct)))
 
 
 (defn get-date-time-string
 (defn get-date-time-string
   ([]
   ([]

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

@@ -1,9 +1,9 @@
 (ns frontend.db
 (ns frontend.db
   (:require [clojure.core.async :as async]
   (:require [clojure.core.async :as async]
             [datascript.core :as d]
             [datascript.core :as d]
-            [logseq.graph-parser.db.schema :as db-schema]
+            [logseq.db.schema :as db-schema]
             [frontend.db.conn :as conn]
             [frontend.db.conn :as conn]
-            [logseq.graph-parser.db.default :as default-db]
+            [logseq.db.default :as default-db]
             [frontend.db.model]
             [frontend.db.model]
             [frontend.db.query-custom]
             [frontend.db.query-custom]
             [frontend.db.query-react]
             [frontend.db.query-react]
@@ -66,7 +66,7 @@
  [frontend.db.query-react
  [frontend.db.query-react
   react-query custom-query-result-transform]
   react-query custom-query-result-transform]
 
 
- [logseq.graph-parser.db.default built-in-pages-names built-in-pages])
+ [logseq.db.default built-in-pages-names built-in-pages])
 
 
 (defn get-schema-version [db]
 (defn get-schema-version [db]
   (d/q
   (d/q

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

@@ -7,7 +7,7 @@
             [frontend.config :as config]
             [frontend.config :as config]
             [frontend.util.text :as text-util]
             [frontend.util.text :as text-util]
             [logseq.graph-parser.text :as text]
             [logseq.graph-parser.text :as text]
-            [logseq.graph-parser.db :as gp-db]))
+            [logseq.db :as ldb]))
 
 
 (defonce conns (atom {}))
 (defonce conns (atom {}))
 
 
@@ -71,7 +71,7 @@
    (start! repo {}))
    (start! repo {}))
   ([repo {:keys [listen-handler]}]
   ([repo {:keys [listen-handler]}]
    (let [db-name (datascript-db repo)
    (let [db-name (datascript-db repo)
-         db-conn (gp-db/start-conn)]
+         db-conn (ldb/start-conn)]
      (swap! conns assoc db-name db-conn)
      (swap! conns assoc db-name db-conn)
      (when listen-handler
      (when listen-handler
        (listen-handler repo)))))
        (listen-handler repo)))))

+ 10 - 15
src/main/frontend/db/model.cljs

@@ -8,15 +8,15 @@
             [datascript.core :as d]
             [datascript.core :as d]
             [frontend.config :as config]
             [frontend.config :as config]
             [frontend.date :as date]
             [frontend.date :as date]
-            [logseq.graph-parser.db.schema :as db-schema]
+            [logseq.db.schema :as db-schema]
             [frontend.db.conn :as conn]
             [frontend.db.conn :as conn]
             [frontend.db.react :as react]
             [frontend.db.react :as react]
             [frontend.db.utils :as db-utils]
             [frontend.db.utils :as db-utils]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.util :as util :refer [react]]
             [frontend.util :as util :refer [react]]
             [logseq.graph-parser.util :as gp-util]
             [logseq.graph-parser.util :as gp-util]
-            [frontend.db.rules :refer [rules]]
-            [logseq.graph-parser.db.default :as default-db]
+            [logseq.db.rules :refer [rules]]
+            [logseq.db.default :as default-db]
             [frontend.util.drawer :as drawer]))
             [frontend.util.drawer :as drawer]))
 
 
 ;; lazy loading
 ;; lazy loading
@@ -802,13 +802,6 @@
     (->> (tree-seq map? (fn [x] [(:block/parent x)]) block)
     (->> (tree-seq map? (fn [x] [(:block/parent x)]) block)
          (some util/collapsed?))))
          (some util/collapsed?))))
 
 
-(defn block-collapsed?
-  ([block-id]
-   (block-collapsed? (state/get-current-repo) block-id))
-  ([repo block-id]
-   (when-let [block (db-utils/entity repo [:block/uuid block-id])]
-     (util/collapsed? block))))
-
 (defn get-block-page
 (defn get-block-page
   [repo block-id]
   [repo block-id]
   (when-let [block (db-utils/entity repo [:block/uuid block-id])]
   (when-let [block (db-utils/entity repo [:block/uuid block-id])]
@@ -1457,11 +1450,13 @@
       (mapv (fn [page] [:db.fn/retractEntity [:block/name page]]) (map util/page-name-sanity-lc pages)))))
       (mapv (fn [page] [:db.fn/retractEntity [:block/name page]]) (map util/page-name-sanity-lc pages)))))
 
 
 (defn set-file-content!
 (defn set-file-content!
-  [repo path content]
-  (when (and repo path)
-    (let [tx-data {:file/path path
-                   :file/content content}]
-      (db-utils/transact! repo [tx-data] {:skip-refresh? true}))))
+  ([repo path content]
+   (set-file-content! repo path content {}))
+  ([repo path content opts]
+   (when (and repo path)
+     (let [tx-data {:file/path path
+                    :file/content content}]
+       (db-utils/transact! repo [tx-data] (merge opts {:skip-refresh? true}))))))
 
 
 (defn get-pre-block
 (defn get-pre-block
   [repo page-id]
   [repo page-id]

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

@@ -4,7 +4,7 @@
             [frontend.db.query-react :as query-react]
             [frontend.db.query-react :as query-react]
             [frontend.db.query-dsl :as query-dsl]
             [frontend.db.query-dsl :as query-dsl]
             [frontend.db.model :as model]
             [frontend.db.model :as model]
-            [frontend.db.rules :as rules]
+            [logseq.db.rules :as rules]
             [frontend.util.datalog :as datalog-util]
             [frontend.util.datalog :as datalog-util]
             [clojure.walk :as walk]))
             [clojure.walk :as walk]))
 
 

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

@@ -11,7 +11,7 @@
             [frontend.db.model :as model]
             [frontend.db.model :as model]
             [frontend.db.query-react :as query-react]
             [frontend.db.query-react :as query-react]
             [frontend.db.utils :as db-utils]
             [frontend.db.utils :as db-utils]
-            [frontend.db.rules :as rules]
+            [logseq.db.rules :as rules]
             [frontend.template :as template]
             [frontend.template :as template]
             [logseq.graph-parser.text :as text]
             [logseq.graph-parser.text :as text]
             [frontend.util.text :as text-util]
             [frontend.util.text :as text-util]

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

@@ -114,6 +114,11 @@
                page-ref (string/lower-case page-ref)]
                page-ref (string/lower-case page-ref)]
            (list 'contains? sym (text/page-ref-un-brackets! page-ref)))
            (list 'contains? sym (text/page-ref-un-brackets! page-ref)))
 
 
+         (and (vector? f)
+              (= (first f) 'page-property)
+              (keyword? (util/nth-safe f 2)))
+         (update f 2 (fn [k] (keyword (string/replace (name k) "_" "-"))))
+
          :else
          :else
          f)) query)))
          f)) query)))
 
 
@@ -132,4 +137,4 @@
           k [:custom query']]
           k [:custom query']]
       (pprint "inputs (post-resolution):" resolved-inputs)
       (pprint "inputs (post-resolution):" resolved-inputs)
       (pprint "query-opts:" query-opts)
       (pprint "query-opts:" query-opts)
-      (apply react/q repo k query-opts query inputs))))
+      (apply react/q repo k query-opts query inputs))))

+ 33 - 12
src/main/frontend/dicts.cljc

@@ -183,6 +183,7 @@
         :settings-page/tab-version-control "Version control"
         :settings-page/tab-version-control "Version control"
         :settings-page/tab-advanced "Advanced"
         :settings-page/tab-advanced "Advanced"
         :settings-page/plugin-system "Plug-in system"
         :settings-page/plugin-system "Plug-in system"
+        :settings-page/enable-flashcards "Flashcards"
         :settings-page/network-proxy "Network proxy"
         :settings-page/network-proxy "Network proxy"
         :logseq "Logseq"
         :logseq "Logseq"
         :on "ON"
         :on "ON"
@@ -1566,9 +1567,9 @@
         :file-sync/graph-deleted "El gráfico remoto actual se ha eliminado"}
         :file-sync/graph-deleted "El gráfico remoto actual se ha eliminado"}
 
 
    :nb-NO {:tutorial/text #?(:cljs (rc/inline "tutorial-no.md")
    :nb-NO {:tutorial/text #?(:cljs (rc/inline "tutorial-no.md")
-                                :default "tutorial-no.md")
+                             :default "tutorial-no.md")
            :tutorial/dummy-notes #?(:cljs (rc/inline "dummy-notes-no.md")
            :tutorial/dummy-notes #?(:cljs (rc/inline "dummy-notes-no.md")
-                                       :default "dummy-notes-no.md")
+                                    :default "dummy-notes-no.md")
            :on-boarding/demo-graph "Dette er en demo graf, endringer vil ikke bli lagret før du åpner en lokal mappe."
            :on-boarding/demo-graph "Dette er en demo graf, endringer vil ikke bli lagret før du åpner en lokal mappe."
            :on-boarding/add-graph "Legg til en graf"
            :on-boarding/add-graph "Legg til en graf"
            :on-boarding/open-local-dir "Åpne en lokal mappe"
            :on-boarding/open-local-dir "Åpne en lokal mappe"
@@ -1625,11 +1626,11 @@
            :right-side-bar/favorites "Favoritter"
            :right-side-bar/favorites "Favoritter"
            :right-side-bar/page-graph "Sidegraf"
            :right-side-bar/page-graph "Sidegraf"
            :right-side-bar/block-ref "Blokkreferanse"
            :right-side-bar/block-ref "Blokkreferanse"
-           :right-side-bar/graph-view "Graph view"
-           :right-side-bar/all-pages "All pages"
+           :right-side-bar/graph-view "Grafvisning"
+           :right-side-bar/all-pages "Alle sider"
            :right-side-bar/flashcards "Flashcards"
            :right-side-bar/flashcards "Flashcards"
-           :right-side-bar/new-page "New page"
-           :left-side-bar/journals "Journals"
+           :right-side-bar/new-page "Ny side"
+           :left-side-bar/journals "Dagbøker"
            :left-side-bar/new-page "Ny side"
            :left-side-bar/new-page "Ny side"
            :left-side-bar/nav-favorites "Favoritter"
            :left-side-bar/nav-favorites "Favoritter"
            :left-side-bar/nav-shortcuts "Snarveier"
            :left-side-bar/nav-shortcuts "Snarveier"
@@ -1866,8 +1867,28 @@
            :select.graph/empty-placeholder-description "Ingen grafer matcher. Vil du legge til en ny?"
            :select.graph/empty-placeholder-description "Ingen grafer matcher. Vil du legge til en ny?"
            :select.graph/add-graph "Ja, legg til en ny graf"
            :select.graph/add-graph "Ja, legg til en ny graf"
 
 
-           :file-sync/other-user-graph "Nåværende lokal graf er bundet til annen brukers fjernkontroll. Så kan ikke begynne å synkronisere."
-           :file-sync/graph-deleted "Nåværende fjernkontrollen er slettet"}
+           :file-sync/other-user-graph "Nåværende lokal graf er bundet til annen brukers fjerngraf. Kan ikke begynne å synkronisere."
+           :file-sync/graph-deleted "Nåværende fjerngraf er slettet"
+           :host "Vert"
+           :port "Port"
+           :re-index-discard-unsaved-changes-warning "Reindeksering vil forkaste nåværende graf, og deretter prosessere alle filene på nytt slik de er på disk akkurat nå. Du vil miste ulagrede endringer, og det kan ta litt tid. Forsette?"
+           :re-index-multiple-windows-warning "Du må lukke de andre vinduene før du kan reindeksere denne grafen"
+           :save "Lagrer..."
+           :settings-of-plugins "Innstillinger for utvidelser"
+           :sync-from-local-changes-detected "Oppfrisk oppdager og prosesserer filer på disk som er modifiserte og avviker fra sideinnholdet som vises i Logseq. Fortsett?"
+           :type "Type"
+           :graph/persist "Logeq synkroniserer intern status, vennligst vent i flere sekunder."
+           :graph/persist-error "Intern status synk feilet"
+           :graph/save "Lagrer..."
+           :graph/save-error "Lagring feilet"
+           :graph/save-success "Lagring vellykket"
+           :page/copy-page-url "Kopier side URL"
+           :page/file-sync-versions "Versjoner av siden"
+           :page/open-backup-directory "Åpne mappe med sidens sikkerhetskopier"
+           :plugin/not-installed "Ikke installert"
+           :settings-page/edit-export-css "Rediger export.css"
+           :settings-page/network-proxy "Nettverksproxy"
+           :settings-page/plugin-system "System for utvidelser"}
 
 
    :pt-BR {:on-boarding/demo-graph "Esse é um gráfico de demonstração, mudanças não serão salvas enquanto uma pasta local não for aberta."
    :pt-BR {:on-boarding/demo-graph "Esse é um gráfico de demonstração, mudanças não serão salvas enquanto uma pasta local não for aberta."
            :on-boarding/add-graph "Adicionar gráfico"
            :on-boarding/add-graph "Adicionar gráfico"
@@ -2180,7 +2201,7 @@
 
 
            :file-sync/other-user-graph "O gráfico local atual é obrigado ao gráfico remoto de outro usuário. Portanto, não consigo iniciar a sincronização."
            :file-sync/other-user-graph "O gráfico local atual é obrigado ao gráfico remoto de outro usuário. Portanto, não consigo iniciar a sincronização."
            :file-sync/graph-deleted "O gráfico remoto atual foi excluído"
            :file-sync/graph-deleted "O gráfico remoto atual foi excluído"
-           
+
            :page/copy-page-url "Copiar URL da página"
            :page/copy-page-url "Copiar URL da página"
            :page/file-sync-versions "Versões da página"
            :page/file-sync-versions "Versões da página"
            :plugin/not-installed "Não instalado"
            :plugin/not-installed "Não instalado"
@@ -3732,7 +3753,7 @@
 
 
         :file-sync/other-user-graph "Geçerli yerel grafik, diğer kullanıcının uzak grafiğine bağlıdır. Bu yüzden senkronizasyon başlatılamıyor."
         :file-sync/other-user-graph "Geçerli yerel grafik, diğer kullanıcının uzak grafiğine bağlıdır. Bu yüzden senkronizasyon başlatılamıyor."
         :file-sync/graph-deleted "Geçerli uzak grafik silindi"}
         :file-sync/graph-deleted "Geçerli uzak grafik silindi"}
-   
+
    :ko {:tutorial/text #?(:cljs (rc/inline "tutorial-ko.md")
    :ko {:tutorial/text #?(:cljs (rc/inline "tutorial-ko.md")
                           :default "tutorial-ko.md")
                           :default "tutorial-ko.md")
         :tutorial/dummy-notes #?(:cljs (rc/inline "dummy-notes-ko.md")
         :tutorial/dummy-notes #?(:cljs (rc/inline "dummy-notes-ko.md")
@@ -3829,7 +3850,7 @@
         :page/show-journals "일지 보기"
         :page/show-journals "일지 보기"
         :page/show-name "페이지 이름 보기"
         :page/show-name "페이지 이름 보기"
         :page/hide-name "페이지 이름 숨기기"
         :page/hide-name "페이지 이름 숨기기"
-        :block/name "페이지 일므"
+        :block/name "페이지 이름"
         :page/last-modified "마지막 편집 시간:"
         :page/last-modified "마지막 편집 시간:"
         :page/new-title "새 문서의 제목이 무엇입니까?"
         :page/new-title "새 문서의 제목이 무엇입니까?"
         :page/earlier "이전"
         :page/earlier "이전"
@@ -4059,7 +4080,7 @@
         :file-sync/other-user-graph "현재 로컬 그래프가 다른 유저의 리모트 그래프와 충돌합니다. 동기화를 시작할 수 없습니다."
         :file-sync/other-user-graph "현재 로컬 그래프가 다른 유저의 리모트 그래프와 충돌합니다. 동기화를 시작할 수 없습니다."
         :file-sync/graph-deleted "현재 리모트 그래프를 삭제했습니다."
         :file-sync/graph-deleted "현재 리모트 그래프를 삭제했습니다."
         }
         }
-     
+
      :tongue/fallback :en})
      :tongue/fallback :en})
 
 
 (def languages [{:label "English" :value :en}
 (def languages [{:label "English" :value :en}

+ 36 - 20
src/main/frontend/extensions/code.css

@@ -13,16 +13,21 @@
     position: absolute;
     position: absolute;
     width: fit-content;
     width: fit-content;
     /* height:2rem; */
     /* height:2rem; */
-    order:3;
-    display: flex;
+    order: 3;
     left: auto;
     left: auto;
     right: 0;
     right: 0;
     /* margin-top: 4px; */
     /* margin-top: 4px; */
     z-index: 9999;
     z-index: 9999;
-  }
+    display: none;
+    padding: 4px 6px;
+    line-height: 1em;
+    opacity: .8;
 
 
-  .code-editor {
-      margin-top: 28px;
+    &:hover {
+      cursor: pointer;
+      opacity: 1;
+      user-select: none;
+    }
   }
   }
 
 
   &-calc {
   &-calc {
@@ -37,25 +42,36 @@
       font-family: Fira Code, Monaco, Menlo, Consolas, 'COURIER NEW', monospace;
       font-family: Fira Code, Monaco, Menlo, Consolas, 'COURIER NEW', monospace;
     }
     }
   }
   }
+
+  @screen md {
+    &-lang {
+      display: flex;
+    }
+  }
 }
 }
 
 
 .CodeMirror {
 .CodeMirror {
-    width: 100%;
-    font-family: Fira Code, Monaco, Menlo, Consolas, 'COURIER NEW', monospace;
-    border-radius: 2px;
-    line-height: 1.45em;
-
-    &:not(.CodeMirror-focused) {
-        .CodeMirror-activeline-background {
-            background: unset !important;
-        }
-    }
+  width: 100%;
+  font-family: Fira Code, Monaco, Menlo, Consolas, 'COURIER NEW', monospace;
+  border-radius: 2px;
+  line-height: 1.45em;
 
 
-    pre.CodeMirror-line {
-        box-shadow: none !important;
-    }
+  &-scroll {
+    padding-top: 18px;
+    padding-bottom: 62px;
+  }
 
 
-    .CodeMirror-hscrollbar {
-        cursor: pointer;
+  &:not(.CodeMirror-focused) {
+    .CodeMirror-activeline-background {
+      background: unset !important;
     }
     }
+  }
+
+  pre.CodeMirror-line {
+    box-shadow: none !important;
+  }
+
+  .CodeMirror-hscrollbar {
+    cursor: pointer;
+  }
 }
 }

+ 11 - 1
src/main/frontend/extensions/excalidraw.cljs

@@ -33,6 +33,15 @@
         (recur (.-parentNode el))))
         (recur (.-parentNode el))))
     state))
     state))
 
 
+(defn excalidraw-theme [ui-theme]
+  ;; One of these constants are meant to be used as a 'theme' argument for escalidraw:
+  ;; https://github.com/excalidraw/excalidraw/blob/master/src/constants.ts#L75
+  ;; But they are missing from the prod build of excalidraw we're using.
+  ;; They map to "light" and "dark", happens that :ui/theme uses same values, so we are safe to pass it directly, for now.
+  ;; Escalidraw may migrate to different values for these constants in future versions,
+  ;; so, in order to not watch out for it every time we bump a new version we better migrate to constants as soon as they appear in a prod build.
+  ui-theme)
+
 (rum/defcs draw-inner < rum/reactive
 (rum/defcs draw-inner < rum/reactive
   (rum/local 800 ::draw-width)
   (rum/local 800 ::draw-width)
   (rum/local true ::zen-mode?)
   (rum/local true ::zen-mode?)
@@ -89,7 +98,8 @@
            :zen-mode-enabled @*zen-mode?
            :zen-mode-enabled @*zen-mode?
            :view-mode-enabled @*view-mode?
            :view-mode-enabled @*view-mode?
            :grid-mode-enabled @*grid-mode?
            :grid-mode-enabled @*grid-mode?
-           :initial-data data}))]])))
+           :initial-data data
+           :theme (excalidraw-theme (state/sub :ui/theme))}))]])))
 
 
 (rum/defc draw
 (rum/defc draw
   [option]
   [option]

+ 7 - 6
src/main/frontend/extensions/html_parser.cljs

@@ -129,12 +129,13 @@
                            :a (let [href (:href attrs)
                            :a (let [href (:href attrs)
                                     label (or (map-join children) "")
                                     label (or (map-join children) "")
                                     has-img-tag? (util/safe-re-find #"\[:img" (str x))]
                                     has-img-tag? (util/safe-re-find #"\[:img" (str x))]
-                                (if has-img-tag?
-                                  (export-hiccup x)
-                                  (case format
-                                    :markdown (util/format "[%s](%s)" label href)
-                                    :org (util/format "[[%s][%s]]" href label)
-                                    nil)))
+                                (when-not (string/blank? href)
+                                  (if has-img-tag?
+                                    (export-hiccup x)
+                                    (case format
+                                      :markdown (util/format "[%s](%s)" label href)
+                                      :org (util/format "[[%s][%s]]" href label)
+                                      nil))))
                            :img (let [src (:src attrs)
                            :img (let [src (:src attrs)
                                       alt (or (:alt attrs) "")]
                                       alt (or (:alt attrs) "")]
                                   (case format
                                   (case format

+ 15 - 7
src/main/frontend/format/block.cljs

@@ -6,19 +6,27 @@
             [frontend.db :as db]
             [frontend.db :as db]
             [frontend.format :as format]
             [frontend.format :as format]
             [frontend.state :as state]
             [frontend.state :as state]
+            [frontend.handler.notification :as notification]
+            ["@sentry/react" :as Sentry]
             [logseq.graph-parser.config :as gp-config]
             [logseq.graph-parser.config :as gp-config]
             [logseq.graph-parser.property :as gp-property]
             [logseq.graph-parser.property :as gp-property]
             [logseq.graph-parser.mldoc :as gp-mldoc]))
             [logseq.graph-parser.mldoc :as gp-mldoc]))
 
 
 (defn extract-blocks
 (defn extract-blocks
-  "Wrapper around logseq.graph-parser.block/extract-blocks that adds in system state"
+  "Wrapper around logseq.graph-parser.block/extract-blocks that adds in system state
+and handles unexpected failure."
   [blocks content with-id? format]
   [blocks content with-id? format]
-  (gp-block/extract-blocks blocks content with-id? format
-                           {:user-config (state/get-config)
-                            :block-pattern (config/get-block-pattern format)
-                            :supported-formats (gp-config/supported-formats)
-                            :db (db/get-db (state/get-current-repo))
-                            :date-formatter (state/get-date-formatter)}))
+  (try
+    (gp-block/extract-blocks blocks content with-id? format
+                             {:user-config (state/get-config)
+                              :block-pattern (config/get-block-pattern format)
+                              :supported-formats (gp-config/supported-formats)
+                              :db (db/get-db (state/get-current-repo))
+                              :date-formatter (state/get-date-formatter)})
+    (catch :default e
+      (Sentry/captureException e)
+      (notification/show! "An unexpected error occurred during block extraction." :error)
+      [])))
 
 
 (defn page-name->map
 (defn page-name->map
   "Wrapper around logseq.graph-parser.block/page-name->map that adds in db"
   "Wrapper around logseq.graph-parser.block/page-name->map that adds in db"

+ 21 - 2
src/main/frontend/fs.cljs

@@ -12,7 +12,8 @@
             [promesa.core :as p]
             [promesa.core :as p]
             [frontend.db :as db]
             [frontend.db :as db]
             [clojure.string :as string]
             [clojure.string :as string]
-            [frontend.encrypt :as encrypt]))
+            [frontend.encrypt :as encrypt]
+            [frontend.state :as state]))
 
 
 (defonce nfs-record (nfs/->Nfs))
 (defonce nfs-record (nfs/->Nfs))
 (defonce bfs-record (bfs/->Bfs))
 (defonce bfs-record (bfs/->Bfs))
@@ -79,7 +80,17 @@
       (p/let [md-or-org? (contains? #{"md" "markdown" "org"} (util/get-file-ext path))
       (p/let [md-or-org? (contains? #{"md" "markdown" "org"} (util/get-file-ext path))
               content (if-not md-or-org? content (encrypt/encrypt content))]
               content (if-not md-or-org? content (encrypt/encrypt content))]
         (->
         (->
-         (p/let [_ (protocol/write-file! (get-fs dir) repo dir path content opts)]
+         (p/let [opts (assoc opts
+                             :error-handler
+                             (fn [error]
+                               (state/pub-event! [:instrument {:type :write-file/failed
+                                                               :payload {:fs (type fs-record)
+                                                                         :user-agent (when js/navigator js/navigator.userAgent)
+                                                                         :path path
+                                                                         :content-length (count content)
+                                                                         :error-str (str error)
+                                                                         :error error}}])))
+                 _ (protocol/write-file! (get-fs dir) repo dir path content opts)]
            (when (= bfs-record fs-record)
            (when (= bfs-record fs-record)
              (db/set-file-last-modified-at! repo (config/get-file-path repo path) (js/Date.))))
              (db/set-file-last-modified-at! repo (config/get-file-path repo path) (js/Date.))))
          (p/catch (fn [error]
          (p/catch (fn [error]
@@ -156,6 +167,10 @@
   [dir]
   [dir]
   (protocol/watch-dir! (get-record) dir))
   (protocol/watch-dir! (get-record) dir))
 
 
+(defn unwatch-dir!
+  [dir]
+  (protocol/unwatch-dir! (get-record) dir))
+
 (defn mkdir-if-not-exists
 (defn mkdir-if-not-exists
   [dir]
   [dir]
   (->
   (->
@@ -189,3 +204,7 @@
    (stat dir path)
    (stat dir path)
    (fn [_stat] true)
    (fn [_stat] true)
    (fn [_e] false)))
    (fn [_e] false)))
+
+(defn dir-exists?
+  [dir]
+  (file-exists? dir ""))

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

@@ -35,4 +35,6 @@
   (get-files [_this _path-or-handle _ok-handler]
   (get-files [_this _path-or-handle _ok-handler]
     nil)
     nil)
   (watch-dir! [_this _dir]
   (watch-dir! [_this _dir]
+    nil)
+  (unwatch-dir! [_this _dir]
     nil))
     nil))

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

@@ -298,4 +298,6 @@
   (watch-dir! [_this dir]
   (watch-dir! [_this dir]
     (p/do!
     (p/do!
      (.unwatch mobile-util/fs-watcher)
      (.unwatch mobile-util/fs-watcher)
-     (.watch mobile-util/fs-watcher #js {:path dir}))))
+     (.watch mobile-util/fs-watcher #js {:path dir})))
+  (unwatch-dir! [_this _dir]
+    (.unwatch mobile-util/fs-watcher)))

+ 4 - 2
src/main/frontend/fs/node.cljs

@@ -70,7 +70,7 @@
          (p/let [result (ipc/ipc "writeFile" repo path content)
          (p/let [result (ipc/ipc "writeFile" repo path content)
                  mtime (gobj/get result "mtime")]
                  mtime (gobj/get result "mtime")]
            (when-not contents-matched?
            (when-not contents-matched?
-             (ipc/ipc "backupDbFile" (config/get-local-dir repo) path db-content content))
+             (ipc/ipc "backupDbFile" (config/get-local-dir repo) path disk-content content))
            (db/set-file-last-modified-at! repo path mtime)
            (db/set-file-last-modified-at! repo path mtime)
            (p/let [content (if (encrypt/encrypted-db? (state/get-current-repo))
            (p/let [content (if (encrypt/encrypted-db? (state/get-current-repo))
                              (encrypt/decrypt content)
                              (encrypt/decrypt content)
@@ -127,4 +127,6 @@
   (get-files [_this path-or-handle _ok-handler]
   (get-files [_this path-or-handle _ok-handler]
     (ipc/ipc "getFiles" path-or-handle))
     (ipc/ipc "getFiles" path-or-handle))
   (watch-dir! [_this dir]
   (watch-dir! [_this dir]
-    (ipc/ipc "addDirWatcher" dir)))
+    (ipc/ipc "addDirWatcher" dir))
+  (unwatch-dir! [_this dir]
+    (ipc/ipc "unwatchDir" dir)))

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

@@ -15,7 +15,8 @@
   (stat [this dir path])
   (stat [this dir path])
   (open-dir [this ok-handler])
   (open-dir [this ok-handler])
   (get-files [this path-or-handle ok-handler])
   (get-files [this path-or-handle ok-handler])
-  (watch-dir! [this dir]) 
+  (watch-dir! [this dir])
+  (unwatch-dir! [this dir])
   ;; Ensure the dir is watched, window agnostic.
   ;; Ensure the dir is watched, window agnostic.
   ;; Implementation should handle the actual watcher's construction / destruction.
   ;; Implementation should handle the actual watcher's construction / destruction.
   ;; So shouldn't consider `unwatch-dir!`.
   ;; So shouldn't consider `unwatch-dir!`.

+ 10 - 12
src/main/frontend/fs/watcher_handler.cljs

@@ -14,7 +14,8 @@
             [electron.ipc :as ipc]
             [electron.ipc :as ipc]
             [promesa.core :as p]
             [promesa.core :as p]
             [frontend.state :as state]
             [frontend.state :as state]
-            [frontend.encrypt :as encrypt]))
+            [frontend.encrypt :as encrypt]
+            [frontend.fs :as fs]))
 
 
 ;; all IPC paths must be normalized! (via gp-util/path-normalize)
 ;; all IPC paths must be normalized! (via gp-util/path-normalize)
 
 
@@ -53,15 +54,10 @@
                  (not (:encryption/graph-parsing? @state/state)))
                  (not (:encryption/graph-parsing? @state/state)))
         (cond
         (cond
           (and (= "unlinkDir" type) dir)
           (and (= "unlinkDir" type) dir)
-          (do
-            (state/pub-event! [:notification/show
-                               {:content (str "The directory " dir " has been renamed or deleted, the editor will be disabled for this graph, you can unlink the graph.")
-                                :status :error
-                                :clear? false}])
-            (state/update-state! :file/unlinked-dirs (fn [dirs] (conj dirs dir))))
+          (state/pub-event! [:graph/dir-gone dir])
 
 
-          (= "addDir" type)
-          (state/update-state! :file/unlinked-dirs (fn [dirs] (disj dirs dir)))
+          (and (= "addDir" type) dir)
+          (state/pub-event! [:graph/dir-back repo dir])
 
 
           (contains? (:file/unlinked-dirs @state/state) dir)
           (contains? (:file/unlinked-dirs @state/state) dir)
           nil
           nil
@@ -90,9 +86,11 @@
 
 
           (and (= "unlink" type)
           (and (= "unlink" type)
                (db/file-exists? repo path))
                (db/file-exists? repo path))
-          (when-let [page-name (db/get-file-page path)]
-            (println "Delete page: " page-name ", file path: " path ".")
-            (page-handler/delete! page-name #() :delete-file? false))
+          (p/let [dir-exists? (fs/file-exists? dir "")]
+            (when dir-exists?
+              (when-let [page-name (db/get-file-page path)]
+                (println "Delete page: " page-name ", file path: " path ".")
+                (page-handler/delete! page-name #() :unlink-file? true))))
 
 
           (and (contains? #{"add" "change" "unlink"} type)
           (and (contains? #{"add" "change" "unlink"} type)
                (string/ends-with? path "logseq/custom.css"))
                (string/ends-with? path "logseq/custom.css"))

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

@@ -6,7 +6,7 @@
             [frontend.config :as config]
             [frontend.config :as config]
             [frontend.context.i18n :as i18n]
             [frontend.context.i18n :as i18n]
             [frontend.db :as db]
             [frontend.db :as db]
-            [logseq.graph-parser.db.schema :as db-schema]
+            [logseq.db.schema :as db-schema]
             [frontend.db.conn :as conn]
             [frontend.db.conn :as conn]
             [frontend.db.react :as react]
             [frontend.db.react :as react]
             [frontend.error :as error]
             [frontend.error :as error]

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

@@ -172,7 +172,8 @@
   (when-let [touches (.-targetTouches event)]
   (when-let [touches (.-targetTouches event)]
     (let [selection-type (.-type (.getSelection js/document))]
     (let [selection-type (.-type (.getSelection js/document))]
       (when-not (= selection-type "Range")
       (when-not (= selection-type "Range")
-        (when (< (- (js/Date.now) @*touch-start) 600)
+        (when (or (not (state/sub :editor/editing?))
+                  (< (- (js/Date.now) @*touch-start) 600))
           (when (and (= (.-length touches) 1) @*swipe)
           (when (and (= (.-length touches) 1) @*swipe)
             (let [{:keys [x0 xi direction]} @*swipe
             (let [{:keys [x0 xi direction]} @*swipe
                   touch (aget touches 0)
                   touch (aget touches 0)

+ 34 - 31
src/main/frontend/handler/editor.cljs

@@ -13,7 +13,7 @@
             [frontend.config :as config]
             [frontend.config :as config]
             [frontend.date :as date]
             [frontend.date :as date]
             [frontend.db :as db]
             [frontend.db :as db]
-            [logseq.graph-parser.db.schema :as db-schema]
+            [logseq.db.schema :as db-schema]
             [frontend.db.model :as db-model]
             [frontend.db.model :as db-model]
             [frontend.db.utils :as db-utils]
             [frontend.db.utils :as db-utils]
             [frontend.diff :as diff]
             [frontend.diff :as diff]
@@ -1060,8 +1060,8 @@
   (when-let [blocks (seq (get-selected-blocks))]
   (when-let [blocks (seq (get-selected-blocks))]
     ;; remove embeds, references and queries
     ;; remove embeds, references and queries
     (let [dom-blocks (remove (fn [block]
     (let [dom-blocks (remove (fn [block]
-                           (or (= "true" (dom/attr block "data-transclude"))
-                               (= "true" (dom/attr block "data-query")))) blocks)]
+                              (or (= "true" (dom/attr block "data-transclude"))
+                                  (= "true" (dom/attr block "data-query")))) blocks)]
       (when (seq dom-blocks)
       (when (seq dom-blocks)
         (let [repo (state/get-current-repo)
         (let [repo (state/get-current-repo)
               block-uuids (distinct (map #(uuid (dom/attr % "blockid")) dom-blocks))
               block-uuids (distinct (map #(uuid (dom/attr % "blockid")) dom-blocks))
@@ -1940,7 +1940,8 @@
               blocks' (map (fn [block]
               blocks' (map (fn [block]
                              (paste-block-cleanup block page exclude-properties format content-update-fn))
                              (paste-block-cleanup block page exclude-properties format content-update-fn))
                         blocks)
                         blocks)
-              result (outliner-core/insert-blocks! blocks' target-block {:sibling? sibling?})]
+              result (outliner-core/insert-blocks! blocks' target-block {:sibling? sibling?
+                                                                         :outliner-op :paste})]
           (edit-last-block-after-inserted! result))))))
           (edit-last-block-after-inserted! result))))))
 
 
 (defn- block-tree->blocks
 (defn- block-tree->blocks
@@ -2914,11 +2915,11 @@
       (do
       (do
         (util/stop e)
         (util/stop e)
         (let [blocks (or
         (let [blocks (or
-                     (:copy/full-blocks copied-blocks)
-                     (get-all-blocks-by-ids (state/get-current-repo) copied-block-ids))]
-         (when (seq blocks)
-           (state/set-copied-full-blocks! blocks)
-           (paste-blocks blocks {}))))
+                      (:copy/full-blocks copied-blocks)
+                      (get-all-blocks-by-ids (state/get-current-repo) copied-block-ids))]
+          (when (seq blocks)
+            (state/set-copied-full-blocks! blocks)
+            (paste-blocks blocks {}))))
 
 
       (and (gp-util/url? text)
       (and (gp-util/url? text)
            (not (string/blank? (util/get-selected-text))))
            (not (string/blank? (util/get-selected-text))))
@@ -2935,35 +2936,28 @@
       :else
       :else
       ;; from external
       ;; from external
       (let [format (or (db/get-page-format (state/get-current-page)) :markdown)]
       (let [format (or (db/get-page-format (state/get-current-page)) :markdown)]
+        (util/stop e)
         (match [format
         (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 #"(?m)^\s*\*+\s+" text))
                 (nil? (util/safe-re-find #"(?m)^\s*\*+\s+" text))
                 (nil? (util/safe-re-find #"(?:\r?\n){2,}" text))]
                 (nil? (util/safe-re-find #"(?:\r?\n){2,}" text))]
           [:markdown false _ _]
           [:markdown false _ _]
-          (do
-            (util/stop e)
-            (paste-text-parseable format text))
+          (paste-text-parseable format text)
 
 
           [:org _ false _]
           [:org _ false _]
-          (do
-            (util/stop e)
-            (paste-text-parseable format text))
+          (paste-text-parseable format text)
 
 
           [:markdown true _ false]
           [:markdown true _ false]
-          (do
-            (util/stop e)
-            (paste-segmented-text format text))
+          (paste-segmented-text format text)
 
 
           [:markdown true _ true]
           [:markdown true _ true]
-          nil
+          (commands/simple-insert! (state/get-edit-input-id) text nil)
 
 
           [:org _ true false]
           [:org _ true false]
-          (do
-            (util/stop e)
-            (paste-segmented-text format text))
+          (paste-segmented-text format text)
 
 
           [:org _ true true]
           [:org _ true true]
-          nil)))))
+          (commands/simple-insert! (state/get-edit-input-id) text nil))))))
 
 
 (defn paste-text-in-one-block-at-point
 (defn paste-text-in-one-block-at-point
   []
   []
@@ -2986,7 +2980,7 @@
           edit-block (state/get-edit-block)
           edit-block (state/get-edit-block)
           format (or (:block/format edit-block) :markdown)
           format (or (:block/format edit-block) :markdown)
           initial-text (.getData clipboard-data "text")
           initial-text (.getData clipboard-data "text")
-          text (or (when (string/blank? html)
+          text (or (when-not (string/blank? html)
                      (html-parser/convert format html))
                      (html-parser/convert format html))
                    initial-text)
                    initial-text)
           input (state/get-input)]
           input (state/get-input)]
@@ -3026,18 +3020,25 @@
 
 
 ;; credits to @pengx17
 ;; credits to @pengx17
 (defn- copy-current-block-ref
 (defn- copy-current-block-ref
-  []
+  [format]
   (when-let [current-block (state/get-edit-block)]
   (when-let [current-block (state/get-edit-block)]
     (when-let [block-id (:block/uuid current-block)]
     (when-let [block-id (:block/uuid current-block)]
-      (copy-block-ref! block-id #(str "((" % "))"))
+      (if (= format "embed")
+       (copy-block-ref! block-id #(str "{{embed ((" % "))}}"))
+       (copy-block-ref! block-id #(str "((" % "))")))
       (notification/show!
       (notification/show!
        [:div
        [:div
-        [:span.mb-1.5 "Block ref copied!"]
-        [:div [:code.whitespace-nowrap (str "((" block-id "))")]]]
+        [:span.mb-1.5 (str "Block " format " copied!")]
+        [:div [:code.whitespace.break-all (if (= format "embed")
+                                         (str "{{embed ((" block-id "))}}")
+                                         (str "((" block-id "))"))]]]
        :success true
        :success true
        ;; use uuid to make sure there is only one toast a time
        ;; use uuid to make sure there is only one toast a time
        (str "copied-block-ref:" block-id)))))
        (str "copied-block-ref:" block-id)))))
 
 
+(defn copy-current-block-embed []
+  (copy-current-block-ref "embed"))
+
 (defn shortcut-copy
 (defn shortcut-copy
   "shortcut copy action:
   "shortcut copy action:
   * when in selection mode, copy selected blocks
   * when in selection mode, copy selected blocks
@@ -3054,7 +3055,7 @@
             selected-start (util/get-selection-start input)
             selected-start (util/get-selection-start input)
             selected-end (util/get-selection-end input)]
             selected-end (util/get-selection-end input)]
         (if (= selected-start selected-end)
         (if (= selected-start selected-end)
-          (copy-current-block-ref)
+          (copy-current-block-ref "ref")
           (js/document.execCommand "copy")))
           (js/document.execCommand "copy")))
 
 
       :else
       :else
@@ -3131,9 +3132,9 @@
           ;; if the move is to cross block boundary, select the whole block
           ;; if the move is to cross block boundary, select the whole block
          (or (and (= direction :up) (cursor/textarea-cursor-rect-first-row? cursor-rect))
          (or (and (= direction :up) (cursor/textarea-cursor-rect-first-row? cursor-rect))
              (and (= direction :down) (cursor/textarea-cursor-rect-last-row? cursor-rect)))
              (and (= direction :down) (cursor/textarea-cursor-rect-last-row? cursor-rect)))
-          (select-block-up-down direction)
+         (select-block-up-down direction)
           ;; simulate text selection
           ;; simulate text selection
-          (cursor/select-up-down input direction anchor cursor-rect)))
+         (cursor/select-up-down input direction anchor cursor-rect)))
       (select-block-up-down direction))))
       (select-block-up-down direction))))
 
 
 (defn open-selected-block!
 (defn open-selected-block!
@@ -3206,6 +3207,8 @@
     (util/forward-kill-word input)
     (util/forward-kill-word input)
     (state/set-edit-content! (state/get-edit-input-id) (.-value input))))
     (state/set-edit-content! (state/get-edit-input-id) (.-value input))))
 
 
+
+
 (defn block-with-title?
 (defn block-with-title?
   [format content semantic?]
   [format content semantic?]
   (and (string/includes? content "\n")
   (and (string/includes? content "\n")

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

@@ -13,7 +13,7 @@
             [frontend.config :as config]
             [frontend.config :as config]
             [frontend.context.i18n :refer [t]]
             [frontend.context.i18n :refer [t]]
             [frontend.db :as db]
             [frontend.db :as db]
-            [logseq.graph-parser.db.schema :as db-schema]
+            [logseq.db.schema :as db-schema]
             [frontend.encrypt :as encrypt]
             [frontend.encrypt :as encrypt]
             [frontend.extensions.srs :as srs]
             [frontend.extensions.srs :as srs]
             [frontend.fs :as fs]
             [frontend.fs :as fs]
@@ -240,6 +240,11 @@
     (state/set-modal! #(git-component/file-specific-version path hash content))))
     (state/set-modal! #(git-component/file-specific-version path hash content))))
 
 
 (defmethod handle :graph/ready [[_ repo]]
 (defmethod handle :graph/ready [[_ repo]]
+  (when (config/local-db? repo)
+    (p/let [dir (config/get-repo-dir repo)
+            dir-exists? (fs/dir-exists? dir)]
+      (when-not dir-exists?
+        (state/pub-event! [:graph/dir-gone dir]))))
   (search-handler/rebuild-indices-when-stale! repo)
   (search-handler/rebuild-indices-when-stale! repo)
   (repo-handler/graph-ready! repo))
   (repo-handler/graph-ready! repo))
 
 
@@ -436,6 +441,24 @@
   (route-handler/redirect! {:to :whiteboard
   (route-handler/redirect! {:to :whiteboard
                             :path-params {:name link}}))
                             :path-params {:name link}}))
 
 
+(defmethod handle :graph/dir-gone [[_ dir]]
+  (state/pub-event! [:notification/show
+                     {:content (str "The directory " dir " has been renamed or deleted, the editor will be disabled for this graph, you can unlink the graph.")
+                      :status :error
+                      :clear? false}])
+  (state/update-state! :file/unlinked-dirs (fn [dirs] (conj dirs dir))))
+
+(defmethod handle :graph/dir-back [[_ repo dir]]
+  (when (contains? (:file/unlinked-dirs @state/state) dir)
+    (notification/clear-all!)
+    (state/pub-event! [:notification/show
+                       {:content (str "The directory " dir " has been back, you can edit your graph now.")
+                        :status :success
+                        :clear? true}])
+    (state/update-state! :file/unlinked-dirs (fn [dirs] (disj dirs dir))))
+  (when (= dir (config/get-repo-dir repo))
+    (fs/watch-dir! dir)))
+
 (defn run!
 (defn run!
   []
   []
   (let [chan (state/get-events-chan)]
   (let [chan (state/get-events-chan)]

+ 40 - 16
src/main/frontend/handler/export.cljs

@@ -1,5 +1,6 @@
 (ns frontend.handler.export
 (ns frontend.handler.export
-  (:require [cljs.pprint :as pprint]
+  (:require ["@capacitor/filesystem" :refer [Encoding Filesystem]]
+            [cljs.pprint :as pprint]
             [clojure.set :as s]
             [clojure.set :as s]
             [clojure.string :as string]
             [clojure.string :as string]
             [clojure.walk :as walk]
             [clojure.walk :as walk]
@@ -9,19 +10,23 @@
             [frontend.extensions.zip :as zip]
             [frontend.extensions.zip :as zip]
             [frontend.external.roam-export :as roam-export]
             [frontend.external.roam-export :as roam-export]
             [frontend.format :as f]
             [frontend.format :as f]
+            [frontend.format.mldoc :as mldoc]
             [frontend.format.protocol :as fp]
             [frontend.format.protocol :as fp]
+            [frontend.mobile.util :as mobile-util]
             [frontend.modules.file.core :as outliner-file]
             [frontend.modules.file.core :as outliner-file]
             [frontend.modules.outliner.tree :as outliner-tree]
             [frontend.modules.outliner.tree :as outliner-tree]
             [frontend.publishing.html :as html]
             [frontend.publishing.html :as html]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.util :as util]
-            [frontend.format.mldoc :as mldoc]
+            [frontend.util.property :as property]
+            [goog.dom :as gdom]
+            [lambdaisland.glogi :as log]
             [logseq.graph-parser.mldoc :as gp-mldoc]
             [logseq.graph-parser.mldoc :as gp-mldoc]
             [logseq.graph-parser.util :as gp-util]
             [logseq.graph-parser.util :as gp-util]
-            [goog.dom :as gdom]
             [promesa.core :as p]
             [promesa.core :as p]
-            [frontend.util.property :as property])
-  (:import [goog.string StringBuffer]))
+            [frontend.handler.notification :as notification])
+  (:import
+   [goog.string StringBuffer]))
 
 
 (defn- get-page-content
 (defn- get-page-content
   [repo page]
   [repo page]
@@ -385,6 +390,18 @@
                                 :format (gp-util/get-format path)})))))
                                 :format (gp-util/get-format path)})))))
 
 
 
 
+(defn- export-file-on-mobile [data path]
+  (p/catch
+      (.writeFile Filesystem (clj->js {:path path
+                                       :data data
+                                       :encoding (.-UTF8 Encoding)
+                                       :recursive true}))
+      (notification/show! "Export succeeded! You can find you exported file in the root directory of your graph." :success)
+    (fn [error]
+        (notification/show! "Export failed!" :error)
+        (log/error :export-file-failed error))))
+
+
 (defn export-repo-as-markdown!
 (defn export-repo-as-markdown!
   [repo]
   [repo]
   (when-let [files (get-file-contents-with-suffix repo)]
   (when-let [files (get-file-contents-with-suffix repo)]
@@ -479,13 +496,17 @@
 
 
 (defn export-repo-as-edn-v2!
 (defn export-repo-as-edn-v2!
   [repo]
   [repo]
-  (when-let [data-str (some->> (export-repo-as-edn-str repo)
-                               js/encodeURIComponent
-                               (str "data:text/edn;charset=utf-8,"))]
-    (when-let [anchor (gdom/getElement "download-as-edn-v2")]
-      (.setAttribute anchor "href" data-str)
-      (.setAttribute anchor "download" (file-name repo :edn))
-      (.click anchor))))
+  (when-let [edn-str (export-repo-as-edn-str repo)]
+    (let [data-str (some->> edn-str
+                            js/encodeURIComponent
+                            (str "data:text/edn;charset=utf-8,"))
+          filename (file-name repo :edn)]
+     (if (mobile-util/native-platform?)
+       (export-file-on-mobile edn-str filename)
+       (when-let [anchor (gdom/getElement "download-as-edn-v2")]
+         (.setAttribute anchor "href" data-str)
+         (.setAttribute anchor "download" filename)
+         (.click anchor))))))
 
 
 (defn- nested-update-id
 (defn- nested-update-id
   [vec-tree]
   [vec-tree]
@@ -504,12 +525,15 @@
               nested-update-id
               nested-update-id
               clj->js
               clj->js
               js/JSON.stringify)
               js/JSON.stringify)
+          filename (file-name repo :json)
           data-str (str "data:text/json;charset=utf-8,"
           data-str (str "data:text/json;charset=utf-8,"
                         (js/encodeURIComponent json-str))]
                         (js/encodeURIComponent json-str))]
-      (when-let [anchor (gdom/getElement "download-as-json-v2")]
-        (.setAttribute anchor "href" data-str)
-        (.setAttribute anchor "download" (file-name repo :json))
-        (.click anchor)))))
+      (if (mobile-util/native-platform?)
+        (export-file-on-mobile json-str filename)
+        (when-let [anchor (gdom/getElement "download-as-json-v2")]
+          (.setAttribute anchor "href" data-str)
+          (.setAttribute anchor "download" filename)
+          (.click anchor))))))
 
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Export to roam json ;;
 ;; Export to roam json ;;

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

@@ -155,16 +155,18 @@
                       #(p/resolved nil)
                       #(p/resolved nil)
                       #(fs/write-file! repo (config/get-repo-dir repo) path content
                       #(fs/write-file! repo (config/get-repo-dir repo) path content
                                        (assoc (when original-content {:old-content original-content})
                                        (assoc (when original-content {:old-content original-content})
-                                              :skip-compare? skip-compare?)))]
+                                              :skip-compare? skip-compare?)))
+        opts {:new-graph? new-graph?
+              :from-disk? from-disk?}]
     (if reset?
     (if reset?
       (do
       (do
         (when-let [page-id (db/get-file-page-id path)]
         (when-let [page-id (db/get-file-page-id path)]
           (db/transact! repo
           (db/transact! repo
             [[:db/retract page-id :block/alias]
             [[:db/retract page-id :block/alias]
-             [:db/retract page-id :block/tags]]))
-        (reset-file! repo path content {:new-graph? new-graph?
-                                        :from-disk? from-disk?}))
-      (db/set-file-content! repo path content))
+             [:db/retract page-id :block/tags]]
+            opts))
+        (reset-file! repo path content opts))
+      (db/set-file-content! repo path content opts))
     (util/p-handle (write-file!)
     (util/p-handle (write-file!)
                    (fn [_]
                    (fn [_]
                      (when (= path (config/get-config-path repo))
                      (when (= path (config/get-config-path repo))
@@ -258,6 +260,7 @@
   []
   []
   (when-let [repo (state/get-current-repo)]
   (when-let [repo (state/get-current-repo)]
     (when-let [dir (config/get-repo-dir repo)]
     (when-let [dir (config/get-repo-dir repo)]
+      (fs/unwatch-dir! dir)
       (fs/watch-dir! dir))))
       (fs/watch-dir! dir))))
 
 
 (defn create-metadata-file
 (defn create-metadata-file

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

@@ -2,7 +2,7 @@
   (:require [clojure.set :as set]
   (:require [clojure.set :as set]
             [clojure.string :as string]
             [clojure.string :as string]
             [frontend.db :as db]
             [frontend.db :as db]
-            [logseq.graph-parser.db.default :as default-db]
+            [logseq.db.default :as default-db]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.util :as util]))
             [frontend.util :as util]))
 
 

+ 15 - 14
src/main/frontend/handler/metadata.cljs

@@ -41,20 +41,21 @@
 
 
 (defn set-pages-metadata!
 (defn set-pages-metadata!
   [repo]
   [repo]
-  (let [path (config/get-pages-metadata-path repo)
-        all-pages (->> (db/get-all-pages repo)
-                       (common-handler/fix-pages-timestamps)
-                       (map #(select-keys % [:block/name :block/created-at :block/updated-at]))
-                       (sort-by :block/name)
-                       (vec))]
-    (p/let [_ (-> (file-handler/create-pages-metadata-file repo)
-                  (p/catch (fn [] nil)))]
-      (let [new-content (with-out-str (cljs.pprint/pprint all-pages))]
-        (fs/write-file! repo
-                        (config/get-repo-dir repo)
-                        path
-                        new-content
-                        {})))))
+  (when-not (state/unlinked-dir? (config/get-repo-dir repo))
+    (let [path (config/get-pages-metadata-path repo)
+          all-pages (->> (db/get-all-pages repo)
+                         (common-handler/fix-pages-timestamps)
+                         (map #(select-keys % [:block/name :block/created-at :block/updated-at]))
+                         (sort-by :block/name)
+                         (vec))]
+      (p/let [_ (-> (file-handler/create-pages-metadata-file repo)
+                    (p/catch (fn [] nil)))]
+        (let [new-content (with-out-str (cljs.pprint/pprint all-pages))]
+          (fs/write-file! repo
+                          (config/get-repo-dir repo)
+                          path
+                          new-content
+                          {}))))))
 
 
 (defn set-db-encrypted-secret!
 (defn set-db-encrypted-secret!
   [encrypted-secret]
   [encrypted-secret]

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

@@ -7,6 +7,10 @@
   (let [contents (state/get-notification-contents)]
   (let [contents (state/get-notification-contents)]
     (state/set-state! :notification/contents (dissoc contents uid))))
     (state/set-state! :notification/contents (dissoc contents uid))))
 
 
+(defn clear-all!
+  []
+  (state/set-state! :notification/contents nil))
+
 (defn show!
 (defn show!
   ([content status]
   ([content status]
    (show! content status true nil 1500))
    (show! content status true nil 1500))

+ 12 - 10
src/main/frontend/handler/page.cljs

@@ -7,7 +7,7 @@
             [frontend.config :as config]
             [frontend.config :as config]
             [frontend.date :as date]
             [frontend.date :as date]
             [frontend.db :as db]
             [frontend.db :as db]
-            [logseq.graph-parser.db.schema :as db-schema]
+            [logseq.db.schema :as db-schema]
             [frontend.db.model :as model]
             [frontend.db.model :as model]
             [frontend.db.utils :as db-utils]
             [frontend.db.utils :as db-utils]
             [frontend.db.conn :as conn]
             [frontend.db.conn :as conn]
@@ -160,19 +160,21 @@
      page-name)))
      page-name)))
 
 
 (defn delete-file!
 (defn delete-file!
-  [repo page-name]
+  [repo page-name unlink-file?]
   (let [file (db/get-page-file page-name)
   (let [file (db/get-page-file page-name)
         file-path (:file/path file)]
         file-path (:file/path file)]
     ;; delete file
     ;; delete file
     (when-not (string/blank? file-path)
     (when-not (string/blank? file-path)
       (db/transact! [[:db.fn/retractEntity [:file/path file-path]]])
       (db/transact! [[:db.fn/retractEntity [:file/path file-path]]])
-      (->
-       (p/let [_ (and (config/local-db? repo)
-                      (mobile-util/native-platform?)
-                      (fs/delete-file! repo file-path file-path {}))
-               _ (fs/unlink! repo (config/get-repo-path repo file-path) nil)])
-       (p/catch (fn [err]
-                  (js/console.error "error: " err)))))))
+      (when unlink-file?
+        (->
+         (p/let [_ (and (config/local-db? repo)
+                        (mobile-util/native-platform?)
+                        ;; TODO: @leizhe remove fs/delete-file! and use fs/unlink!
+                        (fs/delete-file! repo file-path file-path {}))
+                 _ (fs/unlink! repo (config/get-repo-path repo file-path) nil)])
+         (p/catch (fn [err]
+                    (js/console.error "error: " err))))))))
 
 
 (defn- compute-new-file-path
 (defn- compute-new-file-path
   [old-path new-name]
   [old-path new-name]
@@ -316,7 +318,7 @@
             page (db/entity [:block/name page-name])]
             page (db/entity [:block/name page-name])]
         (db/transact! tx-data)
         (db/transact! tx-data)
 
 
-        (when delete-file? (delete-file! repo page-name))
+        (delete-file! repo page-name delete-file?)
 
 
         ;; if other page alias this pagename,
         ;; if other page alias this pagename,
         ;; then just remove some attrs of this entity instead of retractEntity
         ;; then just remove some attrs of this entity instead of retractEntity

+ 22 - 19
src/main/frontend/handler/plugin.cljs

@@ -55,12 +55,13 @@
   (if (or refresh? (nil? (:plugin/marketplace-pkgs @state/state)))
   (if (or refresh? (nil? (:plugin/marketplace-pkgs @state/state)))
     (p/create
     (p/create
       (fn [resolve reject]
       (fn [resolve reject]
-        (-> (util/fetch plugins-url
-                        (fn [res]
-                          (let [pkgs (:packages res)]
-                            (state/set-state! :plugin/marketplace-pkgs pkgs)
-                            (resolve pkgs)))
-                        reject)
+        (-> (ipc/ipc :httpFetchJSON plugins-url)
+            (p/then (fn [res]
+                      (if-let [res (and res (bean/->clj res))]
+                        (let [pkgs (:packages res)]
+                          (state/set-state! :plugin/marketplace-pkgs pkgs)
+                          (resolve pkgs))
+                        (reject nil))))
             (p/catch reject))))
             (p/catch reject))))
     (p/resolved (:plugin/marketplace-pkgs @state/state))))
     (p/resolved (:plugin/marketplace-pkgs @state/state))))
 
 
@@ -69,18 +70,20 @@
   (if (or refresh? (nil? (:plugin/marketplace-stats @state/state)))
   (if (or refresh? (nil? (:plugin/marketplace-stats @state/state)))
     (p/create
     (p/create
       (fn [resolve reject]
       (fn [resolve reject]
-        (util/fetch stats-url
-                    (fn [res]
-                      (when res
-                        (state/set-state!
-                          :plugin/marketplace-stats
-                          (into {} (map (fn [[k stat]]
-                                          [k (assoc stat
-                                               :total_downloads
-                                               (reduce (fn [a b] (+ a (get b 2))) 0 (:releases stat)))])
-                                        res)))
-                        (resolve nil)))
-                    reject)))
+        (-> (ipc/ipc :httpFetchJSON stats-url)
+            (p/then (fn [^js res]
+                      (if-let [res (and res (bean/->clj res))]
+                        (do
+                          (state/set-state!
+                           :plugin/marketplace-stats
+                           (into {} (map (fn [[k stat]]
+                                           [k (assoc stat
+                                                     :total_downloads
+                                                     (reduce (fn [a b] (+ a (get b 2))) 0 (:releases stat)))])
+                                         res)))
+                          (resolve nil))
+                        (reject nil))))
+            (p/catch reject))))
     (p/resolved nil)))
     (p/resolved nil)))
 
 
 (defn installed?
 (defn installed?
@@ -605,7 +608,7 @@
                                             (state/set-custom-theme! mode theme)
                                             (state/set-custom-theme! mode theme)
                                             (state/set-theme-mode! mode))
                                             (state/set-theme-mode! mode))
                                           (state/set-state! :plugin/selected-theme url))))
                                           (state/set-state! :plugin/selected-theme url))))
-                                        
+
                 (.on "reset-custom-theme" (fn [^js themes]
                 (.on "reset-custom-theme" (fn [^js themes]
                                             (let [themes (bean/->clj themes)
                                             (let [themes (bean/->clj themes)
                                                   custom-theme (dissoc themes :mode)
                                                   custom-theme (dissoc themes :mode)

+ 20 - 17
src/main/frontend/handler/repo.cljs

@@ -169,7 +169,7 @@
                                          (not= updated-at (:block/created-at page)))))) metadata)
                                          (not= updated-at (:block/created-at page)))))) metadata)
                            (remove nil?))]
                            (remove nil?))]
              (when (seq metadata)
              (when (seq metadata)
-               (db/transact! repo metadata))))))
+               (db/transact! repo metadata {:new-graph? true}))))))
      (catch js/Error e
      (catch js/Error e
        (log/error :exception e)))))
        (log/error :exception e)))))
 
 
@@ -180,7 +180,7 @@
         files [{:file/path path
         files [{:file/path path
                 :file/content content}]
                 :file/content content}]
         file-paths [path]]
         file-paths [path]]
-    (util/profile "update-pages-metadata!" (load-pages-metadata! repo file-paths files force?))))
+    (load-pages-metadata! repo file-paths files force?)))
 
 
 (defn- parse-and-load-file!
 (defn- parse-and-load-file!
   [repo-url file new-graph?]
   [repo-url file new-graph?]
@@ -381,26 +381,29 @@
 
 
 (defn rebuild-index!
 (defn rebuild-index!
   [url]
   [url]
-  (when url
-    (search/reset-indice! url)
-    (db/remove-conn! url)
-    (db/clear-query-state!)
-    (-> (p/do! (db-persist/delete-graph! url))
-        (p/catch (fn [error]
-                   (prn "Delete repo failed, error: " error))))))
+  (when-not (state/unlinked-dir? (config/get-repo-dir url))
+    (when url
+      (search/reset-indice! url)
+      (db/remove-conn! url)
+      (db/clear-query-state!)
+      (-> (p/do! (db-persist/delete-graph! url))
+          (p/catch (fn [error]
+                     (prn "Delete repo failed, error: " error)))))))
 
 
 (defn re-index!
 (defn re-index!
   [nfs-rebuild-index! ok-handler]
   [nfs-rebuild-index! ok-handler]
-  (route-handler/redirect-to-home!)
   (when-let [repo (state/get-current-repo)]
   (when-let [repo (state/get-current-repo)]
-    (let [local? (config/local-db? repo)]
-      (if local?
-        (p/let [_ (metadata-handler/set-pages-metadata! repo)]
-          (nfs-rebuild-index! repo ok-handler))
-        (rebuild-index! repo))
-      (js/setTimeout
+    (let [dir (config/get-repo-dir repo)]
+      (when-not (state/unlinked-dir? dir)
        (route-handler/redirect-to-home!)
        (route-handler/redirect-to-home!)
-       500))))
+       (let [local? (config/local-db? repo)]
+         (if local?
+           (p/let [_ (metadata-handler/set-pages-metadata! repo)]
+             (nfs-rebuild-index! repo ok-handler))
+           (rebuild-index! repo))
+         (js/setTimeout
+          (route-handler/redirect-to-home!)
+          500))))))
 
 
 (defn persist-db!
 (defn persist-db!
   ([]
   ([]

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

@@ -268,7 +268,8 @@
                       (repo-handler/load-repo-to-db! repo
                       (repo-handler/load-repo-to-db! repo
                                                      {:diffs     diffs
                                                      {:diffs     diffs
                                                       :nfs-files modified-files
                                                       :nfs-files modified-files
-                                                      :refresh? (not re-index?)}))
+                                                      :refresh? (not re-index?)
+                                                      :new-graph? re-index?}))
                     (when (and (util/electron?) (not re-index?))
                     (when (and (util/electron?) (not re-index?))
                       (db/transact! repo new-files))))))))
                       (db/transact! repo new-files))))))))
 
 
@@ -326,9 +327,11 @@
             _ (ok-handler)]
             _ (ok-handler)]
       (state/set-nfs-refreshing! false))))
       (state/set-nfs-refreshing! false))))
 
 
+;; TODO: move to frontend.handler.repo
 (defn refresh!
 (defn refresh!
   [repo ok-handler]
   [repo ok-handler]
-  (when repo
+  (when (and repo
+             (not (state/unlinked-dir? (config/get-repo-dir repo))))
     (state/set-nfs-refreshing! true)
     (state/set-nfs-refreshing! true)
     (p/let [_ (reload-dir! repo)
     (p/let [_ (reload-dir! repo)
             _ (ok-handler)]
             _ (ok-handler)]

+ 10 - 0
src/main/frontend/mobile/mobile_bar.cljs

@@ -31,6 +31,14 @@
     (config-handler/set-config!
     (config-handler/set-config!
      :mobile/toolbar-stats @commands-stats)))
      :mobile/toolbar-stats @commands-stats)))
 
 
+(rum/defc indent-outdent [indent? icon]
+  [:div
+   [:button.bottom-action
+    {:on-mouse-down (fn [e]
+                      (util/stop e)
+                      (editor-handler/indent-outdent indent?))}
+    (ui/icon icon {:style {:fontSize ui/icon-size}})]])
+
 (rum/defc command
 (rum/defc command
   [command-handler icon & [count? event?]]
   [command-handler icon & [count? event?]]
   [:div
   [:div
@@ -119,6 +127,8 @@
           sorted-commands (sort-by (comp :counts second) > @commands-stats)]
           sorted-commands (sort-by (comp :counts second) > @commands-stats)]
       [:div#mobile-editor-toolbar.bg-base-2
       [:div#mobile-editor-toolbar.bg-base-2
        [:div.toolbar-commands
        [:div.toolbar-commands
+        (indent-outdent false "indent-decrease")
+        (indent-outdent true "indent-increase")
         (command (editor-handler/move-up-down true) "arrow-bar-to-up")
         (command (editor-handler/move-up-down true) "arrow-bar-to-up")
         (command (editor-handler/move-up-down false) "arrow-bar-to-down")
         (command (editor-handler/move-up-down false) "arrow-bar-to-down")
         (command #(if (state/sub :document/mode?)
         (command #(if (state/sub :document/mode?)

+ 6 - 4
src/main/frontend/modules/file/core.cljs

@@ -119,15 +119,17 @@
                               (state/get-preferred-format)))
                               (state/get-preferred-format)))
             title (string/capitalize (:block/name page))
             title (string/capitalize (:block/name page))
             journal-page? (date/valid-journal-title? title)
             journal-page? (date/valid-journal-title? title)
+            filename (if journal-page?
+                       (date/date->file-name journal-page?)
+                       (-> (or (:block/original-name page) (:block/name page))
+                           (util/file-name-sanity)))
             path (str
             path (str
                   (if journal-page?
                   (if journal-page?
                     (config/get-journals-directory)
                     (config/get-journals-directory)
                     (config/get-pages-directory))
                     (config/get-pages-directory))
                   "/"
                   "/"
-                  (if journal-page?
-                    (date/date->file-name journal-page?)
-                    (-> (or (:block/original-name page) (:block/name page))
-                        (util/file-name-sanity))) "."
+                  filename
+                  "."
                   (if (= format "markdown") "md" format))
                   (if (= format "markdown") "md" format))
             file-path (config/get-file-path repo path)
             file-path (config/get-file-path repo path)
             file {:file/path file-path}
             file {:file/path file-path}

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

@@ -45,4 +45,4 @@
 (defn init []
 (defn init []
   (when-not config/dev?
   (when-not config/dev?
     (let [config (clj->js config)]
     (let [config (clj->js config)]
-     (Sentry/init config))))
+      (Sentry/init config))))

+ 66 - 46
src/main/frontend/modules/outliner/core.cljs

@@ -4,7 +4,7 @@
             [datascript.impl.entity :as de]
             [datascript.impl.entity :as de]
             [frontend.db :as db]
             [frontend.db :as db]
             [frontend.db.model :as db-model]
             [frontend.db.model :as db-model]
-            [logseq.graph-parser.db.schema :as db-schema]
+            [logseq.db.schema :as db-schema]
             [frontend.db.conn :as conn]
             [frontend.db.conn :as conn]
             [frontend.db.outliner :as db-outliner]
             [frontend.db.outliner :as db-outliner]
             [frontend.modules.outliner.datascript :as ds]
             [frontend.modules.outliner.datascript :as ds]
@@ -370,6 +370,21 @@
 
 
 ;;; ### insert-blocks, delete-blocks, move-blocks
 ;;; ### insert-blocks, delete-blocks, move-blocks
 
 
+(defn- fix-top-level-blocks
+  "Blocks with :block/level"
+  [blocks]
+  (loop [blocks blocks
+         last-top-level-block nil
+         result []]
+    (if-let [block (first blocks)]
+      (if (= 1 (:block/level block))
+        (let [block' (assoc block
+                            :block/left {:db/id (:db/id last-top-level-block)}
+                            :block/parent (:block/parent last-top-level-block))]
+          (recur (rest blocks) block (conj result block')))
+        (recur (rest blocks) last-top-level-block (conj result block)))
+      result)))
+
 (defn- insert-blocks-aux
 (defn- insert-blocks-aux
   [blocks target-block {:keys [sibling? replace-empty-target? keep-uuid? move? outliner-op]}]
   [blocks target-block {:keys [sibling? replace-empty-target? keep-uuid? move? outliner-op]}]
   (let [block-uuids (map :block/uuid blocks)
   (let [block-uuids (map :block/uuid blocks)
@@ -454,6 +469,9 @@
                                      (> (count blocks) 1)
                                      (> (count blocks) 1)
                                      (not move?)))
                                      (not move?)))
         blocks' (blocks-with-level blocks)
         blocks' (blocks-with-level blocks)
+        blocks' (if (= outliner-op ::paste)
+                  (fix-top-level-blocks blocks')
+                  blocks')
         insert-opts {:sibling? sibling?
         insert-opts {:sibling? sibling?
                      :replace-empty-target? replace-empty-target?
                      :replace-empty-target? replace-empty-target?
                      :keep-uuid? keep-uuid?
                      :keep-uuid? keep-uuid?
@@ -693,51 +711,53 @@
   "Indent or outdent `blocks`."
   "Indent or outdent `blocks`."
   [blocks indent?]
   [blocks indent?]
   {:pre [(seq blocks) (boolean? indent?)]}
   {:pre [(seq blocks) (boolean? indent?)]}
-  (let [first-block (db/entity (:db/id (first blocks)))
-        left (db/entity (:db/id (:block/left first-block)))
-        parent (:block/parent first-block)
-        db (db/get-db)
-        top-level-blocks (get-top-level-blocks blocks)
-        concat-tx-fn (fn [& results]
-                       {:tx-data (->> (map :tx-data results)
-                                      (apply util/concat-without-nil))
-                        :tx-meta (:tx-meta (first results))})
-        opts {:outliner-op :indent-outdent-blocks}]
-    (if indent?
-      (when (and left (not (page-first-child? first-block)))
-        (let [last-direct-child-id (db-model/get-block-last-direct-child db (:db/id left) false)
-              blocks' (drop-while (fn [b]
-                                    (= (:db/id (:block/parent b))
-                                       (:db/id left)))
-                                  top-level-blocks)]
-          (when (seq blocks')
-            (if last-direct-child-id
-              (let [last-direct-child (db/entity last-direct-child-id)
-                    result (move-blocks blocks' last-direct-child (merge opts {:sibling? true}))
-                    ;; expand `left` if it's collapsed
-                    collapsed-tx (when (:block/collapsed? left)
-                                   {:tx-data [{:db/id (:db/id left)
-                                               :block/collapsed? false}]})]
-                (concat-tx-fn result collapsed-tx))
-              (move-blocks blocks' left (merge opts {:sibling? false}))))))
-      (when (and parent (not (page-block? (db/entity (:db/id parent)))))
-        (let [blocks' (take-while (fn [b]
-                                    (not= (:db/id (:block/parent b))
-                                          (:db/id (:block/parent parent))))
-                                  top-level-blocks)
-              result (move-blocks blocks' parent (merge opts {:sibling? true}))]
-          (if (state/logical-outdenting?)
-            result
-            ;; direct outdenting (default behavior)
-            (let [last-top-block (db/pull (:db/id (last blocks')))
-                  right-siblings (->> (get-right-siblings (block last-top-block))
-                                      (map :data))]
-              (if (seq right-siblings)
-                (let [result2 (if-let [last-direct-child-id (db-model/get-block-last-direct-child db (:db/id last-top-block) false)]
-                                (move-blocks right-siblings (db/entity last-direct-child-id) (merge opts {:sibling? true}))
-                                (move-blocks right-siblings last-top-block (merge opts {:sibling? false})))]
-                  (concat-tx-fn result result2))
-                result))))))))
+  (let [non-consecutive-blocks (db-model/get-non-consecutive-blocks blocks)]
+    (when (empty? non-consecutive-blocks)
+      (let [first-block (db/entity (:db/id (first blocks)))
+            left (db/entity (:db/id (:block/left first-block)))
+            parent (:block/parent first-block)
+            db (db/get-db)
+            top-level-blocks (get-top-level-blocks blocks)
+            concat-tx-fn (fn [& results]
+                           {:tx-data (->> (map :tx-data results)
+                                          (apply util/concat-without-nil))
+                            :tx-meta (:tx-meta (first results))})
+            opts {:outliner-op :indent-outdent-blocks}]
+        (if indent?
+          (when (and left (not (page-first-child? first-block)))
+            (let [last-direct-child-id (db-model/get-block-last-direct-child db (:db/id left) false)
+                  blocks' (drop-while (fn [b]
+                                        (= (:db/id (:block/parent b))
+                                           (:db/id left)))
+                                      top-level-blocks)]
+              (when (seq blocks')
+                (if last-direct-child-id
+                  (let [last-direct-child (db/entity last-direct-child-id)
+                        result (move-blocks blocks' last-direct-child (merge opts {:sibling? true}))
+                        ;; expand `left` if it's collapsed
+                        collapsed-tx (when (:block/collapsed? left)
+                                       {:tx-data [{:db/id (:db/id left)
+                                                   :block/collapsed? false}]})]
+                    (concat-tx-fn result collapsed-tx))
+                  (move-blocks blocks' left (merge opts {:sibling? false}))))))
+          (when (and parent (not (page-block? (db/entity (:db/id parent)))))
+            (let [blocks' (take-while (fn [b]
+                                        (not= (:db/id (:block/parent b))
+                                              (:db/id (:block/parent parent))))
+                                      top-level-blocks)
+                  result (move-blocks blocks' parent (merge opts {:sibling? true}))]
+              (if (state/logical-outdenting?)
+                result
+                ;; direct outdenting (default behavior)
+                (let [last-top-block (db/pull (:db/id (last blocks')))
+                      right-siblings (->> (get-right-siblings (block last-top-block))
+                                          (map :data))]
+                  (if (seq right-siblings)
+                    (let [result2 (if-let [last-direct-child-id (db-model/get-block-last-direct-child db (:db/id last-top-block) false)]
+                                    (move-blocks right-siblings (db/entity last-direct-child-id) (merge opts {:sibling? true}))
+                                    (move-blocks right-siblings last-top-block (merge opts {:sibling? false})))]
+                      (concat-tx-fn result result2))
+                    result))))))))))
 
 
 ;;; ### write-operations have side-effects (do transactions) ;;;;;;;;;;;;;;;;
 ;;; ### write-operations have side-effects (do transactions) ;;;;;;;;;;;;;;;;
 
 

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

@@ -12,7 +12,8 @@
   (when (and (not (:from-disk? (:tx-meta tx-report)))
   (when (and (not (:from-disk? (:tx-meta tx-report)))
              (not (:new-graph? (:tx-meta tx-report))))
              (not (:new-graph? (:tx-meta tx-report))))
     (let [{:keys [pages blocks]} (ds-report/get-blocks-and-pages tx-report)]
     (let [{:keys [pages blocks]} (ds-report/get-blocks-and-pages tx-report)]
-      (doseq [p (seq pages)] (updated-page-hook tx-report p))
+      (doseq [p (seq pages)]
+        (updated-page-hook tx-report p))
       (when (and state/lsp-enabled? (seq blocks))
       (when (and state/lsp-enabled? (seq blocks))
         (state/pub-event! [:plugin/hook-db-tx
         (state/pub-event! [:plugin/hook-db-tx
                            {:blocks  blocks
                            {:blocks  blocks

+ 5 - 7
src/main/frontend/modules/shortcut/config.cljs

@@ -145,6 +145,8 @@
 
 
    :editor/replace-block-reference-at-point {:binding "mod+shift+r"
    :editor/replace-block-reference-at-point {:binding "mod+shift+r"
                                              :fn      editor-handler/replace-block-reference-with-content-at-point}
                                              :fn      editor-handler/replace-block-reference-with-content-at-point}
+   :editor/copy-embed {:binding "mod+e"
+                       :fn      editor-handler/copy-current-block-embed}
 
 
    :editor/paste-text-in-one-block-at-point {:binding "mod+shift+v"
    :editor/paste-text-in-one-block-at-point {:binding "mod+shift+v"
                                              :fn      editor-handler/paste-text-in-one-block-at-point}
                                              :fn      editor-handler/paste-text-in-one-block-at-point}
@@ -343,10 +345,6 @@
    :ui/toggle-contents              {:binding "alt+shift+c"
    :ui/toggle-contents              {:binding "alt+shift+c"
                                      :fn      ui-handler/toggle-contents!}
                                      :fn      ui-handler/toggle-contents!}
 
 
-   :ui/open-new-window              {:binding "mod+n"
-                                     :inactive (not (util/electron?))
-                                     :fn      #(state/pub-event! [:graph/open-new-window nil])}
-
    :command/toggle-favorite         {:binding "mod+shift+f"
    :command/toggle-favorite         {:binding "mod+shift+f"
                                      :fn      page-handler/toggle-favorite!}
                                      :fn      page-handler/toggle-favorite!}
 
 
@@ -447,6 +445,7 @@
                           :editor/forward-kill-word
                           :editor/forward-kill-word
                           :editor/backward-kill-word
                           :editor/backward-kill-word
                           :editor/replace-block-reference-at-point
                           :editor/replace-block-reference-at-point
+                          :editor/copy-embed
                           :editor/paste-text-in-one-block-at-point
                           :editor/paste-text-in-one-block-at-point
                           :editor/insert-youtube-timestamp])
                           :editor/insert-youtube-timestamp])
      (with-meta {:before m/enable-when-editing-mode!}))
      (with-meta {:before m/enable-when-editing-mode!}))
@@ -522,7 +521,6 @@
                           :ui/toggle-help
                           :ui/toggle-help
                           :ui/toggle-theme
                           :ui/toggle-theme
                           :ui/toggle-contents
                           :ui/toggle-contents
-                          :ui/open-new-window
                           :editor/open-file-in-default-app
                           :editor/open-file-in-default-app
                           :editor/open-file-in-directory
                           :editor/open-file-in-directory
                           :editor/copy-current-file
                           :editor/copy-current-file
@@ -578,8 +576,7 @@
     :go/tomorrow
     :go/tomorrow
     :go/next-journal
     :go/next-journal
     :go/prev-journal
     :go/prev-journal
-    :go/keyboard-shortcuts
-    :ui/open-new-window]
+    :go/keyboard-shortcuts]
 
 
    :shortcut.category/block-editing
    :shortcut.category/block-editing
    [:editor/backspace
    [:editor/backspace
@@ -609,6 +606,7 @@
     :editor/forward-kill-word
     :editor/forward-kill-word
     :editor/backward-kill-word
     :editor/backward-kill-word
     :editor/replace-block-reference-at-point
     :editor/replace-block-reference-at-point
+    :editor/copy-embed
     :editor/paste-text-in-one-block-at-point
     :editor/paste-text-in-one-block-at-point
     :editor/select-up
     :editor/select-up
     :editor/select-down]
     :editor/select-down]

+ 62 - 9
src/main/frontend/modules/shortcut/dicts.cljc

@@ -38,6 +38,7 @@
    :editor/strike-through        "Strikethrough"
    :editor/strike-through        "Strikethrough"
    :editor/clear-block           "Delete entire block content"
    :editor/clear-block           "Delete entire block content"
    :editor/kill-line-before      "Delete line before cursor position"
    :editor/kill-line-before      "Delete line before cursor position"
+   :editor/copy-embed            "Copy a block embed pointing to the current block"
    :editor/kill-line-after       "Delete line after cursor position"
    :editor/kill-line-after       "Delete line after cursor position"
    :editor/beginning-of-block    "Move cursor to the beginning of a block"
    :editor/beginning-of-block    "Move cursor to the beginning of a block"
    :editor/end-of-block          "Move cursor to the end of a block"
    :editor/end-of-block          "Move cursor to the end of a block"
@@ -105,7 +106,7 @@
    :ui/toggle-help                 "Toggle help"
    :ui/toggle-help                 "Toggle help"
    :ui/toggle-theme                "Toggle between dark/light theme"
    :ui/toggle-theme                "Toggle between dark/light theme"
    :ui/toggle-contents             "Toggle Contents in sidebar"
    :ui/toggle-contents             "Toggle Contents in sidebar"
-   :ui/open-new-window             "Open another window"
+  ;;  :ui/open-new-window             "Open another window"
    :command/toggle-favorite        "Add to/remove from favorites"
    :command/toggle-favorite        "Add to/remove from favorites"
    :editor/open-file-in-default-app "Open file in default app"
    :editor/open-file-in-default-app "Open file in default app"
    :editor/open-file-in-directory   "Open file in parent directory"
    :editor/open-file-in-directory   "Open file in parent directory"
@@ -488,7 +489,59 @@
              :command.editor/backward-kill-word       "Slett ett ord bakover"
              :command.editor/backward-kill-word       "Slett ett ord bakover"
              :command.editor/open-edit                "Rediger valgt blokk"
              :command.editor/open-edit                "Rediger valgt blokk"
              :command.editor/delete-selection         "Slett valgte blokker"
              :command.editor/delete-selection         "Slett valgte blokker"
-             :command.editor/toggle-open-blocks       "Veksle åpne blokker (slå sammen eller utvid alle blokker)"}
+             :command.editor/toggle-open-blocks       "Veksle åpne blokker (slå sammen eller utvid alle blokker)"
+             :command.auto-complete/complete "Autofullfør: Bruk valgt punkt"
+             :command.auto-complete/next "Autofullfør: Velg neste punkt"
+             :command.auto-complete/open-link "Autofullfør: Åpne valgt punkt i nettleser"
+             :command.auto-complete/prev "Autofullfør: Velg forrige punkt"
+             :command.auto-complete/shift-complete "Autofullfør: Åpne valgt punkt i sidestolpen"
+             :command.cards/forgotten "Kort: Glemte"
+             :command.cards/next-card "Kort: Neste kort"
+             :command.cards/recall "Kort: bruk litt tid på å huske"
+             :command.cards/remembered "Kort: husket"
+             :command.cards/toggle-answers "Kort: vis/skjul svar/clozes"
+             :command.command/run "Kjør git kommando"
+             :command.command/toggle-favorite "Legg til eller fjern fra favoritter"
+             :command.command-palette/toggle "Veksle kommandolinje"
+             :command.date-picker/complete "Datovelger: Bruk valgt dag"
+             :command.date-picker/next-day "Datovelger: Velg neste dag"
+             :command.date-picker/next-week "Datovelger: Velg neste uke"
+             :command.date-picker/prev-day "Datovelger: Velg forrige dag"
+             :command.date-picker/prev-week "Datovelger: Velg forrige uke"
+             :command.editor/copy-current-file "Kopier nåværende fil"
+             :command.editor/escape-editing "Avslutt redigering"
+             :command.editor/insert-youtube-timestamp "Sett inn YouTube tidsstempel"
+             :command.editor/open-file-in-default-app "Åpne fil i forhåndsvalgt app"
+             :command.editor/open-file-in-directory "Åpne fil i overordnet katalog"
+             :command.editor/paste-text-in-one-block-at-point "Lim inn tekst i blokk ved markør"
+             :command.editor/replace-block-reference-at-point "Erstatt blokkreferanse med dets innhold ved markør"
+             :command.editor/select-down "Velg innhold under"
+             :command.editor/select-up "Velg innhold over"
+             :command.editor/strike-through "Gjennomstreking"
+             :command.go/all-pages "Gå til alle sider"
+             :command.go/backward "Bakover"
+             :command.go/flashcards "Veksle flashcards"
+             :command.go/forward "Fremover"
+             :command.go/graph-view "Gå til graf visning"
+             :command.go/home "Gå hjem"
+             :command.go/keyboard-shortcuts "Gå til tastatursnarveier"
+             :command.go/next-journal "Gå til neste dagbok"
+             :command.go/prev-journal "Gå til forrige dagbok"
+             :command.go/tomorrow "Gå til i morgen"
+             :command.graph/add "Legg til graf"
+             :command.graph/open "Velg graf for å åpne"
+             :command.graph/remove "Fjern en graf"
+             :command.graph/save "Lagre nåværende graf til disk"
+             :command.misc/copy "mod+c"
+             :command.pdf/close "Lukk nåværende pdf leser"
+             :command.pdf/next-page "Neste side i nåværende pdf dok"
+             :command.pdf/previous-page "Forrige side i nåværende pdf dok"
+             :command.sidebar/clear "Fjern alt i høyre sidestolpe"
+             :command.sidebar/open-today-page "Åpne dagens side i høyre sidestolpe"
+             :command.ui/goto-plugins "Gå til dashbord for utvidelser"
+            ;;  :command.ui/open-new-window "Åpne et nytt vindu"
+             :command.ui/select-theme-color "Velg tilgjengelige temafarger"
+             :command.ui/toggle-cards "Veksle kort"}
 
 
    :pt-PT   {:shortcut.category/formatting            "Formatação"
    :pt-PT   {:shortcut.category/formatting            "Formatação"
              :shortcut.category/basics                "Básico"
              :shortcut.category/basics                "Básico"
@@ -666,7 +719,7 @@
              :command.graph/save                      "Salvar gráfico atual no computador"
              :command.graph/save                      "Salvar gráfico atual no computador"
              :command.misc/copy                       "Copiar (copiar seleção ou referência do bloco)"
              :command.misc/copy                       "Copiar (copiar seleção ou referência do bloco)"
              :command.ui/goto-plugins                 "Ir para o painel de plugins"
              :command.ui/goto-plugins                 "Ir para o painel de plugins"
-             :command.ui/open-new-window              "Abra uma nova janela"
+            ;;  :command.ui/open-new-window              "Abra uma nova janela"
              :command.editor/select-down              "Selecione o conteúdo abaixo"
              :command.editor/select-down              "Selecione o conteúdo abaixo"
              :command.editor/select-up                "Selecione o conteúdo acima"}
              :command.editor/select-up                "Selecione o conteúdo acima"}
 
 
@@ -708,7 +761,7 @@
              :command.go/next-journal                 "次の日誌へ移動"
              :command.go/next-journal                 "次の日誌へ移動"
              :command.go/prev-journal                 "前の日誌へ移動"
              :command.go/prev-journal                 "前の日誌へ移動"
              :command.go/keyboard-shortcuts           "キーボードショートカットへ移動"
              :command.go/keyboard-shortcuts           "キーボードショートカットへ移動"
-             :command.ui/open-new-window              "別のウィンドウを開く"
+            ;;  :command.ui/open-new-window              "別のウィンドウを開く"
              :command.go/search-in-page               "ページ内を検索"
              :command.go/search-in-page               "ページ内を検索"
              :command.ui/toggle-document-mode         "ドキュメントモードのトグル"
              :command.ui/toggle-document-mode         "ドキュメントモードのトグル"
              :command.ui/toggle-contents              "目次のトグル"
              :command.ui/toggle-contents              "目次のトグル"
@@ -882,7 +935,7 @@
              :command.ui/toggle-help                 "Attiva/disattiva aiuto"
              :command.ui/toggle-help                 "Attiva/disattiva aiuto"
              :command.ui/toggle-theme                "Passa dal tema scuro a quello chiaro"
              :command.ui/toggle-theme                "Passa dal tema scuro a quello chiaro"
              :command.ui/toggle-contents             "Attiva/disattiva i contenuti nella barra laterale"
              :command.ui/toggle-contents             "Attiva/disattiva i contenuti nella barra laterale"
-             :command.ui/open-new-window             "Apri un'altra finestra"
+            ;;  :command.ui/open-new-window             "Apri un'altra finestra"
              :command.command/toggle-favorite        "Aggiungi a/rimuovi dai preferiti"
              :command.command/toggle-favorite        "Aggiungi a/rimuovi dai preferiti"
              :command.editor/open-file-in-default-app "Apri file nell'app predefinita"
              :command.editor/open-file-in-default-app "Apri file nell'app predefinita"
              :command.editor/open-file-in-directory   "Apri file nella directory principale"
              :command.editor/open-file-in-directory   "Apri file nella directory principale"
@@ -900,9 +953,9 @@
              :shortcut.category/block-command-editing "Modifica comandi blocco"
              :shortcut.category/block-command-editing "Modifica comandi blocco"
              :shortcut.category/block-selection       "Selezione blocco (premi Esc per uscire dalla selezione)"
              :shortcut.category/block-selection       "Selezione blocco (premi Esc per uscire dalla selezione)"
              :shortcut.category/toggle                "Attiva/disattiva"
              :shortcut.category/toggle                "Attiva/disattiva"
-             :shortcut.category/others                "Altri"
+             :shortcut.category/others                "Altri"}
 
 
-   }
+   
    :tr      {:shortcut.category/basics "Temel bilgiler"
    :tr      {:shortcut.category/basics "Temel bilgiler"
              :shortcut.category/formatting "Biçimlendirme"
              :shortcut.category/formatting "Biçimlendirme"
              :shortcut.category/navigating "Gezinme"
              :shortcut.category/navigating "Gezinme"
@@ -1008,7 +1061,7 @@
              :command.ui/toggle-help                 "Yardımı aç/kapat"
              :command.ui/toggle-help                 "Yardımı aç/kapat"
              :command.ui/toggle-theme                "Koyu ve açık tema arasında geçiş yap"
              :command.ui/toggle-theme                "Koyu ve açık tema arasında geçiş yap"
              :command.ui/toggle-contents             "Kenar çubuğundaki içeriği aç/kapat"
              :command.ui/toggle-contents             "Kenar çubuğundaki içeriği aç/kapat"
-             :command.ui/open-new-window             "Başka bir pencere aç"
+            ;;  :command.ui/open-new-window             "Başka bir pencere aç"
              :command.command/toggle-favorite        "Sık kullanılanlara ekle/çıkar"
              :command.command/toggle-favorite        "Sık kullanılanlara ekle/çıkar"
              :command.editor/open-file-in-default-app "Dosyayı varsayılan uygulamada aç"
              :command.editor/open-file-in-default-app "Dosyayı varsayılan uygulamada aç"
              :command.editor/open-file-in-directory   "Dosyayı üst dizinde aç"
              :command.editor/open-file-in-directory   "Dosyayı üst dizinde aç"
@@ -1058,7 +1111,7 @@
              :command.go/next-journal                 "다음 일지로 이동"
              :command.go/next-journal                 "다음 일지로 이동"
              :command.go/prev-journal                 "이전 일지로 이동"
              :command.go/prev-journal                 "이전 일지로 이동"
              :command.go/keyboard-shortcuts           "키보드 단축키로 이동"
              :command.go/keyboard-shortcuts           "키보드 단축키로 이동"
-             :command.ui/open-new-window              "새 창 열기"
+            ;;  :command.ui/open-new-window              "새 창 열기"
              :command.go/search-in-page               "페이지 안에서 검색"
              :command.go/search-in-page               "페이지 안에서 검색"
              :command.ui/toggle-document-mode         "문서 모드 토글"
              :command.ui/toggle-document-mode         "문서 모드 토글"
              :command.ui/toggle-contents              "목차 토글"
              :command.ui/toggle-contents              "목차 토글"

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

@@ -2,7 +2,7 @@
   (:require [frontend.state :as state]
   (:require [frontend.state :as state]
             [datascript.core :as d]
             [datascript.core :as d]
             [frontend.db :as db]
             [frontend.db :as db]
-            [logseq.graph-parser.db.schema :as db-schema]
+            [logseq.db.schema :as db-schema]
             [rum.core :as rum]
             [rum.core :as rum]
             [frontend.handler.route :as route]
             [frontend.handler.route :as route]
             [frontend.page :as page]
             [frontend.page :as page]

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

@@ -369,7 +369,12 @@
 (defn enable-journals?
 (defn enable-journals?
   [repo]
   [repo]
   (not (false? (:feature/enable-journals?
   (not (false? (:feature/enable-journals?
-                 (get (sub-config) repo)))))
+                (get (sub-config) repo)))))
+
+(defn enable-flashcards?
+  [repo]
+  (not (false? (:feature/enable-flashcards?
+                (get (sub-config) repo)))))
 
 
 (defn export-heading-to-list?
 (defn export-heading-to-list?
   []
   []
@@ -684,7 +689,8 @@
   (swap! state assoc
   (swap! state assoc
          :selection/mode false
          :selection/mode false
          :selection/blocks nil
          :selection/blocks nil
-         :selection/direction :down))
+         :selection/direction :down
+         :selection/start-block nil))
 
 
 (defn get-selection-blocks
 (defn get-selection-blocks
   []
   []
@@ -1706,3 +1712,7 @@
   []
   []
   (some-> (get-current-whiteboard)
   (some-> (get-current-whiteboard)
           (gobj/get "api")))
           (gobj/get "api")))
+
+(defn unlinked-dir?
+  [dir]
+  (contains? (:file/unlinked-dirs @state) dir))

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

@@ -302,9 +302,13 @@
 (defn setup-system-theme-effect!
 (defn setup-system-theme-effect!
   []
   []
   (let [^js schemaMedia (js/window.matchMedia "(prefers-color-scheme: dark)")]
   (let [^js schemaMedia (js/window.matchMedia "(prefers-color-scheme: dark)")]
-    (.addEventListener schemaMedia "change" state/sync-system-theme!)
+    (try (.addEventListener schemaMedia "change" state/sync-system-theme!)
+         (catch js/Error _error
+           (.addListener schemaMedia state/sync-system-theme!)))
     (state/sync-system-theme!)
     (state/sync-system-theme!)
-    #(.removeEventListener schemaMedia "change" state/sync-system-theme!)))
+    #(try (.removeEventListener schemaMedia "change" state/sync-system-theme!)
+          (catch js/Error _error
+            (.removeListener schemaMedia state/sync-system-theme!)))))
 
 
 (defn set-global-active-keystroke [val]
 (defn set-global-active-keystroke [val]
   (.setAttribute js/document.body "data-active-keystroke" val))
   (.setAttribute js/document.body "data-active-keystroke" val))

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

@@ -1,3 +1,3 @@
 (ns frontend.version)
 (ns frontend.version)
 
 
-(defonce version "0.7.1")
+(defonce version "0.7.4")

+ 37 - 1
src/test/frontend/modules/outliner/core_test.cljs

@@ -179,6 +179,42 @@
       (outliner-core/indent-outdent-blocks! [(get-block 6) (get-block 9)] true))
       (outliner-core/indent-outdent-blocks! [(get-block 6) (get-block 9)] true))
     (is (= [4 5 6 9] (get-children 3)))))
     (is (= [4 5 6 9] (get-children 3)))))
 
 
+(deftest test-indent-blocks-regression-5604
+  (testing "
+  [22 [[2 [[3
+           [[4]
+            [5]
+            [6 [[7 [[8]]]]]
+            [9 [[10]
+                [11]]]]]]]
+      [12 [[13]                         ; outdents 13, 14, 15
+           [14]
+           [15]]]
+      [16 [[17]]]]]
+  "
+    (transact-tree! tree)
+    (outliner-tx/transact!
+      {:graph test-db}
+      (outliner-core/indent-outdent-blocks! [(get-block 13) (get-block 14) (get-block 15)] false))
+    (is (= [2 12 13 14 15 16] (get-children 22))))
+  (testing "
+  [22 [[2 [[3
+           [[4]
+            [5]
+            [6 [[7 [[8]]]]]
+            [9 [[10]
+                [11]]]]]]]
+      [12 [[13]                         ; outdents 13, 14
+           [14]
+           [15]]]
+      [16 [[17]]]]]
+  "
+    (transact-tree! tree)
+    (outliner-tx/transact!
+      {:graph test-db}
+      (outliner-core/indent-outdent-blocks! [(get-block 13) (get-block 14)] false))
+    (is (= [2 12 13 14 16] (get-children 22)))))
+
 (deftest test-outdent-blocks
 (deftest test-outdent-blocks
   (testing "
   (testing "
   [1 [[2 [[3]
   [1 [[2 [[3]
@@ -415,7 +451,7 @@
     (transact-random-tree!)
     (transact-random-tree!)
     (let [c1 (get-blocks-ids)
     (let [c1 (get-blocks-ids)
           *random-blocks (atom c1)]
           *random-blocks (atom c1)]
-      (dotimes [_i 200]
+      (dotimes [_i 100]
         ;; (prn "random insert: " i)
         ;; (prn "random insert: " i)
         (let [blocks (gen-blocks)]
         (let [blocks (gen-blocks)]
           (swap! *random-blocks (fn [old]
           (swap! *random-blocks (fn [old]

Some files were not shown because too many files changed in this diff