浏览代码

Merge branch 'feat/db' into perf/app-start

Tienson Qin 6 月之前
父节点
当前提交
4169f20a28
共有 84 个文件被更改,包括 1517 次插入795 次删除
  1. 3 3
      .github/workflows/build.yml
  2. 3 3
      .github/workflows/db.yml
  3. 3 3
      .github/workflows/graph-parser.yml
  4. 2 2
      android/app/capacitor.build.gradle
  5. 2 2
      android/build.gradle
  6. 二进制
      android/gradle/wrapper/gradle-wrapper.jar
  7. 3 1
      android/gradle/wrapper/gradle-wrapper.properties
  8. 31 13
      android/gradlew
  9. 21 16
      android/gradlew.bat
  10. 11 11
      android/variables.gradle
  11. 2 1
      deps.edn
  12. 8 20
      deps/db/src/logseq/db/frontend/property.cljs
  13. 36 25
      deps/db/src/logseq/db/frontend/property/build.cljs
  14. 36 22
      deps/db/src/logseq/db/sqlite/build.cljs
  15. 127 64
      deps/db/src/logseq/db/sqlite/export.cljs
  16. 40 1
      deps/db/test/logseq/db/sqlite/build_test.cljs
  17. 68 9
      deps/db/test/logseq/db/sqlite/export_test.cljs
  18. 2 3
      deps/graph-parser/nbb.edn
  19. 2 1
      deps/graph-parser/package.json
  20. 44 21
      deps/graph-parser/src/logseq/graph_parser/exporter.cljs
  21. 10 3
      deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs
  22. 6 0
      deps/graph-parser/test/logseq/graph_parser/test_runner.cljs
  23. 3 1
      deps/shui/deps.edn
  24. 6 5
      deps/shui/src/logseq/shui/demo.cljs
  25. 235 236
      deps/shui/src/logseq/shui/demo2.cljs
  26. 12 8
      deps/shui/src/logseq/shui/dialog/core.cljs
  27. 13 7
      deps/shui/src/logseq/shui/hooks.cljs
  28. 114 113
      deps/shui/src/logseq/shui/select/multi.cljs
  29. 3 2
      deps/shui/src/logseq/shui/table/core.cljc
  30. 17 16
      deps/shui/src/logseq/shui/toaster/core.cljs
  31. 1 1
      gulpfile.js
  32. 1 1
      ios/App/Podfile
  33. 131 0
      ios/App/Podfile.lock
  34. 18 18
      package.json
  35. 1 1
      src/main/frontend/components/assets.cljs
  36. 1 1
      src/main/frontend/components/block.cljs
  37. 1 1
      src/main/frontend/components/bug_report.cljs
  38. 1 1
      src/main/frontend/components/cmdk/core.cljs
  39. 1 1
      src/main/frontend/components/cmdk/list_item.cljs
  40. 1 1
      src/main/frontend/components/container.cljs
  41. 4 2
      src/main/frontend/components/content.cljs
  42. 1 1
      src/main/frontend/components/dnd.cljs
  43. 1 1
      src/main/frontend/components/editor.cljs
  44. 26 4
      src/main/frontend/components/export.cljs
  45. 1 1
      src/main/frontend/components/file_based/block.cljs
  46. 5 5
      src/main/frontend/components/file_based/git.cljs
  47. 1 1
      src/main/frontend/components/file_sync.cljs
  48. 1 1
      src/main/frontend/components/handbooks.cljs
  49. 1 1
      src/main/frontend/components/header.cljs
  50. 1 1
      src/main/frontend/components/icon.cljs
  51. 1 1
      src/main/frontend/components/imports.cljs
  52. 1 0
      src/main/frontend/components/objects.cljs
  53. 3 2
      src/main/frontend/components/page.cljs
  54. 2 1
      src/main/frontend/components/page_menu.cljs
  55. 1 1
      src/main/frontend/components/plugins.cljs
  56. 1 1
      src/main/frontend/components/plugins_settings.cljs
  57. 1 1
      src/main/frontend/components/property.cljs
  58. 1 1
      src/main/frontend/components/property/config.cljs
  59. 1 1
      src/main/frontend/components/property/value.cljs
  60. 1 0
      src/main/frontend/components/query.cljs
  61. 1 1
      src/main/frontend/components/query/builder.cljs
  62. 1 1
      src/main/frontend/components/reference.cljs
  63. 1 1
      src/main/frontend/components/right_sidebar.cljs
  64. 1 1
      src/main/frontend/components/select.cljs
  65. 1 1
      src/main/frontend/components/server.cljs
  66. 1 1
      src/main/frontend/components/settings.cljs
  67. 1 1
      src/main/frontend/components/shortcut.cljs
  68. 1 1
      src/main/frontend/components/theme.cljs
  69. 1 1
      src/main/frontend/components/user/login.cljs
  70. 1 1
      src/main/frontend/components/views.cljs
  71. 1 1
      src/main/frontend/components/whiteboard.cljs
  72. 2 2
      src/main/frontend/db/model.cljs
  73. 1 1
      src/main/frontend/extensions/handbooks/core.cljs
  74. 1 1
      src/main/frontend/extensions/pdf/core.cljs
  75. 1 1
      src/main/frontend/extensions/pdf/toolbar.cljs
  76. 1 1
      src/main/frontend/extensions/tldraw.cljs
  77. 1 1
      src/main/frontend/extensions/zotero.cljs
  78. 25 26
      src/main/frontend/handler/db_based/export.cljs
  79. 1 1
      src/main/frontend/mobile/graph_picker.cljs
  80. 1 1
      src/main/frontend/rum.cljs
  81. 1 1
      src/main/frontend/state.cljs
  82. 1 1
      src/main/frontend/ui.cljs
  83. 2 1
      src/main/frontend/worker/db_worker.cljs
  84. 393 81
      yarn.lock

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

@@ -112,13 +112,13 @@ 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: bb lint:carve 2>/dev/null
+        run: bb lint:carve
 
 
       - name: Lint for vars that are too large
       - name: Lint for vars that are too large
-        run: bb lint:large-vars 2>/dev/null
+        run: bb lint:large-vars
 
 
       - name: Lint for namespaces that aren't documented
       - name: Lint for namespaces that aren't documented
-        run: bb lint:ns-docstrings 2>/dev/null
+        run: bb lint:ns-docstrings
 
 
       - name: Lint invalid translation entries
       - name: Lint invalid translation entries
         run: bb lang:validate-translations
         run: bb lang:validate-translations

+ 3 - 3
.github/workflows/db.yml

@@ -86,13 +86,13 @@ jobs:
         run: clojure -M:clj-kondo --lint src test
         run: clojure -M:clj-kondo --lint src test
 
 
       - name: Carve lint for unused vars
       - name: Carve lint for unused vars
-        run: bb lint:carve 2>/dev/null
+        run: bb lint:carve
 
 
       - name: Lint for vars that are too large
       - name: Lint for vars that are too large
-        run: bb lint:large-vars 2>/dev/null
+        run: bb lint:large-vars
 
 
       - name: Lint datalog rules
       - name: Lint datalog rules
         run: bb lint:rules
         run: bb lint:rules
 
 
       - name: Lint for namespaces that aren't documented
       - name: Lint for namespaces that aren't documented
-        run: bb lint:ns-docstrings 2>/dev/null
+        run: bb lint:ns-docstrings

+ 3 - 3
.github/workflows/graph-parser.yml

@@ -108,10 +108,10 @@ jobs:
         run: 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: bb lint:carve 2>/dev/null
+        run: bb lint:carve
 
 
       - name: Lint for vars that are too large
       - name: Lint for vars that are too large
-        run: bb lint:large-vars 2>/dev/null
+        run: bb lint:large-vars
 
 
       - name: Lint for namespaces that aren't documented
       - name: Lint for namespaces that aren't documented
-        run: bb lint:ns-docstrings 2>/dev/null
+        run: bb lint:ns-docstrings

+ 2 - 2
android/app/capacitor.build.gradle

@@ -2,8 +2,8 @@
 
 
 android {
 android {
   compileOptions {
   compileOptions {
-      sourceCompatibility JavaVersion.VERSION_17
-      targetCompatibility JavaVersion.VERSION_17
+      sourceCompatibility JavaVersion.VERSION_21
+      targetCompatibility JavaVersion.VERSION_21
   }
   }
 }
 }
 
 

+ 2 - 2
android/build.gradle

@@ -8,8 +8,8 @@ buildscript {
         mavenCentral()
         mavenCentral()
     }
     }
     dependencies {
     dependencies {
-        classpath 'com.android.tools.build:gradle:8.1.1'
-        classpath 'com.google.gms:google-services:4.3.15'
+        classpath 'com.android.tools.build:gradle:8.7.2'
+        classpath 'com.google.gms:google-services:4.4.2'
 
 
         // NOTE: Do not place your application dependencies here; they belong
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
         // in the individual module build.gradle files

二进制
android/gradle/wrapper/gradle-wrapper.jar


+ 3 - 1
android/gradle/wrapper/gradle-wrapper.properties

@@ -1,5 +1,7 @@
 distributionBase=GRADLE_USER_HOME
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
+networkTimeout=10000
+validateDistributionUrl=true
 zipStoreBase=GRADLE_USER_HOME
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
 zipStorePath=wrapper/dists

+ 31 - 13
android/gradlew

@@ -15,6 +15,8 @@
 # See the License for the specific language governing permissions and
 # See the License for the specific language governing permissions and
 # limitations under the License.
 # limitations under the License.
 #
 #
+# SPDX-License-Identifier: Apache-2.0
+#
 
 
 ##############################################################################
 ##############################################################################
 #
 #
@@ -55,7 +57,7 @@
 #       Darwin, MinGW, and NonStop.
 #       Darwin, MinGW, and NonStop.
 #
 #
 #   (3) This script is generated from the Groovy template
 #   (3) This script is generated from the Groovy template
-#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
 #       within the Gradle project.
 #       within the Gradle project.
 #
 #
 #       You can find Gradle at https://github.com/gradle/gradle/.
 #       You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,13 +82,12 @@ do
     esac
     esac
 done
 done
 
 
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-
-APP_NAME="Gradle"
+# This is normally unused
+# shellcheck disable=SC2034
 APP_BASE_NAME=${0##*/}
 APP_BASE_NAME=${0##*/}
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
+' "$PWD" ) || exit
 
 
 # Use the maximum available, or set MAX_FD != -1 to use that value.
 # Use the maximum available, or set MAX_FD != -1 to use that value.
 MAX_FD=maximum
 MAX_FD=maximum
@@ -133,22 +134,29 @@ location of your Java installation."
     fi
     fi
 else
 else
     JAVACMD=java
     JAVACMD=java
-    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+    if ! command -v java >/dev/null 2>&1
+    then
+        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
 
 
 Please set the JAVA_HOME variable in your environment to match the
 Please set the JAVA_HOME variable in your environment to match the
 location of your Java installation."
 location of your Java installation."
+    fi
 fi
 fi
 
 
 # Increase the maximum file descriptors if we can.
 # Increase the maximum file descriptors if we can.
 if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
 if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
     case $MAX_FD in #(
     case $MAX_FD in #(
       max*)
       max*)
+        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
         MAX_FD=$( ulimit -H -n ) ||
         MAX_FD=$( ulimit -H -n ) ||
             warn "Could not query maximum file descriptor limit"
             warn "Could not query maximum file descriptor limit"
     esac
     esac
     case $MAX_FD in  #(
     case $MAX_FD in  #(
       '' | soft) :;; #(
       '' | soft) :;; #(
       *)
       *)
+        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
         ulimit -n "$MAX_FD" ||
         ulimit -n "$MAX_FD" ||
             warn "Could not set maximum file descriptor limit to $MAX_FD"
             warn "Could not set maximum file descriptor limit to $MAX_FD"
     esac
     esac
@@ -193,11 +201,15 @@ if "$cygwin" || "$msys" ; then
     done
     done
 fi
 fi
 
 
-# Collect all arguments for the java command;
-#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
-#     shell script including quotes and variable substitutions, so put them in
-#     double quotes to make sure that they get re-expanded; and
-#   * put everything else in single quotes, so that it's not re-expanded.
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+#     and any embedded shellness will be escaped.
+#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+#     treated as '${Hostname}' itself on the command line.
 
 
 set -- \
 set -- \
         "-Dorg.gradle.appname=$APP_BASE_NAME" \
         "-Dorg.gradle.appname=$APP_BASE_NAME" \
@@ -205,6 +217,12 @@ set -- \
         org.gradle.wrapper.GradleWrapperMain \
         org.gradle.wrapper.GradleWrapperMain \
         "$@"
         "$@"
 
 
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+    die "xargs is not available"
+fi
+
 # Use "xargs" to parse quoted args.
 # Use "xargs" to parse quoted args.
 #
 #
 # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
 # With -n1 it outputs one arg per line, with the quotes and backslashes removed.

+ 21 - 16
android/gradlew.bat

@@ -13,8 +13,10 @@
 @rem See the License for the specific language governing permissions and
 @rem See the License for the specific language governing permissions and
 @rem limitations under the License.
 @rem limitations under the License.
 @rem
 @rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
 
 
-@if "%DEBUG%" == "" @echo off
+@if "%DEBUG%"=="" @echo off
 @rem ##########################################################################
 @rem ##########################################################################
 @rem
 @rem
 @rem  Gradle startup script for Windows
 @rem  Gradle startup script for Windows
@@ -25,7 +27,8 @@
 if "%OS%"=="Windows_NT" setlocal
 if "%OS%"=="Windows_NT" setlocal
 
 
 set DIRNAME=%~dp0
 set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
 set APP_BASE_NAME=%~n0
 set APP_BASE_NAME=%~n0
 set APP_HOME=%DIRNAME%
 set APP_HOME=%DIRNAME%
 
 
@@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
 
 
 set JAVA_EXE=java.exe
 set JAVA_EXE=java.exe
 %JAVA_EXE% -version >NUL 2>&1
 %JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
+if %ERRORLEVEL% equ 0 goto execute
 
 
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
 
 
 goto fail
 goto fail
 
 
@@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
 
 
 if exist "%JAVA_EXE%" goto execute
 if exist "%JAVA_EXE%" goto execute
 
 
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
 
 
 goto fail
 goto fail
 
 
@@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
 
 
 :end
 :end
 @rem End local scope for the variables with windows NT shell
 @rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
+if %ERRORLEVEL% equ 0 goto mainEnd
 
 
 :fail
 :fail
 rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
 rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
 rem the _cmd.exe /c_ return code!
 rem the _cmd.exe /c_ return code!
-if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
 
 
 :mainEnd
 :mainEnd
 if "%OS%"=="Windows_NT" endlocal
 if "%OS%"=="Windows_NT" endlocal

+ 11 - 11
android/variables.gradle

@@ -1,16 +1,16 @@
 ext {
 ext {
-    minSdkVersion = 22
-    compileSdkVersion = 33
-    targetSdkVersion = 33
-    androidxActivityVersion = '1.7.0'
-    androidxAppCompatVersion = '1.6.1'
+    minSdkVersion = 23
+    compileSdkVersion = 35
+    targetSdkVersion = 35
+    androidxActivityVersion = '1.9.2'
+    androidxAppCompatVersion = '1.7.0'
     androidxCoordinatorLayoutVersion = '1.2.0'
     androidxCoordinatorLayoutVersion = '1.2.0'
-    androidxCoreVersion = '1.10.0'
-    androidxFragmentVersion = '1.5.6'
+    androidxCoreVersion = '1.15.0'
+    androidxFragmentVersion = '1.8.4'
     junitVersion = '4.13.2'
     junitVersion = '4.13.2'
-    androidxJunitVersion = '1.1.5'
-    androidxEspressoCoreVersion = '3.5.1'
+    androidxJunitVersion = '1.2.1'
+    androidxEspressoCoreVersion = '3.6.1'
     cordovaAndroidVersion = '10.1.1'
     cordovaAndroidVersion = '10.1.1'
-    coreSplashScreenVersion = '1.0.0'
-    androidxWebkitVersion = '1.6.1'
+    coreSplashScreenVersion = '1.0.1'
+    androidxWebkitVersion = '1.12.1'
 }
 }

+ 2 - 1
deps.edn

@@ -1,7 +1,8 @@
 {:paths ["src/main" "src/electron" "src/resources"]
 {:paths ["src/main" "src/electron" "src/resources"]
  :deps
  :deps
  {org.clojure/clojure                   {:mvn/version "1.11.1"}
  {org.clojure/clojure                   {:mvn/version "1.11.1"}
-  rum/rum                               {:mvn/version "0.12.9"}
+  rum/rum                               {:git/url "https://github.com/logseq/rum" ;; fork
+                                         :sha     "5d672bf84ed944414b9f61eeb83808ead7be9127"}
 
 
   datascript/datascript                 {:git/url "https://github.com/logseq/datascript" ;; fork
   datascript/datascript                 {:git/url "https://github.com/logseq/datascript" ;; fork
                                          :sha     "1f84d10df4970f054489b0ee78799f64b8dd4ee2"}
                                          :sha     "1f84d10df4970f054489b0ee78799f64b8dd4ee2"}

+ 8 - 20
deps/db/src/logseq/db/frontend/property.cljs

@@ -430,6 +430,9 @@
                                      {:type :coll
                                      {:type :coll
                                       :hide? true
                                       :hide? true
                                       :public? false}
                                       :public? false}
+                                     ;; ignore this property when rtc,
+                                     ;; since users frequently click the sort button to view table content temporarily,
+                                     ;; but this action does not need to be synchronized with other clients.
                                      :rtc {:rtc/ignore-attr-when-init-upload true
                                      :rtc {:rtc/ignore-attr-when-init-upload true
                                            :rtc/ignore-attr-when-init-download true
                                            :rtc/ignore-attr-when-init-download true
                                            :rtc/ignore-attr-when-syncing true}}
                                            :rtc/ignore-attr-when-syncing true}}
@@ -438,47 +441,32 @@
                                      :schema
                                      :schema
                                      {:type :map
                                      {:type :map
                                       :hide? true
                                       :hide? true
-                                      :public? false}
-                                     :rtc {:rtc/ignore-attr-when-init-upload true
-                                           :rtc/ignore-attr-when-init-download true
-                                           :rtc/ignore-attr-when-syncing true}}
+                                      :public? false}}
 
 
      :logseq.property.table/hidden-columns {:title "View hidden columns"
      :logseq.property.table/hidden-columns {:title "View hidden columns"
                                             :schema
                                             :schema
                                             {:type :keyword
                                             {:type :keyword
                                              :cardinality :many
                                              :cardinality :many
                                              :hide? true
                                              :hide? true
-                                             :public? false}
-                                            :rtc {:rtc/ignore-attr-when-init-upload true
-                                                  :rtc/ignore-attr-when-init-download true
-                                                  :rtc/ignore-attr-when-syncing true}}
+                                             :public? false}}
 
 
      :logseq.property.table/ordered-columns {:title "View ordered columns"
      :logseq.property.table/ordered-columns {:title "View ordered columns"
                                              :schema
                                              :schema
                                              {:type :coll
                                              {:type :coll
                                               :hide? true
                                               :hide? true
-                                              :public? false}
-                                             :rtc {:rtc/ignore-attr-when-init-upload true
-                                                   :rtc/ignore-attr-when-init-download true
-                                                   :rtc/ignore-attr-when-syncing true}}
+                                              :public? false}}
 
 
      :logseq.property.table/sized-columns {:title "View columns settings"
      :logseq.property.table/sized-columns {:title "View columns settings"
                                            :schema
                                            :schema
                                            {:type :map
                                            {:type :map
                                             :hide? true
                                             :hide? true
-                                            :public? false}
-                                           :rtc {:rtc/ignore-attr-when-init-upload true
-                                                 :rtc/ignore-attr-when-init-download true
-                                                 :rtc/ignore-attr-when-syncing true}}
+                                            :public? false}}
      :logseq.property.table/pinned-columns {:title "Table view pinned columns"
      :logseq.property.table/pinned-columns {:title "Table view pinned columns"
                                             :schema
                                             :schema
                                             {:type :property
                                             {:type :property
                                              :cardinality :many
                                              :cardinality :many
                                              :hide? true
                                              :hide? true
-                                             :public? false}
-                                            :rtc {:rtc/ignore-attr-when-init-upload true
-                                                  :rtc/ignore-attr-when-init-download true
-                                                  :rtc/ignore-attr-when-syncing true}}
+                                             :public? false}}
      :logseq.property/view-for {:title "This view belongs to"
      :logseq.property/view-for {:title "This view belongs to"
                                 :schema
                                 :schema
                                 {:type :node
                                 {:type :node

+ 36 - 25
deps/db/src/logseq/db/frontend/property/build.cljs

@@ -65,26 +65,32 @@
     (into [property-tx]
     (into [property-tx]
           (closed-values->blocks property))))
           (closed-values->blocks property))))
 
 
-(defn- build-property-value-block
+(defn build-property-value-block
   "Builds a property value entity given a block map/entity, a property entity or
   "Builds a property value entity given a block map/entity, a property entity or
-  ident and its property value"
-  [block property value & {:keys [block-uuid]}]
+  ident and its property value. Takes the following options:
+   * :block-uuid - :block/uuid for property value entity
+   * :properties - Additional properties and attributes to add to entity"
+  [block property value & {:keys [block-uuid properties]}]
   (let [block-id (or (:db/id block) (:db/ident block))]
   (let [block-id (or (:db/id block) (:db/ident block))]
-    (-> (merge
-         {:block/uuid (or block-uuid (common-uuid/gen-uuid))
-          :block/page (if (:block/page block)
-                        (:db/id (:block/page block))
+    (cond->
+     (merge
+      {:block/uuid (or block-uuid (common-uuid/gen-uuid))
+       :block/page (if (:block/page block)
+                     (:db/id (:block/page block))
                         ;; page block
                         ;; page block
-                        block-id)
-          :block/parent block-id
-          :logseq.property/created-from-property (if (= (:db/ident property) :logseq.property/default-value)
-                                                   block-id
-                                                   (or (:db/id property) {:db/ident (:db/ident property)}))
-          :block/order (db-order/gen-key)}
-         (if (db-property-type/property-value-content? (:logseq.property/type property) property)
-           {:logseq.property/value value}
-           {:block/title value}))
-        common-util/block-with-timestamps)))
+                     block-id)
+       :block/parent block-id
+       :logseq.property/created-from-property (if (= (:db/ident property) :logseq.property/default-value)
+                                                block-id
+                                                (or (:db/id property) {:db/ident (:db/ident property)}))
+       :block/order (db-order/gen-key)}
+      (if (db-property-type/property-value-content? (:logseq.property/type property) property)
+        {:logseq.property/value value}
+        {:block/title value}))
+      true
+      common-util/block-with-timestamps
+      properties
+      (merge properties))))
 
 
 (defn build-property-values-tx-m
 (defn build-property-values-tx-m
   "Builds a map of property names to their property value blocks to be
   "Builds a map of property names to their property value blocks to be
@@ -99,7 +105,7 @@
   (let [block' (if (:db/id block) block (assoc block :db/id [:block/uuid (:block/uuid block)]))]
   (let [block' (if (:db/id block) block (assoc block :db/id [:block/uuid (:block/uuid block)]))]
     (->> properties
     (->> properties
          (map (fn [[k v]]
          (map (fn [[k v]]
-                (let [property-map (if (map? k) k {:db/ident k})
+                (let [{:keys [property-value-properties] :as property-map} (if (map? k) k {:db/ident k})
                       gen-uuid-value-prefix (when pure?
                       gen-uuid-value-prefix (when pure?
                                               (or (:db/ident block) (:block/uuid block)))]
                                               (or (:db/ident block) (:block/uuid block)))]
                   (assert (:db/ident property-map) "Key in map must have a :db/ident")
                   (assert (:db/ident property-map) "Key in map must have a :db/ident")
@@ -108,15 +114,20 @@
                    (if (set? v)
                    (if (set? v)
                      (set (map #(build-property-value-block
                      (set (map #(build-property-value-block
                                  block' property-map %
                                  block' property-map %
-                                 (when pure?
-                                   {:block-uuid
-                                    (common-uuid/gen-uuid :builtin-block-uuid (str gen-uuid-value-prefix "-" %))}))
+                                 (cond-> {}
+                                   property-value-properties
+                                   (assoc :properties property-value-properties)
+                                   pure?
+                                   (assoc :block-uuid
+                                          (common-uuid/gen-uuid :builtin-block-uuid (str gen-uuid-value-prefix "-" %)))))
                                v))
                                v))
                      (build-property-value-block block' property-map v
                      (build-property-value-block block' property-map v
-                                                 (when pure?
-                                                   {:block-uuid
-                                                    (common-uuid/gen-uuid
-                                                     :builtin-block-uuid (str gen-uuid-value-prefix "-" v))})))])))
+                                                 (cond-> {}
+                                                   property-value-properties
+                                                   (assoc :properties property-value-properties)
+                                                   pure?
+                                                   (assoc :block-uuid
+                                                          (common-uuid/gen-uuid :builtin-block-uuid (str gen-uuid-value-prefix "-" v))))))])))
          (into {}))))
          (into {}))))
 
 
 (defn build-properties-with-ref-values
 (defn build-properties-with-ref-values

+ 36 - 22
deps/db/src/logseq/db/sqlite/build.cljs

@@ -99,6 +99,30 @@
   "Provides the next temp :db/id to use in a create-graph transact!"
   "Provides the next temp :db/id to use in a create-graph transact!"
   #(swap! current-db-id dec))
   #(swap! current-db-id dec))
 
 
+(defn- build-property-map-for-pvalue-tx
+  "Returns a property map if the given property pair should have a property value entity constructured
+   or nil if it should not. Property maps must at least contain the :db/ident and :logseq.property/type keys"
+  [k v new-block properties-config all-idents]
+  (if-let [built-in-type (get-in db-property/built-in-properties [k :schema :type])]
+    (if (and (db-property-type/value-ref-property-types built-in-type)
+             ;; closed values are referenced by their :db/ident so no need to create values
+             (not (get-in db-property/built-in-properties [k :closed-values])))
+      {:db/ident k
+       :logseq.property/type built-in-type}
+      (when-let [built-in-type' (get (or (:build/properties-ref-types new-block)
+                                         ;; Reasonable default for properties like logseq.property/default-value
+                                         {:entity :number})
+                                     built-in-type)]
+        {:db/ident k
+         :logseq.property/type built-in-type'}))
+    (when (and (db-property-type/value-ref-property-types (get-in properties-config [k :logseq.property/type]))
+               ;; Don't build property value entity if values are :block/uuid refs
+               (if (set? v) (not (vector? (first v))) (not (vector? v))))
+      (let [prop-type (get-in properties-config [k :logseq.property/type])]
+        {:db/ident (get-ident all-idents k)
+         :original-property-id k
+         :logseq.property/type prop-type}))))
+
 (defn- ->property-value-tx-m
 (defn- ->property-value-tx-m
   "Given a new block and its properties, creates a map of properties which have values of property value tx.
   "Given a new block and its properties, creates a map of properties which have values of property value tx.
    This map is used for both creating the new property values and then adding them to a block.
    This map is used for both creating the new property values and then adding them to a block.
@@ -106,28 +130,18 @@
   [new-block properties properties-config all-idents]
   [new-block properties properties-config all-idents]
   (->> properties
   (->> properties
        (keep (fn [[k v]]
        (keep (fn [[k v]]
-               (if-let [built-in-type (get-in db-property/built-in-properties [k :schema :type])]
-                 (if (and (db-property-type/value-ref-property-types built-in-type)
-                          ;; closed values are referenced by their :db/ident so no need to create values
-                          (not (get-in db-property/built-in-properties [k :closed-values])))
-                   (let [property-map {:db/ident k
-                                       :logseq.property/type built-in-type}]
-                     [property-map v])
-                   (when-let [built-in-type' (get (or (:build/properties-ref-types new-block)
-                                                      ;; Reasonable default for properties like logseq.property/default-value
-                                                      {:entity :number})
-                                                  built-in-type)]
-                     (let [property-map {:db/ident k
-                                         :logseq.property/type built-in-type'}]
-                       [property-map v])))
-                 (when (and (db-property-type/value-ref-property-types (get-in properties-config [k :logseq.property/type]))
-                            ;; TODO: Support translate-property-value without this hack
-                            (not (vector? v)))
-                   (let [prop-type (get-in properties-config [k :logseq.property/type])
-                         property-map {:db/ident (get-ident all-idents k)
-                                       :original-property-id k
-                                       :logseq.property/type prop-type}]
-                     [property-map v])))))
+               (when-let [property-map (build-property-map-for-pvalue-tx k v new-block properties-config all-idents)]
+                 [(let [pvalue-attrs (when (:build/property-value v)
+                                       (merge (:build/properties v)
+                                              {:block/tags (mapv #(hash-map :db/ident (get-ident all-idents %))
+                                                                 (:build/tags v))}
+                                              (select-keys v [:block/created-at :block/updated-at])))]
+                    (cond-> property-map
+                      (and (:build/property-value v) (seq pvalue-attrs))
+                      (assoc :property-value-properties pvalue-attrs)))
+                  (if (:build/property-value v)
+                    (or (:logseq.property/value v) (:block/title v))
+                    v)])))
        (db-property-build/build-property-values-tx-m new-block)))
        (db-property-build/build-property-values-tx-m new-block)))
 
 
 (defn- extract-basic-content-refs
 (defn- extract-basic-content-refs

+ 127 - 64
deps/db/src/logseq/db/sqlite/export.cljs

@@ -12,7 +12,8 @@
             [logseq.db.frontend.entity-plus :as entity-plus]
             [logseq.db.frontend.entity-plus :as entity-plus]
             [logseq.db.frontend.entity-util :as entity-util]
             [logseq.db.frontend.entity-util :as entity-util]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property :as db-property]
-            [logseq.db.sqlite.build :as sqlite-build]))
+            [logseq.db.sqlite.build :as sqlite-build]
+            [medley.core :as medley]))
 
 
 ;; Export fns
 ;; Export fns
 ;; ==========
 ;; ==========
@@ -37,54 +38,84 @@
     {:build/journal (:block/journal-day page-entity)}
     {:build/journal (:block/journal-day page-entity)}
     {:block/title (block-title page-entity)}))
     {:block/title (block-title page-entity)}))
 
 
-(defn- buildable-property-value-entity
-  "Converts property value to a buildable version"
-  [property-ent pvalue {:keys [property-value-uuids?]}]
-  (cond (and (not property-value-uuids?) (ldb/internal-page? pvalue))
+(defn- build-pvalue-entity-for-build-page
+  [pvalue]
+  (cond (ldb/internal-page? pvalue)
         ;; Should page properties be pulled here?
         ;; Should page properties be pulled here?
         [:build/page (cond-> (shallow-copy-page pvalue)
         [:build/page (cond-> (shallow-copy-page pvalue)
                        (seq (:block/tags pvalue))
                        (seq (:block/tags pvalue))
                        (assoc :build/tags (->build-tags (:block/tags pvalue))))]
                        (assoc :build/tags (->build-tags (:block/tags pvalue))))]
-        (and (not property-value-uuids?) (entity-util/journal? pvalue))
-        [:build/page {:build/journal (:block/journal-day pvalue)}]
-        :else
-        (if (contains? #{:node :date} (:logseq.property/type property-ent))
-          ;; Idents take precedence over uuid because they are keep data graph-agnostic
-          (if (:db/ident pvalue)
-            (:db/ident pvalue)
-            ;; Use metadata distinguish from block references that don't exist like closed values
-            ^::existing-property-value? [:block/uuid (:block/uuid pvalue)])
-          (or (:db/ident pvalue)
-              ;; nbb-compatible version of db-property/property-value-content
-              (or (block-title pvalue)
-                  (:logseq.property/value pvalue))))))
+        (entity-util/journal? pvalue)
+        [:build/page {:build/journal (:block/journal-day pvalue)}]))
+
+(defn- build-pvalue-entity-default [ent-properties pvalue options]
+  (if (or (seq ent-properties) (seq (:block/tags pvalue)))
+    (cond-> {:build/property-value :block
+             :block/title (or (block-title pvalue)
+                              (:logseq.property/value pvalue))}
+      (seq (:block/tags pvalue))
+      (assoc :build/tags (->build-tags (:block/tags pvalue)))
+
+      (seq ent-properties)
+      (assoc :build/properties ent-properties)
+
+      (:include-timestamps? options)
+      (merge (select-keys pvalue [:block/created-at :block/updated-at])))
+    ;; nbb-compatible version of db-property/property-value-content
+    (or (block-title pvalue)
+        (:logseq.property/value pvalue))))
 
 
 (defn- buildable-properties
 (defn- buildable-properties
   "Originally copied from db-test/readable-properties. Modified so that property values are
   "Originally copied from db-test/readable-properties. Modified so that property values are
    valid sqlite.build EDN"
    valid sqlite.build EDN"
   [db ent-properties properties-config options]
   [db ent-properties properties-config options]
-  (->> ent-properties
-       (map (fn [[k v]]
-              [k
-               (if (and (:block/closed-value-property v) (not (db-property/logseq-property? k)))
-                 (if-let [closed-uuid (some #(when (= (:value %) (db-property/property-value-content v))
-                                               (:uuid %))
-                                            (get-in properties-config [k :build/closed-values]))]
-                   [:block/uuid closed-uuid]
-                   (throw (ex-info (str "No closed value found for content: " (pr-str (db-property/property-value-content v))) {:properties properties-config})))
-                 (cond
-                   (de/entity? v)
-                   (buildable-property-value-entity (d/entity db k) v options)
-                   (and (set? v) (every? de/entity? v))
-                   (let [property-ent (d/entity db k)]
-                     (set (map #(buildable-property-value-entity property-ent % options) v)))
-                   :else
-                   v))]))
-       (into {})))
+  (letfn [(build-pvalue-entity
+            [db' property-ent pvalue properties-config' {:keys [property-value-uuids?] :as options'}]
+            (if-let [build-page (and (not property-value-uuids?) (build-pvalue-entity-for-build-page pvalue))]
+              build-page
+              (if (contains? #{:node :date} (:logseq.property/type property-ent))
+                ;; Idents take precedence over uuid because they are keep data graph-agnostic
+                (if (:db/ident pvalue)
+                  (:db/ident pvalue)
+                  ;; Use metadata distinguish from block references that don't exist like closed values
+                  ^::existing-property-value? [:block/uuid (:block/uuid pvalue)])
+                (or (:db/ident pvalue)
+                    (let [ent-properties* (->> (apply dissoc (db-property/properties pvalue)
+                                                      :logseq.property/value :logseq.property/created-from-property
+                                                      db-property/public-db-attribute-properties)
+                                               ;; TODO: Allow user properties when sqlite.build supports it
+                                               (medley/filter-keys db-property/internal-property?))
+                          ent-properties (when (and (not (:block/closed-value-property pvalue)) (seq ent-properties*))
+                                           (buildable-properties db' ent-properties* properties-config' options'))]
+                      (build-pvalue-entity-default ent-properties pvalue options'))))))]
+    (->> ent-properties
+         (map (fn [[k v]]
+                [k
+                 ;; handle user closed value properties. built-ins have idents and shouldn't be handled here
+                 (if (and (not (db-property/logseq-property? k))
+                          (or (:block/closed-value-property v)
+                              (and (set? v) (:block/closed-value-property (first v)))))
+                   (let [find-closed-uuid (fn [val]
+                                            (or (some #(when (= (:value %) (db-property/property-value-content val))
+                                                         (:uuid %))
+                                                      (get-in properties-config [k :build/closed-values]))
+                                                (throw (ex-info (str "No closed value found for content: " (pr-str (db-property/property-value-content val))) {:properties properties-config}))))]
+                     (if (set? v)
+                       (set (map #(vector :block/uuid (find-closed-uuid %)) v))
+                       [:block/uuid (find-closed-uuid v)]))
+                   (cond
+                     (de/entity? v)
+                     (build-pvalue-entity db (d/entity db k) v properties-config options)
+                     (and (set? v) (every? de/entity? v))
+                     (let [property-ent (d/entity db k)]
+                       (set (map #(build-pvalue-entity db property-ent % properties-config options) v)))
+                     :else
+                     v))]))
+         (into {}))))
 
 
 (defn- build-export-properties
 (defn- build-export-properties
   "The caller of this fn is responsible for building :build/:property-classes unless shallow-copy?"
   "The caller of this fn is responsible for building :build/:property-classes unless shallow-copy?"
-  [db user-property-idents {:keys [include-properties? include-timestamps? include-uuid? shallow-copy?] :as options}]
+  [db user-property-idents {:keys [include-properties? include-timestamps? include-uuid? shallow-copy? include-alias?] :as options}]
   (let [properties-config-by-ent
   (let [properties-config-by-ent
         (->> user-property-idents
         (->> user-property-idents
              (map (fn [ident]
              (map (fn [ident]
@@ -98,6 +129,8 @@
                          (assoc :block/uuid (:block/uuid property) :build/keep-uuid? true)
                          (assoc :block/uuid (:block/uuid property) :build/keep-uuid? true)
                          include-timestamps?
                          include-timestamps?
                          (merge (select-keys property [:block/created-at :block/updated-at]))
                          (merge (select-keys property [:block/created-at :block/updated-at]))
+                         (and (not shallow-copy?) include-alias? (:block/alias property))
+                         (assoc :block/alias (set (map #(vector :block/uuid (:block/uuid %)) (:block/alias property))))
                          (and (not shallow-copy?) (:logseq.property/classes property))
                          (and (not shallow-copy?) (:logseq.property/classes property))
                          (assoc :build/property-classes (mapv :db/ident (:logseq.property/classes property)))
                          (assoc :build/property-classes (mapv :db/ident (:logseq.property/classes property)))
                          (seq closed-values)
                          (seq closed-values)
@@ -129,8 +162,7 @@
 (defn- build-export-class
 (defn- build-export-class
   "The caller of this fn is responsible for building any classes or properties from this fn
   "The caller of this fn is responsible for building any classes or properties from this fn
    unless shallow-copy?"
    unless shallow-copy?"
-  [class-ent {:keys [include-parents? include-uuid? shallow-copy? include-timestamps?]
-              :or {include-parents? true}}]
+  [class-ent {:keys [include-uuid? shallow-copy? include-timestamps? include-alias?]}]
   (cond-> (select-keys class-ent [:block/title :block/collapsed?])
   (cond-> (select-keys class-ent [:block/title :block/collapsed?])
     include-uuid?
     include-uuid?
     (assoc :block/uuid (:block/uuid class-ent) :build/keep-uuid? true)
     (assoc :block/uuid (:block/uuid class-ent) :build/keep-uuid? true)
@@ -139,11 +171,10 @@
     (and (:logseq.property.class/properties class-ent) (not shallow-copy?))
     (and (:logseq.property.class/properties class-ent) (not shallow-copy?))
     (assoc :build/class-properties
     (assoc :build/class-properties
            (mapv :db/ident (:logseq.property.class/properties class-ent)))
            (mapv :db/ident (:logseq.property.class/properties class-ent)))
-    (and (not shallow-copy?) (:block/alias class-ent))
+    (and (not shallow-copy?) include-alias? (:block/alias class-ent))
     (assoc :block/alias (set (map #(vector :block/uuid (:block/uuid %)) (:block/alias class-ent))))
     (assoc :block/alias (set (map #(vector :block/uuid (:block/uuid %)) (:block/alias class-ent))))
     ;; It's caller's responsibility to ensure parent is included in final export
     ;; It's caller's responsibility to ensure parent is included in final export
-    (and include-parents?
-         (not shallow-copy?)
+    (and (not shallow-copy?)
          (:logseq.property/parent class-ent)
          (:logseq.property/parent class-ent)
          (not= :logseq.class/Root (:db/ident (:logseq.property/parent class-ent))))
          (not= :logseq.class/Root (:db/ident (:logseq.property/parent class-ent))))
     (assoc :build/class-parent
     (assoc :build/class-parent
@@ -364,12 +395,26 @@
      :classes (apply merge (map :classes uuid-block-pages))
      :classes (apply merge (map :classes uuid-block-pages))
      :pages-and-blocks (mapv #(select-keys % [:page :blocks]) uuid-block-pages)}))
      :pages-and-blocks (mapv #(select-keys % [:page :blocks]) uuid-block-pages)}))
 
 
+(defn sort-pages-and-blocks
+  "Provide a reliable sort order since this tends to be large. Helps with diffing
+   and readability"
+  [pages-and-blocks]
+  (vec
+   (sort-by #(or (get-in % [:page :block/title])
+                 (some-> (get-in % [:page :build/journal]) str)
+                 (str (get-in % [:page :block/uuid])))
+            pages-and-blocks)))
+
 (defn- finalize-export-maps
 (defn- finalize-export-maps
-  "Given final export maps, merges them, adds any missing class parents and merges those in"
+  "Given final export maps, merges them, adds any missing class parents and merges those in.
+   If :pages-and-blocks exist, sorts them in order to have reliable sort order"
   [db & export-maps]
   [db & export-maps]
   (let [final-export* (apply merge-export-maps export-maps)
   (let [final-export* (apply merge-export-maps export-maps)
-        class-parents-export (some->> (:classes final-export*) (build-class-parents-export db))]
-    (merge-export-maps final-export* class-parents-export)))
+        class-parents-export (some->> (:classes final-export*) (build-class-parents-export db))
+        merged-map (merge-export-maps final-export* class-parents-export)]
+    (cond-> merged-map
+      (:pages-and-blocks merged-map)
+      (update :pages-and-blocks sort-pages-and-blocks))))
 
 
 (defn- build-block-export
 (defn- build-block-export
   "Exports block for given block eid"
   "Exports block for given block eid"
@@ -387,7 +432,7 @@
     (merge {::block (:node node-export)}
     (merge {::block (:node node-export)}
            block-export)))
            block-export)))
 
 
-(defn- build-page-blocks-export [db page-entity {:keys [properties classes blocks ontology-page?] :as options}]
+(defn- build-page-blocks-export [db page-entity {:keys [properties classes blocks ontology-page? include-alias?] :as options}]
   (let [options' (cond-> (dissoc options :classes :blocks :graph-ontology)
   (let [options' (cond-> (dissoc options :classes :blocks :graph-ontology)
                    (:exclude-ontology? options)
                    (:exclude-ontology? options)
                    (assoc :properties (get-in options [:graph-ontology :properties])))
                    (assoc :properties (get-in options [:graph-ontology :properties])))
@@ -400,7 +445,7 @@
                (:node page-ent-export)
                (:node page-ent-export)
                (merge (dissoc (:node page-ent-export) :block/title)
                (merge (dissoc (:node page-ent-export) :block/title)
                       (shallow-copy-page page-entity)
                       (shallow-copy-page page-entity)
-                      (when (:block/alias page-entity)
+                      (when (and include-alias? (:block/alias page-entity))
                         {:block/alias (set (map #(vector :block/uuid (:block/uuid %)) (:block/alias page-entity)))})))
                         {:block/alias (set (map #(vector :block/uuid (:block/uuid %)) (:block/alias page-entity)))})))
         page-blocks-export {:pages-and-blocks [{:page page :blocks blocks}]
         page-blocks-export {:pages-and-blocks [{:page page :blocks blocks}]
                             :properties properties
                             :properties properties
@@ -439,7 +484,9 @@
         page-export (finalize-export-maps db page-export* uuid-block-export content-ref-export)]
         page-export (finalize-export-maps db page-export* uuid-block-export content-ref-export)]
     page-export))
     page-export))
 
 
-(defn build-view-nodes-export* [db nodes opts]
+(defn- build-nodes-export
+  "Export a mix of pages and blocks"
+  [db nodes opts]
   (let [node-pages (filter entity-util/page? nodes)
   (let [node-pages (filter entity-util/page? nodes)
         pages-export
         pages-export
         (merge
         (merge
@@ -452,9 +499,7 @@
         (->> node-blocks
         (->> node-blocks
              (group-by :block/page)
              (group-by :block/page)
              (map (fn [[parent-page-ent blocks]]
              (map (fn [[parent-page-ent blocks]]
-                    (merge (build-blocks-export db
-                                                (sort-by :block/order blocks)
-                                                (merge opts {:include-children? false}))
+                    (merge (build-blocks-export db (sort-by :block/order blocks) opts)
                            {:page (shallow-copy-page parent-page-ent)}))))
                            {:page (shallow-copy-page parent-page-ent)}))))
         pages-to-blocks-export
         pages-to-blocks-export
         {:properties (apply merge (map :properties pages-to-blocks))
         {:properties (apply merge (map :properties pages-to-blocks))
@@ -474,7 +519,29 @@
         {:keys [content-ref-uuids content-ref-ents] :as content-ref-export}
         {:keys [content-ref-uuids content-ref-ents] :as content-ref-export}
         (build-content-ref-export db (into nodes property-value-ents))
         (build-content-ref-export db (into nodes property-value-ents))
         {:keys [pvalue-uuids] :as nodes-export}
         {:keys [pvalue-uuids] :as nodes-export}
-        (build-view-nodes-export* db nodes {:include-uuid-fn content-ref-uuids})
+        (build-nodes-export db nodes {:include-uuid-fn content-ref-uuids :include-children? false})
+        uuid-block-export (build-uuid-block-export db pvalue-uuids content-ref-ents {})
+        view-nodes-export (finalize-export-maps db nodes-export uuid-block-export content-ref-export)]
+    view-nodes-export))
+
+(defn- build-selected-nodes-export
+  "Exports given nodes selected by a user. Nodes can be a mix of blocks and pages"
+  [db eids]
+  (let [top-level-nodes (map #(d/entity db %) eids)
+        children-nodes (->> top-level-nodes
+                            ;; Remove pages b/c when selected their children are not highlighted
+                            (remove entity-util/page?)
+                            (mapcat #(rest (ldb/get-block-and-children db (:block/uuid %))))
+                            (remove :logseq.property/created-from-property))
+        nodes (concat top-level-nodes children-nodes)
+        property-value-ents (mapcat #(->> (apply dissoc (db-property/properties %) db-property/public-db-attribute-properties)
+                                          vals
+                                          (filter de/entity?))
+                                    nodes)
+        {:keys [content-ref-uuids content-ref-ents] :as content-ref-export}
+        (build-content-ref-export db (into nodes property-value-ents))
+        {:keys [pvalue-uuids] :as nodes-export}
+        (build-nodes-export db nodes {:include-uuid-fn content-ref-uuids :include-children? true})
         uuid-block-export (build-uuid-block-export db pvalue-uuids content-ref-ents {})
         uuid-block-export (build-uuid-block-export db pvalue-uuids content-ref-ents {})
         view-nodes-export (finalize-export-maps db nodes-export uuid-block-export content-ref-export)]
         view-nodes-export (finalize-export-maps db nodes-export uuid-block-export content-ref-export)]
     view-nodes-export))
     view-nodes-export))
@@ -607,16 +674,6 @@
                            :blocks (sqlite-build/update-each-block blocks remove-uuid-if-not-ref)})
                            :blocks (sqlite-build/update-each-block blocks remove-uuid-if-not-ref)})
                         pages-and-blocks))))))
                         pages-and-blocks))))))
 
 
-(defn sort-pages-and-blocks
-  "Provide a reliable sort order since this tends to be large. Helps with diffing
-   and readability"
-  [pages-and-blocks]
-  (vec
-   (sort-by #(or (get-in % [:page :block/title])
-                 (some-> (get-in % [:page :build/journal]) str)
-                 (str (get-in % [:page :block/uuid])))
-            pages-and-blocks)))
-
 (defn- add-ontology-for-include-namespaces
 (defn- add-ontology-for-include-namespaces
   "Adds :properties to export for given namespace parents. Current use case is for :exclude-namespaces
   "Adds :properties to export for given namespace parents. Current use case is for :exclude-namespaces
    so no need to add :classes yet"
    so no need to add :classes yet"
@@ -644,7 +701,8 @@
    * :exclude-built-in-pages? - When set, built-in pages are excluded from export
    * :exclude-built-in-pages? - When set, built-in pages are excluded from export
    * :exclude-files? - When set, files are excluded from export"
    * :exclude-files? - When set, files are excluded from export"
   [db {:keys [exclude-files?] :as options*}]
   [db {:keys [exclude-files?] :as options*}]
-  (let [options (merge options* {:property-value-uuids? true})
+  (let [options (merge options* {:property-value-uuids? true
+                                 :include-alias? true})
         content-ref-uuids (get-graph-content-ref-uuids db options)
         content-ref-uuids (get-graph-content-ref-uuids db options)
         ontology-options (merge options {:include-uuid? true})
         ontology-options (merge options {:include-uuid? true})
         ontology-export (build-graph-ontology-export db ontology-options)
         ontology-export (build-graph-ontology-export db ontology-options)
@@ -702,7 +760,10 @@
         ;; Only looks one-level deep in properties e.g. not inside :build/page
         ;; Only looks one-level deep in properties e.g. not inside :build/page
         ;; Doesn't find :block/link refs
         ;; Doesn't find :block/link refs
         ref-uuids
         ref-uuids
-        (->> (concat (mapcat get-pvalue-uuids (vals classes))
+        (->> (concat (mapcat #(map second (:block/alias %)) (vals classes))
+                     (mapcat #(map second (:block/alias %)) (vals properties))
+                     (mapcat #(map second (:block/alias (:page %))) pages-and-blocks)
+                     (mapcat get-pvalue-uuids (vals classes))
                      (mapcat get-pvalue-uuids (vals properties))
                      (mapcat get-pvalue-uuids (vals properties))
                      (mapcat (comp get-pvalue-uuids :page) pages-and-blocks)
                      (mapcat (comp get-pvalue-uuids :page) pages-and-blocks)
                      (mapcat #(sqlite-build/extract-from-blocks (:blocks %) get-pvalue-uuids) pages-and-blocks))
                      (mapcat #(sqlite-build/extract-from-blocks (:blocks %) get-pvalue-uuids) pages-and-blocks))
@@ -736,6 +797,8 @@
           (build-page-export db (:page-id options))
           (build-page-export db (:page-id options))
           :view-nodes
           :view-nodes
           (build-view-nodes-export db (:node-ids options))
           (build-view-nodes-export db (:node-ids options))
+          :selected-nodes
+          (build-selected-nodes-export db (:node-ids options))
           :graph-ontology
           :graph-ontology
           (build-graph-ontology-export db {})
           (build-graph-ontology-export db {})
           :graph
           :graph

+ 40 - 1
deps/db/test/logseq/db/sqlite/build_test.cljs

@@ -205,4 +205,43 @@
            (->> (d/q '[:find [?b ...] :in $ ?page-id :where [?b :block/page ?page-id]]
            (->> (d/q '[:find [?b ...] :in $ ?page-id :where [?b :block/page ?page-id]]
                      @conn [:block/uuid property-uuid])
                      @conn [:block/uuid property-uuid])
                 (map #(:block/title (d/entity @conn %)))))
                 (map #(:block/title (d/entity @conn %)))))
-        "Property page has correct blocks")))
+        "Property page has correct blocks")))
+
+(deftest property-value-with-properties-and-tags
+  (let [conn (db-test/create-conn-with-blocks
+              {:properties {:p1 {:logseq.property/type :default}}
+               :classes {:C1 {}}
+               :pages-and-blocks
+               [{:page {:block/title "page1"}
+                 :blocks [{:block/title "block has pvalue with built-in tag"
+                           :build/properties
+                           {:p1 {:build/property-value :block
+                                 :block/title "t1"
+                                 :build/tags [:logseq.class/Task]}}}
+                          {:block/title "block has pvalue with user tag"
+                           :build/properties
+                           {:p1 {:build/property-value :block
+                                 :block/title "u1"
+                                 :build/tags [:C1]}}}
+                          {:block/title "Todo query",
+                           :build/tags [:logseq.class/Query],
+                           :build/properties
+                           {:logseq.property/query
+                            {:build/property-value :block
+                             :block/title "{:query (task Todo)}"
+                             :build/properties
+                             {:logseq.property.code/lang "clojure"
+                              :logseq.property.node/display-type :code}}}}]}]})]
+    (is (= {:logseq.property.node/display-type :code
+            :logseq.property.code/lang "clojure"}
+           (-> (db-test/find-block-by-content @conn "{:query (task Todo)}")
+               db-test/readable-properties
+               (dissoc :logseq.property/created-from-property))))
+    (is (= {:block/tags [:logseq.class/Task]}
+           (-> (db-test/find-block-by-content @conn "t1")
+               db-test/readable-properties
+               (dissoc :logseq.property/created-from-property))))
+    (is (= {:block/tags [:user.class/C1]}
+           (-> (db-test/find-block-by-content @conn "u1")
+               db-test/readable-properties
+               (dissoc :logseq.property/created-from-property))))))

+ 68 - 9
deps/db/test/logseq/db/sqlite/export_test.cljs

@@ -57,17 +57,21 @@
     (sqlite-export/build-export @import-conn {:export-type :page :page-id (:db/id page2)})))
     (sqlite-export/build-export @import-conn {:export-type :page :page-id (:db/id page2)})))
 
 
 (defn- import-second-time-assertions [conn conn2 page-title original-data
 (defn- import-second-time-assertions [conn conn2 page-title original-data
-                                      & {:keys [transform-expected-blocks]
+                                      & {:keys [transform-expected-blocks build-journal]
                                          :or {transform-expected-blocks (fn [bs] (into bs bs))}}]
                                          :or {transform-expected-blocks (fn [bs] (into bs bs))}}]
   (let [page (db-test/find-page-by-title @conn2 page-title)
   (let [page (db-test/find-page-by-title @conn2 page-title)
         imported-page (export-page-and-import-to-another-graph conn conn2 page-title)
         imported-page (export-page-and-import-to-another-graph conn conn2 page-title)
         updated-page (db-test/find-page-by-title @conn2 page-title)
         updated-page (db-test/find-page-by-title @conn2 page-title)
         expected-page-and-blocks
         expected-page-and-blocks
-        (update-in (:pages-and-blocks original-data) [0 :blocks] transform-expected-blocks)]
+        (update-in (:pages-and-blocks original-data) [0 :blocks] transform-expected-blocks)
+        filter-imported-page (if build-journal
+                               #(= build-journal (get-in % [:page :build/journal]))
+                               #(= (get-in % [:page :block/title]) page-title))]
 
 
+    (assert (first expected-page-and-blocks))
     ;; Assume first page is one being imported for now
     ;; Assume first page is one being imported for now
     (is (= (first expected-page-and-blocks)
     (is (= (first expected-page-and-blocks)
-           (first (:pages-and-blocks imported-page)))
+           (first (filter filter-imported-page (:pages-and-blocks imported-page))))
         "Blocks are appended to existing page")
         "Blocks are appended to existing page")
     (is (= (:block/created-at page) (:block/created-at updated-page))
     (is (= (:block/created-at page) (:block/created-at updated-page))
         "Existing page didn't get re-created")
         "Existing page didn't get re-created")
@@ -323,7 +327,8 @@
     (is (= (-> (:pages-and-blocks original-data)
     (is (= (-> (:pages-and-blocks original-data)
                (medley/dissoc-in [1 :blocks 0 :build/properties])
                (medley/dissoc-in [1 :blocks 0 :build/properties])
                ;; shallow block means this page doesn't get included
                ;; shallow block means this page doesn't get included
-               butlast)
+               butlast
+               sort-pages-and-blocks)
            (:pages-and-blocks imported-page))
            (:pages-and-blocks imported-page))
         "Page's blocks are imported")
         "Page's blocks are imported")
 
 
@@ -412,7 +417,7 @@
     (is (= (:pages-and-blocks original-data) (:pages-and-blocks imported-page))
     (is (= (:pages-and-blocks original-data) (:pages-and-blocks imported-page))
         "Page's blocks are imported")
         "Page's blocks are imported")
 
 
-    (import-second-time-assertions conn conn2 journal-title original-data)))
+    (import-second-time-assertions conn conn2 journal-title original-data {:build-journal 20250210})))
 
 
 (deftest import-page-with-different-property-types
 (deftest import-page-with-different-property-types
   (let [block-object-uuid (random-uuid)
   (let [block-object-uuid (random-uuid)
@@ -458,7 +463,8 @@
         "Page's classes are imported")
         "Page's classes are imported")
     (is (= (-> (:pages-and-blocks original-data)
     (is (= (-> (:pages-and-blocks original-data)
                ;; adjust shallow block
                ;; adjust shallow block
-               (medley/dissoc-in [1 :blocks 0 :build/tags]))
+               (medley/dissoc-in [1 :blocks 0 :build/tags])
+               sort-pages-and-blocks)
            (:pages-and-blocks imported-page))
            (:pages-and-blocks imported-page))
         "Page's blocks are imported")
         "Page's blocks are imported")
 
 
@@ -527,7 +533,44 @@
         imported-nodes (sqlite-export/build-export @conn2 {:export-type :view-nodes
         imported-nodes (sqlite-export/build-export @conn2 {:export-type :view-nodes
                                                            :node-ids (get-node-ids @conn2)})]
                                                            :node-ids (get-node-ids @conn2)})]
 
 
-    (is (= (:pages-and-blocks original-data) (:pages-and-blocks imported-nodes)))
+    (is (= (sort-pages-and-blocks (:pages-and-blocks original-data)) (:pages-and-blocks imported-nodes)))
+    (is (= (expand-properties (:properties original-data)) (:properties imported-nodes)))
+    (is (= (expand-classes (:classes original-data)) (:classes imported-nodes)))))
+
+(deftest import-selected-nodes
+  (let [original-data
+        ;; Test a mix of pages and blocks
+        {:properties {:user.property/p1 {:logseq.property/type :default}}
+         :classes {:user.class/class1 {}}
+         :pages-and-blocks [{:page {:block/title "page1"}
+                             :blocks [{:block/title "b1"
+                                       :build/properties {:user.property/p1 "ok"}
+                                       :build/children [{:block/title "b2"}]}
+                                      {:block/title "b3"
+                                       :build/tags [:user.class/class1]
+                                       :build/children [{:block/title "b4"}]}]}
+                            {:page {:block/title "page2"}
+                             :blocks [{:block/title "dont export"}]}]}
+        conn (db-test/create-conn-with-blocks original-data)
+        get-node-ids (fn [db]
+                       (->> [(db-test/find-block-by-content db "b1")
+                             (db-test/find-page-by-title db "b3")
+                             (db-test/find-page-by-title db "page2")]
+                            (remove nil?)
+                            (mapv #(vector :block/uuid (:block/uuid %)))))
+        conn2 (db-test/create-conn)
+        {:keys [init-tx block-props-tx] :as _txs}
+        (-> (sqlite-export/build-export @conn {:export-type :selected-nodes :node-ids (get-node-ids @conn)})
+            (sqlite-export/build-import @conn2 {}))
+        ;; _ (cljs.pprint/pprint _txs)
+        _ (d/transact! conn2 init-tx)
+        _ (d/transact! conn2 block-props-tx)
+        _ (validate-db @conn2)
+        imported-nodes (sqlite-export/build-export @conn2 {:export-type :selected-nodes :node-ids (get-node-ids @conn2)})]
+
+    (is (= (->> (:pages-and-blocks original-data)
+                (map #(if (= (get-in % [:page :block/title]) "page2") (dissoc % :blocks) %)))
+           (:pages-and-blocks imported-nodes)))
     (is (= (expand-properties (:properties original-data)) (:properties imported-nodes)))
     (is (= (expand-properties (:properties original-data)) (:properties imported-nodes)))
     (is (= (expand-classes (:classes original-data)) (:classes imported-nodes)))))
     (is (= (expand-classes (:classes original-data)) (:classes imported-nodes)))))
 
 
@@ -558,6 +601,7 @@
                                                    :logseq.property/default-value 42})}
                                                    :logseq.property/default-value 42})}
           :user.property/default-closed
           :user.property/default-closed
           {:logseq.property/type :default
           {:logseq.property/type :default
+           :db/cardinality :db.cardinality/many
            :build/closed-values [{:value "joy" :uuid closed-value-uuid}
            :build/closed-values [{:value "joy" :uuid closed-value-uuid}
                                  {:value "sad" :uuid (random-uuid)}]}
                                  {:value "sad" :uuid (random-uuid)}]}
           :user.property/checkbox {:logseq.property/type :checkbox}
           :user.property/checkbox {:logseq.property/type :checkbox}
@@ -587,10 +631,25 @@
                                      :user.property/node #{[:block/uuid page-pvalue-uuid]}}}
                                      :user.property/node #{[:block/uuid page-pvalue-uuid]}}}
            :blocks [{:block/title "b1"
            :blocks [{:block/title "b1"
                      :build/properties {:user.property/num 1
                      :build/properties {:user.property/num 1
-                                        :user.property/default-closed [:block/uuid closed-value-uuid]
+                                        :user.property/default-closed #{[:block/uuid closed-value-uuid]}
                                         :user.property/date [:block/uuid journal-uuid]}}
                                         :user.property/date [:block/uuid journal-uuid]}}
                     {:block/title "b2" :build/properties {:user.property/node #{[:block/uuid page-object-uuid]}}}
                     {:block/title "b2" :build/properties {:user.property/node #{[:block/uuid page-object-uuid]}}}
-                    {:block/title "b3" :build/properties {:user.property/node #{[:block/uuid page-object-uuid]}}}]}
+                    {:block/title "b3" :build/properties {:user.property/node #{[:block/uuid page-object-uuid]}}}
+                    {:block/title "Example advanced query",
+                     :build/tags [:logseq.class/Query],
+                     :build/properties
+                     {:logseq.property/query
+                      {:build/property-value :block
+                       :block/title "{:query (task Todo)}"
+                       :build/properties
+                       {:logseq.property.code/lang "clojure"
+                        :logseq.property.node/display-type :code}}}}
+                    {:block/title "block has property value with tags and properties"
+                     :build/properties
+                     {:user.property/url
+                      {:build/property-value :block
+                       :block/title "https://example.com"
+                       :build/tags [:user.class/MyClass]}}}]}
           {:page {:block/title "page object"
           {:page {:block/title "page object"
                   :block/uuid page-object-uuid
                   :block/uuid page-object-uuid
                   :build/keep-uuid? true}
                   :build/keep-uuid? true}

+ 2 - 3
deps/graph-parser/nbb.edn

@@ -2,9 +2,8 @@
  :deps
  :deps
  {logseq/common
  {logseq/common
   {:local/root "../common"}
   {:local/root "../common"}
-
   logseq/db
   logseq/db
   {:local/root "../db"}
   {:local/root "../db"}
-
   io.github.nextjournal/nbb-test-runner
   io.github.nextjournal/nbb-test-runner
-  {:git/sha "60ed57aa04bca8d604f5ba6b28848bd887109347"}}}
+  {:git/sha "60ed57aa04bca8d604f5ba6b28848bd887109347"}
+  io.github.pez/baldr {:mvn/version "1.0.9"}}}

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

@@ -10,6 +10,7 @@
     "mldoc": "^1.5.9"
     "mldoc": "^1.5.9"
   },
   },
   "scripts": {
   "scripts": {
-    "test": "nbb-logseq -cp test:../outliner/src -m nextjournal.test-runner"
+    "test": "nbb-logseq -cp test:../outliner/src -m nextjournal.test-runner",
+    "test-v": "nbb-logseq -cp test:../outliner/src -m logseq.graph-parser.test-runner"
   }
   }
 }
 }

+ 44 - 21
deps/graph-parser/src/logseq/graph_parser/exporter.cljs

@@ -918,30 +918,41 @@
       (dissoc :block/whiteboard?)
       (dissoc :block/whiteboard?)
       (update-page-tags db user-options per-file-state all-idents)))
       (update-page-tags db user-options per-file-state all-idents)))
 
 
+(defn- get-page-parents
+  "Like ldb/get-page-parents but using all-existing-page-uuids"
+  [node all-existing-page-uuids]
+  (let [get-parent (fn get-parent [n]
+                     (when (:block/uuid (:logseq.property/parent n))
+                       (or (get all-existing-page-uuids (:block/uuid (:logseq.property/parent n)))
+                           (throw (ex-info (str "No parent page found for " (pr-str (:block/uuid (:logseq.property/parent n))))
+                                           {:node n})))))]
+    (when-let [parent (get-parent node)]
+      (loop [current-parent parent
+             parents' []]
+        (if (and current-parent (not (contains? parents' current-parent)))
+          (recur (get-parent current-parent)
+                 (conj parents' current-parent))
+          (vec (reverse parents')))))))
+
 (defn- get-all-existing-page-uuids
 (defn- get-all-existing-page-uuids
   "Returns a map of unique page names mapped to their uuids. The page names
   "Returns a map of unique page names mapped to their uuids. The page names
    are in a format that is compatible with extract/extract e.g. namespace pages have
    are in a format that is compatible with extract/extract e.g. namespace pages have
    their full hierarchy in the name"
    their full hierarchy in the name"
-  [db classes-from-property-parents]
-  (->> db
-       ;; don't fetch built-in as that would give the wrong entity if a user used
-       ;; a db-only built-in property name e.g. description
-       (d/q '[:find [?b ...]
-              :where [?b :block/name] [(missing? $ ?b :logseq.property/built-in?)]])
-       (map #(d/entity db %))
-       (map #(vector
-              (if-let [parents (and (or (ldb/internal-page? %) (ldb/class? %))
+  [classes-from-property-parents all-existing-page-uuids]
+  (->> all-existing-page-uuids
+       (map (fn [[_ p]]
+              (vector
+               (if-let [parents (and (or (contains? (:block/tags p) :logseq.class/Tag)
+                                         (contains? (:block/tags p) :logseq.class/Page))
                                     ;; These classes have parents now but don't in file graphs (and in extract)
                                     ;; These classes have parents now but don't in file graphs (and in extract)
-                                    (not (contains? classes-from-property-parents (:block/title %)))
-                                    (->> (ldb/get-page-parents %)
-                                         (remove (fn [e] (= :logseq.class/Root (:db/ident e))))
-                                         seq))]
+                                     (not (contains? classes-from-property-parents (:block/title p)))
+                                     (get-page-parents p all-existing-page-uuids))]
                 ;; Build a :block/name for namespace pages that matches data from extract/extract
                 ;; Build a :block/name for namespace pages that matches data from extract/extract
-                (string/join ns-util/namespace-char (map :block/name (conj (vec parents) %)))
-                (:block/name %))
-              (or (:block/uuid %)
-                  (throw (ex-info (str "No uuid for existing page " (pr-str (:block/name %)))
-                                  (select-keys % [:block/name :block/tags]))))))
+                 (string/join ns-util/namespace-char (map :block/name (conj (vec parents) p)))
+                 (:block/name p))
+               (or (:block/uuid p)
+                   (throw (ex-info (str "No uuid for existing page " (pr-str (:block/name p)))
+                                   (select-keys p [:block/name :block/tags])))))))
        (into {})))
        (into {})))
 
 
 (defn- build-existing-page
 (defn- build-existing-page
@@ -1008,8 +1019,9 @@
                                       (not (:block/file %))))
                                       (not (:block/file %))))
                         ;; remove file path relative
                         ;; remove file path relative
                         (map #(dissoc % :block/file)))
                         (map #(dissoc % :block/file)))
-        ;; Fetch all named ents once per import file to speed up named lookups
-        all-existing-page-uuids (get-all-existing-page-uuids @conn @(:classes-from-property-parents import-state))
+        ;; Build all named ents once per import file to speed up named lookups
+        all-existing-page-uuids (get-all-existing-page-uuids @(:classes-from-property-parents import-state)
+                                                             @(:all-existing-page-uuids import-state))
         all-pages (map #(modify-page-tx % all-existing-page-uuids) all-pages*)
         all-pages (map #(modify-page-tx % all-existing-page-uuids) all-pages*)
         all-new-page-uuids (->> all-pages
         all-new-page-uuids (->> all-pages
                                 (remove #(all-existing-page-uuids (or (::original-name %) (:block/name %))))
                                 (remove #(all-existing-page-uuids (or (::original-name %) (:block/name %))))
@@ -1119,6 +1131,8 @@
    ;; Map of property names (keyword) and their current schemas (map of qualified properties).
    ;; Map of property names (keyword) and their current schemas (map of qualified properties).
    ;; Used for adding schemas to properties and detecting changes across a property's usage
    ;; Used for adding schemas to properties and detecting changes across a property's usage
    :property-schemas (atom {})
    :property-schemas (atom {})
+   ;; Indexes all created pages by uuid. Index is used to fetch all parents of a page
+   :all-existing-page-uuids (atom {})
    ;; Map of property or class names (keyword) to db-ident keywords
    ;; Map of property or class names (keyword) to db-ident keywords
    :all-idents (atom {})
    :all-idents (atom {})
    ;; Set of children pages turned into classes by :property-parent-classes option
    ;; Set of children pages turned into classes by :property-parent-classes option
@@ -1312,6 +1326,12 @@
                  classes-tx)
                  classes-tx)
            retract-page-tag-from-existing-pages)}))
            retract-page-tag-from-existing-pages)}))
 
 
+(defn- save-from-tx
+  "Save importer state from given txs"
+  [txs {:keys [import-state]}]
+  (when-let [nodes (seq (filter :block/name txs))]
+    (swap! (:all-existing-page-uuids import-state) merge (into {} (map (juxt :block/uuid identity) nodes)))))
+
 (defn add-file-to-db-graph
 (defn add-file-to-db-graph
   "Parse file and save parsed data to the given db graph. Options available:
   "Parse file and save parsed data to the given db graph. Options available:
 
 
@@ -1351,6 +1371,7 @@
         ;; _ (when (seq property-pages-tx) (cljs.pprint/pprint {:property-pages-tx property-pages-tx}))
         ;; _ (when (seq property-pages-tx) (cljs.pprint/pprint {:property-pages-tx property-pages-tx}))
         ;; Necessary to transact new property entities first so that block+page properties can be transacted next
         ;; Necessary to transact new property entities first so that block+page properties can be transacted next
         main-props-tx-report (d/transact! conn property-pages-tx {::new-graph? true ::path file})
         main-props-tx-report (d/transact! conn property-pages-tx {::new-graph? true ::path file})
+        _ (save-from-tx property-pages-tx options)
 
 
         classes-tx @(:classes-tx tx-options)
         classes-tx @(:classes-tx tx-options)
         {:keys [retract-page-tags-tx] pages-tx'' :pages-tx} (clean-extra-invalid-tags @conn pages-tx' classes-tx existing-pages)
         {:keys [retract-page-tags-tx] pages-tx'' :pages-tx} (clean-extra-invalid-tags @conn pages-tx' classes-tx existing-pages)
@@ -1376,11 +1397,13 @@
         ;;                        [whiteboard-pages pages-index page-properties-tx property-page-properties-tx pages-tx' classes-tx blocks-index blocks-tx]))
         ;;                        [whiteboard-pages pages-index page-properties-tx property-page-properties-tx pages-tx' classes-tx blocks-index blocks-tx]))
         ;; _ (when (not (seq whiteboard-pages)) (cljs.pprint/pprint {#_:property-pages-tx #_property-pages-tx :pages-tx pages-tx :tx tx'}))
         ;; _ (when (not (seq whiteboard-pages)) (cljs.pprint/pprint {#_:property-pages-tx #_property-pages-tx :pages-tx pages-tx :tx tx'}))
         main-tx-report (d/transact! conn tx' {::new-graph? true ::path file})
         main-tx-report (d/transact! conn tx' {::new-graph? true ::path file})
+        _ (save-from-tx tx' options)
 
 
         upstream-properties-tx
         upstream-properties-tx
         (build-upstream-properties-tx @conn @(:upstream-properties tx-options) (:import-state options) log-fn)
         (build-upstream-properties-tx @conn @(:upstream-properties tx-options) (:import-state options) log-fn)
         ;; _ (when (seq upstream-properties-tx) (cljs.pprint/pprint {:upstream-properties-tx upstream-properties-tx}))
         ;; _ (when (seq upstream-properties-tx) (cljs.pprint/pprint {:upstream-properties-tx upstream-properties-tx}))
-        upstream-tx-report (when (seq upstream-properties-tx) (d/transact! conn upstream-properties-tx {::new-graph? true ::path file}))]
+        upstream-tx-report (when (seq upstream-properties-tx) (d/transact! conn upstream-properties-tx {::new-graph? true ::path file}))
+        _ (save-from-tx upstream-properties-tx options)]
 
 
     ;; Return all tx-reports that occurred in this fn as UI needs to know what changed
     ;; Return all tx-reports that occurred in this fn as UI needs to know what changed
     [main-props-tx-report main-tx-report upstream-tx-report]))
     [main-props-tx-report main-tx-report upstream-tx-report]))

+ 10 - 3
deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs

@@ -10,6 +10,7 @@
             [logseq.common.util.date-time :as date-time-util]
             [logseq.common.util.date-time :as date-time-util]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
             [logseq.db.frontend.content :as db-content]
             [logseq.db.frontend.content :as db-content]
+            [logseq.db.frontend.entity-plus :as entity-plus]
             [logseq.db.frontend.malli-schema :as db-malli-schema]
             [logseq.db.frontend.malli-schema :as db-malli-schema]
             [logseq.db.frontend.rules :as rules]
             [logseq.db.frontend.rules :as rules]
             [logseq.db.frontend.validate :as db-validate]
             [logseq.db.frontend.validate :as db-validate]
@@ -19,8 +20,7 @@
             [logseq.graph-parser.test.docs-graph-helper :as docs-graph-helper]
             [logseq.graph-parser.test.docs-graph-helper :as docs-graph-helper]
             [logseq.graph-parser.test.helper :as test-helper :include-macros true :refer [deftest-async]]
             [logseq.graph-parser.test.helper :as test-helper :include-macros true :refer [deftest-async]]
             [logseq.outliner.db-pipeline :as db-pipeline]
             [logseq.outliner.db-pipeline :as db-pipeline]
-            [promesa.core :as p]
-            [logseq.db.frontend.entity-plus :as entity-plus]))
+            [promesa.core :as p]))
 
 
 ;; Helpers
 ;; Helpers
 ;; =======
 ;; =======
@@ -130,11 +130,18 @@
 
 
 (deftest-async ^:integration export-docs-graph-with-convert-all-tags
 (deftest-async ^:integration export-docs-graph-with-convert-all-tags
   (p/let [file-graph-dir "test/resources/docs-0.10.9"
   (p/let [file-graph-dir "test/resources/docs-0.10.9"
+          start-time (cljs.core/system-time)
           _ (docs-graph-helper/clone-docs-repo-if-not-exists file-graph-dir "v0.10.9")
           _ (docs-graph-helper/clone-docs-repo-if-not-exists file-graph-dir "v0.10.9")
           conn (db-test/create-conn)
           conn (db-test/create-conn)
           _ (db-pipeline/add-listener conn)
           _ (db-pipeline/add-listener conn)
           {:keys [import-state]}
           {:keys [import-state]}
-          (import-file-graph-to-db file-graph-dir conn {:convert-all-tags? true})]
+          (import-file-graph-to-db file-graph-dir conn {:convert-all-tags? true})
+          end-time (cljs.core/system-time)]
+
+    ;; Add multiplicative factor for CI as it runs about twice as slow
+    (let [max-time (-> 15 (* (if js/process.env.CI 2 1)))]
+      (is (< (-> end-time (- start-time) (/ 1000)) max-time)
+          (str "Importing large graph takes less than " max-time "s")))
 
 
     (is (empty? (map :entity (:errors (db-validate/validate-db! @conn))))
     (is (empty? (map :entity (:errors (db-validate/validate-db! @conn))))
         "Created graph has no validation errors")
         "Created graph has no validation errors")

+ 6 - 0
deps/graph-parser/test/logseq/graph_parser/test_runner.cljs

@@ -0,0 +1,6 @@
+(ns logseq.graph-parser.test-runner
+  "Test runner which enables https://github.com/PEZ/baldr by default"
+  (:require [nextjournal.test-runner :as next-runner]
+            [pez.baldr]))
+
+(def -main next-runner/-main)

+ 3 - 1
deps/shui/deps.edn

@@ -3,6 +3,8 @@
  {org.clojure/clojure                   {:mvn/version "1.11.1"}
  {org.clojure/clojure                   {:mvn/version "1.11.1"}
   org.clojure/clojurescript             {:mvn/version "1.11.132"}
   org.clojure/clojurescript             {:mvn/version "1.11.132"}
   funcool/promesa                       {:mvn/version "11.0.678"}
   funcool/promesa                       {:mvn/version "11.0.678"}
-  rum/rum                               {:mvn/version "0.12.9"}
+  rum/rum                               {:git/url "https://github.com/logseq/rum" ;; fork
+                                         :sha     "5d672bf84ed944414b9f61eeb83808ead7be9127"}
+
   medley/medley                         {:mvn/version "1.4.0"}
   medley/medley                         {:mvn/version "1.4.0"}
   cljs-bean/cljs-bean                   {:mvn/version "1.5.0"}}}
   cljs-bean/cljs-bean                   {:mvn/version "1.5.0"}}}

+ 6 - 5
deps/shui/src/logseq/shui/demo.cljs

@@ -1,10 +1,11 @@
 (ns logseq.shui.demo
 (ns logseq.shui.demo
-  (:require [rum.core :as rum]
-            [logseq.shui.ui :as ui]
-            [dommy.core :refer-macros [sel1]]
+  (:require [dommy.core :refer-macros [sel1]]
+            [logseq.shui.dialog.core :as dialog-core]
             [logseq.shui.form.core :refer [yup yup-resolver] :as form-core]
             [logseq.shui.form.core :refer [yup yup-resolver] :as form-core]
+            [logseq.shui.hooks :as hooks]
+            [logseq.shui.ui :as ui]
             [promesa.core :as p]
             [promesa.core :as p]
-            [logseq.shui.dialog.core :as dialog-core]))
+            [rum.core :as rum]))
 
 
 (rum/defc section-item
 (rum/defc section-item
   [title children]
   [title children]
@@ -511,7 +512,7 @@
   []
   []
 
 
   (let [el-ref (rum/use-ref nil)]
   (let [el-ref (rum/use-ref nil)]
-    (rum/use-effect!
+    (hooks/use-effect!
      (fn []
      (fn []
        (let [^js container (get-main-scroll-container)
        (let [^js container (get-main-scroll-container)
              ^js el (rum/deref el-ref)
              ^js el (rum/deref el-ref)

+ 235 - 236
deps/shui/src/logseq/shui/demo2.cljs

@@ -1,23 +1,24 @@
 (ns logseq.shui.demo2
 (ns logseq.shui.demo2
-  (:require [clojure.string :as string]
-            [rum.core :as rum]
-            [logseq.shui.ui :as ui]
-            [logseq.shui.popup.core :refer [install-popups update-popup! get-popup]]
-            [logseq.shui.select.multi :refer [x-select-content]]
+  (:require [cljs-bean.core :as bean]
+            [clojure.string :as string]
             [frontend.components.icon :refer [emojis-cp emojis icon-search]]
             [frontend.components.icon :refer [emojis-cp emojis icon-search]]
             [frontend.storage :as storage]
             [frontend.storage :as storage]
-            [cljs-bean.core :as bean]
-            [promesa.core :as p]))
+            [logseq.shui.hooks :as hooks]
+            [logseq.shui.popup.core :refer [install-popups update-popup! get-popup]]
+            [logseq.shui.select.multi :refer [x-select-content]]
+            [logseq.shui.ui :as ui]
+            [promesa.core :as p]
+            [rum.core :as rum]))
 
 
 (defn do-fetch!
 (defn do-fetch!
   ([action] (do-fetch! action nil))
   ([action] (do-fetch! action nil))
   ([action query-str]
   ([action query-str]
    (-> (js/window.fetch
    (-> (js/window.fetch
-         (str "https://movies-api14.p.rapidapi.com/" (name action) (when query-str (str "?" query-str)))
-         #js {:method "GET"
-              :headers #js {:X-RapidAPI-Key "808ffd08c0mshc67d496f6024b46p164350jsn7b35179966c9",
-                            :X-RapidAPI-Host "movies-api14.p.rapidapi.com"}})
-     (p/then #(.json %)))))
+        (str "https://movies-api14.p.rapidapi.com/" (name action) (when query-str (str "?" query-str)))
+        #js {:method "GET"
+             :headers #js {:X-RapidAPI-Key "808ffd08c0mshc67d496f6024b46p164350jsn7b35179966c9",
+                           :X-RapidAPI-Host "movies-api14.p.rapidapi.com"}})
+       (p/then #(.json %)))))
 
 
 (rum/defc multi-select-demo
 (rum/defc multi-select-demo
   []
   []
@@ -35,96 +36,96 @@
 
 
          rm-item! (fn [item-or-id]
          rm-item! (fn [item-or-id]
                     (set-selected-items!
                     (set-selected-items!
-                      (remove #(or (= item-or-id %)
-                                 (= item-or-id (str (:id %))))
-                        selected-items)))
+                     (remove #(or (= item-or-id %)
+                                  (= item-or-id (str (:id %))))
+                             selected-items)))
          add-item! (fn [item] (set-selected-items! (conj selected-items item)))
          add-item! (fn [item] (set-selected-items! (conj selected-items item)))
 
 
          [open? set-open!] (rum/use-state false)]
          [open? set-open!] (rum/use-state false)]
 
 
-     (rum/use-effect!
-       (fn []
-         (storage/set :ls-demo-multi-selected-items selected-items))
-       [selected-items])
+     (hooks/use-effect!
+      (fn []
+        (storage/set :ls-demo-multi-selected-items selected-items))
+      [selected-items])
 
 
      (ui/card
      (ui/card
-       (ui/card-header
-         (ui/card-title "Search Movies")
-         (ui/card-description "x multiselect for the remote items"))
-       (ui/card-content
+      (ui/card-header
+       (ui/card-title "Search Movies")
+       (ui/card-description "x multiselect for the remote items"))
+      (ui/card-content
 
 
          ;; Basic
          ;; Basic
-         (ui/dropdown-menu
-           {:open open?}
+       (ui/dropdown-menu
+        {:open open?}
            ;; trigger
            ;; trigger
-           (ui/dropdown-menu-trigger
-             [:div.border.p-2.rounded.w-full.cursor-pointer.flex.items-center.gap-1.flex-wrap
-              {:on-click (fn [^js e]
-                           (let [^js target (.-target e)]
-                             (if-let [^js c (some-> target (.closest ".close"))]
-                               (some-> (.-dataset c) (.-k) (rm-item!))
-                               (set-open! true))))}
-              (for [{:keys [id original_title class poster_path]} selected-items]
-                (ui/badge {:variant :secondary :class (str class " group relative")}
-                  [:span.flex.items-center.gap-1.flex-nowrap
-                   [:img {:src poster_path :class "w-[16px] scale-75"}]
-                   [:b original_title]]
-                  (ui/button
-                    {:variant :destructive
-                     :size :icon
-                     :data-k id
-                     :class "!rounded-full !h-4 !w-4 absolute top-[-7px] right-[-3px] group-hover:visible invisible close"}
-                    (ui/tabler-icon "x" {:size 12}))))
-              (ui/button {:variant :link :size :sm} "+")])
+        (ui/dropdown-menu-trigger
+         [:div.border.p-2.rounded.w-full.cursor-pointer.flex.items-center.gap-1.flex-wrap
+          {:on-click (fn [^js e]
+                       (let [^js target (.-target e)]
+                         (if-let [^js c (some-> target (.closest ".close"))]
+                           (some-> (.-dataset c) (.-k) (rm-item!))
+                           (set-open! true))))}
+          (for [{:keys [id original_title class poster_path]} selected-items]
+            (ui/badge {:variant :secondary :class (str class " group relative")}
+                      [:span.flex.items-center.gap-1.flex-nowrap
+                       [:img {:src poster_path :class "w-[16px] scale-75"}]
+                       [:b original_title]]
+                      (ui/button
+                       {:variant :destructive
+                        :size :icon
+                        :data-k id
+                        :class "!rounded-full !h-4 !w-4 absolute top-[-7px] right-[-3px] group-hover:visible invisible close"}
+                       (ui/tabler-icon "x" {:size 12}))))
+          (ui/button {:variant :link :size :sm} "+")])
            ;; content
            ;; content
-           (x-select-content items selected-items
-             {;; test item render
-              :open? open?
-              :close! #(set-open! false)
-              :search-enabled? true
-              :search-key q
-              :search-fn (fn [items]
-                           (when (not fetching?) items))
-              :on-search-key-change (fn [v]
-                                      (set-q! v)
-                                      (if (string/blank? v)
-                                        (set-items! [])
-                                        (when (not fetching?)
-                                          (set-fetching? true)
-                                          (-> (do-fetch! :search (str "query=" v))
-                                            (p/then #(when-let [ret (bean/->clj %)]
-                                                       (when-let [items (:contents ret)]
-                                                         (set-items! (map (fn [item] (assoc item :id (:_id item))) (take 12 items))))))
-                                            (p/finally #(set-fetching? false))))))
-
-              :item-render (fn [item {:keys [selected?]}]
-                             (if item
-                               (ui/dropdown-menu-checkbox-item
-                                 {:checked selected?
-                                  :on-click (fn []
-                                              (if selected?
-                                                (rm-item! item)
-                                                (add-item! item))
+        (x-select-content items selected-items
+                          {;; test item render
+                           :open? open?
+                           :close! #(set-open! false)
+                           :search-enabled? true
+                           :search-key q
+                           :search-fn (fn [items]
+                                        (when (not fetching?) items))
+                           :on-search-key-change (fn [v]
+                                                   (set-q! v)
+                                                   (if (string/blank? v)
+                                                     (set-items! [])
+                                                     (when (not fetching?)
+                                                       (set-fetching? true)
+                                                       (-> (do-fetch! :search (str "query=" v))
+                                                           (p/then #(when-let [ret (bean/->clj %)]
+                                                                      (when-let [items (:contents ret)]
+                                                                        (set-items! (map (fn [item] (assoc item :id (:_id item))) (take 12 items))))))
+                                                           (p/finally #(set-fetching? false))))))
+
+                           :item-render (fn [item {:keys [selected?]}]
+                                          (if item
+                                            (ui/dropdown-menu-checkbox-item
+                                             {:checked selected?
+                                              :on-click (fn []
+                                                          (if selected?
+                                                            (rm-item! item)
+                                                            (add-item! item))
                                               ;(set-open! false)
                                               ;(set-open! false)
-                                              )}
-                                 [:div.flex.items-center.gap-2
-                                  [:span [:img {:src (:poster_path item)
-                                                :class "w-[20px]"}]]
-                                  [:span.flex.flex-col
-                                   [:b (:original_title item)]
-                                   [:small.opacity-50
-                                    {:class "text-[10px]"}
-                                    (:release_date item)]]])
-                               (ui/dropdown-menu-separator)))
-
-              :head-render (fn [] (when (and fetching? (not (string/blank? q)))
-                                    [:b.flex.items-center.justify-center.py-4
-                                     (ui/tabler-icon "loader" {:class "animate-spin"})]))
+                                                          )}
+                                             [:div.flex.items-center.gap-2
+                                              [:span [:img {:src (:poster_path item)
+                                                            :class "w-[20px]"}]]
+                                              [:span.flex.flex-col
+                                               [:b (:original_title item)]
+                                               [:small.opacity-50
+                                                {:class "text-[10px]"}
+                                                (:release_date item)]]])
+                                            (ui/dropdown-menu-separator)))
+
+                           :head-render (fn [] (when (and fetching? (not (string/blank? q)))
+                                                 [:b.flex.items-center.justify-center.py-4
+                                                  (ui/tabler-icon "loader" {:class "animate-spin"})]))
               ;:foot-render (fn [] [:b "footer"])
               ;:foot-render (fn [] [:b "footer"])
 
 
-              :content-props
-              {:align "start"
-               :class "w-80"}})))))
+                           :content-props
+                           {:align "start"
+                            :class "w-80"}})))))
 
 
    [:hr]
    [:hr]
 
 
@@ -146,50 +147,50 @@
          [open? set-open!] (rum/use-state false)]
          [open? set-open!] (rum/use-state false)]
 
 
      (ui/card
      (ui/card
-       (ui/card-header
-         (ui/card-title "Basic")
-         (ui/card-description "x multiselect for shui"))
-       (ui/card-content
-         [:label.block.flex.items-center.pb-3.cursor-pointer
-          (ui/checkbox {:checked search?
-                        :on-click #(set-search? (not search?))})
-          [:small.pl-2 "Enable basic search input"]]
+      (ui/card-header
+       (ui/card-title "Basic")
+       (ui/card-description "x multiselect for shui"))
+      (ui/card-content
+       [:label.block.flex.items-center.pb-3.cursor-pointer
+        (ui/checkbox {:checked search?
+                      :on-click #(set-search? (not search?))})
+        [:small.pl-2 "Enable basic search input"]]
          ;; Basic
          ;; Basic
-         (ui/dropdown-menu
-           {:open open?}
+       (ui/dropdown-menu
+        {:open open?}
            ;; trigger
            ;; trigger
-           (ui/dropdown-menu-trigger
-             [:p.border.p-2.rounded.w-full.cursor-pointer
-              {:on-click #(set-open! true)}
-              (for [{:keys [key value class]} selected-items]
-                (ui/badge {:variant :secondary :class class} (str "#" key " " value)))
-              (ui/button {:variant :link :size :sm} "+")])
+        (ui/dropdown-menu-trigger
+         [:p.border.p-2.rounded.w-full.cursor-pointer
+          {:on-click #(set-open! true)}
+          (for [{:keys [key value class]} selected-items]
+            (ui/badge {:variant :secondary :class class} (str "#" key " " value)))
+          (ui/button {:variant :link :size :sm} "+")])
            ;; content
            ;; content
-           (x-select-content items selected-items
-             {:close! #(set-open! false)
-              :search-enabled? search?
-              :search-key-render (fn [q {:keys [items]}]
-                                   (when (and (not (string/blank? q))
-                                           (not (seq items)))
-                                     [:b.flex.items-center.justify-center.py-4.gap-2.font-normal.opacity-80
-                                      (ui/tabler-icon "lemon") [:small "No fruits!"]]))
-              :on-chosen on-chosen
-              :value-render (fn [v {:keys [selected?]}]
-                              (if selected?
-                                [:b.text-red-800 v]
-                                [:b.text-green-800 v]))
-              :content-props
-              {:class "w-48"}})))))
+        (x-select-content items selected-items
+                          {:close! #(set-open! false)
+                           :search-enabled? search?
+                           :search-key-render (fn [q {:keys [items]}]
+                                                (when (and (not (string/blank? q))
+                                                           (not (seq items)))
+                                                  [:b.flex.items-center.justify-center.py-4.gap-2.font-normal.opacity-80
+                                                   (ui/tabler-icon "lemon") [:small "No fruits!"]]))
+                           :on-chosen on-chosen
+                           :value-render (fn [v {:keys [selected?]}]
+                                           (if selected?
+                                             [:b.text-red-800 v]
+                                             [:b.text-green-800 v]))
+                           :content-props
+                           {:class "w-48"}})))))
 
 
    [:hr]
    [:hr]
 
 
    (let [[items set-items!]
    (let [[items set-items!]
          (rum/use-state
          (rum/use-state
-           [{:key 1 :value "Apple" :class "bg-gray-800 text-gray-50"}
-            {:key 2 :value "Orange" :class "bg-orange-700 text-gray-50"}
-            nil
-            {:key 3 :value "Pear"}
-            {:key 4 :value "Banana" :class "bg-yellow-700 text-gray-700"}])
+          [{:key 1 :value "Apple" :class "bg-gray-800 text-gray-50"}
+           {:key 2 :value "Orange" :class "bg-orange-700 text-gray-50"}
+           nil
+           {:key 3 :value "Pear"}
+           {:key 4 :value "Banana" :class "bg-yellow-700 text-gray-700"}])
 
 
          [selected-items set-selected-items!]
          [selected-items set-selected-items!]
          (rum/use-state [(last items) (first items)])
          (rum/use-state [(last items) (first items)])
@@ -202,55 +203,54 @@
          [open? set-open!] (rum/use-state false)]
          [open? set-open!] (rum/use-state false)]
 
 
      (ui/card
      (ui/card
-       (ui/card-header
-         (ui/card-title "Search & Custom")
-         (ui/card-description "x multiselect for shui"))
-       (ui/card-content
+      (ui/card-header
+       (ui/card-title "Search & Custom")
+       (ui/card-description "x multiselect for shui"))
+      (ui/card-content
 
 
          ;; Basic
          ;; Basic
-         (ui/dropdown-menu
-           {:open open?}
+       (ui/dropdown-menu
+        {:open open?}
            ;; trigger
            ;; trigger
-           (ui/dropdown-menu-trigger
-             [:p.border.p-2.rounded.w-full.cursor-pointer
-              {:on-click #(set-open! true)}
-              (for [{:keys [key value class]} selected-items]
-                (ui/badge {:variant :secondary :class class} (str "#" key " " value)))
-              (ui/button {:variant :link :size :sm} "+")])
+        (ui/dropdown-menu-trigger
+         [:p.border.p-2.rounded.w-full.cursor-pointer
+          {:on-click #(set-open! true)}
+          (for [{:keys [key value class]} selected-items]
+            (ui/badge {:variant :secondary :class class} (str "#" key " " value)))
+          (ui/button {:variant :link :size :sm} "+")])
            ;; content
            ;; content
-           (x-select-content items selected-items
-             {;; test item render
-              :open? open?
-              :close! #(set-open! false)
-              :search-enabled? true
-              :item-render (fn [item {:keys [selected?]}]
-                             (if item
-                               (ui/dropdown-menu-checkbox-item
-                                 {:checked selected?
-                                  :on-click (fn []
-                                              (if selected?
-                                                (rm-item! item)
-                                                (add-item! item)))}
-                                 (:value item))
-                               (ui/dropdown-menu-separator)))
-
-              :search-key-render
-              (fn [k {:keys [items x-item exist-fn]}]
-                (when (and
-                        (not (string/blank? k))
-                        (not (exist-fn)))
-                  (x-item
-                    {:on-click (fn []
-                                 (ui/toast! (str "Create: " k) :warning)
-                                 (set-open! false))}
-                    (str "+ create: " k))))
+        (x-select-content items selected-items
+                          {;; test item render
+                           :open? open?
+                           :close! #(set-open! false)
+                           :search-enabled? true
+                           :item-render (fn [item {:keys [selected?]}]
+                                          (if item
+                                            (ui/dropdown-menu-checkbox-item
+                                             {:checked selected?
+                                              :on-click (fn []
+                                                          (if selected?
+                                                            (rm-item! item)
+                                                            (add-item! item)))}
+                                             (:value item))
+                                            (ui/dropdown-menu-separator)))
+
+                           :search-key-render
+                           (fn [k {:keys [items x-item exist-fn]}]
+                             (when (and
+                                    (not (string/blank? k))
+                                    (not (exist-fn)))
+                               (x-item
+                                {:on-click (fn []
+                                             (ui/toast! (str "Create: " k) :warning)
+                                             (set-open! false))}
+                                (str "+ create: " k))))
 
 
               ;:head-render (fn [] [:b "header"])
               ;:head-render (fn [] [:b "header"])
               ;:foot-render (fn [] [:b "footer"])
               ;:foot-render (fn [] [:b "footer"])
-              :content-props
-              {:align "start"
-               :class "w-48"}})))))
-   ])
+                           :content-props
+                           {:align "start"
+                            :class "w-48"}})))))])
 
 
 (rum/defc icon-picker-demo
 (rum/defc icon-picker-demo
   []
   []
@@ -282,27 +282,27 @@
             [:a.underline
             [:a.underline
              {:on-click
              {:on-click
               #(ui/popup-show! %
               #(ui/popup-show! %
-                 (fn [_config]
-                   [:div.max-h-72.overflow-auto.p-1
-                    (emojis-cp (take 80 emojis)
-                      {:on-chosen
-                       (fn [_ t]
-                         (set-emoji! t)
-                         (ui/popup-hide-all!))})])
-                 {:content-props {:class "w-72 p-0"}
-                  :as-dropdown? true})}
+                               (fn [_config]
+                                 [:div.max-h-72.overflow-auto.p-1
+                                  (emojis-cp (take 80 emojis)
+                                             {:on-chosen
+                                              (fn [_ t]
+                                                (set-emoji! t)
+                                                (ui/popup-hide-all!))})])
+                               {:content-props {:class "w-72 p-0"}
+                                :as-dropdown? true})}
              (if emoji [:strong.px-1.text-6xl [:em-emoji emoji]] "emoji :O")] "."])]
              (if emoji [:strong.px-1.text-6xl [:em-emoji emoji]] "emoji :O")] "."])]
      [:<>
      [:<>
       (emoji-picker nil)
       (emoji-picker nil)
 
 
       [:p.py-4
       [:p.py-4
        (ui/button
        (ui/button
-         {:variant :secondary
-          :on-click #(ui/popup-show! %
-                       (fn []
-                         [:p.p-4
-                          (emoji-picker true)]))}
-         "Play a nested x popup.")]
+        {:variant :secondary
+         :on-click #(ui/popup-show! %
+                                    (fn []
+                                      [:p.p-4
+                                       (emoji-picker true)]))}
+        "Play a nested x popup.")]
 
 
       [:p.py-4
       [:p.py-4
        (let [gen-content
        (let [gen-content
@@ -312,60 +312,60 @@
                 (emoji-picker true)
                 (emoji-picker true)
                 [:strong.px-1.text-6xl q]])]
                 [:strong.px-1.text-6xl q]])]
          (ui/input
          (ui/input
-           {:placeholder "Select a fruit."
-            :ref *q-ref
-            :value q
-            :on-change (fn [^js e]
-                         (let [val (.-value (.-target e))]
-                           (set-q! val)
-                           (update-popup! :select-a-fruit-input [:content] (gen-content val))))
-            :class "w-1/5"
-            :on-focus (fn [^js e]
-                        (let [id :select-a-fruit-input
-                              [_ popup] (get-popup id)]
-                          (if (not popup)
-                            (ui/popup-show! (.-target e)
-                              (gen-content q)
-                              {:id id
-                               :align "start"
-                               :content-props
-                               {:class "x-input-popup-content"
-                                :onPointerDownOutside
-                                (fn [^js e]
-                                  (js/console.log "===>> onPointerDownOutside:" e (rum/deref *q-ref))
-                                  (when-let [q-ref (rum/deref *q-ref)]
-                                    (let [^js target (or (.-relatedTarget e)
-                                                       (.-target e))]
-                                      (js/console.log "t:" target)
-                                      (when (and
-                                              (not (.contains q-ref target))
-                                              (not (.closest target ".x-input-popup-content")))
-                                        (ui/popup-hide! id)))))
-                                :onOpenAutoFocus #(.preventDefault %)}})
+          {:placeholder "Select a fruit."
+           :ref *q-ref
+           :value q
+           :on-change (fn [^js e]
+                        (let [val (.-value (.-target e))]
+                          (set-q! val)
+                          (update-popup! :select-a-fruit-input [:content] (gen-content val))))
+           :class "w-1/5"
+           :on-focus (fn [^js e]
+                       (let [id :select-a-fruit-input
+                             [_ popup] (get-popup id)]
+                         (if (not popup)
+                           (ui/popup-show! (.-target e)
+                                           (gen-content q)
+                                           {:id id
+                                            :align "start"
+                                            :content-props
+                                            {:class "x-input-popup-content"
+                                             :onPointerDownOutside
+                                             (fn [^js e]
+                                               (js/console.log "===>> onPointerDownOutside:" e (rum/deref *q-ref))
+                                               (when-let [q-ref (rum/deref *q-ref)]
+                                                 (let [^js target (or (.-relatedTarget e)
+                                                                      (.-target e))]
+                                                   (js/console.log "t:" target)
+                                                   (when (and
+                                                          (not (.contains q-ref target))
+                                                          (not (.closest target ".x-input-popup-content")))
+                                                     (ui/popup-hide! id)))))
+                                             :onOpenAutoFocus #(.preventDefault %)}})
 
 
                             ;; update content
                             ;; update content
-                            (update-popup! id [:content]
-                              (gen-content q)))))
+                           (update-popup! id [:content]
+                                          (gen-content q)))))
             ;:on-blur     (fn [^js e]
             ;:on-blur     (fn [^js e]
             ;               (let [^js target (.-relatedTarget e)]
             ;               (let [^js target (.-relatedTarget e)]
             ;                 (js/console.log "==>>>" target)
             ;                 (js/console.log "==>>>" target)
             ;                 (when-not (.closest target ".x-input-popup-content")
             ;                 (when-not (.closest target ".x-input-popup-content")
             ;                   (hide-x-popup! :select-a-fruit-input))))
             ;                   (hide-x-popup! :select-a-fruit-input))))
-            }))]
+           }))]
 
 
       [:div.w-full.p-4.border.rounded.dotted.h-48.mt-8.bg-gray-02
       [:div.w-full.p-4.border.rounded.dotted.h-48.mt-8.bg-gray-02
        {:on-click #(ui/popup-show! %
        {:on-click #(ui/popup-show! %
-                     (->> (range 8)
-                       (map (fn [it]
-                              (ui/dropdown-menu-item
-                                {:on-select (fn []
-                                              (ui/toast! it)
-                                              (ui/popup-hide-all!))}
-                                [:strong it]))))
-                     {:as-dropdown? true
-                      :content-props {:class "w-48"}})
+                                   (->> (range 8)
+                                        (map (fn [it]
+                                               (ui/dropdown-menu-item
+                                                {:on-select (fn []
+                                                              (ui/toast! it)
+                                                              (ui/popup-hide-all!))}
+                                                [:strong it]))))
+                                   {:as-dropdown? true
+                                    :content-props {:class "w-48"}})
         :on-context-menu #(ui/popup-show! %
         :on-context-menu #(ui/popup-show! %
-                            [:h1.text-3xl.font-bold "hi x popup for custom context menu!"])}]])])
+                                          [:h1.text-3xl.font-bold "hi x popup for custom context menu!"])}]])])
 
 
 (rum/defc custom-trigger-content
 (rum/defc custom-trigger-content
   []
   []
@@ -381,17 +381,16 @@
    [:h1.text-3xl.font-bold.border-b.pb-4 "Sample dropdown/menu trigger"]
    [:h1.text-3xl.font-bold.border-b.pb-4 "Sample dropdown/menu trigger"]
    [:div.py-4
    [:div.py-4
     (ui/dropdown-menu
     (ui/dropdown-menu
-      (ui/dropdown-menu-trigger
-        {:as-child true}
-        (ui/trigger-child-wrap
-          {:class "border p-6 border"}
-          (custom-trigger-content)))
-      (ui/dropdown-menu-content
-        (ui/dropdown-menu-item "A item")
-        (ui/dropdown-menu-item "B item")
-        (ui/dropdown-menu-item "C item")))]
-   ])
+     (ui/dropdown-menu-trigger
+      {:as-child true}
+      (ui/trigger-child-wrap
+       {:class "border p-6 border"}
+       (custom-trigger-content)))
+     (ui/dropdown-menu-content
+      (ui/dropdown-menu-item "A item")
+      (ui/dropdown-menu-item "B item")
+      (ui/dropdown-menu-item "C item")))]])
 
 
 (rum/defc page
 (rum/defc page
   []
   []
-  (sample-dropdown-trigger))
+  (sample-dropdown-trigger))

+ 12 - 8
deps/shui/src/logseq/shui/dialog/core.cljs

@@ -2,6 +2,7 @@
   (:require [daiquiri.interpreter :refer [interpret]]
   (:require [daiquiri.interpreter :refer [interpret]]
             [logseq.shui.base.core :as base]
             [logseq.shui.base.core :as base]
             [logseq.shui.form.core :as form]
             [logseq.shui.form.core :as form]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.util :as util]
             [logseq.shui.util :as util]
             [medley.core :as medley]
             [medley.core :as medley]
             [promesa.core :as p]
             [promesa.core :as p]
@@ -130,10 +131,11 @@
                       :close :align :on-open-change :open? :root-props :content-props)
                       :close :align :on-open-change :open? :root-props :content-props)
         props (assoc-in props [:overlay-props :data-align] (name (or align :center)))]
         props (assoc-in props [:overlay-props :data-align] (name (or align :center)))]
 
 
-    (rum/use-effect!
+    (hooks/use-effect!
      (fn []
      (fn []
        (when (false? open?)
        (when (false? open?)
-         (js/setTimeout #(detach-modal! id) 128)))
+         (let [timeout (js/setTimeout #(detach-modal! id) 128)]
+           #(js/clearTimeout timeout))))
      [open?])
      [open?])
 
 
     (dialog
     (dialog
@@ -171,10 +173,11 @@
   (let [{:keys [id title description content footer deferred open?]} config
   (let [{:keys [id title description content footer deferred open?]} config
         props (dissoc config :id :title :description :content :footer :deferred :open? :alert?)]
         props (dissoc config :id :title :description :content :footer :deferred :open? :alert?)]
 
 
-    (rum/use-effect!
+    (hooks/use-effect!
      (fn []
      (fn []
        (when (false? open?)
        (when (false? open?)
-         (js/setTimeout #(detach-modal! id) 128)))
+         (let [timeout (js/setTimeout #(detach-modal! id) 128)]
+           #(js/clearTimeout timeout))))
      [open?])
      [open?])
 
 
     (alert-dialog
     (alert-dialog
@@ -209,14 +212,15 @@
         *ok-ref (rum/use-ref nil)
         *ok-ref (rum/use-ref nil)
         *reminder-ref (rum/use-ref nil)]
         *reminder-ref (rum/use-ref nil)]
 
 
-    (rum/use-effect!
+    (hooks/use-effect!
      (fn []
      (fn []
        (when ready?
        (when ready?
-         (js/setTimeout
-          #(some-> (rum/deref *ok-ref) (.focus)) 128)))
+         (let [timeout (js/setTimeout
+                        #(some-> (rum/deref *ok-ref) (.focus)) 128)]
+           #(js/clearTimeout timeout))))
      [ready?])
      [ready?])
 
 
-    (rum/use-effect!
+    (hooks/use-effect!
      (fn []
      (fn []
        (try
        (try
          (if-let [reminder-v (and reminder? (js/localStorage.getItem (str id)))]
          (if-let [reminder-v (and reminder? (js/localStorage.getItem (str id)))]

+ 13 - 7
src/main/frontend/hooks.cljs → deps/shui/src/logseq/shui/hooks.cljs

@@ -1,4 +1,4 @@
-(ns frontend.hooks
+(ns logseq.shui.hooks
   "React custom hooks."
   "React custom hooks."
   (:refer-clojure :exclude [ref deref])
   (:refer-clojure :exclude [ref deref])
   (:require [frontend.common.missionary :as c.m]
   (:require [frontend.common.missionary :as c.m]
@@ -26,17 +26,23 @@
   "setup-fn will be invoked every render of component when no deps arg provided"
   "setup-fn will be invoked every render of component when no deps arg provided"
   ([setup-fn] (rum/use-effect! setup-fn))
   ([setup-fn] (rum/use-effect! setup-fn))
   ([setup-fn deps & {:keys [equal-fn]}]
   ([setup-fn deps & {:keys [equal-fn]}]
-   (rum/use-effect! setup-fn (if (empty? deps)
-                               deps
-                               #js[(memo-deps equal-fn deps)]))))
+   (rum/use-effect! (fn [& deps]
+                      (let [result (apply setup-fn deps)]
+                        (when (fn? result) result)))
+                    (if (empty? deps)
+                      deps
+                      #js[(memo-deps equal-fn deps)]))))
 
 
 #_{:clj-kondo/ignore [:discouraged-var]}
 #_{:clj-kondo/ignore [:discouraged-var]}
 (defn use-layout-effect!
 (defn use-layout-effect!
   ([setup-fn] (rum/use-layout-effect! setup-fn))
   ([setup-fn] (rum/use-layout-effect! setup-fn))
   ([setup-fn deps & {:keys [equal-fn]}]
   ([setup-fn deps & {:keys [equal-fn]}]
-   (rum/use-layout-effect! setup-fn (if (empty? deps)
-                                      deps
-                                      #js[(memo-deps equal-fn deps)]))))
+   (rum/use-layout-effect! (fn [& deps]
+                             (let [result (apply setup-fn deps)]
+                               (when (fn? result) result)))
+                           (if (empty? deps)
+                             deps
+                             #js[(memo-deps equal-fn deps)]))))
 
 
 #_{:clj-kondo/ignore [:discouraged-var]}
 #_{:clj-kondo/ignore [:discouraged-var]}
 (defn use-callback
 (defn use-callback

+ 114 - 113
deps/shui/src/logseq/shui/select/multi.cljs

@@ -1,14 +1,15 @@
 (ns logseq.shui.select.multi
 (ns logseq.shui.select.multi
   (:require [clojure.string :as string]
   (:require [clojure.string :as string]
-            [rum.core :as rum]
+            [logseq.shui.form.core :as form]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.popup.core :as popup]
             [logseq.shui.popup.core :as popup]
-            [logseq.shui.form.core :as form]))
+            [rum.core :as rum]))
 
 
 (defn- get-k [item]
 (defn- get-k [item]
   (if (map? item)
   (if (map? item)
     (some->> ((juxt :id :key :label) item)
     (some->> ((juxt :id :key :label) item)
-      (remove nil?)
-      (first))
+             (remove nil?)
+             (first))
     item))
     item))
 
 
 (defn- get-v
 (defn- get-v
@@ -21,27 +22,27 @@
   (let [*el (rum/use-ref nil)
   (let [*el (rum/use-ref nil)
         [down set-down!] (rum/use-state 0)]
         [down set-down!] (rum/use-state 0)]
 
 
-    (rum/use-effect!
-      (fn []
-        (when-let [^js item (and (> down 0)
-                              (some-> (rum/deref *el)
-                                (.closest ".head")
-                                (.-nextSibling)))]
-          (some-> (if valid-search-key? (.-nextSibling item) item)
-            (.focus))))
-      [down])
+    (hooks/use-effect!
+     (fn []
+       (when-let [^js item (and (> down 0)
+                                (some-> (rum/deref *el)
+                                        (.closest ".head")
+                                        (.-nextSibling)))]
+         (some-> (if valid-search-key? (.-nextSibling item) item)
+                 (.focus))))
+     [down])
 
 
     [:div.search-input
     [:div.search-input
      {:ref *el}
      {:ref *el}
      (form/input
      (form/input
-       (merge {:placeholder "search"
-               :on-key-up #(case (.-key %)
-                             "ArrowDown" (set-down! (inc down))
-                             "ArrowUp" nil
-                             "Enter" (when (fn? on-enter) (on-enter))
-                             :dune)
-               :auto-focus true}
-         input-props))]))
+      (merge {:placeholder "search"
+              :on-key-up #(case (.-key %)
+                            "ArrowDown" (set-down! (inc down))
+                            "ArrowUp" nil
+                            "Enter" (when (fn? on-enter) (on-enter))
+                            :dune)
+              :auto-focus true}
+             input-props))]))
 
 
 (defn- simple-search-fn
 (defn- simple-search-fn
   [items q]
   [items q]
@@ -49,8 +50,8 @@
     (if (string/blank? q)
     (if (string/blank? q)
       items
       items
       (filter #(some-> (get-v %)
       (filter #(some-> (get-v %)
-                 (string/lower-case)
-                 (string/includes? q)) items))))
+                       (string/lower-case)
+                       (string/includes? q)) items))))
 
 
 (rum/defc x-select-content
 (rum/defc x-select-content
   [items selected-items & {:keys [on-chosen item-render value-render
   [items selected-items & {:keys [on-chosen item-render value-render
@@ -70,8 +71,8 @@
                               (when (and search-enabled? (not= "INPUT" (.-nodeName target)))
                               (when (and search-enabled? (not= "INPUT" (.-nodeName target)))
                                 ;; focus search input
                                 ;; focus search input
                                 (some-> (get-content-el target)
                                 (some-> (get-content-el target)
-                                  (.querySelector "input")
-                                  (.focus))))
+                                        (.querySelector "input")
+                                        (.focus))))
         items (if search-enabled?
         items (if search-enabled?
                 (if (fn? search-fn)
                 (if (fn? search-fn)
                   (search-fn items search-key1)
                   (search-fn items search-key1)
@@ -79,100 +80,100 @@
                 items)
                 items)
         close1! #(when (fn? close!) (close!))]
         close1! #(when (fn? close!) (close!))]
 
 
-    (rum/use-effect!
-      (fn []
-        (when (fn? on-search-key-change)
-          (on-search-key-change search-key1')))
-      [search-key1'])
+    (hooks/use-effect!
+     (fn []
+       (when (fn? on-search-key-change)
+         (on-search-key-change search-key1')))
+     [search-key1'])
 
 
-    (rum/use-effect!
-      (fn []
-        (when-let [t (when (and search-enabled? (false? open?))
-                       (js/setTimeout #(set-search-key! "") 500))]
-          #(js/clearTimeout t)))
-      [open?])
+    (hooks/use-effect!
+     (fn []
+       (when-let [t (when (and search-enabled? (false? open?))
+                      (js/setTimeout #(set-search-key! "") 500))]
+         #(js/clearTimeout t)))
+     [open?])
 
 
     (x-content
     (x-content
-      (merge
-        {:onInteractOutside close1!
-         :onEscapeKeyDown close1!
-         :on-key-down (fn [^js e]
-                        (when-let [^js target (.-target e)]
-                          (case (.-key e)
-                            "ArrowUp"
-                            (when (= (some-> (get-item-nodes target) (first))
-                                    js/document.activeElement)
-                              (focus-search-input! target))
-                            "l" (when (or (.-metaKey e) (.-ctrlKey e))
-                                  (focus-search-input! target))
-                            :dune)))
-         :class (str (:class content-props)
-                  " ui__multi-select-content"
-                  (when valid-search-key? " has-search-key"))}
-        (dissoc content-props :class))
+     (merge
+      {:onInteractOutside close1!
+       :onEscapeKeyDown close1!
+       :on-key-down (fn [^js e]
+                      (when-let [^js target (.-target e)]
+                        (case (.-key e)
+                          "ArrowUp"
+                          (when (= (some-> (get-item-nodes target) (first))
+                                   js/document.activeElement)
+                            (focus-search-input! target))
+                          "l" (when (or (.-metaKey e) (.-ctrlKey e))
+                                (focus-search-input! target))
+                          :dune)))
+       :class (str (:class content-props)
+                   " ui__multi-select-content"
+                   (when valid-search-key? " has-search-key"))}
+      (dissoc content-props :class))
       ;; header
       ;; header
-      (when (or search-enabled? (fn? head-render))
-        [:div.head
-         {:ref *head-ref}
-         (when search-enabled?
-           (search-input
-             {:value search-key1
-              :on-key-down (fn [^js e]
-                             (.stopPropagation e)
-                             (case (.-key e)
-                               "Escape" (if (string/blank? search-key1)
-                                          (some-> (.-target e) (.closest "[data-radix-menu-content]") (.focus))
-                                          (set-search-key! ""))
-                               :dune))
-              :on-change #(set-search-key! (.-value (.-target %)))}
+     (when (or search-enabled? (fn? head-render))
+       [:div.head
+        {:ref *head-ref}
+        (when search-enabled?
+          (search-input
+           {:value search-key1
+            :on-key-down (fn [^js e]
+                           (.stopPropagation e)
+                           (case (.-key e)
+                             "Escape" (if (string/blank? search-key1)
+                                        (some-> (.-target e) (.closest "[data-radix-menu-content]") (.focus))
+                                        (set-search-key! ""))
+                             :dune))
+            :on-change #(set-search-key! (.-value (.-target %)))}
 
 
-             {:on-enter (fn []
-                          (when-let [head-el (and (not (string/blank? search-key1'))
-                                               (rum/deref *head-ref))]
-                            (when-let [^js item (.-nextSibling head-el)]
-                              (when (contains? #{"menuitemcheckbox" "menuitem"}
-                                      (.getAttribute item "role"))
-                                (.click item)))))
-              :valid-search-key? valid-search-key?}))
-         (when head-render (head-render))])
+           {:on-enter (fn []
+                        (when-let [head-el (and (not (string/blank? search-key1'))
+                                                (rum/deref *head-ref))]
+                          (when-let [^js item (.-nextSibling head-el)]
+                            (when (contains? #{"menuitemcheckbox" "menuitem"}
+                                             (.getAttribute item "role"))
+                              (.click item)))))
+            :valid-search-key? valid-search-key?}))
+        (when head-render (head-render))])
       ;; items
       ;; items
-      (for [item items
-            :let [selected? (some #(let [k (get-k item)
-                                         k' (get-k %)]
-                                     (or (= item %)
-                                       (and (not (nil? k))
-                                         (not (nil? k'))
-                                         (= k k'))))
-                              selected-items)]]
-        (if (fn? item-render)
-          (item-render item {:x-item x-item :selected? selected?})
-          (let [k (get-k item)
-                v (get-v item)]
-            (when k
-              (let [opts {:selected? selected?}
-                    on-click' (:on-click item-props)
-                    on-click (fn [e]
+     (for [item items
+           :let [selected? (some #(let [k (get-k item)
+                                        k' (get-k %)]
+                                    (or (= item %)
+                                        (and (not (nil? k))
+                                             (not (nil? k'))
+                                             (= k k'))))
+                                 selected-items)]]
+       (if (fn? item-render)
+         (item-render item {:x-item x-item :selected? selected?})
+         (let [k (get-k item)
+               v (get-v item)]
+           (when k
+             (let [opts {:selected? selected?}
+                   on-click' (:on-click item-props)
+                   on-click (fn [e]
                                ;; TODO: return value
                                ;; TODO: return value
-                               (when (fn? on-click') (on-click' e))
-                               (when (fn? on-chosen)
-                                 (on-chosen item opts)))]
-                (x-item (merge {:data-k k :on-click on-click} item-props)
-                  [:span.flex.items-center.gap-2.w-full
-                   (form/checkbox {:checked selected?})
-                   (let [v' (if (fn? v) (v item opts) v)]
-                     (if (fn? value-render)
-                       (value-render v' (assoc opts :item item)) v'))]))))))
+                              (when (fn? on-click') (on-click' e))
+                              (when (fn? on-chosen)
+                                (on-chosen item opts)))]
+               (x-item (merge {:data-k k :on-click on-click} item-props)
+                       [:span.flex.items-center.gap-2.w-full
+                        (form/checkbox {:checked selected?})
+                        (let [v' (if (fn? v) (v item opts) v)]
+                          (if (fn? value-render)
+                            (value-render v' (assoc opts :item item)) v'))]))))))
 
 
-      (when (and search-enabled?
-              (fn? search-key-render))
-        (let [exist-fn (fn []
-                         (and (not (string/blank? search-key1))
-                           (seq items)
-                           (contains? (into #{} (map #(some-> (get-v %) (string/lower-case)) items))
-                             (string/lower-case search-key1))))]
-          (search-key-render search-key1
-            {:items items :x-item x-item :exist-fn exist-fn})))
+     (when (and search-enabled?
+                (fn? search-key-render))
+       (let [exist-fn (fn []
+                        (and (not (string/blank? search-key1))
+                             (seq items)
+                             (contains? (into #{} (map #(some-> (get-v %) (string/lower-case)) items))
+                                        (string/lower-case search-key1))))]
+         (search-key-render search-key1
+                            {:items items :x-item x-item :exist-fn exist-fn})))
       ;; footer
       ;; footer
-      (when (fn? foot-render)
-        [:div.foot
-         (foot-render)]))))
+     (when (fn? foot-render)
+       [:div.foot
+        (foot-render)]))))

+ 3 - 2
deps/shui/src/logseq/shui/table/core.cljc

@@ -2,6 +2,7 @@
   "Table"
   "Table"
   (:require [clojure.set :as set]
   (:require [clojure.set :as set]
             [dommy.core :refer-macros [sel1]]
             [dommy.core :refer-macros [sel1]]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.table.impl :as impl]
             [logseq.shui.table.impl :as impl]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 
@@ -147,7 +148,7 @@
 ;; FIXME: ux
 ;; FIXME: ux
 (defn- use-sticky-element!
 (defn- use-sticky-element!
   [^js/HTMLElement container target-ref]
   [^js/HTMLElement container target-ref]
-  (rum/use-effect!
+  (hooks/use-effect!
    (fn []
    (fn []
      (let [^js el (rum/deref target-ref)
      (let [^js el (rum/deref target-ref)
            ^js cls (.-classList el)
            ^js cls (.-classList el)
@@ -189,7 +190,7 @@
 ;; FIXME: another solution for the sticky header
 ;; FIXME: another solution for the sticky header
 (defn- use-sticky-element2!
 (defn- use-sticky-element2!
   [^js/HTMLDivElement target-ref]
   [^js/HTMLDivElement target-ref]
-  (rum/use-effect!
+  (hooks/use-effect!
    (fn []
    (fn []
      (let [^js target (rum/deref target-ref)
      (let [^js target (rum/deref target-ref)
            ^js container (or (.closest target ".sidebar-item-list") (get-main-scroll-container))
            ^js container (or (.closest target ".sidebar-item-list") (get-main-scroll-container))

+ 17 - 16
deps/shui/src/logseq/shui/toaster/core.cljs

@@ -1,8 +1,9 @@
 (ns logseq.shui.toaster.core
 (ns logseq.shui.toaster.core
-  (:require [rum.core :as rum]
+  (:require [cljs-bean.core :as bean]
             [daiquiri.interpreter :refer [interpret]]
             [daiquiri.interpreter :refer [interpret]]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.util :as util]
             [logseq.shui.util :as util]
-            [cljs-bean.core :as bean]))
+            [rum.core :as rum]))
 
 
 (defonce ^:private Toaster (util/lsui-wrap "Toaster"))
 (defonce ^:private Toaster (util/lsui-wrap "Toaster"))
 (defonce ^:private *toast (atom nil))
 (defonce ^:private *toast (atom nil))
@@ -23,22 +24,22 @@
   < rum/static
   < rum/static
   []
   []
   (let [^js js-toast (js/window.LSUI.useToast)]
   (let [^js js-toast (js/window.LSUI.useToast)]
-    (rum/use-effect!
-      (fn []
-        (reset! *toast {:toast   (.-toast js-toast)
-                        :dismiss (.-dismiss js-toast)
-                        :update  (.-update js-toast)})
-        #())
-      [])
+    (hooks/use-effect!
+     (fn []
+       (reset! *toast {:toast   (.-toast js-toast)
+                       :dismiss (.-dismiss js-toast)
+                       :update  (.-update js-toast)})
+       #())
+     [])
     [:<> (Toaster)]))
     [:<> (Toaster)]))
 
 
 (defn update-html-props
 (defn update-html-props
   [v]
   [v]
   (update-keys v
   (update-keys v
-    #(case %
-       :class :className
-       :for :htmlFor
-       %)))
+               #(case %
+                  :class :className
+                  :for :htmlFor
+                  %)))
 
 
 (defn interpret-vals
 (defn interpret-vals
   [config ks & args]
   [config ks & args]
@@ -46,7 +47,7 @@
             (let [v (get config k)
             (let [v (get config k)
                   v (if (fn? v) (apply v args) v)]
                   v (if (fn? v) (apply v args) v)]
               (if (vector? v) (assoc config k (interpret v)) config)))
               (if (vector? v) (assoc config k (interpret v)) config)))
-    config ks))
+          config ks))
 
 
 (defn toast!
 (defn toast!
   ([content-or-config] (toast! content-or-config :default nil))
   ([content-or-config] (toast! content-or-config :default nil))
@@ -56,12 +57,12 @@
      (let [config (if (map? content-or-config)
      (let [config (if (map? content-or-config)
                     content-or-config
                     content-or-config
                     (-> {:description content-or-config}
                     (-> {:description content-or-config}
-                      (merge (if (map? status) status {:variant status}))))
+                        (merge (if (map? status) status {:variant status}))))
            config (update-html-props (merge config opts))
            config (update-html-props (merge config opts))
            id (or (:id config) (gen-id))
            id (or (:id config) (gen-id))
            config (assoc config :id id)
            config (assoc config :id id)
            config (interpret-vals config [:title :description :action :icon]
            config (interpret-vals config [:title :description :action :icon]
-                    {:id id :dismiss! #(dismiss id) :update! #(toast! (assoc %1 :id id))})]
+                                  {:id id :dismiss! #(dismiss id) :update! #(toast! (assoc %1 :id id))})]
        (js->clj (toast (clj->js config))))
        (js->clj (toast (clj->js config))))
      :exception)))
      :exception)))
 
 

+ 1 - 1
gulpfile.js

@@ -161,7 +161,7 @@ const common = {
       stdio: 'inherit',
       stdio: 'inherit',
     })
     })
 
 
-    cp.execSync(`npx cap run ${mode} --external`, {
+    cp.execSync(`npx cap run ${mode}`, {
       stdio: 'inherit',
       stdio: 'inherit',
       env: Object.assign(process.env, {
       env: Object.assign(process.env, {
         LOGSEQ_APP_SERVER_URL,
         LOGSEQ_APP_SERVER_URL,

+ 1 - 1
ios/App/Podfile

@@ -1,6 +1,6 @@
 require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'
 require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'
 
 
-platform :ios, '13.0'
+platform :ios, '14.0'
 use_frameworks!
 use_frameworks!
 
 
 # workaround to avoid Xcode caching of Pods that requires
 # workaround to avoid Xcode caching of Pods that requires

+ 131 - 0
ios/App/Podfile.lock

@@ -0,0 +1,131 @@
+PODS:
+  - AgeEncryption (1.0.6)
+  - Alamofire (5.6.2)
+  - AWSCore (2.27.10)
+  - AWSS3 (2.27.10):
+    - AWSCore (= 2.27.10)
+  - Capacitor (7.2.0):
+    - CapacitorCordova
+  - CapacitorActionSheet (7.0.1):
+    - Capacitor
+  - CapacitorApp (7.0.1):
+    - Capacitor
+  - CapacitorCamera (7.0.1):
+    - Capacitor
+  - CapacitorClipboard (7.0.1):
+    - Capacitor
+  - CapacitorCordova (7.2.0)
+  - CapacitorFilesystem (7.0.1):
+    - Capacitor
+  - CapacitorHaptics (7.0.1):
+    - Capacitor
+  - CapacitorKeyboard (7.0.1):
+    - Capacitor
+  - CapacitorShare (7.0.1):
+    - Capacitor
+  - CapacitorSplashScreen (7.0.1):
+    - Capacitor
+  - CapacitorStatusBar (7.0.1):
+    - Capacitor
+  - CapacitorVoiceRecorder (5.0.0):
+    - Capacitor
+  - CapawesomeCapacitorBackgroundTask (7.0.1):
+    - Capacitor
+  - CapgoCapacitorNavigationBar (7.1.2):
+    - Capacitor
+  - LogseqCapacitorFileSync (5.0.2):
+    - AgeEncryption (~> 1.0.6)
+    - Alamofire
+    - AWSS3
+    - Capacitor
+  - SendIntent (0.0.1):
+    - Capacitor
+
+DEPENDENCIES:
+  - "Capacitor (from `../../node_modules/@capacitor/ios`)"
+  - "CapacitorActionSheet (from `../../node_modules/@capacitor/action-sheet`)"
+  - "CapacitorApp (from `../../node_modules/@capacitor/app`)"
+  - "CapacitorCamera (from `../../node_modules/@capacitor/camera`)"
+  - "CapacitorClipboard (from `../../node_modules/@capacitor/clipboard`)"
+  - "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
+  - "CapacitorFilesystem (from `../../node_modules/@capacitor/filesystem`)"
+  - "CapacitorHaptics (from `../../node_modules/@capacitor/haptics`)"
+  - "CapacitorKeyboard (from `../../node_modules/@capacitor/keyboard`)"
+  - "CapacitorShare (from `../../node_modules/@capacitor/share`)"
+  - "CapacitorSplashScreen (from `../../node_modules/@capacitor/splash-screen`)"
+  - "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)"
+  - CapacitorVoiceRecorder (from `../../node_modules/capacitor-voice-recorder`)
+  - "CapawesomeCapacitorBackgroundTask (from `../../node_modules/@capawesome/capacitor-background-task`)"
+  - "CapgoCapacitorNavigationBar (from `../../node_modules/@capgo/capacitor-navigation-bar`)"
+  - "LogseqCapacitorFileSync (from `../../node_modules/@logseq/capacitor-file-sync`)"
+  - SendIntent (from `../../node_modules/send-intent`)
+
+SPEC REPOS:
+  trunk:
+    - AgeEncryption
+    - Alamofire
+    - AWSCore
+    - AWSS3
+
+EXTERNAL SOURCES:
+  Capacitor:
+    :path: "../../node_modules/@capacitor/ios"
+  CapacitorActionSheet:
+    :path: "../../node_modules/@capacitor/action-sheet"
+  CapacitorApp:
+    :path: "../../node_modules/@capacitor/app"
+  CapacitorCamera:
+    :path: "../../node_modules/@capacitor/camera"
+  CapacitorClipboard:
+    :path: "../../node_modules/@capacitor/clipboard"
+  CapacitorCordova:
+    :path: "../../node_modules/@capacitor/ios"
+  CapacitorFilesystem:
+    :path: "../../node_modules/@capacitor/filesystem"
+  CapacitorHaptics:
+    :path: "../../node_modules/@capacitor/haptics"
+  CapacitorKeyboard:
+    :path: "../../node_modules/@capacitor/keyboard"
+  CapacitorShare:
+    :path: "../../node_modules/@capacitor/share"
+  CapacitorSplashScreen:
+    :path: "../../node_modules/@capacitor/splash-screen"
+  CapacitorStatusBar:
+    :path: "../../node_modules/@capacitor/status-bar"
+  CapacitorVoiceRecorder:
+    :path: "../../node_modules/capacitor-voice-recorder"
+  CapawesomeCapacitorBackgroundTask:
+    :path: "../../node_modules/@capawesome/capacitor-background-task"
+  CapgoCapacitorNavigationBar:
+    :path: "../../node_modules/@capgo/capacitor-navigation-bar"
+  LogseqCapacitorFileSync:
+    :path: "../../node_modules/@logseq/capacitor-file-sync"
+  SendIntent:
+    :path: "../../node_modules/send-intent"
+
+SPEC CHECKSUMS:
+  AgeEncryption: 23c203675d5e4883a18b133ab1d5e61ff29e0c18
+  Alamofire: d368e1ff8a298e6dde360e35a3e68e6c610e7204
+  AWSCore: dbad318b7ff0fd86fb8387d3ad1f30049c05bc58
+  AWSS3: 0cf2cedb263368f624ca721e5c56a8cb59fc44bc
+  Capacitor: 106e7a4205f4618d582b886a975657c61179138d
+  CapacitorActionSheet: 24609588961cc27c87e8b033be92b5eee65b5d4c
+  CapacitorApp: d63334c052278caf5d81585d80b21905c6f93f39
+  CapacitorCamera: eb8687d8687fed853598ec9460d94bcd5e16babe
+  CapacitorClipboard: b98aead5dc7ec595547fc2c5d75bacd2ae3338bc
+  CapacitorCordova: 5967b9ba03915ef1d585469d6e31f31dc49be96f
+  CapacitorFilesystem: 307f97c27a265edf8396a1c9c235592fd8572fe3
+  CapacitorHaptics: 70e47470fa1a6bd6338cd102552e3846b7f9a1b3
+  CapacitorKeyboard: 969647d0ca2e5c737d7300088e2517aa832434e2
+  CapacitorShare: 58d6c2da63b093e8693287b2d36db92435538435
+  CapacitorSplashScreen: 19cd3573e57507e02d6f34597a8c421e00931487
+  CapacitorStatusBar: 275cbf2f4dfc00388f519ef80c7ec22edda342c9
+  CapacitorVoiceRecorder: 872ea857b497ce2c71afe3e4eb5de0a74290c0db
+  CapawesomeCapacitorBackgroundTask: 834d797abc9933fac4354490d1a2f3c0e389b98d
+  CapgoCapacitorNavigationBar: 3a0e93a40b7da3d3cb74c0410bb761a1525406be
+  LogseqCapacitorFileSync: 541dcd5492b8aeb2257cce3d18bb9ed5800cfcad
+  SendIntent: 0a17b6984c4f27e9dfa56513267ba2c044a5a6c8
+
+PODFILE CHECKSUM: d9e4df9f9578f817a4148a5e29ee928f271e4cc8
+
+COCOAPODS: 1.11.2

+ 18 - 18
package.json

@@ -5,7 +5,7 @@
     "main": "static/electron.js",
     "main": "static/electron.js",
     "devDependencies": {
     "devDependencies": {
         "@axe-core/playwright": "=4.4.4",
         "@axe-core/playwright": "=4.4.4",
-        "@capacitor/cli": "^5.0.0",
+        "@capacitor/cli": "7.2.0",
         "@playwright/test": "=1.51.0",
         "@playwright/test": "=1.51.0",
         "@tailwindcss/aspect-ratio": "0.4.2",
         "@tailwindcss/aspect-ratio": "0.4.2",
         "@tailwindcss/forms": "0.5.3",
         "@tailwindcss/forms": "0.5.3",
@@ -85,21 +85,21 @@
         "postinstall": "yarn tldraw:build && yarn amplify:build && yarn ui:build"
         "postinstall": "yarn tldraw:build && yarn amplify:build && yarn ui:build"
     },
     },
     "dependencies": {
     "dependencies": {
-        "@capacitor/action-sheet": "^5.0.7",
-        "@capacitor/android": "^5.0.0",
-        "@capacitor/app": "^5.0.0",
-        "@capacitor/camera": "^5.0.0",
-        "@capacitor/clipboard": "^5.0.0",
-        "@capacitor/core": "^5.0.0",
-        "@capacitor/filesystem": "^5.0.0",
-        "@capacitor/haptics": "^5.0.0",
-        "@capacitor/ios": "^5.0.0",
-        "@capacitor/keyboard": "^5.0.0",
-        "@capacitor/share": "^5.0.0",
-        "@capacitor/splash-screen": "^5.0.0",
-        "@capacitor/status-bar": "^5.0.0",
-        "@capawesome/capacitor-background-task": "^5.0.0",
-        "@capgo/capacitor-navigation-bar": "^6.0.0",
+        "@capacitor/action-sheet": "7.0.1",
+        "@capacitor/android": "7.2.0",
+        "@capacitor/app": "7.0.1",
+        "@capacitor/camera": "7.0.1",
+        "@capacitor/clipboard": "7.0.1",
+        "@capacitor/core": "7.2.0",
+        "@capacitor/filesystem": "7.0.1",
+        "@capacitor/haptics": "7.0.1",
+        "@capacitor/ios": "7.2.0",
+        "@capacitor/keyboard": "7.0.1",
+        "@capacitor/share": "7.0.1",
+        "@capacitor/splash-screen": "7.0.1",
+        "@capacitor/status-bar": "7.0.1",
+        "@capawesome/capacitor-background-task": "7.0.1",
+        "@capgo/capacitor-navigation-bar": "7.1.2",
         "@dnd-kit/core": "^6.0.8",
         "@dnd-kit/core": "^6.0.8",
         "@dnd-kit/sortable": "^7.0.2",
         "@dnd-kit/sortable": "^7.0.2",
         "@emoji-mart/data": "^1.1.2",
         "@emoji-mart/data": "^1.1.2",
@@ -155,8 +155,8 @@
         "pixi.js": "6.2.0",
         "pixi.js": "6.2.0",
         "posthog-js": "1.10.2",
         "posthog-js": "1.10.2",
         "prop-types": "^15.7.2",
         "prop-types": "^15.7.2",
-        "react": "17.0.2",
-        "react-dom": "17.0.2",
+        "react": "18.3.1",
+        "react-dom": "18.3.1",
         "react-grid-layout": "0.16.6",
         "react-grid-layout": "0.16.6",
         "react-intersection-observer": "^9.3.5",
         "react-intersection-observer": "^9.3.5",
         "react-resize-context": "3.0.0",
         "react-resize-context": "3.0.0",

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

@@ -8,10 +8,10 @@
    [frontend.context.i18n :refer [t]]
    [frontend.context.i18n :refer [t]]
    [frontend.handler.assets :as assets-handler]
    [frontend.handler.assets :as assets-handler]
    [frontend.handler.notification :as notification]
    [frontend.handler.notification :as notification]
-   [frontend.hooks :as hooks]
    [frontend.state :as state]
    [frontend.state :as state]
    [frontend.ui :as ui]
    [frontend.ui :as ui]
    [frontend.util :as util]
    [frontend.util :as util]
+   [logseq.shui.hooks :as hooks]
    [logseq.shui.ui :as shui]
    [logseq.shui.ui :as shui]
    [medley.core :as medley]
    [medley.core :as medley]
    [promesa.core :as p]
    [promesa.core :as p]

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

@@ -55,7 +55,6 @@
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
-            [frontend.hooks :as hooks]
             [frontend.mixins :as mixins]
             [frontend.mixins :as mixins]
             [frontend.mobile.intent :as mobile-intent]
             [frontend.mobile.intent :as mobile-intent]
             [frontend.mobile.util :as mobile-util]
             [frontend.mobile.util :as mobile-util]
@@ -87,6 +86,7 @@
             [logseq.graph-parser.text :as text]
             [logseq.graph-parser.text :as text]
             [logseq.outliner.property :as outliner-property]
             [logseq.outliner.property :as outliner-property]
             [logseq.shui.dialog.core :as shui-dialog]
             [logseq.shui.dialog.core :as shui-dialog]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [medley.core :as medley]
             [medley.core :as medley]
             [promesa.core :as p]
             [promesa.core :as p]

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

@@ -3,9 +3,9 @@
             [frontend.components.header :as header]
             [frontend.components.header :as header]
             [frontend.context.i18n :refer [t]]
             [frontend.context.i18n :refer [t]]
             [frontend.handler.notification :as notification]
             [frontend.handler.notification :as notification]
-            [frontend.hooks :as hooks]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
+            [logseq.shui.hooks :as hooks]
             [reitit.frontend.easy :as rfe]
             [reitit.frontend.easy :as rfe]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 

+ 1 - 1
src/main/frontend/components/cmdk/core.cljs

@@ -18,7 +18,6 @@
             [frontend.handler.page :as page-handler]
             [frontend.handler.page :as page-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
-            [frontend.hooks :as hooks]
             [frontend.mixins :as mixins]
             [frontend.mixins :as mixins]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.modules.shortcut.utils :as shortcut-utils]
             [frontend.modules.shortcut.utils :as shortcut-utils]
@@ -36,6 +35,7 @@
             [logseq.common.util.block-ref :as block-ref]
             [logseq.common.util.block-ref :as block-ref]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
             [logseq.graph-parser.text :as text]
             [logseq.graph-parser.text :as text]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))

+ 1 - 1
src/main/frontend/components/cmdk/list_item.cljs

@@ -2,8 +2,8 @@
   (:require
   (:require
    ["remove-accents" :as remove-accents]
    ["remove-accents" :as remove-accents]
    [clojure.string :as string]
    [clojure.string :as string]
-   [frontend.hooks :as hooks]
    [goog.string :as gstring]
    [goog.string :as gstring]
+   [logseq.shui.hooks :as hooks]
    [logseq.shui.ui :as shui]
    [logseq.shui.ui :as shui]
    [rum.core :as rum]))
    [rum.core :as rum]))
 
 

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

@@ -32,7 +32,6 @@
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.user :as user-handler]
             [frontend.handler.user :as user-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
-            [frontend.hooks :as hooks]
             [frontend.mixins :as mixins]
             [frontend.mixins :as mixins]
             [frontend.mobile.action-bar :as action-bar]
             [frontend.mobile.action-bar :as action-bar]
             [frontend.mobile.footer :as footer]
             [frontend.mobile.footer :as footer]
@@ -53,6 +52,7 @@
             [logseq.common.util.namespace :as ns-util]
             [logseq.common.util.namespace :as ns-util]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
             [logseq.shui.dialog.core :as shui-dialog]
             [logseq.shui.dialog.core :as shui-dialog]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.popup.core :as shui-popup]
             [logseq.shui.popup.core :as shui-popup]
             [logseq.shui.toaster.core :as shui-toaster]
             [logseq.shui.toaster.core :as shui-toaster]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]

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

@@ -81,7 +81,8 @@
                           (let [block-uuids (state/get-selection-block-ids)]
                           (let [block-uuids (state/get-selection-block-ids)]
                             (shui/popup-hide!)
                             (shui/popup-hide!)
                             (shui/dialog-open!
                             (shui/dialog-open!
-                             #(export/export-blocks block-uuids {:whiteboard? false}))))}
+                             #(export/export-blocks block-uuids {:whiteboard? false
+                                                                 :export-type :selected-nodes}))))}
       (t :content/copy-export-as))
       (t :content/copy-export-as))
 
 
      (shui/dropdown-menu-item
      (shui/dropdown-menu-item
@@ -260,7 +261,8 @@
           {:key      "Copy as"
           {:key      "Copy as"
            :on-click (fn [_]
            :on-click (fn [_]
                        (shui/dialog-open!
                        (shui/dialog-open!
-                        #(export/export-blocks [block-id] {:whiteboard? false})))}
+                        #(export/export-blocks [block-id] {:whiteboard? false
+                                                           :export-type :block})))}
           (t :content/copy-export-as))
           (t :content/copy-export-as))
 
 
          (when-not property-default-value?
          (when-not property-default-value?

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

@@ -3,9 +3,9 @@
             ["@dnd-kit/sortable" :refer [useSortable arrayMove SortableContext verticalListSortingStrategy horizontalListSortingStrategy] :as sortable]
             ["@dnd-kit/sortable" :refer [useSortable arrayMove SortableContext verticalListSortingStrategy horizontalListSortingStrategy] :as sortable]
             ["@dnd-kit/utilities" :refer [CSS]]
             ["@dnd-kit/utilities" :refer [CSS]]
             [cljs-bean.core :as bean]
             [cljs-bean.core :as bean]
-            [frontend.hooks :as hooks]
             [frontend.rum :as r]
             [frontend.rum :as r]
             [frontend.state :as state]
             [frontend.state :as state]
+            [logseq.shui.hooks :as hooks]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 
 (def dnd-context (r/adapt-class DndContext))
 (def dnd-context (r/adapt-class DndContext))

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

@@ -18,7 +18,6 @@
             [frontend.handler.paste :as paste-handler]
             [frontend.handler.paste :as paste-handler]
             [frontend.handler.property.util :as pu]
             [frontend.handler.property.util :as pu]
             [frontend.handler.search :as search-handler]
             [frontend.handler.search :as search-handler]
-            [frontend.hooks :as hooks]
             [frontend.mixins :as mixins]
             [frontend.mixins :as mixins]
             [frontend.search :refer [fuzzy-search]]
             [frontend.search :refer [fuzzy-search]]
             [frontend.state :as state]
             [frontend.state :as state]
@@ -33,6 +32,7 @@
             [logseq.db :as ldb]
             [logseq.db :as ldb]
             [logseq.db.frontend.class :as db-class]
             [logseq.db.frontend.class :as db-class]
             [logseq.graph-parser.property :as gp-property]
             [logseq.graph-parser.property :as gp-property]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [react-draggable]
             [react-draggable]

+ 26 - 4
src/main/frontend/components/export.cljs

@@ -1,6 +1,7 @@
 (ns frontend.components.export
 (ns frontend.components.export
   (:require ["/frontend/utils" :as utils]
   (:require ["/frontend/utils" :as utils]
             [cljs-time.core :as t]
             [cljs-time.core :as t]
+            [cljs.pprint :as pprint]
             [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]
@@ -160,6 +161,20 @@
              current-repo top-level-ids {:remove-options text-remove-options :other-options text-other-options})
              current-repo top-level-ids {:remove-options text-remove-options :other-options text-other-options})
       "")))
       "")))
 
 
+(defn- <export-edn-helper
+  [root-block-uuids-or-page-uuid export-type]
+  (let [export-args (case export-type
+                      :page
+                      {:page-id [:block/uuid root-block-uuids-or-page-uuid]}
+                      :block
+                      {:block-id [:block/uuid (first root-block-uuids-or-page-uuid)]}
+                      :selected-nodes
+                      {:node-ids (mapv #(vector :block/uuid %) root-block-uuids-or-page-uuid)}
+                      {})]
+    (state/<invoke-db-worker :thread-api/export-edn
+                             (state/get-current-repo)
+                             (merge {:export-type export-type} export-args))))
+
 (defn- get-zoom-level
 (defn- get-zoom-level
   [page-uuid]
   [page-uuid]
   (let [uuid (:block/uuid (db/get-page page-uuid))
   (let [uuid (:block/uuid (db/get-page page-uuid))
@@ -225,7 +240,7 @@
                    (reset! (::text-indent-style state) (state/get-export-block-text-indent-style))
                    (reset! (::text-indent-style state) (state/get-export-block-text-indent-style))
                    (reset! (::text-other-options state) (state/get-export-block-text-other-options))
                    (reset! (::text-other-options state) (state/get-export-block-text-other-options))
                    (assoc state ::top-level-uuids top-level-uuids)))}
                    (assoc state ::top-level-uuids top-level-uuids)))}
-  [state _selection-ids {:keys [whiteboard?] :as options}]
+  [state _selection-ids {:keys [whiteboard? export-type] :as options}]
   (let [top-level-uuids (::top-level-uuids state)
   (let [top-level-uuids (::top-level-uuids state)
         tp @*export-block-type
         tp @*export-block-type
         *text-other-options (::text-other-options state)
         *text-other-options (::text-other-options state)
@@ -252,11 +267,18 @@
                                    (reset! *content (export-helper top-level-uuids))))
                                    (reset! *content (export-helper top-level-uuids))))
          (when-not (seq? top-level-uuids)
          (when-not (seq? top-level-uuids)
            (ui/button "PNG"
            (ui/button "PNG"
-                      :class "w-20"
+                      :class "mr-4 w-20"
                       :on-click #(do (reset! *export-block-type :png)
                       :on-click #(do (reset! *export-block-type :png)
                                      (reset! *content nil)
                                      (reset! *content nil)
-                                     (get-image-blob top-level-uuids (merge options {:transparent-bg? false}) (fn [blob] (reset! *content blob))))))])
-
+                                     (get-image-blob top-level-uuids (merge options {:transparent-bg? false}) (fn [blob] (reset! *content blob))))))
+         (when (config/db-based-graph?)
+           (ui/button "EDN"
+                      :class "w-20"
+                      :on-click #(do (reset! *export-block-type :edn)
+                                     (p/let [result (<export-edn-helper top-level-uuids export-type)
+                                             pull-data (with-out-str (pprint/pprint result))]
+                                       (when-not (= :export-edn-error result)
+                                         (reset! *content pull-data))))))])
       (if (= :png tp)
       (if (= :png tp)
         [:div.flex.items-center.justify-center.relative
         [:div.flex.items-center.justify-center.relative
          (when (not @*content) [:div.absolute (ui/loading "")])
          (when (not @*content) [:div.absolute (ui/loading "")])

+ 1 - 1
src/main/frontend/components/file_based/block.cljs

@@ -4,12 +4,12 @@
             [frontend.components.file-based.datetime :as datetime-comp]
             [frontend.components.file-based.datetime :as datetime-comp]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.file-based.repeated :as repeated]
             [frontend.handler.file-based.repeated :as repeated]
-            [frontend.hooks :as hooks]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
             [frontend.util.file-based.clock :as clock]
             [frontend.util.file-based.clock :as clock]
             [frontend.util.file-based.drawer :as drawer]
             [frontend.util.file-based.drawer :as drawer]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [reitit.frontend.easy :as rfe]
             [reitit.frontend.easy :as rfe]
             [rum.core :as rum]))
             [rum.core :as rum]))

+ 5 - 5
src/main/frontend/components/file_based/git.cljs

@@ -2,10 +2,10 @@
   (:require [clojure.string :as string]
   (:require [clojure.string :as string]
             [frontend.handler.file-based.file :as file-handler]
             [frontend.handler.file-based.file :as file-handler]
             [frontend.handler.shell :as shell]
             [frontend.handler.shell :as shell]
-            [frontend.hooks :as hooks]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
+            [logseq.shui.hooks :as hooks]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 
@@ -73,7 +73,7 @@
        (ui/button "Revert"
        (ui/button "Revert"
                   :on-click (fn []
                   :on-click (fn []
                               (file-handler/alter-file (state/get-current-repo)
                               (file-handler/alter-file (state/get-current-repo)
-                                               path
-                                               content
-                                               {:re-render-root? true
-                                                :skip-compare? true})))]]]))
+                                                       path
+                                                       content
+                                                       {:re-render-root? true
+                                                        :skip-compare? true})))]]]))

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

@@ -19,7 +19,6 @@
             [frontend.handler.page :as page-handler]
             [frontend.handler.page :as page-handler]
             [frontend.handler.repo :as repo-handler]
             [frontend.handler.repo :as repo-handler]
             [frontend.handler.user :as user-handler]
             [frontend.handler.user :as user-handler]
-            [frontend.hooks :as hooks]
             [frontend.mobile.util :as mobile-util]
             [frontend.mobile.util :as mobile-util]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.storage :as storage]
             [frontend.storage :as storage]
@@ -29,6 +28,7 @@
             [frontend.util.persist-var :as persist-var]
             [frontend.util.persist-var :as persist-var]
             [goog.functions :refer [debounce]]
             [goog.functions :refer [debounce]]
             [logseq.common.util :as common-util]
             [logseq.common.util :as common-util]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [reitit.frontend.easy :as rfe]
             [reitit.frontend.easy :as rfe]

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

@@ -1,9 +1,9 @@
 (ns frontend.components.handbooks
 (ns frontend.components.handbooks
   (:require ;[shadow.lazy :as lazy]
   (:require ;[shadow.lazy :as lazy]
    [frontend.extensions.handbooks.core :as handbooks]
    [frontend.extensions.handbooks.core :as handbooks]
-   [frontend.hooks :as hooks]
    [frontend.modules.layout.core :as layout]
    [frontend.modules.layout.core :as layout]
    [frontend.state :as state]
    [frontend.state :as state]
+   [logseq.shui.hooks :as hooks]
    [rum.core :as rum]))
    [rum.core :as rum]))
 
 
 #_:clj-kondo/ignore
 #_:clj-kondo/ignore

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

@@ -23,7 +23,6 @@
             [frontend.handler.plugin :as plugin-handler]
             [frontend.handler.plugin :as plugin-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.user :as user-handler]
             [frontend.handler.user :as user-handler]
-            [frontend.hooks :as hooks]
             [frontend.mobile.util :as mobile-util]
             [frontend.mobile.util :as mobile-util]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.storage :as storage]
             [frontend.storage :as storage]
@@ -31,6 +30,7 @@
             [frontend.util :as util]
             [frontend.util :as util]
             [frontend.version :refer [version]]
             [frontend.version :refer [version]]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [logseq.shui.util :as shui-util]
             [logseq.shui.util :as shui-util]
             [missionary.core :as m]
             [missionary.core :as m]

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

@@ -6,7 +6,6 @@
             [clojure.string :as string]
             [clojure.string :as string]
             [frontend.config :as config]
             [frontend.config :as config]
             [frontend.handler.property.util :as pu]
             [frontend.handler.property.util :as pu]
-            [frontend.hooks :as hooks]
             [frontend.search :as search]
             [frontend.search :as search]
             [frontend.storage :as storage]
             [frontend.storage :as storage]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
@@ -14,6 +13,7 @@
             [goog.functions :refer [debounce]]
             [goog.functions :refer [debounce]]
             [goog.object :as gobj]
             [goog.object :as gobj]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [medley.core :as medley]
             [medley.core :as medley]
             [promesa.core :as p]
             [promesa.core :as p]

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

@@ -18,7 +18,6 @@
             [frontend.handler.repo :as repo-handler]
             [frontend.handler.repo :as repo-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.ui :as ui-handler]
-            [frontend.hooks :as hooks]
             [frontend.persist-db.browser :as db-browser]
             [frontend.persist-db.browser :as db-browser]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
@@ -32,6 +31,7 @@
             [logseq.graph-parser.exporter :as gp-exporter]
             [logseq.graph-parser.exporter :as gp-exporter]
             [logseq.shui.dialog.core :as shui-dialog]
             [logseq.shui.dialog.core :as shui-dialog]
             [logseq.shui.form.core :as form-core]
             [logseq.shui.form.core :as form-core]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))

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

@@ -9,6 +9,7 @@
             [frontend.state :as state]
             [frontend.state :as state]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property :as db-property]
             [logseq.outliner.property :as outliner-property]
             [logseq.outliner.property :as outliner-property]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))

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

@@ -33,7 +33,6 @@
             [frontend.handler.notification :as notification]
             [frontend.handler.notification :as notification]
             [frontend.handler.page :as page-handler]
             [frontend.handler.page :as page-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
-            [frontend.hooks :as hooks]
             [frontend.mixins :as mixins]
             [frontend.mixins :as mixins]
             [frontend.mobile.util :as mobile-util]
             [frontend.mobile.util :as mobile-util]
             [frontend.rum :as frontend-rum]
             [frontend.rum :as frontend-rum]
@@ -46,6 +45,7 @@
             [logseq.common.util.page-ref :as page-ref]
             [logseq.common.util.page-ref :as page-ref]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
             [logseq.graph-parser.mldoc :as gp-mldoc]
             [logseq.graph-parser.mldoc :as gp-mldoc]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [reitit.frontend.easy :as rfe]
             [reitit.frontend.easy :as rfe]
@@ -404,7 +404,8 @@
                                        :preview? preview?})
                                        :preview? preview?})
               [:span.title.block
               [:span.title.block
                {:on-click (fn []
                {:on-click (fn []
-                            (when (and (state/home?) (not preview?))
+                            (when (and (not preview?)
+                                       (contains? #{:home :all-journals} (get-in (state/get-route-match) [:data :name])))
                               (route-handler/redirect-to-page! (:block/uuid page))))
                               (route-handler/redirect-to-page! (:block/uuid page))))
                 :data-value @*input-value
                 :data-value @*input-value
                 :data-ref   (:block/title page)
                 :data-ref   (:block/title page)

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

@@ -132,7 +132,8 @@
             {:title   (t :export-page)
             {:title   (t :export-page)
              :options {:on-click #(shui/dialog-open!
              :options {:on-click #(shui/dialog-open!
                                    (fn []
                                    (fn []
-                                     (export/export-blocks [(:block/uuid page)] {:whiteboard? whiteboard?}))
+                                     (export/export-blocks [(:block/uuid page)] {:whiteboard? whiteboard?
+                                                                                 :export-type :page}))
                                    {:class "w-auto md:max-w-4xl max-h-[80vh] overflow-y-auto"})}})
                                    {:class "w-auto md:max-w-4xl max-h-[80vh] overflow-y-auto"})}})
 
 
           (when (util/electron?)
           (when (util/electron?)

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

@@ -12,7 +12,6 @@
             [frontend.handler.plugin :as plugin-handler]
             [frontend.handler.plugin :as plugin-handler]
             [frontend.handler.plugin-config :as plugin-config-handler]
             [frontend.handler.plugin-config :as plugin-config-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.ui :as ui-handler]
-            [frontend.hooks :as hooks]
             [frontend.mixins :as mixins]
             [frontend.mixins :as mixins]
             [frontend.rum :as rum-utils]
             [frontend.rum :as rum-utils]
             [frontend.search :as search]
             [frontend.search :as search]
@@ -20,6 +19,7 @@
             [frontend.storage :as storage]
             [frontend.storage :as storage]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))

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

@@ -3,10 +3,10 @@
             [frontend.components.lazy-editor :as lazy-editor]
             [frontend.components.lazy-editor :as lazy-editor]
             [frontend.handler.notification :as notification]
             [frontend.handler.notification :as notification]
             [frontend.handler.plugin :as plugin-handler]
             [frontend.handler.plugin :as plugin-handler]
-            [frontend.hooks :as hooks]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
             [goog.functions :refer [debounce]]
             [goog.functions :refer [debounce]]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 

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

@@ -18,7 +18,6 @@
             [frontend.handler.property :as property-handler]
             [frontend.handler.property :as property-handler]
             [frontend.handler.property.util :as pu]
             [frontend.handler.property.util :as pu]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
-            [frontend.hooks :as hooks]
             [frontend.mixins :as mixins]
             [frontend.mixins :as mixins]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.state :as state]
             [frontend.state :as state]
@@ -31,6 +30,7 @@
             [logseq.db.frontend.property.type :as db-property-type]
             [logseq.db.frontend.property.type :as db-property-type]
             [logseq.outliner.core :as outliner-core]
             [logseq.outliner.core :as outliner-core]
             [logseq.outliner.property :as outliner-property]
             [logseq.outliner.property :as outliner-property]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))

+ 1 - 1
src/main/frontend/components/property/config.cljs

@@ -15,7 +15,6 @@
             [frontend.handler.db-based.property :as db-property-handler]
             [frontend.handler.db-based.property :as db-property-handler]
             [frontend.handler.property :as property-handler]
             [frontend.handler.property :as property-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
-            [frontend.hooks :as hooks]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
@@ -25,6 +24,7 @@
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property.type :as db-property-type]
             [logseq.db.frontend.property.type :as db-property-type]
             [logseq.outliner.core :as outliner-core]
             [logseq.outliner.core :as outliner-core]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.popup.core :as shui-popup]
             [logseq.shui.popup.core :as shui-popup]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]

+ 1 - 1
src/main/frontend/components/property/value.cljs

@@ -22,7 +22,6 @@
             [frontend.handler.property :as property-handler]
             [frontend.handler.property :as property-handler]
             [frontend.handler.property.util :as pu]
             [frontend.handler.property.util :as pu]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
-            [frontend.hooks :as hooks]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.search :as search]
             [frontend.search :as search]
             [frontend.state :as state]
             [frontend.state :as state]
@@ -37,6 +36,7 @@
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property.type :as db-property-type]
             [logseq.db.frontend.property.type :as db-property-type]
             [logseq.outliner.property :as outliner-property]
             [logseq.outliner.property :as outliner-property]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))

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

@@ -15,6 +15,7 @@
             [frontend.util :as util]
             [frontend.util :as util]
             [lambdaisland.glogi :as log]
             [lambdaisland.glogi :as log]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
+            [logseq.shui.hooks :as hooks]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 
 (defn- built-in-custom-query?
 (defn- built-in-custom-query?

+ 1 - 1
src/main/frontend/components/query/builder.cljs

@@ -11,7 +11,6 @@
             [frontend.db.query-dsl :as query-dsl]
             [frontend.db.query-dsl :as query-dsl]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.query.builder :as query-builder]
             [frontend.handler.query.builder :as query-builder]
-            [frontend.hooks :as hooks]
             [frontend.mixins :as mixins]
             [frontend.mixins :as mixins]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
@@ -22,6 +21,7 @@
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.sqlite.util :as sqlite-util]
             [logseq.db.sqlite.util :as sqlite-util]
             [logseq.graph-parser.db :as gp-db]
             [logseq.graph-parser.db :as gp-db]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))

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

@@ -10,10 +10,10 @@
             [frontend.db-mixins :as db-mixins]
             [frontend.db-mixins :as db-mixins]
             [frontend.db.async :as db-async]
             [frontend.db.async :as db-async]
             [frontend.db.utils :as db-utils]
             [frontend.db.utils :as db-utils]
-            [frontend.hooks :as hooks]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [logseq.db.common.view :as db-view]
             [logseq.db.common.view :as db-view]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [missionary.core :as m]
             [missionary.core :as m]
             [promesa.core :as p]
             [promesa.core :as p]

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

@@ -17,11 +17,11 @@
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.ui :as ui-handler]
-            [frontend.hooks :as hooks]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [medley.core :as medley]
             [medley.core :as medley]
             [promesa.core :as p]
             [promesa.core :as p]

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

@@ -8,13 +8,13 @@
             [frontend.context.i18n :refer [t]]
             [frontend.context.i18n :refer [t]]
             [frontend.handler.common.developer :as dev-common-handler]
             [frontend.handler.common.developer :as dev-common-handler]
             [frontend.handler.repo :as repo-handler]
             [frontend.handler.repo :as repo-handler]
-            [frontend.hooks :as hooks]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.search :as search]
             [frontend.search :as search]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
             [frontend.util.text :as text-util]
             [frontend.util.text :as text-util]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [reitit.frontend.easy :as rfe]
             [reitit.frontend.easy :as rfe]
             [rum.core :as rum]))
             [rum.core :as rum]))

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

@@ -3,10 +3,10 @@
    [clojure.string :as string]
    [clojure.string :as string]
    [electron.ipc :as ipc]
    [electron.ipc :as ipc]
    [frontend.handler.notification :as notification]
    [frontend.handler.notification :as notification]
-   [frontend.hooks :as hooks]
    [frontend.state :as state]
    [frontend.state :as state]
    [frontend.ui :as ui]
    [frontend.ui :as ui]
    [frontend.util :as util]
    [frontend.util :as util]
+   [logseq.shui.hooks :as hooks]
    [logseq.shui.ui :as shui]
    [logseq.shui.ui :as shui]
    [medley.core :as medley]
    [medley.core :as medley]
    [promesa.core :as p]
    [promesa.core :as p]

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

@@ -21,7 +21,6 @@
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.user :as user-handler]
             [frontend.handler.user :as user-handler]
-            [frontend.hooks :as hooks]
             [frontend.mobile.util :as mobile-util]
             [frontend.mobile.util :as mobile-util]
             [frontend.modules.instrumentation.core :as instrument]
             [frontend.modules.instrumentation.core :as instrument]
             [frontend.modules.shortcut.data-helper :as shortcut-helper]
             [frontend.modules.shortcut.data-helper :as shortcut-helper]
@@ -34,6 +33,7 @@
             [goog.object :as gobj]
             [goog.object :as gobj]
             [goog.string :as gstring]
             [goog.string :as gstring]
             [logseq.db :as ldb]
             [logseq.db :as ldb]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [reitit.frontend.easy :as rfe]
             [reitit.frontend.easy :as rfe]

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

@@ -3,7 +3,6 @@
             [clojure.string :as string]
             [clojure.string :as string]
             [frontend.context.i18n :refer [t]]
             [frontend.context.i18n :refer [t]]
             [frontend.handler.notification :as notification]
             [frontend.handler.notification :as notification]
-            [frontend.hooks :as hooks]
             [frontend.modules.shortcut.config :as shortcut-config]
             [frontend.modules.shortcut.config :as shortcut-config]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.modules.shortcut.data-helper :as dh]
             [frontend.modules.shortcut.data-helper :as dh]
@@ -14,6 +13,7 @@
             [frontend.util :as util]
             [frontend.util :as util]
             [goog.events :as events]
             [goog.events :as events]
             [logseq.shui.dialog.core :as shui-dialog]
             [logseq.shui.dialog.core :as shui-dialog]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum])
             [rum.core :as rum])

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

@@ -8,12 +8,12 @@
             [frontend.handler.plugin-config :as plugin-config-handler]
             [frontend.handler.plugin-config :as plugin-config-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.ui :as ui-handler]
-            [frontend.hooks :as hooks]
             [frontend.rum :refer [use-mounted]]
             [frontend.rum :refer [use-mounted]]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.storage :as storage]
             [frontend.storage :as storage]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 

+ 1 - 1
src/main/frontend/components/user/login.cljs

@@ -6,10 +6,10 @@
             [frontend.handler.notification :as notification]
             [frontend.handler.notification :as notification]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.user :as user]
             [frontend.handler.user :as user]
-            [frontend.hooks :as hooks]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.rum :refer [adapt-class]]
             [frontend.rum :refer [adapt-class]]
             [frontend.state :as state]
             [frontend.state :as state]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [rum.core :as rum]))
             [rum.core :as rum]))
 
 

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

@@ -27,7 +27,6 @@
             [frontend.handler.property :as property-handler]
             [frontend.handler.property :as property-handler]
             [frontend.handler.property.util :as pu]
             [frontend.handler.property.util :as pu]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.ui :as ui-handler]
-            [frontend.hooks :as hooks]
             [frontend.mixins :as mixins]
             [frontend.mixins :as mixins]
             [frontend.modules.outliner.op :as outliner-op]
             [frontend.modules.outliner.op :as outliner-op]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
@@ -40,6 +39,7 @@
             [logseq.db :as ldb]
             [logseq.db :as ldb]
             [logseq.db.common.view :as db-view]
             [logseq.db.common.view :as db-view]
             [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property :as db-property]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [medley.core :as medley]
             [medley.core :as medley]
             [missionary.core :as m]
             [missionary.core :as m]

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

@@ -12,13 +12,13 @@
             [frontend.db.model :as model]
             [frontend.db.model :as model]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
-            [frontend.hooks :as hooks]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.rum :refer [use-bounding-client-rect use-breakpoint]]
             [frontend.rum :refer [use-bounding-client-rect use-breakpoint]]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
             [logseq.common.util :as common-util]
             [logseq.common.util :as common-util]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]
             [rum.core :as rum]

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

@@ -12,7 +12,6 @@
             [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.hooks :as hooks]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.util :as util :refer [react]]
             [frontend.util :as util :refer [react]]
             [logseq.common.util :as common-util]
             [logseq.common.util :as common-util]
@@ -22,7 +21,8 @@
             [logseq.db.frontend.class :as db-class]
             [logseq.db.frontend.class :as db-class]
             [logseq.db.frontend.content :as db-content]
             [logseq.db.frontend.content :as db-content]
             [logseq.db.frontend.rules :as rules]
             [logseq.db.frontend.rules :as rules]
-            [logseq.graph-parser.db :as gp-db]))
+            [logseq.graph-parser.db :as gp-db]
+            [logseq.shui.hooks :as hooks]))
 
 
 ;; TODO: extract to specific models and move data transform logic to the
 ;; TODO: extract to specific models and move data transform logic to the
 ;; corresponding handlers.
 ;; corresponding handlers.

+ 1 - 1
src/main/frontend/extensions/handbooks/core.cljs

@@ -9,7 +9,6 @@
             [frontend.extensions.lightbox :as lightbox]
             [frontend.extensions.lightbox :as lightbox]
             [frontend.extensions.video.youtube :as youtube]
             [frontend.extensions.video.youtube :as youtube]
             [frontend.handler.notification :as notification]
             [frontend.handler.notification :as notification]
-            [frontend.hooks :as hooks]
             [frontend.modules.shortcut.config :as shortcut-config]
             [frontend.modules.shortcut.config :as shortcut-config]
             [frontend.rum :as r]
             [frontend.rum :as r]
             [frontend.search :as search]
             [frontend.search :as search]
@@ -17,6 +16,7 @@
             [frontend.storage :as storage]
             [frontend.storage :as storage]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
+            [logseq.shui.hooks :as hooks]
             [medley.core :as medley]
             [medley.core :as medley]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))

+ 1 - 1
src/main/frontend/extensions/pdf/core.cljs

@@ -14,13 +14,13 @@
             [frontend.extensions.pdf.windows :as pdf-windows]
             [frontend.extensions.pdf.windows :as pdf-windows]
             [frontend.handler.notification :as notification]
             [frontend.handler.notification :as notification]
             [frontend.handler.property :as property-handler]
             [frontend.handler.property :as property-handler]
-            [frontend.hooks :as hooks]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.rum :refer [use-atom]]
             [frontend.rum :refer [use-atom]]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
             [goog.functions :refer [debounce]]
             [goog.functions :refer [debounce]]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [medley.core :as medley]
             [medley.core :as medley]
             [promesa.core :as p]
             [promesa.core :as p]

+ 1 - 1
src/main/frontend/extensions/pdf/toolbar.cljs

@@ -13,13 +13,13 @@
             [frontend.extensions.pdf.windows :refer [resolve-own-container] :as pdf-windows]
             [frontend.extensions.pdf.windows :refer [resolve-own-container] :as pdf-windows]
             [frontend.handler.assets :as assets-handler]
             [frontend.handler.assets :as assets-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.notification :as notification]
-            [frontend.hooks :as hooks]
             [frontend.rum :refer [use-atom]]
             [frontend.rum :refer [use-atom]]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.storage :as storage]
             [frontend.storage :as storage]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
             [logseq.publishing.db :as publish-db]
             [logseq.publishing.db :as publish-db]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))

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

@@ -19,7 +19,6 @@
             [frontend.handler.page :as page-handler]
             [frontend.handler.page :as page-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
-            [frontend.hooks :as hooks]
             [frontend.rum :as r]
             [frontend.rum :as r]
             [frontend.search :as search]
             [frontend.search :as search]
             [frontend.state :as state]
             [frontend.state :as state]
@@ -28,6 +27,7 @@
             [frontend.util.text :as text-util]
             [frontend.util.text :as text-util]
             [goog.object :as gobj]
             [goog.object :as gobj]
             [logseq.common.util :as common-util]
             [logseq.common.util :as common-util]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))

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

@@ -9,11 +9,11 @@
             [frontend.extensions.zotero.setting :as setting]
             [frontend.extensions.zotero.setting :as setting]
             [frontend.handler.notification :as notification]
             [frontend.handler.notification :as notification]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
-            [frontend.hooks :as hooks]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
             [goog.dom :as gdom]
             [goog.dom :as gdom]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
             [promesa.core :as p]
             [rum.core :as rum]))
             [rum.core :as rum]))

+ 25 - 26
src/main/frontend/handler/db_based/export.cljs

@@ -17,10 +17,10 @@
                                             (state/get-current-repo)
                                             (state/get-current-repo)
                                             {:export-type :block :block-id [:block/uuid block-uuid]})
                                             {:export-type :block :block-id [:block/uuid block-uuid]})
             pull-data (with-out-str (pprint/pprint result))]
             pull-data (with-out-str (pprint/pprint result))]
-      (.writeText js/navigator.clipboard pull-data)
-      (println pull-data)
-      (notification/show! "Copied block's data!" :success))
-
+      (when-not (= :export-edn-error result)
+        (.writeText js/navigator.clipboard pull-data)
+        (println pull-data)
+        (notification/show! "Copied block's data!" :success)))
     (notification/show! "No block found" :warning)))
     (notification/show! "No block found" :warning)))
 
 
 (defn export-view-nodes-data [nodes]
 (defn export-view-nodes-data [nodes]
@@ -29,9 +29,10 @@
                                             (state/get-current-repo)
                                             (state/get-current-repo)
                                             {:export-type :view-nodes :node-ids block-uuids})
                                             {:export-type :view-nodes :node-ids block-uuids})
             pull-data (with-out-str (pprint/pprint result))]
             pull-data (with-out-str (pprint/pprint result))]
-      (.writeText js/navigator.clipboard pull-data)
-      (println pull-data)
-      (notification/show! "Copied block's data!" :success))))
+      (when-not (= :export-edn-error result)
+        (.writeText js/navigator.clipboard pull-data)
+        (println pull-data)
+        (notification/show! "Copied view nodes' data!" :success)))))
 
 
 (defn ^:export export-page-data []
 (defn ^:export export-page-data []
   (if-let [page-id (page-util/get-current-page-id)]
   (if-let [page-id (page-util/get-current-page-id)]
@@ -39,9 +40,10 @@
                                             (state/get-current-repo)
                                             (state/get-current-repo)
                                             {:export-type :page :page-id page-id})
                                             {:export-type :page :page-id page-id})
             pull-data (with-out-str (pprint/pprint result))]
             pull-data (with-out-str (pprint/pprint result))]
-      (.writeText js/navigator.clipboard pull-data)
-      (println pull-data)
-      (notification/show! "Copied page's data!" :success))
+      (when-not (= :export-edn-error result)
+        (.writeText js/navigator.clipboard pull-data)
+        (println pull-data)
+        (notification/show! "Copied page's data!" :success)))
     (notification/show! "No page found" :warning)))
     (notification/show! "No page found" :warning)))
 
 
 (defn ^:export export-graph-ontology-data []
 (defn ^:export export-graph-ontology-data []
@@ -49,19 +51,12 @@
                                           (state/get-current-repo)
                                           (state/get-current-repo)
                                           {:export-type :graph-ontology})
                                           {:export-type :graph-ontology})
           pull-data (with-out-str (pprint/pprint result))]
           pull-data (with-out-str (pprint/pprint result))]
-    (.writeText js/navigator.clipboard pull-data)
-    (println pull-data)
-    (js/console.log (str "Exported " (count (:classes result)) " classes and "
-                         (count (:properties result)) " properties"))
-    (notification/show! "Copied graphs's ontology data!" :success)))
-
-(defn- export-graph-edn-data []
-  (p/let [result (state/<invoke-db-worker :thread-api/export-edn
-                                          (state/get-current-repo)
-                                          {:export-type :graph
-                                           :graph-options {:include-timestamps? true}})
-          pull-data (with-out-str (pprint/pprint result))]
-    pull-data))
+    (when-not (= :export-edn-error result)
+      (.writeText js/navigator.clipboard pull-data)
+      (println pull-data)
+      (js/console.log (str "Exported " (count (:classes result)) " classes and "
+                           (count (:properties result)) " properties"))
+      (notification/show! "Copied graphs's ontology data!" :success))))
 
 
 ;; Copied from handler.export
 ;; Copied from handler.export
 (defn- file-name [repo extension]
 (defn- file-name [repo extension]
@@ -72,9 +67,13 @@
 
 
 (defn export-repo-as-db-edn!
 (defn export-repo-as-db-edn!
   [repo]
   [repo]
-  (p/let [edn-str (export-graph-edn-data)]
-    (when edn-str
-      (let [data-str (some->> edn-str
+  (p/let [result (state/<invoke-db-worker :thread-api/export-edn
+                                          (state/get-current-repo)
+                                          {:export-type :graph
+                                           :graph-options {:include-timestamps? true}})
+          pull-data (with-out-str (pprint/pprint result))]
+    (when-not (= :export-edn-error result)
+      (let [data-str (some->> pull-data
                               js/encodeURIComponent
                               js/encodeURIComponent
                               (str "data:text/edn;charset=utf-8,"))
                               (str "data:text/edn;charset=utf-8,"))
             filename (file-name repo :edn)]
             filename (file-name repo :edn)]

+ 1 - 1
src/main/frontend/mobile/graph_picker.cljs

@@ -6,13 +6,13 @@
    [frontend.handler.file-based.nfs :as nfs-handler]
    [frontend.handler.file-based.nfs :as nfs-handler]
    [frontend.handler.notification :as notification]
    [frontend.handler.notification :as notification]
    [frontend.handler.page :as page-handler]
    [frontend.handler.page :as page-handler]
-   [frontend.hooks :as hooks]
    [frontend.mobile.util :as mobile-util]
    [frontend.mobile.util :as mobile-util]
    [frontend.modules.shortcut.core :as shortcut]
    [frontend.modules.shortcut.core :as shortcut]
    [frontend.state :as state]
    [frontend.state :as state]
    [frontend.ui :as ui]
    [frontend.ui :as ui]
    [frontend.util :as util]
    [frontend.util :as util]
    [logseq.common.path :as path]
    [logseq.common.path :as path]
+   [logseq.shui.hooks :as hooks]
    [logseq.shui.ui :as shui]
    [logseq.shui.ui :as shui]
    [promesa.core :as p]
    [promesa.core :as p]
    [rum.core :as rum]))
    [rum.core :as rum]))

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

@@ -5,7 +5,7 @@
             [clojure.string :as string]
             [clojure.string :as string]
             [clojure.walk :as w]
             [clojure.walk :as w]
             [daiquiri.interpreter :as interpreter]
             [daiquiri.interpreter :as interpreter]
-            [frontend.hooks :as hooks]
+            [logseq.shui.hooks :as hooks]
             [rum.core :refer [use-state] :as rum]))
             [rum.core :refer [use-state] :as rum]))
 
 
 ;; copy from https://github.com/priornix/antizer/blob/35ba264cf48b84e6597743e28b3570d8aa473e74/src/antizer/core.cljs
 ;; copy from https://github.com/priornix/antizer/blob/35ba264cf48b84e6597743e28b3570d8aa473e74/src/antizer/core.cljs

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

@@ -12,7 +12,6 @@
             [frontend.db.conn-state :as db-conn-state]
             [frontend.db.conn-state :as db-conn-state]
             [frontend.db.transact :as db-transact]
             [frontend.db.transact :as db-transact]
             [frontend.flows :as flows]
             [frontend.flows :as flows]
-            [frontend.hooks :as hooks]
             [frontend.mobile.util :as mobile-util]
             [frontend.mobile.util :as mobile-util]
             [frontend.spec.storage :as storage-spec]
             [frontend.spec.storage :as storage-spec]
             [frontend.storage :as storage]
             [frontend.storage :as storage]
@@ -25,6 +24,7 @@
             [logseq.db.frontend.entity-plus :as entity-plus]
             [logseq.db.frontend.entity-plus :as entity-plus]
             [logseq.db.sqlite.util :as sqlite-util]
             [logseq.db.sqlite.util :as sqlite-util]
             [logseq.shui.dialog.core :as shui-dialog]
             [logseq.shui.dialog.core :as shui-dialog]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [missionary.core :as m]
             [missionary.core :as m]
             [promesa.core :as p]
             [promesa.core :as p]

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

@@ -18,7 +18,6 @@
             [frontend.db-mixins :as db-mixins]
             [frontend.db-mixins :as db-mixins]
             [frontend.handler.notification :as notification]
             [frontend.handler.notification :as notification]
             [frontend.handler.plugin :as plugin-handler]
             [frontend.handler.plugin :as plugin-handler]
-            [frontend.hooks :as hooks]
             [frontend.mixins :as mixins]
             [frontend.mixins :as mixins]
             [frontend.mobile.util :as mobile-util]
             [frontend.mobile.util :as mobile-util]
             [frontend.modules.shortcut.config :as shortcut-config]
             [frontend.modules.shortcut.config :as shortcut-config]
@@ -32,6 +31,7 @@
             [goog.dom :as gdom]
             [goog.dom :as gdom]
             [goog.object :as gobj]
             [goog.object :as gobj]
             [lambdaisland.glogi :as log]
             [lambdaisland.glogi :as log]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.icon.v2 :as shui.icon.v2]
             [logseq.shui.icon.v2 :as shui.icon.v2]
             [logseq.shui.popup.core :as shui-popup]
             [logseq.shui.popup.core :as shui-popup]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]

+ 2 - 1
src/main/frontend/worker/db_worker.cljs

@@ -743,7 +743,8 @@
         (js/console.error "export-edn error: " e)
         (js/console.error "export-edn error: " e)
         (worker-util/post-message :notification
         (worker-util/post-message :notification
                                   ["An unexpected error occurred during export. See the javascript console for details."
                                   ["An unexpected error occurred during export. See the javascript console for details."
-                                   :error])))))
+                                   :error])
+        :export-edn-error))))
 
 
 (def-thread-api :thread-api/get-view-data
 (def-thread-api :thread-api/get-view-data
   [repo view-id option]
   [repo view-id option]

+ 393 - 81
yarn.lock

@@ -240,25 +240,48 @@
     "@babel/helper-validator-identifier" "^7.22.20"
     "@babel/helper-validator-identifier" "^7.22.20"
     to-fast-properties "^2.0.0"
     to-fast-properties "^2.0.0"
 
 
-"@capacitor/action-sheet@^5.0.7":
-  version "5.0.7"
-  resolved "https://registry.yarnpkg.com/@capacitor/action-sheet/-/action-sheet-5.0.7.tgz#4808af8c483a85f58c7fffdea76f8d374883e3a0"
-  integrity sha512-8q8fYYy9dwLi4eiDiYq+ys/uOm9C2YpjfAnlG7fYdha8QNpaz2314jR+dxsJO423zYB2Wag9NXlupHpXeGdGOA==
+"@capacitor/action-sheet@7.0.1":
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/@capacitor/action-sheet/-/action-sheet-7.0.1.tgz#8b6fd171375d1e4d9392ee9968ef4152309dcaa7"
+  integrity sha512-0ROMy6S62B/Ci9nZ7CRxhkK2LS0JBmhN+sxOXQOtntd2WHKW7+8J+VAtu8vk0LgvTjj7SjzJnicYC75KUjwLWw==
 
 
-"@capacitor/android@^5.0.0":
-  version "5.4.1"
-  resolved "https://registry.yarnpkg.com/@capacitor/android/-/android-5.4.1.tgz#5b0445202ca5e48fcb79d0c88e4403acc32504bc"
-  integrity sha512-25k/GyIly/8xKQo0EO6eIvJStpVWsBPyYh9Eiv+Or9DCz9iuVlGZY3vui+zjFhfHQ5f9Uwywa8B+thOGWU8f6g==
+"@capacitor/android@7.2.0":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@capacitor/android/-/android-7.2.0.tgz#f30e313315ab92c500bd57d9f0c4716a46b42f40"
+  integrity sha512-zdhEy3jZPG5Toe/pGzKtDgIiBGywjaoEuQWnGVjBYPlSAEUtAhpZ2At7V0SCb26yluAuzrAUV0Ue+LQeEtHwFQ==
 
 
-"@capacitor/app@^5.0.0":
-  version "5.0.6"
-  resolved "https://registry.yarnpkg.com/@capacitor/app/-/app-5.0.6.tgz#2ee02551115fd2e92dc7e81bc30a6c6fa78efa66"
-  integrity sha512-6ZXVdnNmaYILasC/RjQw+yfTmq2ZO7Q3v5lFcDVfq3PFGnybyYQh+RstBrYri+376OmXOXxBD7E6UxBhrMzXGA==
+"@capacitor/[email protected]":
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/@capacitor/app/-/app-7.0.1.tgz#0d0709fb4dde5046c24853f2d6b77a7ea411f748"
+  integrity sha512-ArlVZAAla4MwQoKh26x2AaTDOBh5Vhp1VhMKR3RwqZSsZnazKTFGNrPbr9Ez5r1knnEDfApyjwp1uZnXK1WTYQ==
+
+"@capacitor/[email protected]":
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/@capacitor/camera/-/camera-7.0.1.tgz#bcbd1c05cce648fa54b844bf7a6745854cae6ba4"
+  integrity sha512-gDUFsYlhMra5VVOa4iJV6+MQRhp3VXpTLQY4JDATj7UvoZ8Hv4DG8qplPL9ufUFNoR3QbDDnf8+gbQOsKdkDjg==
 
 
-"@capacitor/camera@^5.0.0":
-  version "5.0.7"
-  resolved "https://registry.yarnpkg.com/@capacitor/camera/-/camera-5.0.7.tgz#42f60168d731fc521df797aacc7cda703b7ac2b1"
-  integrity sha512-1Wk3Dk0UhhNHdBB07UrPvUOSL7Wi5gFZRyLY1LZL2awt34iqy2cnajtfJplFmEZHk8lD0i7NAl3HbkWm4td4OQ==
+"@capacitor/[email protected]":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@capacitor/cli/-/cli-7.2.0.tgz#ea279f14e575b059edb7fe8dc64a8e3379088616"
+  integrity sha512-RNW9vtYYYSDmOdguYBSW0VpRnG/d6lGydlc9DLrJ7qbSPxFrotTz9IjkM48O+SruUma61DyuSqJttdbay2xSxg==
+  dependencies:
+    "@ionic/cli-framework-output" "^2.2.8"
+    "@ionic/utils-subprocess" "^3.0.1"
+    "@ionic/utils-terminal" "^2.3.5"
+    commander "^12.1.0"
+    debug "^4.4.0"
+    env-paths "^2.2.0"
+    fs-extra "^11.2.0"
+    kleur "^4.1.5"
+    native-run "^2.0.1"
+    open "^8.4.0"
+    plist "^3.1.0"
+    prompts "^2.4.2"
+    rimraf "^6.0.1"
+    semver "^7.6.3"
+    tar "^6.1.11"
+    tslib "^2.8.1"
+    xml2js "^0.6.2"
 
 
 "@capacitor/cli@^5.0.0":
 "@capacitor/cli@^5.0.0":
   version "5.4.1"
   version "5.4.1"
@@ -283,10 +306,17 @@
     tslib "^2.4.0"
     tslib "^2.4.0"
     xml2js "^0.5.0"
     xml2js "^0.5.0"
 
 
-"@capacitor/clipboard@^5.0.0":
-  version "5.0.6"
-  resolved "https://registry.yarnpkg.com/@capacitor/clipboard/-/clipboard-5.0.6.tgz#abc0101fb1b2780e829542b2d31f9eb1480b8ee8"
-  integrity sha512-VsokRAn+0HVWj6riSRdspczEfqFoHbrhS/XRhGoEPsj0uvYPSufy0Kb2dpnSqkeeElhh2Jvn8jmVAzII2XeR9w==
+"@capacitor/[email protected]":
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/@capacitor/clipboard/-/clipboard-7.0.1.tgz#159fefa56b7d23632e9d08c9efd9cbd75a868b21"
+  integrity sha512-n4XEHma7apLOYvyeaR9S5u3uGzDYG7WeQxmtZlwP01HneIzMnusVgw4Im6I+pMBcoUN9TfVdf6eqKph97B1bAw==
+
+"@capacitor/[email protected]":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@capacitor/core/-/core-7.2.0.tgz#fdd1c5d2fbfa257887ce6065f66151a51f0e6d1b"
+  integrity sha512-2zCnA6RJeZ9ec4470o8QMZEQTWpekw9FNoqm5TLc10jeCrhvHVI8MPgxdZVc3mOdFlyieYu4AS1fNxSqbS57Pw==
+  dependencies:
+    tslib "^2.1.0"
 
 
 "@capacitor/core@^5.0.0":
 "@capacitor/core@^5.0.0":
   version "5.4.1"
   version "5.4.1"
@@ -295,50 +325,50 @@
   dependencies:
   dependencies:
     tslib "^2.1.0"
     tslib "^2.1.0"
 
 
-"@capacitor/filesystem@^5.0.0":
-  version "5.1.4"
-  resolved "https://registry.yarnpkg.com/@capacitor/filesystem/-/filesystem-5.1.4.tgz#6e3278f4c47c72416d586367889736b576113ef5"
-  integrity sha512-10EM1KvFMs+pTzxkcflspzxBWcX9sOnS9nTP5Afjr5hn4OxLrwTFySw2Z12Uv6jdN4OnhY3jXtDKXPljXvXILg==
+"@capacitor/filesystem@7.0.1":
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/@capacitor/filesystem/-/filesystem-7.0.1.tgz#b0518d781f7640e936f529b80a59724e221d0471"
+  integrity sha512-dxuKNLFoUejm7tBKkQPs1j7+BLpDh5JKPSVu7nT8jgCbA/KXt5FoCLiepfkjWkYfq60X0JNFzGnIquc5FPOLzQ==
 
 
-"@capacitor/haptics@^5.0.0":
-  version "5.0.6"
-  resolved "https://registry.yarnpkg.com/@capacitor/haptics/-/haptics-5.0.6.tgz#c22fd6acbc62cbdff39279ded687c418f2c89a5a"
-  integrity sha512-UrMcR7p2X10ql4VLlowUuH/VckTeu0lj+RQpekxox14uxDmu5AGIFDK/iDTi8W6QZkxTJRZK6sbCjgwYgNJ7Pw==
+"@capacitor/haptics@7.0.1":
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/@capacitor/haptics/-/haptics-7.0.1.tgz#8e992837e87c0b65a4a38430944762d1e2fce6dc"
+  integrity sha512-ewZmspE5krgDUj5ZvUDcfNZvgerAIr+3bDSk6DLzyvBZ/dYmr/tMLu5H6WtYaaKYZJ32aZAudGpIal5epDyBYA==
 
 
-"@capacitor/ios@^5.0.0":
-  version "5.4.1"
-  resolved "https://registry.yarnpkg.com/@capacitor/ios/-/ios-5.4.1.tgz#79c1620ac7b57dc1ea85653ad1e5a32e8ec7dbea"
-  integrity sha512-s5djQEX1HFM6u4gQlWz7DUWG7ma0d3AwwnjbSbCh25aYEt40Dbei0TjzlKrfU6b3nUbyVVAZJBJgMuk7Bq59qA==
-
-"@capacitor/keyboard@^5.0.0":
-  version "5.0.6"
-  resolved "https://registry.yarnpkg.com/@capacitor/keyboard/-/keyboard-5.0.6.tgz#04400e71b677abf9f1fc1ceaffd1211e7d864319"
-  integrity sha512-9GewAa/y2Hwkdw/Be8MTdiAjrFZ7TPDKpR44M0Y/0QMnK+mBbgzcoZ/UkuumWv6e2F1IAI+VY5eYVQHDeZcRoA==
-
-"@capacitor/share@^5.0.0":
-  version "5.0.6"
-  resolved "https://registry.yarnpkg.com/@capacitor/share/-/share-5.0.6.tgz#76d2f6f13d1596999c49a0d99d21e7a63b8ecfef"
-  integrity sha512-/dVOW7kcuuD7hWB9Z1drArIpEk+5JCoMnMrAs2c7CLp3q5PeaXNJjTkGr6RJ1OtMhsHyXc6DAFwQ4frFkmZsgw==
-
-"@capacitor/splash-screen@^5.0.0":
-  version "5.0.6"
-  resolved "https://registry.yarnpkg.com/@capacitor/splash-screen/-/splash-screen-5.0.6.tgz#d29320de49cc68add59e17fecb64aa2f1a08447f"
-  integrity sha512-9B8wSm89D+LlshFw8B+mjPU8pJNf1WOx2mkMjMvcH0/EqxNaE+ZaO8lPCX+9WvWSEZs3O3l11qiSnOFHeK0t9A==
-
-"@capacitor/status-bar@^5.0.0":
-  version "5.0.6"
-  resolved "https://registry.yarnpkg.com/@capacitor/status-bar/-/status-bar-5.0.6.tgz#281568a7f7aeacf80777702cb9947c29dc32685c"
-  integrity sha512-7od8CxsBnot1XMK3IeOkproFL4hgoKoWAc3pwUvmDOkQsXoxwQm4SR9mLwQavv1XfxtHbFV9Ukd7FwMxOPSViw==
-
-"@capawesome/capacitor-background-task@^5.0.0":
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/@capawesome/capacitor-background-task/-/capacitor-background-task-5.0.0.tgz#eb985131065df59f95ffa1e9ef3abf0f3573a35f"
-  integrity sha512-puZ/MaNNlIMmwojuMX5Z5rqWJFHjvpdgMB+xGe/QXG+IMrcY5eMtR3l/NLGtbH8izwid1dAsxzBACX5RGHdVzA==
+"@capacitor/[email protected]":
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@capacitor/ios/-/ios-7.2.0.tgz#c3d9435582e5267b57085229e5678d1d53b15b5a"
+  integrity sha512-MQgRZcXZpbpjN83bjkGrzQd7s3XeHBZplmWf38/msF/siMGJKLrXNmNzmmPIWA5Xpi/aH6UoJFk1wXuU2U+zMg==
 
 
-"@capgo/capacitor-navigation-bar@^6.0.0":
-  version "6.0.6"
-  resolved "https://registry.yarnpkg.com/@capgo/capacitor-navigation-bar/-/capacitor-navigation-bar-6.0.6.tgz#e2c8d346298abf470d5a4583059fca34d4d4ff94"
-  integrity sha512-X0w0BFB07vyb2BipWaok8itPgn6inR5zuZIJgZGhsJXYmmyVAwsTbSRZZluIrDbMzd3LHD4bfzbzFzU02tfG3g==
+"@capacitor/[email protected]":
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/@capacitor/keyboard/-/keyboard-7.0.1.tgz#edf212528cd1587494ceb1d260868c1a9540aed1"
+  integrity sha512-Gi064vOARMac+x9/DmEFeywN9oAETMf3OYsMuYm9gA8SvdsDJ3QJqMoFnSEIORYXe21Jzt2SIEdLlpT65P/b2g==
+
+"@capacitor/[email protected]":
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/@capacitor/share/-/share-7.0.1.tgz#1d1e6d312504703c1f34fa0de94bed14af4bcbf8"
+  integrity sha512-7GAtWrb2inEWohC8E7mx38qAX6D9yqPDDnUtJaZ8JRpo15jjFRS40Cx388M8h4NlBWjV5NU3qf1sHXnyOBSJ5g==
+
+"@capacitor/[email protected]":
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/@capacitor/splash-screen/-/splash-screen-7.0.1.tgz#ea70d39c4346db4558f749eeb2d3ac2f5c34c550"
+  integrity sha512-Nbqw9bEIe7uHj/HOT81mf4jT6uK1YykozpQw/uIKQDueMg6RJYaJK2/TMajIOohLk8fJF4TYIc1i9nGjNLnfGg==
+
+"@capacitor/[email protected]":
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/@capacitor/status-bar/-/status-bar-7.0.1.tgz#6bd3769ef35158c961ff2a6b571c03e9bce55809"
+  integrity sha512-iDv3mXYo9CdxYRVwt3/pRyuk25p7Sn4GfaS/zMZyVIqTzsvKLCIIH3GdKK+ta+nsNcAVpCw/t5jFEBt1D18ctA==
+
+"@capawesome/[email protected]":
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/@capawesome/capacitor-background-task/-/capacitor-background-task-7.0.1.tgz#5531717de4cea255156c7f83fd4bf0f1e472c534"
+  integrity sha512-ILkJ0bCOLperUc+fezzhpiH3Bfnr/318TI9XSrPU/vwvBXjMH7p7xYxKtjDA4VpJfbVh1cHmWLtRSWIk2wUglg==
+
+"@capgo/[email protected]":
+  version "7.1.2"
+  resolved "https://registry.yarnpkg.com/@capgo/capacitor-navigation-bar/-/capacitor-navigation-bar-7.1.2.tgz#d017f22007e6e848c6a94aa38d70546b08d95473"
+  integrity sha512-lganepu29pay05+clCE41yEICE34xDzB61dmvtwWxZlWccvlu+XWbS8WnMSncvIotqBUmU1owfivG+usfrp4CA==
 
 
 "@colors/[email protected]":
 "@colors/[email protected]":
   version "1.5.0"
   version "1.5.0"
@@ -430,6 +460,15 @@
     debug "^4.0.0"
     debug "^4.0.0"
     tslib "^2.0.1"
     tslib "^2.0.1"
 
 
+"@ionic/cli-framework-output@^2.2.8":
+  version "2.2.8"
+  resolved "https://registry.yarnpkg.com/@ionic/cli-framework-output/-/cli-framework-output-2.2.8.tgz#29d541acc7773a6aaceec5f3b079937fbcef5402"
+  integrity sha512-TshtaFQsovB4NWRBydbNFawql6yul7d5bMiW1WYYf17hd99V6xdDdk3vtF51bw6sLkxON3bDQpWsnUc9/hVo3g==
+  dependencies:
+    "@ionic/utils-terminal" "2.3.5"
+    debug "^4.0.0"
+    tslib "^2.0.1"
+
 "@ionic/[email protected]":
 "@ionic/[email protected]":
   version "2.1.6"
   version "2.1.6"
   resolved "https://registry.yarnpkg.com/@ionic/utils-array/-/utils-array-2.1.6.tgz#eee863be945ee1a28b9a10ff16fdea776fa18c22"
   resolved "https://registry.yarnpkg.com/@ionic/utils-array/-/utils-array-2.1.6.tgz#eee863be945ee1a28b9a10ff16fdea776fa18c22"
@@ -438,7 +477,7 @@
     debug "^4.0.0"
     debug "^4.0.0"
     tslib "^2.0.1"
     tslib "^2.0.1"
 
 
-"@ionic/[email protected]", "@ionic/utils-fs@^3.1.6":
+"@ionic/[email protected]", "@ionic/utils-fs@^3.1.6", "@ionic/utils-fs@^3.1.7":
   version "3.1.7"
   version "3.1.7"
   resolved "https://registry.yarnpkg.com/@ionic/utils-fs/-/utils-fs-3.1.7.tgz#e0d41225272c346846867e88a0b84b1a4ee9d9c9"
   resolved "https://registry.yarnpkg.com/@ionic/utils-fs/-/utils-fs-3.1.7.tgz#e0d41225272c346846867e88a0b84b1a4ee9d9c9"
   integrity sha512-2EknRvMVfhnyhL1VhFkSLa5gOcycK91VnjfrTB0kbqkTFCOXyXgVLI5whzq7SLrgD9t1aqos3lMMQyVzaQ5gVA==
   integrity sha512-2EknRvMVfhnyhL1VhFkSLa5gOcycK91VnjfrTB0kbqkTFCOXyXgVLI5whzq7SLrgD9t1aqos3lMMQyVzaQ5gVA==
@@ -468,6 +507,18 @@
     tree-kill "^1.2.2"
     tree-kill "^1.2.2"
     tslib "^2.0.1"
     tslib "^2.0.1"
 
 
+"@ionic/[email protected]":
+  version "2.1.12"
+  resolved "https://registry.yarnpkg.com/@ionic/utils-process/-/utils-process-2.1.12.tgz#17b05d66201859fe11f53b47be22b85aa90b9556"
+  integrity sha512-Jqkgyq7zBs/v/J3YvKtQQiIcxfJyplPgECMWgdO0E1fKrrH8EF0QGHNJ9mJCn6PYe2UtHNS8JJf5G21e09DfYg==
+  dependencies:
+    "@ionic/utils-object" "2.1.6"
+    "@ionic/utils-terminal" "2.3.5"
+    debug "^4.0.0"
+    signal-exit "^3.0.3"
+    tree-kill "^1.2.2"
+    tslib "^2.0.1"
+
 "@ionic/[email protected]":
 "@ionic/[email protected]":
   version "3.1.6"
   version "3.1.6"
   resolved "https://registry.yarnpkg.com/@ionic/utils-stream/-/utils-stream-3.1.6.tgz#7c2fdcf4d9e621e8b2260e2fee2471825a4e214f"
   resolved "https://registry.yarnpkg.com/@ionic/utils-stream/-/utils-stream-3.1.6.tgz#7c2fdcf4d9e621e8b2260e2fee2471825a4e214f"
@@ -476,6 +527,14 @@
     debug "^4.0.0"
     debug "^4.0.0"
     tslib "^2.0.1"
     tslib "^2.0.1"
 
 
+"@ionic/[email protected]":
+  version "3.1.7"
+  resolved "https://registry.yarnpkg.com/@ionic/utils-stream/-/utils-stream-3.1.7.tgz#224f8c99012aa54e7dbf59950de903b6a61cd811"
+  integrity sha512-eSELBE7NWNFIHTbTC2jiMvh1ABKGIpGdUIvARsNPMNQhxJB3wpwdiVnoBoTYp+5a6UUIww4Kpg7v6S7iTctH1w==
+  dependencies:
+    debug "^4.0.0"
+    tslib "^2.0.1"
+
 "@ionic/utils-subprocess@^2.1.11":
 "@ionic/utils-subprocess@^2.1.11":
   version "2.1.12"
   version "2.1.12"
   resolved "https://registry.yarnpkg.com/@ionic/utils-subprocess/-/utils-subprocess-2.1.12.tgz#08ef08a13928aa97b9156b153e39fe5ff9c1e661"
   resolved "https://registry.yarnpkg.com/@ionic/utils-subprocess/-/utils-subprocess-2.1.12.tgz#08ef08a13928aa97b9156b153e39fe5ff9c1e661"
@@ -490,6 +549,20 @@
     debug "^4.0.0"
     debug "^4.0.0"
     tslib "^2.0.1"
     tslib "^2.0.1"
 
 
+"@ionic/utils-subprocess@^3.0.1":
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/@ionic/utils-subprocess/-/utils-subprocess-3.0.1.tgz#561608fecf432c28fd80f94c1563dc0d092581b7"
+  integrity sha512-cT4te3AQQPeIM9WCwIg8ohroJ8TjsYaMb2G4ZEgv9YzeDqHZ4JpeIKqG2SoaA3GmVQ3sOfhPM6Ox9sxphV/d1A==
+  dependencies:
+    "@ionic/utils-array" "2.1.6"
+    "@ionic/utils-fs" "3.1.7"
+    "@ionic/utils-process" "2.1.12"
+    "@ionic/utils-stream" "3.1.7"
+    "@ionic/utils-terminal" "2.3.5"
+    cross-spawn "^7.0.3"
+    debug "^4.0.0"
+    tslib "^2.0.1"
+
 "@ionic/[email protected]", "@ionic/utils-terminal@^2.3.3":
 "@ionic/[email protected]", "@ionic/utils-terminal@^2.3.3":
   version "2.3.4"
   version "2.3.4"
   resolved "https://registry.yarnpkg.com/@ionic/utils-terminal/-/utils-terminal-2.3.4.tgz#e40c44b676265ed6a07a68407bda6e135870f879"
   resolved "https://registry.yarnpkg.com/@ionic/utils-terminal/-/utils-terminal-2.3.4.tgz#e40c44b676265ed6a07a68407bda6e135870f879"
@@ -505,6 +578,33 @@
     untildify "^4.0.0"
     untildify "^4.0.0"
     wrap-ansi "^7.0.0"
     wrap-ansi "^7.0.0"
 
 
+"@ionic/[email protected]", "@ionic/utils-terminal@^2.3.4", "@ionic/utils-terminal@^2.3.5":
+  version "2.3.5"
+  resolved "https://registry.yarnpkg.com/@ionic/utils-terminal/-/utils-terminal-2.3.5.tgz#a48465f40496ee8f29c6d92e4506d5f19762ac3c"
+  integrity sha512-3cKScz9Jx2/Pr9ijj1OzGlBDfcmx7OMVBt4+P1uRR0SSW4cm1/y3Mo4OY3lfkuaYifMNBW8Wz6lQHbs1bihr7A==
+  dependencies:
+    "@types/slice-ansi" "^4.0.0"
+    debug "^4.0.0"
+    signal-exit "^3.0.3"
+    slice-ansi "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+    tslib "^2.0.1"
+    untildify "^4.0.0"
+    wrap-ansi "^7.0.0"
+
+"@isaacs/cliui@^8.0.2":
+  version "8.0.2"
+  resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
+  integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==
+  dependencies:
+    string-width "^5.1.2"
+    string-width-cjs "npm:string-width@^4.2.0"
+    strip-ansi "^7.0.1"
+    strip-ansi-cjs "npm:strip-ansi@^6.0.1"
+    wrap-ansi "^8.1.0"
+    wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
+
 "@isomorphic-git/[email protected]":
 "@isomorphic-git/[email protected]":
   version "3.3.2"
   version "3.3.2"
   resolved "https://registry.yarnpkg.com/@isomorphic-git/idb-keyval/-/idb-keyval-3.3.2.tgz#c0509a6c5987d8a62efb3e47f2815bcc5eda2489"
   resolved "https://registry.yarnpkg.com/@isomorphic-git/idb-keyval/-/idb-keyval-3.3.2.tgz#c0509a6c5987d8a62efb3e47f2815bcc5eda2489"
@@ -1246,6 +1346,11 @@ ansi-regex@^5.0.1:
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
   integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
   integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
 
 
+ansi-regex@^6.0.1:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654"
+  integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==
+
 ansi-styles@^3.2.1:
 ansi-styles@^3.2.1:
   version "3.2.1"
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
@@ -1260,6 +1365,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
   dependencies:
   dependencies:
     color-convert "^2.0.1"
     color-convert "^2.0.1"
 
 
+ansi-styles@^6.1.0:
+  version "6.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
+  integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
+
 [email protected], ansi-wrap@^0.1.0:
 [email protected], ansi-wrap@^0.1.0:
   version "0.1.0"
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf"
   resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf"
@@ -2210,6 +2320,11 @@ comlink@^4.4.1:
   resolved "https://registry.yarnpkg.com/comlink/-/comlink-4.4.1.tgz#e568b8e86410b809e8600eb2cf40c189371ef981"
   resolved "https://registry.yarnpkg.com/comlink/-/comlink-4.4.1.tgz#e568b8e86410b809e8600eb2cf40c189371ef981"
   integrity sha512-+1dlx0aY5Jo1vHy/tSsIGpSkN4tS9rZSW8FIhG0JH/crs9wwweswIo/POr451r7bZww3hFbPAKnTpimzL/mm4Q==
   integrity sha512-+1dlx0aY5Jo1vHy/tSsIGpSkN4tS9rZSW8FIhG0JH/crs9wwweswIo/POr451r7bZww3hFbPAKnTpimzL/mm4Q==
 
 
+commander@^12.1.0:
+  version "12.1.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3"
+  integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==
+
 commander@^4.0.0:
 commander@^4.0.0:
   version "4.1.1"
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
   resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
@@ -2390,6 +2505,15 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.3:
     shebang-command "^2.0.0"
     shebang-command "^2.0.0"
     which "^2.0.1"
     which "^2.0.1"
 
 
+cross-spawn@^7.0.6:
+  version "7.0.6"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
+  integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
+  dependencies:
+    path-key "^3.1.0"
+    shebang-command "^2.0.0"
+    which "^2.0.1"
+
 crypto-browserify@^3.11.0:
 crypto-browserify@^3.11.0:
   version "3.12.0"
   version "3.12.0"
   resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
   resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
@@ -2570,6 +2694,13 @@ debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, d
   dependencies:
   dependencies:
     ms "2.1.2"
     ms "2.1.2"
 
 
+debug@^4.4.0:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
+  integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
+  dependencies:
+    ms "^2.1.3"
+
 debug@~4.3.1, debug@~4.3.2, debug@~4.3.4:
 debug@~4.3.1, debug@~4.3.2, debug@~4.3.4:
   version "4.3.6"
   version "4.3.6"
   resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b"
   resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b"
@@ -2878,6 +3009,11 @@ earcut@^2.2.2:
   resolved "https://registry.yarnpkg.com/earcut/-/earcut-2.2.4.tgz#6d02fd4d68160c114825d06890a92ecaae60343a"
   resolved "https://registry.yarnpkg.com/earcut/-/earcut-2.2.4.tgz#6d02fd4d68160c114825d06890a92ecaae60343a"
   integrity sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==
   integrity sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==
 
 
+eastasianwidth@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
+  integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
+
 [email protected]:
 [email protected]:
   version "1.1.1"
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@@ -2943,6 +3079,11 @@ emoji-regex@^8.0.0:
   resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
   resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
   integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
   integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
 
 
+emoji-regex@^9.2.2:
+  version "9.2.2"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
+  integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
+
 encodeurl@~1.0.2:
 encodeurl@~1.0.2:
   version "1.0.2"
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
   resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
@@ -3519,6 +3660,14 @@ for-own@^1.0.0:
   dependencies:
   dependencies:
     for-in "^1.0.1"
     for-in "^1.0.1"
 
 
+foreground-child@^3.1.0:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f"
+  integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==
+  dependencies:
+    cross-spawn "^7.0.6"
+    signal-exit "^4.0.1"
+
 fraction.js@^4.3.6:
 fraction.js@^4.3.6:
   version "4.3.6"
   version "4.3.6"
   resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.6.tgz#e9e3acec6c9a28cf7bc36cbe35eea4ceb2c5c92d"
   resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.6.tgz#e9e3acec6c9a28cf7bc36cbe35eea4ceb2c5c92d"
@@ -3550,6 +3699,15 @@ fs-extra@^10.0.0:
     jsonfile "^6.0.1"
     jsonfile "^6.0.1"
     universalify "^2.0.0"
     universalify "^2.0.0"
 
 
+fs-extra@^11.2.0:
+  version "11.3.0"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.0.tgz#0daced136bbaf65a555a326719af931adc7a314d"
+  integrity sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==
+  dependencies:
+    graceful-fs "^4.2.0"
+    jsonfile "^6.0.1"
+    universalify "^2.0.0"
+
 fs-extra@^8.1.0:
 fs-extra@^8.1.0:
   version "8.1.0"
   version "8.1.0"
   resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
   resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
@@ -3784,6 +3942,18 @@ [email protected]:
     minipass "^4.2.4"
     minipass "^4.2.4"
     path-scurry "^1.5.0"
     path-scurry "^1.5.0"
 
 
+glob@^11.0.0:
+  version "11.0.1"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.1.tgz#1c3aef9a59d680e611b53dcd24bb8639cef064d9"
+  integrity sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==
+  dependencies:
+    foreground-child "^3.1.0"
+    jackspeak "^4.0.1"
+    minimatch "^10.0.0"
+    minipass "^7.1.2"
+    package-json-from-dist "^1.0.0"
+    path-scurry "^2.0.0"
+
 glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.7:
 glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.7:
   version "7.2.3"
   version "7.2.3"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
@@ -4300,6 +4470,11 @@ ini@^3.0.1:
   resolved "https://registry.yarnpkg.com/ini/-/ini-3.0.1.tgz#c76ec81007875bc44d544ff7a11a55d12294102d"
   resolved "https://registry.yarnpkg.com/ini/-/ini-3.0.1.tgz#c76ec81007875bc44d544ff7a11a55d12294102d"
   integrity sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==
   integrity sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==
 
 
+ini@^4.1.1:
+  version "4.1.3"
+  resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.3.tgz#4c359675a6071a46985eb39b14e4a2c0ec98a795"
+  integrity sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==
+
 inter-ui@^3.19.3:
 inter-ui@^3.19.3:
   version "3.19.3"
   version "3.19.3"
   resolved "https://registry.yarnpkg.com/inter-ui/-/inter-ui-3.19.3.tgz#cf4b4b6d30de8d5463e2462588654b325206488c"
   resolved "https://registry.yarnpkg.com/inter-ui/-/inter-ui-3.19.3.tgz#cf4b4b6d30de8d5463e2462588654b325206488c"
@@ -4765,6 +4940,13 @@ istextorbinary@^3.0.0:
     binaryextensions "^2.2.0"
     binaryextensions "^2.2.0"
     textextensions "^3.2.0"
     textextensions "^3.2.0"
 
 
+jackspeak@^4.0.1:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.1.0.tgz#c489c079f2b636dc4cbe9b0312a13ff1282e561b"
+  integrity sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==
+  dependencies:
+    "@isaacs/cliui" "^8.0.2"
+
 jiti@^1.19.1:
 jiti@^1.19.1:
   version "1.21.0"
   version "1.21.0"
   resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d"
   resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d"
@@ -4941,7 +5123,7 @@ kleur@^3.0.3:
   resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
   resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
   integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
   integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
 
 
-kleur@^4.1.4:
+kleur@^4.1.4, kleur@^4.1.5:
   version "4.1.5"
   version "4.1.5"
   resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
   resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
   integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==
   integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==
@@ -5140,6 +5322,11 @@ lru-cache@^10.2.0:
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
   integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
   integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
 
 
+lru-cache@^11.0.0:
+  version "11.1.0"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.1.0.tgz#afafb060607108132dbc1cf8ae661afb69486117"
+  integrity sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==
+
 lru-cache@^5.1.1:
 lru-cache@^5.1.1:
   version "5.1.1"
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
@@ -5413,6 +5600,13 @@ minimalistic-crypto-utils@^1.0.1:
   resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
   resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
   integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==
   integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==
 
 
+minimatch@^10.0.0:
+  version "10.0.1"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b"
+  integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==
+  dependencies:
+    brace-expansion "^2.0.1"
+
 minimatch@^3.0.4, minimatch@^3.1.1:
 minimatch@^3.0.4, minimatch@^3.1.1:
   version "3.1.2"
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
@@ -5470,6 +5664,11 @@ minipass@^5.0.0:
   resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.3.tgz#05ea638da44e475037ed94d1c7efcc76a25e1974"
   resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.3.tgz#05ea638da44e475037ed94d1c7efcc76a25e1974"
   integrity sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==
   integrity sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==
 
 
+minipass@^7.1.2:
+  version "7.1.2"
+  resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
+  integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
+
 minizlib@^2.1.1:
 minizlib@^2.1.1:
   version "2.1.2"
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
   resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
@@ -5515,6 +5714,11 @@ [email protected]:
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
   integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
   integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
 
 
+ms@^2.1.3:
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+  integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
 mute-stdout@^1.0.0:
 mute-stdout@^1.0.0:
   version "1.0.1"
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.1.tgz#acb0300eb4de23a7ddeec014e3e96044b3472331"
   resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.1.tgz#acb0300eb4de23a7ddeec014e3e96044b3472331"
@@ -5578,6 +5782,23 @@ native-run@^1.7.3:
     tslib "^2.4.0"
     tslib "^2.4.0"
     yauzl "^2.10.0"
     yauzl "^2.10.0"
 
 
+native-run@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/native-run/-/native-run-2.0.1.tgz#a9b213c32824b007cbdd0279e0edd3c24bcc2f7a"
+  integrity sha512-XfG1FBZLM50J10xH9361whJRC9SHZ0Bub4iNRhhI61C8Jv0e1ud19muex6sNKB51ibQNUJNuYn25MuYET/rE6w==
+  dependencies:
+    "@ionic/utils-fs" "^3.1.7"
+    "@ionic/utils-terminal" "^2.3.4"
+    bplist-parser "^0.3.2"
+    debug "^4.3.4"
+    elementtree "^0.1.7"
+    ini "^4.1.1"
+    plist "^3.1.0"
+    split2 "^4.2.0"
+    through2 "^4.0.2"
+    tslib "^2.6.2"
+    yauzl "^2.10.0"
+
 [email protected]:
 [email protected]:
   version "0.6.3"
   version "0.6.3"
   resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
   resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
@@ -5944,6 +6165,11 @@ p-try@^2.0.0:
   resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
   resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
   integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
   integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
 
 
+package-json-from-dist@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505"
+  integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==
+
 pako@~1.0.2, pako@~1.0.5:
 pako@~1.0.2, pako@~1.0.5:
   version "1.0.11"
   version "1.0.11"
   resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
   resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
@@ -6118,6 +6344,14 @@ path-scurry@^1.6.1:
     lru-cache "^9.1.1 || ^10.0.0"
     lru-cache "^9.1.1 || ^10.0.0"
     minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
     minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
 
 
+path-scurry@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580"
+  integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==
+  dependencies:
+    lru-cache "^11.0.0"
+    minipass "^7.1.2"
+
 path-type@^1.0.0:
 path-type@^1.0.0:
   version "1.1.0"
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
   resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
@@ -6334,7 +6568,7 @@ [email protected], playwright@=1.51.0:
   optionalDependencies:
   optionalDependencies:
     fsevents "2.3.2"
     fsevents "2.3.2"
 
 
-plist@^3.0.5, plist@^3.0.6:
+plist@^3.0.5, plist@^3.0.6, plist@^3.1.0:
   version "3.1.0"
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9"
   resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9"
   integrity sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==
   integrity sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==
@@ -6943,14 +7177,13 @@ [email protected]:
     iconv-lite "0.4.24"
     iconv-lite "0.4.24"
     unpipe "1.0.0"
     unpipe "1.0.0"
 
 
-react-dom@17.0.2:
-  version "17.0.2"
-  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
-  integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
+react-dom@18.3.1:
+  version "18.3.1"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4"
+  integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==
   dependencies:
   dependencies:
     loose-envify "^1.1.0"
     loose-envify "^1.1.0"
-    object-assign "^4.1.1"
-    scheduler "^0.20.2"
+    scheduler "^0.23.2"
 
 
 [email protected]:
 [email protected]:
   version "3.3.2"
   version "3.3.2"
@@ -7038,13 +7271,12 @@ [email protected]:
   resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.12.5.tgz#cf92efc2527e56d6df1d4d63c6e4dd3fac5a4030"
   resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.12.5.tgz#cf92efc2527e56d6df1d4d63c6e4dd3fac5a4030"
   integrity sha512-YeCbRRsC9CLf0buD0Rct7WsDbzf+yBU1wGbo05/XjbcN2nJuhgh040m3y3+6HVogTZxEqVm45ac9Fpae4/MxRQ==
   integrity sha512-YeCbRRsC9CLf0buD0Rct7WsDbzf+yBU1wGbo05/XjbcN2nJuhgh040m3y3+6HVogTZxEqVm45ac9Fpae4/MxRQ==
 
 
-react@17.0.2:
-  version "17.0.2"
-  resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
-  integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
+react@18.3.1:
+  version "18.3.1"
+  resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"
+  integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==
   dependencies:
   dependencies:
     loose-envify "^1.1.0"
     loose-envify "^1.1.0"
-    object-assign "^4.1.1"
 
 
 read-cache@^1.0.0:
 read-cache@^1.0.0:
   version "1.0.0"
   version "1.0.0"
@@ -7387,6 +7619,14 @@ rimraf@^4.4.1:
   dependencies:
   dependencies:
     glob "^9.2.0"
     glob "^9.2.0"
 
 
+rimraf@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-6.0.1.tgz#ffb8ad8844dd60332ab15f52bc104bc3ed71ea4e"
+  integrity sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==
+  dependencies:
+    glob "^11.0.0"
+    package-json-from-dist "^1.0.0"
+
 ripemd160@^2.0.0, ripemd160@^2.0.1:
 ripemd160@^2.0.0, ripemd160@^2.0.1:
   version "2.0.2"
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
   resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
@@ -7472,13 +7712,12 @@ sax@>=0.6.0:
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
   integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
   integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
 
 
-scheduler@^0.20.2:
-  version "0.20.2"
-  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
-  integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
+scheduler@^0.23.2:
+  version "0.23.2"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3"
+  integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==
   dependencies:
   dependencies:
     loose-envify "^1.1.0"
     loose-envify "^1.1.0"
-    object-assign "^4.1.1"
 
 
 semver-compare@^1.0.0:
 semver-compare@^1.0.0:
   version "1.0.0"
   version "1.0.0"
@@ -7516,6 +7755,11 @@ semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7:
   dependencies:
   dependencies:
     lru-cache "^6.0.0"
     lru-cache "^6.0.0"
 
 
+semver@^7.6.3:
+  version "7.7.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f"
+  integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==
+
 send-intent@^5.0.0:
 send-intent@^5.0.0:
   version "5.0.0"
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/send-intent/-/send-intent-5.0.0.tgz#95d00455a7db4d95d9f79f8203698e96760c7408"
   resolved "https://registry.yarnpkg.com/send-intent/-/send-intent-5.0.0.tgz#95d00455a7db4d95d9f79f8203698e96760c7408"
@@ -7652,6 +7896,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3:
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
   integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
   integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
 
 
+signal-exit@^4.0.1:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
+  integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
+
 simple-concat@^1.0.0:
 simple-concat@^1.0.0:
   version "1.0.1"
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
   resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
@@ -7854,7 +8103,7 @@ split-string@^3.0.1, split-string@^3.0.2:
   dependencies:
   dependencies:
     extend-shallow "^3.0.0"
     extend-shallow "^3.0.0"
 
 
-split2@^4.1.0:
+split2@^4.1.0, split2@^4.2.0:
   version "4.2.0"
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4"
   resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4"
   integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==
   integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==
@@ -7935,6 +8184,15 @@ strictdom@^1.0.1:
   resolved "https://registry.yarnpkg.com/strictdom/-/strictdom-1.0.1.tgz#189de91649f73d44d59b8432efa68ef9d2659460"
   resolved "https://registry.yarnpkg.com/strictdom/-/strictdom-1.0.1.tgz#189de91649f73d44d59b8432efa68ef9d2659460"
   integrity sha512-cEmp9QeXXRmjj/rVp9oyiqcvyocWab/HaoN4+bwFeZ7QzykJD6L3yD4v12K1x0tHpqRqVpJevN3gW7kyM39Bqg==
   integrity sha512-cEmp9QeXXRmjj/rVp9oyiqcvyocWab/HaoN4+bwFeZ7QzykJD6L3yD4v12K1x0tHpqRqVpJevN3gW7kyM39Bqg==
 
 
+"string-width-cjs@npm:string-width@^4.2.0":
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
+
 string-width@^1.0.1, string-width@^1.0.2:
 string-width@^1.0.1, string-width@^1.0.2:
   version "1.0.2"
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
@@ -7961,6 +8219,15 @@ string-width@^2.0.0, string-width@^2.1.1:
     is-fullwidth-code-point "^2.0.0"
     is-fullwidth-code-point "^2.0.0"
     strip-ansi "^4.0.0"
     strip-ansi "^4.0.0"
 
 
+string-width@^5.0.1, string-width@^5.1.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
+  integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
+  dependencies:
+    eastasianwidth "^0.2.0"
+    emoji-regex "^9.2.2"
+    strip-ansi "^7.0.1"
+
 string.prototype.padend@^3.0.0:
 string.prototype.padend@^3.0.0:
   version "3.1.5"
   version "3.1.5"
   resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.5.tgz#311ef3a4e3c557dd999cdf88fbdde223f2ac0f95"
   resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.5.tgz#311ef3a4e3c557dd999cdf88fbdde223f2ac0f95"
@@ -8011,6 +8278,13 @@ string_decoder@~1.1.1:
   dependencies:
   dependencies:
     safe-buffer "~5.1.0"
     safe-buffer "~5.1.0"
 
 
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
 strip-ansi@^3.0.0, strip-ansi@^3.0.1:
 strip-ansi@^3.0.0, strip-ansi@^3.0.1:
   version "3.0.1"
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
@@ -8032,6 +8306,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
   dependencies:
   dependencies:
     ansi-regex "^5.0.1"
     ansi-regex "^5.0.1"
 
 
+strip-ansi@^7.0.1:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
+  integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
+  dependencies:
+    ansi-regex "^6.0.1"
+
 strip-bom@^2.0.0:
 strip-bom@^2.0.0:
   version "2.0.0"
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
   resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
@@ -8474,6 +8755,11 @@ tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.4.0:
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
   integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
   integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
 
 
+tslib@^2.6.2, tslib@^2.8.1:
+  version "2.8.1"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
+  integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
+
 [email protected]:
 [email protected]:
   version "0.0.0"
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
   resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
@@ -8974,6 +9260,15 @@ wide-align@^1.1.2:
   dependencies:
   dependencies:
     string-width "^1.0.2 || 2 || 3 || 4"
     string-width "^1.0.2 || 2 || 3 || 4"
 
 
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+  dependencies:
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+
 wrap-ansi@^2.0.0:
 wrap-ansi@^2.0.0:
   version "2.1.0"
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
@@ -8991,6 +9286,15 @@ wrap-ansi@^7.0.0:
     string-width "^4.1.0"
     string-width "^4.1.0"
     strip-ansi "^6.0.0"
     strip-ansi "^6.0.0"
 
 
+wrap-ansi@^8.1.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
+  integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==
+  dependencies:
+    ansi-styles "^6.1.0"
+    string-width "^5.0.1"
+    strip-ansi "^7.0.1"
+
 wrappy@1:
 wrappy@1:
   version "1.0.2"
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
   resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
@@ -9024,6 +9328,14 @@ xml2js@^0.5.0:
     sax ">=0.6.0"
     sax ">=0.6.0"
     xmlbuilder "~11.0.0"
     xmlbuilder "~11.0.0"
 
 
+xml2js@^0.6.2:
+  version "0.6.2"
+  resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499"
+  integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==
+  dependencies:
+    sax ">=0.6.0"
+    xmlbuilder "~11.0.0"
+
 xmlbuilder@^15.1.1:
 xmlbuilder@^15.1.1:
   version "15.1.1"
   version "15.1.1"
   resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5"
   resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5"