Parcourir la source

Merge branch 'feat/db' into refactor/tag-as-type

Tienson Qin il y a 1 an
Parent
commit
2599b45768

+ 9 - 0
.clj-kondo/funcool/promesa/config.edn

@@ -0,0 +1,9 @@
+{:lint-as {promesa.core/->          clojure.core/->
+           promesa.core/->>         clojure.core/->>
+           promesa.core/as->        clojure.core/as->
+           promesa.core/let         clojure.core/let
+           promesa.core/plet        clojure.core/let
+           promesa.core/loop        clojure.core/loop
+           promesa.core/recur       clojure.core/recur
+           promesa.core/with-redefs clojure.core/with-redefs
+           promesa.core/doseq       clojure.core/doseq}}

+ 19 - 0
packages/ui/@/hooks/use-mobile.tsx

@@ -0,0 +1,19 @@
+import * as React from "react"
+
+const MOBILE_BREAKPOINT = 768
+
+export function useIsMobile() {
+  const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
+
+  React.useEffect(() => {
+    const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
+    const onChange = () => {
+      setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
+    }
+    mql.addEventListener("change", onChange)
+    setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
+    return () => mql.removeEventListener("change", onChange)
+  }, [])
+
+  return !!isMobile
+}

+ 6 - 6
packages/ui/package.json

@@ -20,26 +20,26 @@
     "@radix-ui/react-avatar": "^1.0.4",
     "@radix-ui/react-checkbox": "^1.0.4",
     "@radix-ui/react-context-menu": "^2.1.5",
-    "@radix-ui/react-dialog": "^1.0.5",
+    "@radix-ui/react-dialog": "^1.1.2",
     "@radix-ui/react-dropdown-menu": "^2.0.6",
     "@radix-ui/react-label": "^2.0.2",
     "@radix-ui/react-popover": "^1.0.7",
     "@radix-ui/react-radio-group": "^1.1.3",
     "@radix-ui/react-select": "^2.0.0",
-    "@radix-ui/react-separator": "^1.0.3",
+    "@radix-ui/react-separator": "^1.1.0",
     "@radix-ui/react-slider": "^1.1.2",
-    "@radix-ui/react-slot": "^1.0.2",
+    "@radix-ui/react-slot": "^1.1.0",
     "@radix-ui/react-switch": "^1.0.3",
     "@radix-ui/react-tabs": "^1.1.1",
     "@radix-ui/react-toast": "^1.1.5",
     "@radix-ui/react-toggle": "^1.0.3",
     "@radix-ui/react-toggle-group": "^1.0.4",
-    "@radix-ui/react-tooltip": "^1.0.7",
-    "class-variance-authority": "^0.7.0",
+    "@radix-ui/react-tooltip": "^1.1.4",
+    "class-variance-authority": "^0.7.1",
     "clsx": "^2.0.0",
     "cmdk": "^0.2.0",
     "date-fns": "^2.30.0",
-    "lucide-react": "^0.292.0",
+    "lucide-react": "^0.468.0",
     "react": "^18",
     "react-day-picker": "^8.9.1",
     "react-dom": "^18",

+ 0 - 1
packages/ui/tailwind.config.js

@@ -76,7 +76,6 @@ module.exports = {
           DEFAULT: 'hsl(var(--card))',
           foreground: 'hsl(var(--card-foreground))',
         },
-
         red: mapRadixColorToTailwind('red'),
         pink: mapRadixColorToTailwind('pink'),
         orange: mapRadixColorToTailwind('orange'),

+ 170 - 31
packages/ui/yarn.lock

@@ -2223,6 +2223,13 @@
     "@babel/runtime" "^7.13.10"
     "@radix-ui/react-primitive" "1.0.3"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz#744f388182d360b86285217e43b6c63633f39e7a"
+  integrity sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==
+  dependencies:
+    "@radix-ui/react-primitive" "2.0.0"
+
 "@radix-ui/react-avatar@^1.0.4":
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-avatar/-/react-avatar-1.0.4.tgz#de9a5349d9e3de7bbe990334c4d2011acbbb9623"
@@ -2347,7 +2354,7 @@
     aria-hidden "^1.1.1"
     react-remove-scroll "2.5.4"
 
-"@radix-ui/[email protected]", "@radix-ui/react-dialog@^1.0.5":
+"@radix-ui/[email protected]":
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz#71657b1b116de6c7a0b03242d7d43e01062c7300"
   integrity sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==
@@ -2368,6 +2375,26 @@
     aria-hidden "^1.1.1"
     react-remove-scroll "2.5.5"
 
+"@radix-ui/react-dialog@^1.1.2":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.2.tgz#d9345575211d6f2d13e209e84aec9a8584b54d6c"
+  integrity sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==
+  dependencies:
+    "@radix-ui/primitive" "1.1.0"
+    "@radix-ui/react-compose-refs" "1.1.0"
+    "@radix-ui/react-context" "1.1.1"
+    "@radix-ui/react-dismissable-layer" "1.1.1"
+    "@radix-ui/react-focus-guards" "1.1.1"
+    "@radix-ui/react-focus-scope" "1.1.0"
+    "@radix-ui/react-id" "1.1.0"
+    "@radix-ui/react-portal" "1.1.2"
+    "@radix-ui/react-presence" "1.1.1"
+    "@radix-ui/react-primitive" "2.0.0"
+    "@radix-ui/react-slot" "1.1.0"
+    "@radix-ui/react-use-controllable-state" "1.1.0"
+    aria-hidden "^1.1.1"
+    react-remove-scroll "2.6.0"
+
 "@radix-ui/[email protected]":
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.0.1.tgz#9cb61bf2ccf568f3421422d182637b7f47596c9b"
@@ -2416,6 +2443,17 @@
     "@radix-ui/react-use-callback-ref" "1.0.1"
     "@radix-ui/react-use-escape-keydown" "1.0.3"
 
+"@radix-ui/[email protected]":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz#cbdcb739c5403382bdde5f9243042ba643883396"
+  integrity sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==
+  dependencies:
+    "@radix-ui/primitive" "1.1.0"
+    "@radix-ui/react-compose-refs" "1.1.0"
+    "@radix-ui/react-primitive" "2.0.0"
+    "@radix-ui/react-use-callback-ref" "1.1.0"
+    "@radix-ui/react-use-escape-keydown" "1.1.0"
+
 "@radix-ui/react-dropdown-menu@^2.0.6":
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz#cdf13c956c5e263afe4e5f3587b3071a25755b63"
@@ -2444,6 +2482,11 @@
   dependencies:
     "@babel/runtime" "^7.13.10"
 
+"@radix-ui/[email protected]":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz#8635edd346304f8b42cae86b05912b61aef27afe"
+  integrity sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==
+
 "@radix-ui/[email protected]":
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.0.tgz#95a0c1188276dc8933b1eac5f1cdb6471e01ade5"
@@ -2474,6 +2517,15 @@
     "@radix-ui/react-primitive" "1.0.3"
     "@radix-ui/react-use-callback-ref" "1.0.1"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz#ebe2891a298e0a33ad34daab2aad8dea31caf0b2"
+  integrity sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==
+  dependencies:
+    "@radix-ui/react-compose-refs" "1.1.0"
+    "@radix-ui/react-primitive" "2.0.0"
+    "@radix-ui/react-use-callback-ref" "1.1.0"
+
 "@radix-ui/[email protected]":
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.0.0.tgz#8d43224910741870a45a8c9d092f25887bb6d11e"
@@ -2586,6 +2638,22 @@
     "@radix-ui/react-use-size" "1.0.1"
     "@radix-ui/rect" "1.0.1"
 
+"@radix-ui/[email protected]":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.0.tgz#a3e500193d144fe2d8f5d5e60e393d64111f2a7a"
+  integrity sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==
+  dependencies:
+    "@floating-ui/react-dom" "^2.0.0"
+    "@radix-ui/react-arrow" "1.1.0"
+    "@radix-ui/react-compose-refs" "1.1.0"
+    "@radix-ui/react-context" "1.1.0"
+    "@radix-ui/react-primitive" "2.0.0"
+    "@radix-ui/react-use-callback-ref" "1.1.0"
+    "@radix-ui/react-use-layout-effect" "1.1.0"
+    "@radix-ui/react-use-rect" "1.1.0"
+    "@radix-ui/react-use-size" "1.1.0"
+    "@radix-ui/rect" "1.1.0"
+
 "@radix-ui/[email protected]":
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.0.tgz#7220b66743394fabb50c55cb32381395cc4a276b"
@@ -2610,6 +2678,14 @@
     "@babel/runtime" "^7.13.10"
     "@radix-ui/react-primitive" "1.0.3"
 
+"@radix-ui/[email protected]":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.2.tgz#51eb46dae7505074b306ebcb985bf65cc547d74e"
+  integrity sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==
+  dependencies:
+    "@radix-ui/react-primitive" "2.0.0"
+    "@radix-ui/react-use-layout-effect" "1.1.0"
+
 "@radix-ui/[email protected]":
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.0.tgz#814fe46df11f9a468808a6010e3f3ca7e0b2e84a"
@@ -2763,7 +2839,7 @@
     aria-hidden "^1.1.1"
     react-remove-scroll "2.5.5"
 
-"@radix-ui/[email protected]", "@radix-ui/react-separator@^1.0.3":
+"@radix-ui/[email protected]":
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-separator/-/react-separator-1.0.3.tgz#be5a931a543d5726336b112f465f58585c04c8aa"
   integrity sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==
@@ -2771,6 +2847,13 @@
     "@babel/runtime" "^7.13.10"
     "@radix-ui/react-primitive" "1.0.3"
 
+"@radix-ui/react-separator@^1.1.0":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-separator/-/react-separator-1.1.0.tgz#ee0f4d86003b0e3ea7bc6ccab01ea0adee32663e"
+  integrity sha512-3uBAs+egzvJBDZAzvb/n4NxxOYpnspmWxO2u5NbZ8Y6FM/NdrGSF9bop3Cf6F6C71z1rTSn8KV0Fo2ZVd79lGA==
+  dependencies:
+    "@radix-ui/react-primitive" "2.0.0"
+
 "@radix-ui/react-slider@^1.1.2":
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-slider/-/react-slider-1.1.2.tgz#330ff2a0e1f6c19aace76590004f229a7e8fbe6c"
@@ -2797,7 +2880,7 @@
     "@babel/runtime" "^7.13.10"
     "@radix-ui/react-compose-refs" "1.0.0"
 
-"@radix-ui/[email protected]", "@radix-ui/react-slot@^1.0.2":
+"@radix-ui/[email protected]":
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.2.tgz#a9ff4423eade67f501ffb32ec22064bc9d3099ab"
   integrity sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==
@@ -2805,7 +2888,7 @@
     "@babel/runtime" "^7.13.10"
     "@radix-ui/react-compose-refs" "1.0.1"
 
-"@radix-ui/[email protected]":
+"@radix-ui/[email protected]", "@radix-ui/react-slot@^1.1.0":
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.0.tgz#7c5e48c36ef5496d97b08f1357bb26ed7c714b84"
   integrity sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==
@@ -2897,24 +2980,23 @@
     "@radix-ui/react-separator" "1.0.3"
     "@radix-ui/react-toggle-group" "1.0.4"
 
-"@radix-ui/react-tooltip@^1.0.7":
-  version "1.0.7"
-  resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz#8f55070f852e7e7450cc1d9210b793d2e5a7686e"
-  integrity sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==
+"@radix-ui/react-tooltip@^1.1.4":
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.1.4.tgz#152d8485859b80d395d6b3229f676fef3cec56b3"
+  integrity sha512-QpObUH/ZlpaO4YgHSaYzrLO2VuO+ZBFFgGzjMUPwtiYnAzzNNDPJeEGRrT7qNOrWm/Jr08M1vlp+vTHtnSQ0Uw==
   dependencies:
-    "@babel/runtime" "^7.13.10"
-    "@radix-ui/primitive" "1.0.1"
-    "@radix-ui/react-compose-refs" "1.0.1"
-    "@radix-ui/react-context" "1.0.1"
-    "@radix-ui/react-dismissable-layer" "1.0.5"
-    "@radix-ui/react-id" "1.0.1"
-    "@radix-ui/react-popper" "1.1.3"
-    "@radix-ui/react-portal" "1.0.4"
-    "@radix-ui/react-presence" "1.0.1"
-    "@radix-ui/react-primitive" "1.0.3"
-    "@radix-ui/react-slot" "1.0.2"
-    "@radix-ui/react-use-controllable-state" "1.0.1"
-    "@radix-ui/react-visually-hidden" "1.0.3"
+    "@radix-ui/primitive" "1.1.0"
+    "@radix-ui/react-compose-refs" "1.1.0"
+    "@radix-ui/react-context" "1.1.1"
+    "@radix-ui/react-dismissable-layer" "1.1.1"
+    "@radix-ui/react-id" "1.1.0"
+    "@radix-ui/react-popper" "1.2.0"
+    "@radix-ui/react-portal" "1.1.2"
+    "@radix-ui/react-presence" "1.1.1"
+    "@radix-ui/react-primitive" "2.0.0"
+    "@radix-ui/react-slot" "1.1.0"
+    "@radix-ui/react-use-controllable-state" "1.1.0"
+    "@radix-ui/react-visually-hidden" "1.1.0"
 
 "@radix-ui/[email protected]":
   version "1.0.0"
@@ -2974,6 +3056,13 @@
     "@babel/runtime" "^7.13.10"
     "@radix-ui/react-use-callback-ref" "1.0.1"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz#31a5b87c3b726504b74e05dac1edce7437b98754"
+  integrity sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==
+  dependencies:
+    "@radix-ui/react-use-callback-ref" "1.1.0"
+
 "@radix-ui/[email protected]":
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz#2fc19e97223a81de64cd3ba1dc42ceffd82374dc"
@@ -3008,6 +3097,13 @@
     "@babel/runtime" "^7.13.10"
     "@radix-ui/rect" "1.0.1"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz#13b25b913bd3e3987cc9b073a1a164bb1cf47b88"
+  integrity sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==
+  dependencies:
+    "@radix-ui/rect" "1.1.0"
+
 "@radix-ui/[email protected]":
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz#1c5f5fea940a7d7ade77694bb98116fb49f870b2"
@@ -3016,6 +3112,13 @@
     "@babel/runtime" "^7.13.10"
     "@radix-ui/react-use-layout-effect" "1.0.1"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz#b4dba7fbd3882ee09e8d2a44a3eed3a7e555246b"
+  integrity sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==
+  dependencies:
+    "@radix-ui/react-use-layout-effect" "1.1.0"
+
 "@radix-ui/[email protected]":
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz#51aed9dd0fe5abcad7dee2a234ad36106a6984ac"
@@ -3024,6 +3127,13 @@
     "@babel/runtime" "^7.13.10"
     "@radix-ui/react-primitive" "1.0.3"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz#ad47a8572580f7034b3807c8e6740cd41038a5a2"
+  integrity sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==
+  dependencies:
+    "@radix-ui/react-primitive" "2.0.0"
+
 "@radix-ui/[email protected]":
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.0.1.tgz#bf8e7d947671996da2e30f4904ece343bc4a883f"
@@ -3031,6 +3141,11 @@
   dependencies:
     "@babel/runtime" "^7.13.10"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.1.0.tgz#f817d1d3265ac5415dadc67edab30ae196696438"
+  integrity sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==
+
 "@sinclair/typebox@^0.27.8":
   version "0.27.8"
   resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
@@ -4981,12 +5096,12 @@ ci-info@^3.2.0:
   resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4"
   integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==
 
-class-variance-authority@^0.7.0:
-  version "0.7.0"
-  resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.0.tgz#1c3134d634d80271b1837452b06d821915954522"
-  integrity sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==
+class-variance-authority@^0.7.1:
+  version "0.7.1"
+  resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz#4008a798a0e4553a781a57ac5177c9fb5d043787"
+  integrity sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==
   dependencies:
-    clsx "2.0.0"
+    clsx "^2.1.1"
 
 clean-css@^5.2.2:
   version "5.3.2"
@@ -5049,11 +5164,16 @@ clone@^2.1.1:
   resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
   integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
 
-[email protected], clsx@^2.0.0:
+clsx@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b"
   integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==
 
+clsx@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
+  integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
+
 cmdk@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/cmdk/-/cmdk-0.2.0.tgz#53c52d56d8776c8bb8ced1055b5054100c388f7c"
@@ -7214,10 +7334,10 @@ lru-cache@^6.0.0:
   dependencies:
     semver "^7.3.5"
 
-lucide-react@^0.292.0:
-  version "0.292.0"
-  resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.292.0.tgz#c8a06b2ccd8a348a88669def3c0291c035de2884"
-  integrity sha512-rRgUkpEHWpa5VCT66YscInCQmQuPCB1RFRzkkxMxg4b+jaL0V12E3riWWR2Sh5OIiUhCwGW/ZExuEO4Az32E6Q==
+lucide-react@^0.468.0:
+  version "0.468.0"
+  resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.468.0.tgz#830c1bfd905575ddd23b832baa420c87db166910"
+  integrity sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==
 
 lz-string@^1.5.0:
   version "1.5.0"
@@ -8304,6 +8424,14 @@ react-remove-scroll-bar@^2.3.4:
     react-style-singleton "^2.2.1"
     tslib "^2.0.0"
 
+react-remove-scroll-bar@^2.3.6:
+  version "2.3.6"
+  resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz#3e585e9d163be84a010180b18721e851ac81a29c"
+  integrity sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==
+  dependencies:
+    react-style-singleton "^2.2.1"
+    tslib "^2.0.0"
+
 [email protected]:
   version "2.5.4"
   resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.4.tgz#afe6491acabde26f628f844b67647645488d2ea0"
@@ -8326,6 +8454,17 @@ [email protected]:
     use-callback-ref "^1.3.0"
     use-sidecar "^1.1.2"
 
[email protected]:
+  version "2.6.0"
+  resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz#fb03a0845d7768a4f1519a99fdb84983b793dc07"
+  integrity sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==
+  dependencies:
+    react-remove-scroll-bar "^2.3.6"
+    react-style-singleton "^2.2.1"
+    tslib "^2.1.0"
+    use-callback-ref "^1.3.0"
+    use-sidecar "^1.1.2"
+
 react-remove-scroll@^2.5.7:
   version "2.5.7"
   resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz#15a1fd038e8497f65a695bf26a4a57970cac1ccb"

+ 3 - 1
scripts/src/logseq/tasks/lang.clj

@@ -122,6 +122,8 @@
   {"(t (shortcut-helper/decorate-namespace" [] ;; shortcuts related so can ignore
    "(t (keyword" [:color/yellow :color/red :color/pink :color/green :color/blue
                   :color/purple :color/gray]
+   "(tt (keyword" [:left-side-bar/assets :left-side-bar/tasks]
+
    ;; from 3 files
    "(t (if" [:asset/show-in-folder :asset/open-in-browser
              :search-item/whiteboard :search-item/page
@@ -164,7 +166,7 @@
                                  ;; This currently assumes all ui translations
                                  ;; use (t and src/main. This can easily be
                                  ;; tweaked as needed
-                                 "grep -E -oh '\\(t :[^ )]+' -r src/main")
+                                 "grep -E -oh '\\(tt? :[^ )]+' -r src/main")
                           :out
                           string/split-lines
                           (map #(keyword (subs % 4)))

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

@@ -761,11 +761,15 @@
 
 (defn- input-placeholder
   [sidebar?]
-  (let [search-mode (:search/mode @state/state)]
+  (let [search-mode (:search/mode @state/state)
+        search-args (:search/args @state/state)]
     (cond
       (and (= search-mode :graph) (not sidebar?))
       "Add graph filter"
 
+      (= search-args :new-page)
+      "Type a page name to create"
+
       :else
       "What are you looking for?")))
 
@@ -949,6 +953,7 @@
                     ::input (atom (or (:initial-input opts) "")))))
    :will-unmount (fn [state]
                    (state/set-state! :search/mode nil)
+                   (state/set-state! :search/args nil)
                    state)}
   (mixins/event-mixin
    (fn [state]

+ 287 - 226
src/main/frontend/components/container.cljs

@@ -16,7 +16,7 @@
             [dommy.core :as d]
             [frontend.components.content :as cp-content]
             [frontend.config :as config]
-            [frontend.context.i18n :refer [t]]
+            [frontend.context.i18n :refer [t tt]]
             [frontend.db :as db]
             [electron.ipc :as ipc]
             [frontend.db-mixins :as db-mixins]
@@ -59,18 +59,24 @@
             [frontend.extensions.fsrs :as fsrs]
             [logseq.common.util.namespace :as ns-util]))
 
-(rum/defc nav-content-item < rum/reactive
-  [name {:keys [class count]} child]
+(rum/defc sidebar-content-group < rum/reactive
+  [name {:keys [class count more header-props enter-show-more? collapsable?]} child]
   (let [collapsed? (state/sub [:ui/navigation-item-collapsed? class])]
-    [:div.nav-content-item
+    [:div.sidebar-content-group
      {:class (util/classnames [class {:is-expand (not collapsed?)
                                       :has-children (and (number? count) (> count 0))}])}
-     [:div.nav-content-item-inner
-      [:div.header.items-center
-       {:on-click (fn [^js/MouseEvent _e]
-                    (state/toggle-navigation-item-collapsed! class))}
-       [:div.a name]
-       [:div.b (ui/icon "chevron-left" {:class "more" :size 14})]]
+     [:div.sidebar-content-group-inner
+      [:div.hd.items-center
+       (cond-> (merge header-props
+                      {:class (util/classnames [(:class header-props)
+                                                {:non-collapsable (false? collapsable?)
+                                                 :enter-show-more (true? enter-show-more?)}])})
+
+         (not (false? collapsable?))
+         (assoc :on-click (fn [^js/MouseEvent _e]
+                            (state/toggle-navigation-item-collapsed! class))))
+       [:span.a name]
+       [:span.b (or more (ui/icon "chevron-right" {:class "more" :size 15}))]]
       (when child [:div.bd child])]]))
 
 (rum/defc page-name
@@ -119,7 +125,7 @@
                              (x-menu-shortcut (shortcut-utils/decorate-binding "shift+click")))]))]
 
     ;; TODO: move to standalone component
-    [:a.flex.items-center.justify-between.relative.group.h-6
+    [:a.link-item.group
      (cond->
       {:on-click
        (fn [e]
@@ -134,7 +140,7 @@
                           (util/stop e))}
        (ldb/object? page)
        (assoc :title (block-handler/block-unique-title page)))
-     [:span.page-icon.ml-3.justify-center icon]
+     [:span.page-icon icon]
      [:span.page-title {:class (when untitled? "opacity-50")
                         :style {:display "ruby"}}
       (cond
@@ -151,25 +157,174 @@
      (shui/button
       {:size :sm
        :variant :ghost
-       :class "absolute right-2 top-0 px-1.5 scale-75 opacity-30 hidden group-hover:block hover:opacity-80 active:opacity-100"
+       :class "absolute !bg-transparent right-0 top-0 px-1.5 scale-75 opacity-40 hidden group-hover:block hover:opacity-80 active:opacity-100"
        :on-click #(do
                     (shui/popup-show! (.-target %) (x-menu-content)
                                       {:as-dropdown? true
                                        :content-props {:on-click (fn [] (shui/popup-hide!))
                                                        :class "w-60"}})
                     (util/stop %))}
-      [:i.relative {:style {:top "1px"}} (shui/tabler-icon "dots")])]))
+      [:i.relative {:style {:top "4px"}} (shui/tabler-icon "dots")])]))
 
-;; Fall back to default if icon is undefined or empty
+(defn sidebar-item
+  [{:keys [on-click-handler class title icon icon-extension? active href shortcut more]}]
+  [:div
+   {:class (util/classnames [class {:active active}])}
+   [:a.item.group.flex.items-center.text-sm.font-medium.rounded-md
+    {:on-click on-click-handler
+     :class (when active "active")
+     :href href}
+    (ui/icon (str icon) {:extension? icon-extension?})
+    [:span.flex-1 title]
+    (when shortcut
+      [:span.ml-1
+       (ui/render-keyboard-shortcut
+        (ui/keyboard-shortcut-from-config shortcut {:pick-first? true}))])
+    more]])
 
-(rum/defc favorites < rum/reactive
-  [t]
+(rum/defc sidebar-graphs
+  []
+  [:div.sidebar-graphs
+   (repo/graphs-selector)])
+
+(rum/defc sidebar-navigations-edit-content
+  [{:keys [_id navs checked-navs set-checked-navs!]}]
+  (let [[local-navs set-local-navs!] (rum/use-state checked-navs)]
+
+    (rum/use-effect!
+     (fn []
+       (set-checked-navs! local-navs))
+     [local-navs])
+
+    (for [nav navs
+          :let [name' (name nav)]]
+      (shui/dropdown-menu-checkbox-item
+       {:checked (contains? (set local-navs) nav)
+        :onCheckedChange (fn [v] (set-local-navs!
+                                  (fn []
+                                    (if v
+                                      (conj local-navs nav)
+                                      (filterv #(not= nav %) local-navs)))))}
+       (tt (keyword "left-side-bar" name')
+           (keyword "right-side-bar" name'))))))
+
+(rum/defc ^:large-vars/cleanup-todo sidebar-navigations
+  [{:keys [default-home route-match route-name srs-open? db-based? enable-whiteboards?]}]
+  (let [navs [:whiteboards :flashcards :graph-view :all-pages :tag/tasks :tag/assets]
+        [checked-navs set-checked-navs!] (rum/use-state (or (storage/get :ls-sidebar-navigations)
+                                                            [:whiteboards :flashcards :graph-view :all-pages :tag/tasks]))]
+
+    (rum/use-effect!
+     (fn []
+       (when (vector? checked-navs)
+         (storage/set :ls-sidebar-navigations checked-navs)))
+     [checked-navs])
+
+    (sidebar-content-group
+     [:a.wrap-th [:strong.flex-1 "Navigations"]]
+     {:collapsable? false
+      :enter-show-more? true
+      :header-props {:on-click (fn [^js e] (when-let [^js _el (some-> (.-target e) (.closest ".as-edit"))]
+                                             (shui/popup-show! _el
+                                                               #(sidebar-navigations-edit-content
+                                                                 {:id (:id %) :navs navs
+                                                                  :checked-navs checked-navs
+                                                                  :set-checked-navs! set-checked-navs!})
+                                                               {:as-dropdown? false})))}
+      :more [:a.as-edit {:class "!opacity-60 hover:!opacity-80 relative -top-0.5 right-0"}
+             (shui/tabler-icon "filter-edit" {:size 15})]}
+     [:div.sidebar-navigations.flex.flex-col.mt-1
+       ;; required custom home page
+      (let [page (:page default-home)]
+        (if (and page (not (state/enable-journals? (state/get-current-repo))))
+          (sidebar-item
+           {:class "home-nav"
+            :title page
+            :on-click-handler route-handler/redirect-to-home!
+            :active (and (not srs-open?)
+                         (= route-name :page)
+                         (= page (get-in route-match [:path-params :name])))
+            :icon "home"
+            :shortcut :go/home})
+
+          (sidebar-item
+           {:class "journals-nav"
+            :active (and (not srs-open?)
+                         (or (= route-name :all-journals) (= route-name :home)))
+            :title (t :left-side-bar/journals)
+            :on-click-handler (fn [e]
+                                (if (gobj/get e "shiftKey")
+                                  (route-handler/sidebar-journals!)
+                                  (route-handler/go-to-journals!)))
+            :icon "calendar"
+            :shortcut :go/journals})))
+
+      (for [nav checked-navs]
+        (cond
+          (= nav :whiteboards)
+          (when enable-whiteboards?
+            (when (or config/dev? (not db-based?))
+              (sidebar-item
+               {:class "whiteboard"
+                :title (t :right-side-bar/whiteboards)
+                :href (rfe/href :whiteboards)
+                :on-click-handler (fn [_e] (whiteboard-handler/onboarding-show))
+                :active (and (not srs-open?) (#{:whiteboard :whiteboards} route-name))
+                :icon "whiteboard"
+                :icon-extension? true
+                :shortcut :go/whiteboards})))
+
+          (= nav :flashcards)
+          (when (state/enable-flashcards? (state/get-current-repo))
+            (let [num (state/sub :srs/cards-due-count)]
+              (sidebar-item
+               {:class "flashcards-nav"
+                :title (t :right-side-bar/flashcards)
+                :icon "infinity"
+                :shortcut :go/flashcards
+                :active srs-open?
+                :on-click-handler #(do (fsrs/update-due-cards-count)
+                                       (state/pub-event! [:modal/show-cards]))
+                :more (when (and num (not (zero? num)))
+                        [:span.ml-1.inline-block.py-0.5.px-3.text-xs.font-medium.rounded-full.fade-in num])})))
+
+          (= nav :graph-view)
+          (sidebar-item
+           {:class "graph-view-nav"
+            :title (t :right-side-bar/graph-view)
+            :href (rfe/href :graph)
+            :active (and (not srs-open?) (= route-name :graph))
+            :icon "hierarchy"
+            :shortcut :go/graph-view})
+
+          (= nav :all-pages)
+          (sidebar-item
+           {:class "all-pages-nav"
+            :title (t :right-side-bar/all-pages)
+            :href (rfe/href :all-pages)
+            :active (and (not srs-open?) (= route-name :all-pages))
+            :icon "files"})
+
+          (= (namespace nav) "tag")
+          (when db-based?
+            (let [name'' (name nav)
+                  name' (get {"assets" "Asset" "tasks" "Task"} name'')]
+              (when-let [tag-uuid (and name' (:block/uuid (db/entity (keyword "logseq.class" name'))))]
+                (sidebar-item
+                 {:class (str "tag-view-nav " name'')
+                  :title (tt (keyword "left-side-bar" name'')
+                             (keyword "right-side-bar" name''))
+                  :href (rfe/href :page {:name tag-uuid})
+                  :active (= (str tag-uuid) (get-in route-match [:path-params :name]))
+                  :icon "hash"}))))))])))
+
+(rum/defc sidebar-favorites < rum/reactive
+  []
   (let [_favorites-updated? (state/sub :favorites/updated?)
         favorite-entities (page-handler/get-favorites)]
-    (nav-content-item
-     [:a.flex.items-center.text-sm.font-medium.rounded-md.wrap-th
-      (ui/icon "star" {:size 16})
-      [:strong.flex-1.ml-2 (string/upper-case (t :left-side-bar/nav-favorites))]]
+    (sidebar-content-group
+     [:a.wrap-th
+      [:strong.flex-1 (t :left-side-bar/nav-favorites)]]
 
      {:class "favorites"
       :count (count favorite-entities)
@@ -180,7 +335,7 @@
      (when (seq favorite-entities)
        (let [favorite-items (map
                              (fn [e]
-                               (let [icon (icon/get-node-icon-cp e {})]
+                               (let [icon (icon/get-node-icon-cp e {:size 16})]
                                  {:id (str (:db/id e))
                                   :value (:block/uuid e)
                                   :content [:li.favorite-item (page-name e icon false)]}))
@@ -190,14 +345,11 @@
                                               (page-handler/<reorder-favorites! favorites'))
                                :parent-node :ul.favorites.text-sm}))))))
 
-(rum/defc recent-pages < rum/reactive db-mixins/query
-  [t]
+(rum/defc sidebar-recent-pages < rum/reactive db-mixins/query
+  []
   (let [pages (recent-handler/get-recent-pages)]
-    (nav-content-item
-     [:a.flex.items-center.text-sm.font-medium.rounded-md.wrap-th
-      (ui/icon "history" {:size 16})
-      [:strong.flex-1.ml-2
-       (string/upper-case (t :left-side-bar/nav-recent-pages))]]
+    (sidebar-content-group
+     [:a.wrap-th [:strong.flex-1 (t :left-side-bar/nav-recent-pages)]]
 
      {:class "recent"
       :count (count pages)}
@@ -210,23 +362,7 @@
           :draggable true
           :on-drag-start (fn [event] (editor-handler/block->data-transfer! (:block/name page) event true))
           :data-ref name}
-         (page-name page (icon/get-node-icon-cp page {}) true)])])))
-
-(rum/defc flashcards < db-mixins/query rum/reactive
-  [srs-open?]
-  (let [num (state/sub :srs/cards-due-count)]
-    [:a.item.group.flex.items-center.px-2.py-2.text-sm.font-medium.rounded-md
-     {:class (util/classnames [{:active srs-open?}])
-      :on-click #(do
-                   (fsrs/update-due-cards-count)
-                   (state/pub-event! [:modal/show-cards]))}
-     (ui/icon "infinity")
-     [:span.flex-1 (t :right-side-bar/flashcards)]
-     [:span.ml-1 (ui/render-keyboard-shortcut
-                  (ui/keyboard-shortcut-from-config :go/flashcards
-                                                    {:pick-first? true}))]
-     (when (and num (not (zero? num)))
-       [:span.ml-1.inline-block.py-0.5.px-3.text-xs.font-medium.rounded-full.fade-in num])]))
+         (page-name page (icon/get-node-icon-cp page {:size 16}) true)])])))
 
 (defn get-default-home-if-valid
   []
@@ -239,37 +375,24 @@
         default-home
         (dissoc default-home :page)))))
 
-(defn sidebar-item
-  [{:keys [on-click-handler class title icon icon-extension? active href shortcut]}]
-  [:div
-   {:class class}
-   [:a.item.group.flex.items-center.text-sm.font-medium.rounded-md
-    {:on-click on-click-handler
-     :class (when active "active")
-     :href href}
-    (ui/icon (str icon) {:extension? icon-extension?})
-    [:span.flex-1 title]
-    (when shortcut
-      [:span.ml-1 (ui/render-keyboard-shortcut (ui/keyboard-shortcut-from-config shortcut))])]])
-
-(rum/defc ^:large-vars/cleanup-todo sidebar-nav
+(rum/defc ^:large-vars/cleanup-todo sidebar-container
   [route-match close-modal-fn left-sidebar-open? enable-whiteboards? srs-open?
    *closing? close-signal touching-x-offset]
   (let [[local-closing? set-local-closing?] (rum/use-state false)
         [el-rect set-el-rect!] (rum/use-state nil)
-        ref-el              (rum/use-ref nil)
-        ref-open?           (rum/use-ref left-sidebar-open?)
-        db-based?           (config/db-based-graph? (state/get-current-repo))
-        default-home        (get-default-home-if-valid)
-        route-name          (get-in route-match [:data :name])
-        on-contents-scroll  #(when-let [^js el (.-target %)]
-                               (let [top  (.-scrollTop el)
-                                     cls  (.-classList el)
-                                     cls' "is-scrolled"]
-                                 (if (> top 2)
-                                   (.add cls cls')
-                                   (.remove cls cls'))))
-        close-fn            #(set-local-closing? true)
+        ref-el (rum/use-ref nil)
+        ref-open? (rum/use-ref left-sidebar-open?)
+        db-based? (config/db-based-graph? (state/get-current-repo))
+        default-home (get-default-home-if-valid)
+        route-name (get-in route-match [:data :name])
+        on-contents-scroll #(when-let [^js el (.-target %)]
+                              (let [top (.-scrollTop el)
+                                    cls (.-classList el)
+                                    cls' "is-scrolled"]
+                                (if (> top 2)
+                                  (.add cls cls')
+                                  (.remove cls cls'))))
+        close-fn #(set-local-closing? true)
         touching-x-offset (when (number? touching-x-offset)
                             (if-not left-sidebar-open?
                               (when (> touching-x-offset 0)
@@ -306,109 +429,46 @@
 
     [:<>
      [:div.left-sidebar-inner.flex-1.flex.flex-col.min-h-0
-      {:ref               ref-el
-       :style             (cond-> {}
-                            (and (number? offset-ratio)
-                                 (> touching-x-offset 0))
-                            (assoc :transform (str "translate3d(calc(" touching-x-offset "px - 100%), 0, 0)"))
-
-                            (and (number? offset-ratio)
-                                 (< touching-x-offset 0))
-                            (assoc :transform (str "translate3d(" (* offset-ratio 100) "%, 0, 0)")))
+      {:ref ref-el
+       :style (cond-> {}
+                (and (number? offset-ratio)
+                     (> touching-x-offset 0))
+                (assoc :transform (str "translate3d(calc(" touching-x-offset "px - 100%), 0, 0)"))
+
+                (and (number? offset-ratio)
+                     (< touching-x-offset 0))
+                (assoc :transform (str "translate3d(" (* offset-ratio 100) "%, 0, 0)")))
        :on-transition-end (fn []
                             (when local-closing?
                               (reset! *closing? false)
                               (set-local-closing? false)
                               (close-modal-fn)))
-       :on-click          #(when-let [^js target (and (util/sm-breakpoint?) (.-target %))]
-                             (when (some (fn [sel] (boolean (.closest target sel)))
-                                         [".favorites .bd" ".recent .bd" ".dropdown-wrapper" ".nav-header"])
-                               (close-fn)))}
-
-      [:div.flex.flex-col.wrap.gap-1.relative
-       [:nav.px-4.flex.flex-col.gap-1.cp__menubar-repos
-        {:aria-label "Navigation menu"}
-        (repo/repos-dropdown)
-
-        [:div.nav-header.flex.flex-col.mt-1
-         (let [page (:page default-home)]
-           (if (and page (not (state/enable-journals? (state/get-current-repo))))
-             (sidebar-item
-              {:class "home-nav"
-               :title page
-               :on-click-handler route-handler/redirect-to-home!
-               :active (and (not srs-open?)
-                            (= route-name :page)
-                            (= page (get-in route-match [:path-params :name])))
-               :icon "home"
-               :shortcut :go/home})
-             (sidebar-item
-              {:class "journals-nav"
-               :active (and (not srs-open?)
-                            (or (= route-name :all-journals) (= route-name :home)))
-               :title (t :left-side-bar/journals)
-               :on-click-handler (fn [e]
-                                   (if (gobj/get e "shiftKey")
-                                     (route-handler/sidebar-journals!)
-                                     (route-handler/go-to-journals!)))
-               :icon "calendar"
-               :shortcut :go/journals})))
-
-         (when db-based?
-           (let [tag-uuid (:block/uuid (db/entity :logseq.class/Task))]
-             (sidebar-item
-              {:class "task-view-nav"
-               :title (t :left-side-bar/tasks)
-               :href (rfe/href :page {:name tag-uuid})
-               :active (= (str tag-uuid) (get-in route-match [:path-params :name]))
-               :icon "hash"})))
-
-         (when db-based?
-           (let [tag-uuid (:block/uuid (db/entity :logseq.class/Asset))]
-             (sidebar-item
-              {:class "asset-view-nav"
-               :title (t :left-side-bar/assets)
-               :href (rfe/href :page {:name tag-uuid})
-               :active (= (str tag-uuid) (get-in route-match [:path-params :name]))
-               :icon "hash"})))
-
-         (when enable-whiteboards?
-           (when (or config/dev? (not db-based?))
-             (sidebar-item
-              {:class "whiteboard"
-               :title (t :right-side-bar/whiteboards)
-               :href (rfe/href :whiteboards)
-               :on-click-handler (fn [_e] (whiteboard-handler/onboarding-show))
-               :active (and (not srs-open?) (#{:whiteboard :whiteboards} route-name))
-               :icon "whiteboard"
-               :icon-extension? true
-               :shortcut :go/whiteboards})))
-
-         (when (state/enable-flashcards? (state/get-current-repo))
-           [:div.flashcards-nav
-            (flashcards srs-open?)])
-
-         (sidebar-item
-          {:class "graph-view-nav"
-           :title (t :right-side-bar/graph-view)
-           :href (rfe/href :graph)
-           :active (and (not srs-open?) (= route-name :graph))
-           :icon "hierarchy"
-           :shortcut :go/graph-view})
-
-         (sidebar-item
-          {:class "all-pages-nav"
-           :title (t :right-side-bar/all-pages)
-           :href (rfe/href :all-pages)
-           :active (and (not srs-open?) (= route-name :all-pages))
-           :icon "files"})]]
-
-       [:div.nav-contents-container.flex.flex-col.gap-1.pt-1
+       :on-click #(when-let [^js target (and (util/sm-breakpoint?) (.-target %))]
+                    (when (some (fn [sel] (boolean (.closest target sel)))
+                                [".favorites .bd" ".recent .bd" ".dropdown-wrapper" ".nav-header"])
+                      (close-fn)))}
+
+      [:div.wrap
+       [:div.sidebar-header-container
+        ;; sidebar graphs
+        (sidebar-graphs)
+
+        ;; sidebar sticky navigations
+        (sidebar-navigations
+         {:default-home default-home
+          :route-match route-match
+          :db-based? db-based?
+          :enable-whiteboards? enable-whiteboards?
+          :route-name route-name
+          :srs-open? srs-open?})]
+
+       [:div.sidebar-contents-container
         {:on-scroll on-contents-scroll}
-        (favorites t)
+        (sidebar-favorites)
 
         (when (not config/publishing?)
-          (recent-pages t))]]]
+          (sidebar-recent-pages))]]]
+
      [:span.shade-mask
       (cond-> {:on-click close-fn}
         (number? offset-ratio)
@@ -459,22 +519,22 @@
   (rum/local -1 ::close-signal)
   (rum/local nil ::touch-state)
   [s {:keys [left-sidebar-open? route-match]}]
-  (let [close-fn             #(state/set-left-sidebar-open! false)
-        *closing?            (::closing? s)
-        *touch-state         (::touch-state s)
-        *close-signal        (::close-signal s)
-        enable-whiteboards?  (state/enable-whiteboards?)
-        touch-point-fn       (fn [^js e] (some-> (gobj/get e "touches") (aget 0) (#(hash-map :x (.-clientX %) :y (.-clientY %)))))
-        srs-open?            (= :srs (state/sub :modal/id))
-        touching-x-offset    (and (some-> @*touch-state :after)
-                                  (some->> @*touch-state
-                                           ((juxt :after :before))
-                                           (map :x) (apply -)))
-        touch-pending?       (> (abs touching-x-offset) 20)]
+  (let [close-fn #(state/set-left-sidebar-open! false)
+        *closing? (::closing? s)
+        *touch-state (::touch-state s)
+        *close-signal (::close-signal s)
+        enable-whiteboards? (state/enable-whiteboards?)
+        touch-point-fn (fn [^js e] (some-> (gobj/get e "touches") (aget 0) (#(hash-map :x (.-clientX %) :y (.-clientY %)))))
+        srs-open? (= :srs (state/sub :modal/id))
+        touching-x-offset (and (some-> @*touch-state :after)
+                               (some->> @*touch-state
+                                        ((juxt :after :before))
+                                        (map :x) (apply -)))
+        touch-pending? (> (abs touching-x-offset) 20)]
 
     [:div#left-sidebar.cp__sidebar-left-layout
-     {:class (util/classnames [{:is-open     left-sidebar-open?
-                                :is-closing  @*closing?
+     {:class (util/classnames [{:is-open left-sidebar-open?
+                                :is-closing @*closing?
                                 :is-touching touch-pending?}])
       :on-touch-start
       (fn [^js e]
@@ -495,8 +555,9 @@
         (reset! *touch-state nil))}
 
      ;; sidebar contents
-     (sidebar-nav route-match close-fn left-sidebar-open? enable-whiteboards? srs-open? *closing?
-                  @*close-signal (and touch-pending? touching-x-offset))
+     (sidebar-container route-match close-fn left-sidebar-open? enable-whiteboards? srs-open? *closing?
+                        @*close-signal (and touch-pending? touching-x-offset))
+
      ;; resizer
      (sidebar-resizer)]))
 
@@ -539,13 +600,13 @@
         onboarding-and-home? (and (or (nil? (state/get-current-repo)) (config/demo-graph?))
                                   (not config/publishing?)
                                   (= :home route-name))
-        margin-less-pages?   (or (and (mobile-util/native-platform?) onboarding-and-home?) margin-less-pages?)]
+        margin-less-pages? (or (and (mobile-util/native-platform?) onboarding-and-home?) margin-less-pages?)]
     [:div#main-container.cp__sidebar-main-layout.flex-1.flex
      {:class (util/classnames [{:is-left-sidebar-open left-sidebar-open?}])}
 
      ;; desktop left sidebar layout
      (left-sidebar {:left-sidebar-open? left-sidebar-open?
-                    :route-match        route-match})
+                    :route-match route-match})
 
      [:div#main-content-container.scrollbar-spacing.w-full.flex.justify-center.flex-row.outline-none.relative
 
@@ -557,8 +618,8 @@
 
       [:div.cp__sidebar-main-content
        {:data-is-margin-less-pages margin-less-pages?
-        :data-is-full-width        (or margin-less-pages?
-                                       (contains? #{:all-files :all-pages :my-publishing} route-name))}
+        :data-is-full-width (or margin-less-pages?
+                                (contains? #{:all-files :all-pages :my-publishing} route-name))}
 
        (when show-recording-bar?
          (recording-bar))
@@ -584,10 +645,10 @@
          :else
          [:div
           {:class (if (or onboarding-and-home? margin-less-pages?) "" (util/hiccup->class "mx-auto.pb-24"))
-           :style {:margin-bottom  (cond
-                                     margin-less-pages? 0
-                                     onboarding-and-home? 0
-                                     :else 120)}}
+           :style {:margin-bottom (cond
+                                    margin-less-pages? 0
+                                    onboarding-and-home? 0
+                                    :else 120)}}
           main-content])
 
        (comment
@@ -714,7 +775,7 @@
                       [:ul
                        [:li
                         [:div.inline-block.mr-1 (ui/render-keyboard-shortcut (shortcut-dh/gen-shortcut-seq :editor/new-line))]
-                        [:p.inline-block  "to create new block"]]
+                        [:p.inline-block "to create new block"]]
                        [:li
                         [:p.inline-block.mr-1 "Click `D` or type"]
                         [:div.inline-block.mr-1 (ui/render-keyboard-shortcut (shortcut-dh/gen-shortcut-seq :ui/toggle-document-mode))]
@@ -761,7 +822,7 @@
 
         ;; default
         [:a.it.flex.items-center.px-4.py-1.select-none
-         {:key      title
+         {:key title
           :on-click (fn []
                       (cond
                         (fn? on-click) (on-click)
@@ -774,12 +835,12 @@
 
 (rum/defc help-button < rum/reactive
   []
-  (let [help-open?      (state/sub :ui/help-open?)
+  (let [help-open? (state/sub :ui/help-open?)
         handbooks-open? (state/sub :ui/handbooks-open?)]
     [:<>
      [:div.cp__sidebar-help-btn
       [:div.inner
-       {:title    (t :help-shortcut-title)
+       {:title (t :help-shortcut-title)
         :on-click #(state/toggle! :ui/help-open?)}
        [:svg.scale-125 {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :width "24", :view-box "0 0 24 24", :xmlns "http://www.w3.org/2000/svg", :stroke-linecap "round", :stroke-width "2", :class "icon icon-tabler icon-tabler-help-small", :height "24"}
         [:path {:stroke "none", :d "M0 0h24v24H0z", :fill "none"}]
@@ -862,7 +923,7 @@
                         (if (and (state/modal-opened?)
                                  (not
                                   (and
-                                   ;; FIXME: this does not work on CI tests
+                                                                          ;; FIXME: this does not work on CI tests
                                    util/node-test?
                                    (state/editing?))))
                           (state/close-modal!)
@@ -879,9 +940,9 @@
         editor-font (some-> (state/sub :ui/editor-font) (name))
         system-theme? (state/sub :ui/system-theme?)
         light? (= "light" (state/sub :ui/theme))
-        sidebar-open?  (state/sub :ui/sidebar-open?)
+        sidebar-open? (state/sub :ui/sidebar-open?)
         settings-open? (state/sub :ui/settings-open?)
-        left-sidebar-open?  (state/sub :ui/left-sidebar-open?)
+        left-sidebar-open? (state/sub :ui/left-sidebar-open?)
         wide-mode? (state/sub :ui/wide-mode?)
         ls-block-hl-colored? (state/sub :pdf/block-highlight-colored?)
         onboarding-state (state/sub :file-sync/onboarding-state)
@@ -903,14 +964,14 @@
         show-recording-bar? (state/sub :mobile/show-recording-bar?)
         preferred-language (state/sub [:preferred-language])]
     (theme/container
-     {:t             t
-      :theme         theme
-      :accent-color  accent-color
-      :editor-font   editor-font
-      :route         route-match
-      :current-repo  current-repo
-      :edit?         edit?
-      :nfs-granted?  granted?
+     {:t t
+      :theme theme
+      :accent-color accent-color
+      :editor-font editor-font
+      :route route-match
+      :current-repo current-repo
+      :edit? edit?
+      :nfs-granted? granted?
       :db-restoring? db-restoring?
       :sidebar-open? sidebar-open?
       :settings-open? settings-open?
@@ -918,9 +979,9 @@
       :system-theme? system-theme?
       :onboarding-state onboarding-state
       :preferred-language preferred-language
-      :on-click      (fn [e]
-                       (editor-handler/unhighlight-blocks!)
-                       (util/fix-open-external-with-shift! e))}
+      :on-click (fn [e]
+                  (editor-handler/unhighlight-blocks!)
+                  (util/fix-open-external-with-shift! e))}
 
      [:main.theme-container-inner#app-container-wrapper
       {:class (util/classnames
@@ -945,26 +1006,26 @@
       [:div.#app-container
        [:div#left-container
         {:class (if (state/sub :ui/sidebar-open?) "overflow-hidden" "w-full")}
-        (header/header {:light?         light?
-                        :current-repo   current-repo
-                        :logged?        logged?
-                        :page?          page?
-                        :route-match    route-match
-                        :default-home   default-home
+        (header/header {:light? light?
+                        :current-repo current-repo
+                        :logged? logged?
+                        :page? page?
+                        :route-match route-match
+                        :default-home default-home
                         :new-block-mode new-block-mode})
         (when (util/electron?)
           (find-in-page/search))
 
-        (main {:route-match         route-match
-               :margin-less-pages?  margin-less-pages?
-               :logged?             logged?
-               :home?               home?
-               :route-name          route-name
-               :indexeddb-support?  indexeddb-support?
-               :light?              light?
-               :db-restoring?       db-restoring?
-               :main-content        main-content'
-               :show-action-bar?    show-action-bar?
+        (main {:route-match route-match
+               :margin-less-pages? margin-less-pages?
+               :logged? logged?
+               :home? home?
+               :route-name route-name
+               :indexeddb-support? indexeddb-support?
+               :light? light?
+               :db-restoring? db-restoring?
+               :main-content main-content'
+               :show-action-bar? show-action-bar?
                :show-recording-bar? show-recording-bar?})]
 
        (when window-controls?

+ 109 - 220
src/main/frontend/components/container.css

@@ -58,32 +58,18 @@
   @apply py-4 sm:pl-8 sm:pr-4;
 }
 
-#main-content-container[data-is-margin-less-pages=true] {
-  padding: 0 !important;
-  position: relative;
-  overflow: auto;
-}
-
 .left-sidebar-inner {
-  position: relative;
-  height: 100%;
-  padding-top: 12px;
+  @apply relative h-full pt-3 overflow-y-auto overflow-x-hidden transition-transform;
+  @apply transform-gpu translate-x-[-100%] z-[3] antialiased;
+
   width: var(--ls-left-sidebar-sm-width);
-  overflow-y: auto;
-  overflow-x: hidden;
   background-color: var(--left-sidebar-bg-color);
-  border-right: 1px solid or(--ls-left-sidebar-border-color, --lx-gray-03, --ls-tertiary-background-color);
-  transition: transform .3s;
-  transform: translate3d(-100%, 0, 0);
-  z-index: 3;
-
-  -webkit-font-smoothing: antialiased;
+  border-right: 1px solid var(--lx-gray-03, var(--ls-tertiary-background-color));
 
   > .wrap {
+    @apply flex flex-col relative w-full mt-6;
+
     height: calc(100vh - var(--ls-headbar-inner-top-padding) - 50px);
-    margin-top: 30px;
-    width: 100%;
-    padding-top: var(--ls-win32-title-bar-height);
 
     > .fake-bar {
       @apply w-full px-5 pt-1 sm:hidden;
@@ -106,7 +92,7 @@
     }
   }
 
-  .nav-header {
+  .sidebar-navigations {
     @apply gap-0.5;
 
     a {
@@ -126,58 +112,47 @@
   }
 
   .page-icon {
-    @apply flex items-center text-center mr-1 align-baseline;
-
-    width: 20px;
-    flex-shrink: 0;
-    line-height: 1em;
+    @apply flex items-center mr-2 text-center align-baseline leading-none;
   }
 
   a.item {
-    @apply px-2 py-2 sm:py-1.5;
-
-    user-select: none;
-    transition: background-color .3s;
+    @apply flex items-center pl-1.5 pr-0.5 h-8 select-none;
 
     .ui__icon {
-      @apply flex justify-center;
-      width: 20px;
-      font-size: 16px;
-      margin-right: 8px;
-      opacity: .7;
-      position: relative;
-    }
-
-    .graph-icon .ui__icon {
-      padding: 0;
-      width: unset;
-      margin-right: 0px;
+      @apply relative flex justify-center w-[20px] text-base mr-2 opacity-80;
     }
 
     .graph-icon {
-      margin-left: 3px;
-      margin-right: 11px;
+      @apply ml-[3px] mr-[11px];
+
+      .ui__icon {
+        @apply p-0 w-auto mr-0;
+      }
     }
 
-    &:hover, &.active {
-      background-color: var(--lx-gray-04, var(--color-level-3, var(--rx-gray-04)));
-      color: var(--lx-gray-12, var(--rx-gray-12));
+    &:hover, &.active, > .thumb {
+      background-color: var(--lx-gray-04, var(--ls-quaternary-background-color, var(--rx-gray-04)));
 
       .ui__icon {
-        opacity: .9;
+        @apply opacity-100;
       }
     }
   }
 
-  .nav-contents-container {
-    @apply relative h-full flex-grow-0 overflow-x-hidden overflow-y-auto;
+  .sidebar-header-container {
+    @apply flex flex-col gap-1 px-3 mb-1;
+  }
+
+  .sidebar-contents-container {
+    @apply flex flex-col gap-1 pt-1;
+    @apply px-3 relative h-full flex-grow-0 overflow-x-hidden overflow-y-auto;
 
     &.is-scrolled {
       border-top: 1px solid var(--ls-tertiary-border-color);
     }
   }
 
-  .nav-content-item {
+  .sidebar-content-group {
     &:not(:hover) {
       ::-webkit-scrollbar-thumb,
       ::-webkit-scrollbar,
@@ -186,168 +161,116 @@
       }
     }
 
-    .nav-content-item-inner {
+    &-inner {
       @apply flex flex-col;
-    }
-
-    .header {
-      @apply pl-6 pr-4 py-1 flex justify-between items-center select-none sticky top-[-4px];
-      @apply cursor-pointer z-[2] active:opacity-80;
 
-      background-color: var(--left-sidebar-bg-color);
+      > .hd {
+        @apply pl-2 pr-1 h-[32px] flex justify-between items-center select-none sticky top-[-4px];
+        @apply cursor-pointer z-[2] active:opacity-80 rounded-md;
 
-      .ui__icon {
-        @apply flex justify-center;
-        width: 20px;
-      }
+        background-color: var(--left-sidebar-bg-color);
 
-      .more {
-        opacity: 0;
-        transition: .15s transform;
-      }
+        .ui__icon {
+          @apply flex justify-center w-[20px];
+        }
 
-      &:hover {
-        background-color: var(--lx-gray-04, var(--ls-tertiary-background-color, var(--rx-gray-04)));
+        &.non-collapsable {
+          @apply cursor-default active:opacity-100;
 
-        * {
-          opacity: 1 !important;
+          .wrap-th {
+            @apply cursor-default;
+          }
         }
 
-        .more {
-          opacity: .8 !important;
-        }
-      }
+        &:not(.non-collapsable) {
 
-      .wrap-th {
-        @apply opacity-50;
+          &:hover {
+            background-color: var(--lx-gray-04, var(--ls-quaternary-background-color, var(--rx-gray-04)));
 
-        > .ui__icon {
-          @apply relative top-[-1px];
-        }
+            * {
+              @apply !opacity-100;
+            }
 
-        > strong {
-          @apply text-[11px] font-semibold;
+            .more {
+              @apply opacity-80;
+            }
+          }
         }
-      }
-    }
-
-    .bd {
-      @apply py-1 overflow-y-auto;
 
-      display: none;
-      min-height: 40px;
+        .wrap-th {
+          @apply flex items-center text-sm font-medium opacity-50;
 
-      ul {
-        list-style: none;
-        padding: 0;
-        margin: 0;
-
-        li {
-          margin: 0;
-        }
+          > .ui__icon {
+            @apply relative top-[-1px];
+          }
 
-        a {
-          width: 100%;
-          padding: 4px 24px;
-          opacity: .8;
-          transition: background-color .3s, opacity .3s;
-
-          .page-title {
-            white-space: nowrap;
-            overflow: hidden;
-            text-overflow: ellipsis;
-            flex-grow: 1;
+          > strong {
+            @apply text-xs font-medium;
           }
+        }
 
-          .page-icon {
-            display: flex;
-            align-items: center;
+        &.enter-show-more {
+          > .b {
+            @apply transition-opacity opacity-0 delay-300;
           }
 
-          &:hover {
-            background-color: var(--lx-gray-04, var(--ls-quaternary-background-color, var(--rx-gray-04)));
-            opacity: 1;
+          &:hover > .b {
+            @apply opacity-80;
           }
         }
       }
-    }
 
-    &.is-expand {
-      .header .more {
-        opacity: 0;
-        transform: rotate(-90deg);
-      }
+      > .bd {
+        @apply overflow-y-auto hidden;
 
-      .bd {
-        display: block;
-      }
-    }
+        ul {
+          @apply list-none p-0 m-0;
 
-    &.has-children:not(.is-expand) {
-      .header .more {
-        opacity: .4;
-      }
-    }
-  }
+          li {
+            @apply m-0;
+          }
 
-  .create {
-    width: 100%;
-    padding: 4px 14px 14px;
-    background-image: linear-gradient(transparent, var(--ls-primary-background-color));
-    user-select: none;
+          a {
+            @apply px-2 flex items-center justify-between relative h-[32px] w-full rounded-md;
 
-    @screen sm {
-      background-image: linear-gradient(transparent, or(--ls-left-sidebar-bottom-gradient, --lx-gray-02, --ls-secondary-background-color));
+            .page-title {
+              @apply whitespace-nowrap hidden text-ellipsis flex-grow overflow-hidden pr-2;
+            }
 
-      .dark & {
-        background-image: linear-gradient(transparent, or(--ls-left-sidebar-bottom-gradient, --lx-gray-01, --ls-secondary-background-color));
+            .page-icon {
+              @apply flex items-center;
+            }
+
+            &:hover {
+              background-color: var(--lx-gray-04, var(--ls-quaternary-background-color, var(--rx-gray-04)));
+              opacity: 1;
+            }
+          }
+        }
       }
     }
 
-    &-link {
-      background-color: var(--ls-primary-background-color);
-      box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
+    .hd .more {
+      @apply transition-transform;
     }
 
-    .dropdown-wrapper {
-      top: initial;
-      right: initial;
-      bottom: calc(100% + 6px);
-      left: 0;
-      width: max-content;
-
-      @screen sm {
-        bottom: 0;
-        left: calc(100% + 6px);
+    &.is-expand {
+      .hd .more {
+        @apply opacity-40 rotate-90;
       }
-    }
 
-    #create-button {
-      @apply flex items-center justify-center p-2 text-sm font-medium rounded-md w-full border;
-      background-color: or(--ls-create-button-color, --lx-gray-03, --ls-secondary-background-color) !important;
-      border-color: transparent;
-
-      &:hover,
-      &:focus {
-        border-color: var(--ls-border-color);
-        background-color: or(--ls-create-button-color-focus, --lx-gray-03, --ls-primary-background-color) !important;
+      .bd {
+        @apply block;
       }
+    }
 
-      @screen sm {
-        background-color: or(--ls-create-button-color-sm, --lx-gray-03, --ls-primary-background-color) !important;
-
-        &:hover,
-        &:focus {
-          background-color: or(--ls-create-button-color-sm-focus, --lx-gray-04, --ls-secondary-background-color) !important;
-        }
+    &.has-children:not(.is-expand) {
+      .hd .more {
+        @apply opacity-50;
       }
     }
   }
 
-  .dark & {
-    --left-sidebar-bg-color: var(--lx-gray-02, var(--ls-secondary-background-color, hsl(var(--secondary, var(--rx-gray-03-hsl)))));
-  }
-
   @screen sm {
     padding-top: 0;
     width: var(--ls-left-sidebar-width);
@@ -355,25 +278,14 @@
     > .wrap {
       margin-top: 52px;
     }
-
-    .create {
-      &-link {
-        background-color: var(--ls-primary-background-color);
-      }
-    }
   }
 }
 
 .cp__sidebar-left-layout {
-  @apply fixed top-0 left-0 w-[10px];
-
-  z-index: var(--ls-z-index-level-5);
+  @apply fixed top-0 left-0 w-[10px] z-[var(--ls-z-index-level-5)];
 
   a {
-    @apply opacity-90 hover:opacity-100;
-    transition: all 120ms ease-out;
-
-    color: var(--ls-left-sidebar-text-color, var(--ls-header-button-background));
+    @apply opacity-80 hover:opacity-100 text-foreground;
   }
 
   > .left-sidebar-inner {
@@ -454,10 +366,7 @@
 
   .left-sidebar-resizer {
     @apply absolute w-[3px] top-0 right-[-2px] bottom-0 overflow-hidden cursor-col-resize;
-    @apply z-10;
-
-    transition: background-color 300ms;
-    transition-delay: 300ms;
+    @apply z-10 transition-[background-color] duration-200 delay-300;
 
     &.is-active, &:hover,
     &:focus, &:active {
@@ -466,26 +375,22 @@
   }
 
   @screen sm {
-    width: 0;
-    z-index: var(--ls-z-index-level-1);
-    transition: width .3s;
+    @apply w-0 z-[var(--ls-z-index-level-1)] transition-[width];
 
     &:before {
-      background-color: or(--ls-left-sidebar-container-sm, --lx-gray-02, --ls-secondary-background-color);
-      width: 0;
-      overflow: hidden;
+      @apply w-0 overflow-hidden;
     }
 
     &.is-open {
-      width: var(--ls-left-sidebar-width);
+      @apply w-[var(--ls-left-sidebar-width)];
 
       .left-sidebar-inner {
-        overflow: visible;
+        @apply overflow-visible;
       }
     }
 
     > .shade-mask {
-      display: none;
+      @apply hidden;
     }
   }
 }
@@ -601,7 +506,7 @@
   .resizer {
     @apply absolute top-0 bottom-0 touch-none left-[1px] w-[3px] select-none;
     @apply cursor-col-resize hover:bg-primary/90 focus:bg-primary/90 active:bg-primary/90;
-    @apply z-[1000] delay-300 transition-[background-color] duration-300;
+    @apply z-[1000] delay-300 transition-[background-color] duration-200;
   }
 
   &.closed {
@@ -748,20 +653,6 @@
   padding: 0;
 }
 
-.cp__menubar-repos {
-  .title-wrap {
-    line-height: 1.2em;
-    padding: 1px 0;
-  }
-}
-
-/* Workaround for Linux Intel GPU text rendering issue GH#7233 */
-.is-electron.is-linux .cp__menubar-repos {
-  .repo-switch, .nav-header .flex-1 {
-    position: relative;
-  }
-}
-
 @supports not (overflow-y: overlay) {
   .scrollbar-spacing {
     overflow-y: auto;
@@ -833,11 +724,9 @@ html[data-theme='dark'] {
   }
 }
 
-.blocks-selection-mode .page-title, .blocks-selection-mode .block-content-inner, .blocks-selection-mode .block-body, .blocks-selection-mode .ls-properties-area {
-    user-select: none;
-}
-
-.favorite-item {
-    @apply overflow-hidden;
-    max-height: 24px;
+.blocks-selection-mode .page-title,
+.blocks-selection-mode .block-content-inner,
+.blocks-selection-mode .block-body,
+.blocks-selection-mode .ls-properties-area {
+  @apply select-none;
 }

+ 0 - 6
src/main/frontend/components/header.css

@@ -265,12 +265,6 @@ html.is-native-ipad {
     }
   }
 
-  .left-sidebar-inner  {
-    > .wrap {
-      padding-top: 20px;
-    }
-  }
-
   .cp__right-sidebar {
     &-settings {
       margin-top: -4px;

+ 90 - 49
src/main/frontend/components/repo.cljs

@@ -21,6 +21,7 @@
             [frontend.util.text :as text-util]
             [goog.object :as gobj]
             [logseq.shui.ui :as shui]
+            [medley.core :as medley]
             [promesa.core :as p]
             [rum.core :as rum]))
 
@@ -293,39 +294,79 @@
                    :on-click #(route-handler/redirect-to-all-graphs)}
                   (shui/tabler-icon "layout-2") [:span (t :all-graphs)]))])
 
+(rum/defcs repos-dropdown-content < rum/reactive
+  [_state & {:keys [contentid] :as opts}]
+  (let [multiple-windows? false
+        current-repo (state/sub :git/current-repo)
+        login? (boolean (state/sub :auth/id-token))
+        repos (state/sub [:me :repos])
+        remotes (state/sub [:file-sync/remote-graphs :graphs])
+        rtc-graphs (state/sub :rtc/graphs)
+        downloading-graph-id (state/sub :rtc/downloading-graph-uuid)
+        remotes-loading? (state/sub [:file-sync/remote-graphs :loading])
+        db-based? (config/db-based-graph? current-repo)
+        repos (sort-repos-with-metadata-local repos)
+        repos (distinct
+               (if (and (or (seq remotes) (seq rtc-graphs)) login?)
+                 (repo-handler/combine-local-&-remote-graphs repos (concat remotes rtc-graphs)) repos))
+        items-fn #(repos-dropdown-links repos current-repo downloading-graph-id opts)
+        header-fn #(when (> (count repos) 1)                ; show switch to if there are multiple repos
+                     [:div.font-medium.text-sm.opacity-50.px-1.py-1.flex.flex-row.justify-between.items-center
+                      [:h4.pb-1 (t :left-side-bar/switch)]
+
+                      (when (and (file-sync/enable-sync?) login?)
+                        (if remotes-loading?
+                          (ui/loading "")
+                          (shui/button
+                           {:variant :ghost
+                            :size :sm
+                            :title "Refresh remote graphs"
+                            :class "!h-6 !px-1 relative right-[-4px]"
+                            :on-click (fn []
+                                        (file-sync/load-session-graphs)
+                                        (rtc-handler/<get-remote-graphs))}
+                           (ui/icon "refresh" {:size 15}))))])
+        _remote? (and current-repo (:remote? (first (filter #(= current-repo (:url %)) repos))))
+        _repo-name (when current-repo (db/get-repo-name current-repo))]
+
+    [:div
+     {:class (when (<= (count repos) 1) "no-repos")}
+     (header-fn)
+     [:div.cp__repos-list-wrap
+      (for [{:keys [hr item hover-detail title options icon]} (items-fn)]
+        (let [on-click' (:on-click options)
+              href' (:href options)]
+          (if hr
+            (shui/dropdown-menu-separator)
+            (shui/dropdown-menu-item
+             (assoc options
+                    :title hover-detail
+                    :on-click (fn [^js e]
+                                (when on-click'
+                                  (when-not (false? (on-click' e))
+                                    (shui/popup-hide! contentid)))))
+             (or item
+                 (if href'
+                   [:a.flex.items-center.w-full
+                    {:href href' :on-click #(shui/popup-hide! contentid)
+                     :style {:color "inherit"}} title]
+                   [:span.flex.items-center.gap-1.w-full
+                    icon [:div title]]))))))]
+     (repos-footer multiple-windows? db-based?)]))
+
 (rum/defcs repos-dropdown < rum/reactive
   (rum/local false ::electron-multiple-windows?)
   [state & {:as opts}]
-  (let [multiple-windows? (::electron-multiple-windows? state)
-        current-repo (state/sub :git/current-repo)
-        login? (boolean (state/sub :auth/id-token))
-        remotes-loading? (state/sub [:file-sync/remote-graphs :loading])]
+  (let [current-repo (state/sub :git/current-repo)
+        login? (boolean (state/sub :auth/id-token))]
     (let [repos (state/sub [:me :repos])
           remotes (state/sub [:file-sync/remote-graphs :graphs])
           rtc-graphs (state/sub :rtc/graphs)
-          downloading-graph-id (state/sub :rtc/downloading-graph-uuid)
           db-based? (config/db-based-graph? current-repo)
           repos (sort-repos-with-metadata-local repos)
           repos (distinct
                  (if (and (or (seq remotes) (seq rtc-graphs)) login?)
-                   (repo-handler/combine-local-&-remote-graphs repos (concat remotes rtc-graphs)) repos))
-          items-fn #(repos-dropdown-links repos current-repo downloading-graph-id opts)
-          header-fn #(when (> (count repos) 1)              ; show switch to if there are multiple repos
-                       [:div.font-medium.text-sm.opacity-50.px-1.py-1.flex.flex-row.justify-between.items-center
-                        [:h4.pb-1 (t :left-side-bar/switch)]
-
-                        (when (and (file-sync/enable-sync?) login?)
-                          (if remotes-loading?
-                            (ui/loading "")
-                            (shui/button
-                             {:variant :ghost
-                              :size :sm
-                              :title "Refresh remote graphs"
-                              :class "!h-6 !px-1 relative right-[-4px]"
-                              :on-click (fn []
-                                          (file-sync/load-session-graphs)
-                                          (rtc-handler/<get-remote-graphs))}
-                             (ui/icon "refresh" {:size 15}))))])]
+                   (repo-handler/combine-local-&-remote-graphs repos (concat remotes rtc-graphs)) repos))]
       (let [remote? (and current-repo (:remote? (first (filter #(= current-repo (:url %)) repos))))
             repo-name (when current-repo (db/get-repo-name current-repo))
             short-repo-name (if current-repo
@@ -340,36 +381,13 @@
                             (some-> (.-target e)
                                     (.closest "a.item")
                                     (shui/popup-show!
-                                     (fn [{:keys [id]}]
-                                       [:<>
-                                        (header-fn)
-                                        [:div.cp__repos-list-wrap
-                                         (for [{:keys [hr item hover-detail title options icon]} (items-fn)]
-                                           (let [on-click' (:on-click options)
-                                                 href' (:href options)]
-                                             (if hr
-                                               (shui/dropdown-menu-separator)
-                                               (shui/dropdown-menu-item
-                                                (assoc options
-                                                       :title hover-detail
-                                                       :on-click (fn [^js e]
-                                                                   (when on-click'
-                                                                     (when-not (false? (on-click' e))
-                                                                       (shui/popup-hide! id)))))
-                                                (or item
-                                                    (if href'
-                                                      [:a.flex.items-center.w-full
-                                                       {:href href' :on-click #(shui/popup-hide! id)
-                                                        :style {:color "inherit"}} title]
-                                                      [:span.flex.items-center.gap-1.w-full
-                                                       icon [:div title]]))))))]
-                                        (repos-footer multiple-windows? db-based?)])
+                                     (fn [{:keys [id]}] (repos-dropdown-content (assoc opts :contentid id)))
                                      {:as-dropdown? true
                                       :auto-focus? false
                                       :align "start"
-                                      :content-props {:class (str "repos-list " (when (<= (count repos) 1) " no-repos"))
+                                      :content-props {:class "repos-list"
                                                       :data-mode (when db-based? "db")}})))
-                          :title repo-name}                              ;; show full path on hover
+                          :title repo-name}      ;; show full path on hover
                          [:div.flex.relative.graph-icon.rounded
                           (shui/tabler-icon "database" {:size 15})]
 
@@ -380,6 +398,29 @@
                            (when remote? [:span.pl-1 (ui/icon "cloud")])]
                           [:span.dropdown-caret]])))))
 
+(rum/defcs graphs-selector < rum/reactive
+  [_state]
+  (let [current-repo (state/get-current-repo)
+        user-repos (state/get-repos)
+        current-repo' (some->> user-repos (medley/find-first #(= current-repo (:url %))))
+        repo-name (when current-repo (db/get-repo-name current-repo))
+        db-based? (config/db-based-graph? current-repo)
+        remote? (:remote? current-repo')
+        short-repo-name (if current-repo
+                          (db/get-short-repo-name repo-name)
+                          "Select a Graph")]
+    [:div.cp__graphs-selector.flex.items-center.justify-between
+     [:a.item.flex.items-center.gap-1.select-none
+      {:on-click (fn [^js e]
+                   (shui/popup-show! (.closest (.-target e) "a")
+                                     (fn [{:keys [id]}] (repos-dropdown-content {:contentid id}))
+                                     {:as-dropdown? true
+                                      :content-props {:class "repos-list"}
+                                      :align :start}))}
+      [:span.thumb (shui/tabler-icon (if remote? "cloud" (if db-based? "database" "folder")) {:size 16})]
+      [:strong short-repo-name]
+      (shui/tabler-icon "selector" {:size 18})]]))
+
 (defn invalid-graph-name-warning
   []
   (notification/show!

+ 36 - 3
src/main/frontend/components/repo.css

@@ -16,7 +16,7 @@
 
 .ui__dropdown-menu-content {
   &.repos-list {
-    @apply flex flex-col px-2 relative overflow-hidden min-w-[280px] sm:max-w-[320px];
+    @apply flex flex-col px-2 relative overflow-hidden min-w-[300px] sm:max-w-[400px];
 
     .ui__dropdown-menu-item {
       @apply overflow-hidden overflow-ellipsis;
@@ -26,7 +26,7 @@
       @apply max-h-80 overflow-scroll mx-[-8px] px-2 pb-2;
     }
 
-    &.no-repos {
+    .no-repos {
       .cp__repos-list-wrap {
         @apply hidden;
       }
@@ -67,4 +67,37 @@
     @apply w-full !py-4 !justify-start opacity-70 font-medium hover:opacity-90
     items-center gap-1.5 hover:bg-gray-03;
   }
-}
+}
+
+.cp__graphs-selector {
+  > a.item {
+    @apply flex items-center relative flex-1 overflow-hidden
+    pl-1 py-1 pr-4 opacity-90 active:opacity-70 rounded-md;
+
+    > .thumb {
+      @apply w-6 h-6 overflow-hidden flex flex-shrink-0 items-center
+      justify-center rounded opacity-80 dark:opacity-50;
+
+      > .ui__icon {
+        @apply mr-0;
+      }
+    }
+
+    > strong {
+      @apply whitespace-nowrap overflow-hidden text-ellipsis pl-1
+      font-medium relative pr-4 text-sm;
+    }
+
+    > .ui__icon {
+      @apply absolute -right-1 top-2 opacity-60;
+    }
+  }
+
+  > span {
+    @apply relative flex items-center -mr-1;
+
+    > .ui__button {
+      @apply p-1 opacity-40 hover:opacity-70 active:opacity-100;
+    }
+  }
+}

+ 0 - 4
src/main/frontend/components/right_sidebar.css

@@ -21,10 +21,6 @@ html[data-theme=light] {
   height: calc(100vh - 48px);
 }
 
-html[data-theme=light] a.toggle:hover {
-  color: var(--ls-primary-text-color);
-}
-
 .cp__header {
   > .r > div:not(.ui__dropdown-trigger) a, button {
     color: var(--lx-gray-11, var(--ls-header-button-background, var(--rx-gray-11)));

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

@@ -156,7 +156,7 @@ main.ls-fold-button-on-right {
 }
 
 main.theme-container-inner {
-  --left-sidebar-bg-color: var(--lx-gray-02, hsl(var(--secondary, var(--rx-gray-03-hsl))));
+  --left-sidebar-bg-color: var(--lx-gray-02, var(--ls-secondary-background-color, hsl(var(--secondary, var(--rx-gray-03-hsl)))));
 }
 
 html[data-font='serif'] .ls-block, .ls-font-serif {

+ 11 - 1
src/main/frontend/context/i18n.cljs

@@ -2,7 +2,9 @@
   "This ns is a system component that handles translation for the entire
   application. The ns dependencies for this ns must be small since it is used
   throughout the application."
-  (:require [frontend.dicts :as dicts]
+  (:require [clojure.string :as string]
+            [frontend.dicts :as dicts]
+            [medley.core :as medley]
             [tongue.core :as tongue]
             [frontend.state :as state]
             [lambdaisland.glogi :as log]))
@@ -26,6 +28,14 @@
                                                      :lang preferred-language}}])
         (apply translate :en args)))))
 
+(defn tt
+  [& keys]
+  (some->
+   (medley/find-first
+    #(not (string/starts-with? (t %) "{Missing key"))
+    keys)
+   t))
+
 (defn- fetch-local-language []
   (.. js/window -navigator -language))
 

+ 6 - 5
src/main/frontend/handler/route.cljs

@@ -201,11 +201,12 @@
                    100)))
 
 (defn go-to-search!
-  [search-mode]
-  (search-handler/clear-search! false)
-  (when search-mode
-    (state/set-search-mode! search-mode))
-  (state/pub-event! [:go/search]))
+  ([search-mode] (go-to-search! search-mode nil))
+  ([search-mode args]
+   (search-handler/clear-search! false)
+   (when search-mode
+     (state/set-search-mode! search-mode args))
+   (state/pub-event! [:go/search])))
 
 (defn sidebar-journals!
   []

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

@@ -62,6 +62,7 @@
 
       :search/q                              ""
       :search/mode                           nil ; nil -> global mode, :graph -> add graph filter, etc.
+      :search/args                           nil
       :search/result                         nil
       :search/graph-filters                  []
       :search/engines                        {}
@@ -1084,8 +1085,10 @@ Similar to re-frame subscriptions"
   (set-state! :editor/cursor-range range))
 
 (defn set-search-mode!
-  [value]
-  (set-state! :search/mode value))
+  ([value] (set-search-mode! value nil))
+  ([value args]
+   (set-state! :search/mode value)
+   (set-state! :search/args args)))
 
 (defn set-editor-action!
   [value]

+ 29 - 16
src/main/frontend/worker/rtc/remote_update.cljs

@@ -236,28 +236,41 @@
      (= :db.cardinality/many (:db/cardinality k-schema))]))
 
 (defn- patch-remote-attr-map-by-local-av-coll
-  [attr-map av-coll]
+  [remote-attr-map local-av-coll]
   (let [a->add->v-set
         (reduce
          (fn [m [a v _t add?]]
            (let [{add-vset true retract-vset false} (get m a {true #{} false #{}})]
              (assoc m a {true ((if add? conj disj) add-vset v)
                          false ((if add? disj conj) retract-vset v)})))
-         {} av-coll)]
-    (into attr-map
-          (keep
-           (fn [[remote-a remote-v]]
-             (when-let [{add-vset true retract-vset false} (get a->add->v-set remote-a)]
-               [remote-a
-                (if (coll? remote-v)
-                  (-> (set remote-v)
-                      (set/union add-vset)
-                      (set/difference retract-vset)
-                      vec)
-                  (cond
-                    (seq add-vset) (first add-vset)
-                    (contains? retract-vset remote-v) nil))])))
-          attr-map)))
+         {} local-av-coll)
+        updated-remote-attr-map1
+        (keep
+         (fn [[remote-a remote-v]]
+           (when-let [{add-vset true retract-vset false} (get a->add->v-set remote-a)]
+             [remote-a
+              (if (coll? remote-v)
+                (-> (set remote-v)
+                    (set/union add-vset)
+                    (set/difference retract-vset)
+                    vec)
+                (cond
+                  (seq add-vset) (first add-vset)
+                  (contains? retract-vset remote-v) nil))]))
+         remote-attr-map)
+        updated-remote-attr-map2
+        (keep
+         (fn [[a add->v-set]]
+           (when-let [ns (namespace a)]
+             (when (and (not (contains? #{"block"} ns))
+                        ;; FIXME: only handle non-block/xxx attrs,
+                        ;; because some :block/xxx attrs are card-one, we only generate card-many values here
+                        (not (contains? remote-attr-map a)))
+               (when-let [v-set (not-empty (get add->v-set true))]
+                 [a (vec v-set)]))))
+         a->add->v-set)]
+    (into remote-attr-map
+          (concat updated-remote-attr-map1 updated-remote-attr-map2))))
 
 (defn- update-remote-data-by-local-unpushed-ops
   "when remote-data request client to move/update/remove/... blocks,

+ 65 - 25
src/test/frontend/worker/rtc/rtc_fns_test.cljs

@@ -112,15 +112,56 @@
                :user.property/ppp
                [#uuid "6752bdee-7963-4a6a-84a4-86cd456b470c"
                 #uuid "6752bdf0-ee32-40af-8abb-3f8d179ba888"]}}
+             r))))
+  (testing "case6: toggle status"
+    (let [[uuid1 uuid2 status-value-uuid] (repeatedly random-uuid)
+          affected-blocks-map
+          {uuid1
+           {:op :update-attrs
+            :self uuid1
+            :parents [uuid2]}}
+          unpushed-ops
+          [[:update
+            536872312
+            {:block-uuid uuid1
+             :av-coll
+             [[:logseq.task/status status-value-uuid 536872312 true]]}]]
+          r (#'r.remote/update-remote-data-by-local-unpushed-ops affected-blocks-map unpushed-ops)]
+      (is (= {uuid1
+              {:op :update-attrs
+               :self uuid1
+               :parents [uuid2]
+               :logseq.task/status [status-value-uuid]}}
+             r))))
+  (testing "case7: toggle status(2)"
+    (let [[uuid1 uuid2 status-value-uuid1 status-value-uuid2] (repeatedly random-uuid)
+          affected-blocks-map
+          {uuid1
+           {:op :update-attrs
+            :self uuid1
+            :parents [uuid2]}}
+          unpushed-ops
+          [[:update
+            536872314
+            {:block-uuid uuid1
+             :av-coll
+             [[:logseq.task/status status-value-uuid1 536872312 true]
+              [:logseq.task/status status-value-uuid1 536872312 false]
+              [:logseq.task/status status-value-uuid2 536872314 true]]}]]
+          r (#'r.remote/update-remote-data-by-local-unpushed-ops affected-blocks-map unpushed-ops)]
+      (is (= {uuid1
+              {:op :update-attrs
+               :self uuid1
+               :parents [uuid2]
+               :logseq.task/status [status-value-uuid2]}}
              r)))))
 
-(deftest ^:fix-me apply-remote-move-ops-test
+(deftest apply-remote-move-ops-test
   (let [repo (state/get-current-repo)
         conn (conn/get-db repo false)
         opts {:persist-op? false
               :transact-opts {:repo repo
                               :conn conn}}
-        date-formatter (common-config/get-date-formatter (worker-state/get-config repo))
         page-name "apply-remote-move-ops-test"
         [page-uuid
          uuid1-client uuid2-client
@@ -132,10 +173,10 @@
       repo
       conn
       [{:block/uuid uuid1-client :block/title "uuid1-client"
-        :block/left [:block/uuid page-uuid]
+        :block/order "a1"
         :block/parent [:block/uuid page-uuid]}
        {:block/uuid uuid2-client :block/title "uuid2-client"
-        :block/left [:block/uuid uuid1-client]
+        :block/order "a2"
         :block/parent [:block/uuid page-uuid]}]
       (ldb/get-page @conn page-name)
       {:sibling? true :keep-uuid? true}))
@@ -147,17 +188,17 @@
                           {uuid1-remote {:op :move
                                          :self uuid1-remote
                                          :parents [page-uuid]
-                                         :left page-uuid
-                                         :content "uuid1-remote"}}}
+                                         :block/order "a0"}}}
             move-ops (#'r.remote/move-ops-map->sorted-move-ops
                       (:move-ops-map
                        (#'r.remote/affected-blocks->diff-type-ops
                         repo (:affected-blocks data-from-ws))))]
-        (is (rtc-const/data-from-ws-validator data-from-ws))
-        (#'r.remote/apply-remote-move-ops repo conn date-formatter move-ops)
+        (is (rtc-const/data-from-ws-validator data-from-ws) data-from-ws)
+        (#'r.remote/apply-remote-move-ops repo conn move-ops)
         (let [page-blocks (ldb/get-page-blocks @conn (:db/id (ldb/get-page @conn page-name)) {})]
-          (is (= #{uuid1-remote uuid1-client uuid2-client} (set (map :block/uuid page-blocks))))
-          (is (= page-uuid (:block/uuid (:block/left (d/entity @conn [:block/uuid uuid1-remote]))))))))
+          (is (= #{uuid1-remote uuid1-client uuid2-client} (set (map :block/uuid page-blocks)))
+              [uuid1-remote uuid1-client uuid2-client])
+          (is (= page-uuid (:block/uuid (:block/parent (d/entity @conn [:block/uuid uuid1-remote]))))))))
 
     (testing "apply-remote-move-ops-test2"
       (let [data-from-ws {:req-id "req-id"
@@ -167,24 +208,24 @@
                           {uuid2-remote {:op :move
                                          :self uuid2-remote
                                          :parents [uuid1-client]
-                                         :left uuid1-client
-                                         :content "uuid2-remote"}
+                                         :block/order "a0"}
                            uuid1-remote {:op :move
                                          :self uuid1-remote
                                          :parents [uuid2-remote]
-                                         :left uuid2-remote}}}
+                                         :block/order "a1"}}}
             move-ops (#'r.remote/move-ops-map->sorted-move-ops
                       (:move-ops-map
                        (#'r.remote/affected-blocks->diff-type-ops
                         repo (:affected-blocks data-from-ws))))]
         (is (rtc-const/data-from-ws-validator data-from-ws))
-        (#'r.remote/apply-remote-move-ops repo conn date-formatter move-ops)
+        (#'r.remote/apply-remote-move-ops repo conn move-ops)
         (let [page-blocks (ldb/get-page-blocks @conn (:db/id (ldb/get-page @conn page-name)) {})]
           (is (= #{uuid1-remote uuid2-remote uuid1-client uuid2-client} (set (map :block/uuid page-blocks))))
-          (is (= uuid1-client (:block/uuid (:block/left (d/entity @conn [:block/uuid uuid2-remote])))))
-          (is (= uuid2-remote (:block/uuid (:block/left (d/entity @conn [:block/uuid uuid1-remote]))))))))))
+          (is (= ["a0" "a1"]
+                 (mapv (fn [uuid*] (:block/order (d/entity @conn [:block/uuid uuid*])))
+                       [uuid2-remote uuid1-remote]))))))))
 
-(deftest ^:fix-me apply-remote-remove-ops-test
+(deftest apply-remote-remove-ops-test
   (let [repo (state/get-current-repo)
         conn (conn/get-db repo false)
         opts {:persist-op? false
@@ -292,10 +333,9 @@ result:
         (let [page-blocks (ldb/get-page-blocks @conn (:db/id (ldb/get-page @conn page-name)))]
           (is (= [uuid3 uuid1] (map :block/uuid (sort-by :block/order page-blocks)))))))))
 
-(deftest ^:fix-me apply-remote-update&remove-page-ops-test
+(deftest apply-remote-update&remove-page-ops-test
   (let [repo (state/get-current-repo)
         conn (conn/get-db repo false)
-        date-formatter (common-config/get-date-formatter (worker-state/get-config repo))
         [page1-uuid ;; page2-uuid page3-uuid page4-uuid
          ](repeatedly random-uuid)]
     (testing "apply-remote-update-page-ops-test1"
@@ -303,13 +343,13 @@ result:
                           :affected-blocks
                           {page1-uuid {:op :update-page
                                        :self page1-uuid
-                                       :page-name (str page1-uuid)
-                                       :block/title (str page1-uuid)}}}
+                                       :page-name (ldb/write-transit-str (str "X" page1-uuid))
+                                       :block/title (ldb/write-transit-str (str "X" page1-uuid))}}}
             update-page-ops (vals
                              (:update-page-ops-map
                               (#'r.remote/affected-blocks->diff-type-ops repo (:affected-blocks data-from-ws))))]
         (is (rtc-const/data-from-ws-validator data-from-ws))
-        (#'r.remote/apply-remote-update-page-ops repo conn date-formatter update-page-ops)
+        (#'r.remote/apply-remote-update-page-ops repo conn update-page-ops)
         (is (= page1-uuid (:block/uuid (d/entity @conn [:block/uuid page1-uuid]))))))
 
     (testing "apply-remote-update-page-ops-test2"
@@ -317,13 +357,13 @@ result:
                           :affected-blocks
                           {page1-uuid {:op :update-page
                                        :self page1-uuid
-                                       :page-name (str page1-uuid "-rename")
-                                       :block/title (str page1-uuid "-rename")}}}
+                                       :page-name (ldb/write-transit-str (str page1-uuid "-rename"))
+                                       :block/title (ldb/write-transit-str (str page1-uuid "-rename"))}}}
             update-page-ops (vals
                              (:update-page-ops-map
                               (#'r.remote/affected-blocks->diff-type-ops repo (:affected-blocks data-from-ws))))]
         (is (rtc-const/data-from-ws-validator data-from-ws))
-        (#'r.remote/apply-remote-update-page-ops repo conn date-formatter update-page-ops)
+        (#'r.remote/apply-remote-update-page-ops repo conn update-page-ops)
         (is (= (str page1-uuid "-rename") (:block/name (d/entity @conn [:block/uuid page1-uuid]))))))
 
     (testing "apply-remote-remove-page-ops-test1"