Browse Source

Merge branch 'master' into feat/web-bi-client

charlie 1 year ago
parent
commit
1389fe36c6
100 changed files with 2701 additions and 1858 deletions
  1. 1 1
      README.md
  2. 2 18
      android/app/build.gradle
  3. 1 0
      android/app/capacitor.build.gradle
  4. 1 16
      android/app/src/main/AndroidManifest.xml
  5. 4 0
      android/app/src/main/assets/capacitor.plugins.json
  6. 18 0
      android/app/src/main/java/com/logseq/app/FolderPicker.java
  7. 3 0
      android/capacitor.settings.gradle
  8. 0 2
      deps/shui/src/logseq/shui/core.cljs
  9. 5 5
      deps/shui/src/logseq/shui/list_item/v1.cljs
  10. 19 14
      deps/shui/src/logseq/shui/shortcut/v1.cljs
  11. 5 0
      fastlane/metadata/android/en-US/full_description.txt
  12. 1 0
      fastlane/metadata/android/en-US/short_description.txt
  13. 4 4
      ios/App/App.xcodeproj/project.pbxproj
  14. 1 0
      package.json
  15. 26 7
      packages/ui/@/components/ui/context-menu.tsx
  16. 1 1
      packages/ui/@/components/ui/dropdown-menu.tsx
  17. 71 30
      packages/ui/src/colors.css
  18. 3 3
      packages/ui/src/index.css
  19. 174 0
      packages/ui/src/vars-classic.css
  20. 2 2
      packages/ui/tailwind.config.js
  21. 9 16
      resources/css/codemirror.lsradix.css
  22. 147 40
      resources/css/shui.css
  23. 1 1
      resources/forge.config.js
  24. 0 0
      resources/js/ui.js
  25. 1 1
      resources/package.json
  26. 1 1
      src/main/frontend/colors.cljs
  27. 26 225
      src/main/frontend/common.css
  28. 6 4
      src/main/frontend/components/block.cljs
  29. 29 23
      src/main/frontend/components/block.css
  30. 1 3
      src/main/frontend/components/cmdk.cljs
  31. 6 0
      src/main/frontend/components/cmdk.css
  32. 2 3
      src/main/frontend/components/command_palette.css
  33. 55 35
      src/main/frontend/components/container.cljs
  34. 24 55
      src/main/frontend/components/container.css
  35. 1 6
      src/main/frontend/components/content.css
  36. 0 2
      src/main/frontend/components/datepicker.css
  37. 12 0
      src/main/frontend/components/editor.css
  38. 42 39
      src/main/frontend/components/file_sync.cljs
  39. 3 8
      src/main/frontend/components/file_sync.css
  40. 39 19
      src/main/frontend/components/git.cljs
  41. 2 2
      src/main/frontend/components/header.cljs
  42. 3 12
      src/main/frontend/components/header.css
  43. 1 1
      src/main/frontend/components/journal.css
  44. 6 16
      src/main/frontend/components/onboarding/index.css
  45. 129 127
      src/main/frontend/components/onboarding/setups.cljs
  46. 110 24
      src/main/frontend/components/page.cljs
  47. 5 15
      src/main/frontend/components/page.css
  48. 2 0
      src/main/frontend/components/page_menu.cljs
  49. 4 3
      src/main/frontend/components/plugins.cljs
  50. 20 17
      src/main/frontend/components/plugins.css
  51. 13 1
      src/main/frontend/components/query/builder.css
  52. 69 58
      src/main/frontend/components/right_sidebar.cljs
  53. 8 15
      src/main/frontend/components/right_sidebar.css
  54. 44 33
      src/main/frontend/components/settings.cljs
  55. 28 1
      src/main/frontend/components/settings.css
  56. 3 2
      src/main/frontend/components/shortcut.cljs
  57. 3 3
      src/main/frontend/components/svg.cljs
  58. 3 3
      src/main/frontend/components/table.css
  59. 8 9
      src/main/frontend/components/theme.cljs
  60. 27 37
      src/main/frontend/components/theme.css
  61. 1 1
      src/main/frontend/components/whiteboard.cljs
  62. 24 21
      src/main/frontend/components/whiteboard.css
  63. 3 1
      src/main/frontend/dicts.cljc
  64. 2 7
      src/main/frontend/extensions/code.css
  65. 2 2
      src/main/frontend/extensions/graph.cljs
  66. 44 11
      src/main/frontend/extensions/graph/pixi.cljs
  67. 27 22
      src/main/frontend/extensions/handbooks/handbooks.css
  68. 2 2
      src/main/frontend/extensions/pdf/core.cljs
  69. 12 18
      src/main/frontend/extensions/pdf/pdf.css
  70. 10 6
      src/main/frontend/extensions/tldraw.cljs
  71. 7 3
      src/main/frontend/extensions/video/youtube.cljs
  72. 93 62
      src/main/frontend/handler/events.cljs
  73. 9 30
      src/main/frontend/handler/shell.cljs
  74. 22 0
      src/main/frontend/mobile/intent.cljs
  75. 351 351
      src/main/frontend/modules/shortcut/config.cljs
  76. 1 0
      src/main/frontend/schema/handler/common_config.cljc
  77. 10 14
      src/main/frontend/state.cljs
  78. 11 10
      src/main/frontend/tippy-tooltip.css
  79. 29 16
      src/main/frontend/ui.cljs
  80. 32 34
      src/main/frontend/ui.css
  81. 1 1
      src/main/frontend/version.cljs
  82. 6 6
      src/resources/dicts/en.edn
  83. 0 2
      src/resources/dicts/es.edn
  84. 515 0
      src/resources/dicts/fa.edn
  85. 0 2
      src/resources/dicts/fr.edn
  86. 0 2
      src/resources/dicts/it.edn
  87. 0 2
      src/resources/dicts/ja.edn
  88. 0 2
      src/resources/dicts/nb-no.edn
  89. 0 2
      src/resources/dicts/pt-br.edn
  90. 0 2
      src/resources/dicts/sk.edn
  91. 1 2
      src/resources/dicts/tr.edn
  92. 7 0
      src/resources/templates/config.edn
  93. 14 0
      src/resources/tutorials/dummy-notes-fa.md
  94. 25 0
      src/resources/tutorials/tutorial-fa.md
  95. 1 0
      tailwind.all.css
  96. 49 98
      tailwind.config.js
  97. 110 100
      tldraw/apps/tldraw-logseq/src/components/ContextMenu/ContextMenu.tsx
  98. 3 3
      tldraw/apps/tldraw-logseq/src/components/KeyboardShortcut/KeyboardShortcut.tsx
  99. 3 1
      tldraw/apps/tldraw-logseq/src/lib/logseq-context.ts
  100. 39 59
      tldraw/apps/tldraw-logseq/src/styles.css

+ 1 - 1
README.md

@@ -152,7 +152,7 @@ Logseq is also made possible by the following projects:
 * [Clojure & ClojureScript](https://clojure.org/) - A dynamic, functional, general-purpose programming language
 * [DataScript](https://github.com/tonsky/datascript) - An immutable database and Datalog query-engine for Clojure,
 ClojureScript and JS
-* [OCaml](https://ocaml.org/) & [Angstrom](https://github.com/inhabitedtype/angstrom), for the document parser [maldoc](https://github.com/mldoc/mldoc)
+* [OCaml](https://ocaml.org/) & [Angstrom](https://github.com/inhabitedtype/angstrom), for the document parser [mldoc](https://github.com/logseq/mldoc)
 * [isomorphic-git](https://isomorphic-git.org/) - A pure JavaScript implementation of Git for NodeJS and web browsers
 * [SCI](https://github.com/borkdude/sci) - A Small Clojure Interpreter
 

+ 2 - 18
android/app/build.gradle

@@ -1,8 +1,3 @@
-
-plugins {
-    id 'io.sentry.android.gradle' version '4.1.1'
-}
-
 apply plugin: 'com.android.application'
 
 android {
@@ -12,15 +7,14 @@ android {
         applicationId "com.logseq.app"
         minSdkVersion rootProject.ext.minSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
-        versionCode 79
-        versionName "0.10.5"
+        versionCode 80
+        versionName "0.10.6"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         aaptOptions {
              // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
              // Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61
             ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
         }
-        manifestPlaceholders = [LOGSEQ_SENTRY_DSN: "$System.env.LOGSEQ_SENTRY_DSN"]
     }
     buildTypes {
         release {
@@ -59,13 +53,3 @@ try {
 } catch(Exception e) {
     logger.warn("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
 }
-
-
-sentry {
-    org = "logseq"
-    projectName = "logseq"
-
-    // this will upload your source code to Sentry to show it as part of the stack traces
-    // disable if you don't want to expose your sources
-    includeSourceContext = false
-}

+ 1 - 0
android/app/capacitor.build.gradle

@@ -9,6 +9,7 @@ android {
 
 apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
 dependencies {
+    implementation project(':capacitor-action-sheet')
     implementation project(':capacitor-app')
     implementation project(':capacitor-camera')
     implementation project(':capacitor-clipboard')

+ 1 - 16
android/app/src/main/AndroidManifest.xml

@@ -60,20 +60,5 @@
                 android:name="android.support.FILE_PROVIDER_PATHS"
                 android:resource="@xml/file_paths" />
         </provider>
-
-    <!-- Required: set your sentry.io project identifier (DSN) -->
-    <meta-data android:name="io.sentry.dsn" android:value="${LOGSEQ_SENTRY_DSN}" />
-
-    <!-- enable automatic breadcrumbs for user interactions (clicks, swipes, scrolls) -->
-    <meta-data android:name="io.sentry.traces.user-interaction.enable" android:value="true" />
-    <!-- enable screenshot for crashes (could contain sensitive/PII data) -->
-    <meta-data android:name="io.sentry.attach-screenshot" android:value="true" />
-    <!-- enable view hierarchy for crashes -->
-    <meta-data android:name="io.sentry.attach-view-hierarchy" android:value="true" />
-
-    <!-- enable the performance API by setting a sample-rate, adjust in production env -->
-    <meta-data android:name="io.sentry.traces.sample-rate" android:value="1.0" />
-    <!-- enable profiling when starting transactions, adjust in production env -->
-    <meta-data android:name="io.sentry.traces.profiling.sample-rate" android:value="1.0" />
-</application>
+    </application>
 </manifest>

+ 4 - 0
android/app/src/main/assets/capacitor.plugins.json

@@ -1,4 +1,8 @@
 [
+	{
+		"pkg": "@capacitor/action-sheet",
+		"classpath": "com.capacitorjs.plugins.actionsheet.ActionSheetPlugin"
+	},
 	{
 		"pkg": "@capacitor/app",
 		"classpath": "com.capacitorjs.plugins.app.AppPlugin"

+ 18 - 0
android/app/src/main/java/com/logseq/app/FolderPicker.java

@@ -11,6 +11,7 @@ import android.provider.Settings;
 import android.util.Log;
 
 import androidx.activity.result.ActivityResult;
+import androidx.core.content.FileProvider;
 import androidx.documentfile.provider.DocumentFile;
 
 import com.getcapacitor.JSObject;
@@ -49,6 +50,23 @@ public class FolderPicker extends Plugin {
         }
     }
 
+    @PluginMethod()
+    public void openFile(PluginCall call) {
+        Uri uri = Uri.parse(call.getString("uri"));
+        File file = new File(uri.getPath());
+
+        // Get URI and MIME type of file
+        String appId = getAppId();
+        uri = FileProvider.getUriForFile(getActivity(), appId + ".fileprovider", file);
+        String mime = getContext().getContentResolver().getType(uri);
+
+        Intent intent = new Intent();
+        intent.setAction(Intent.ACTION_VIEW);
+        intent.setDataAndType(uri, mime);
+        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        getContext().startActivity(intent);
+    }
+
     @ActivityCallback
     private void folderPickerResult(PluginCall call, ActivityResult result) {
         if (call == null) {

+ 3 - 0
android/capacitor.settings.gradle

@@ -2,6 +2,9 @@
 include ':capacitor-android'
 project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
 
+include ':capacitor-action-sheet'
+project(':capacitor-action-sheet').projectDir = new File('../node_modules/@capacitor/action-sheet/android')
+
 include ':capacitor-app'
 project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android')
 

+ 0 - 2
deps/shui/src/logseq/shui/core.cljs

@@ -12,11 +12,9 @@
 
 ;; shortcut
 (def shortcut shui.shortcut.v1/root)
-(def shortcut-v1 shui.shortcut.v1/root)
 
 ;; icon
 (def icon shui.icon.v2/root)
-(def icon-v2 shui.icon.v2/root)
 
 ;; list-item
 (def list-item shui.list-item.v1/root)

+ 5 - 5
deps/shui/src/logseq/shui/list_item/v1.cljs

@@ -90,11 +90,11 @@
      [highlighted on-highlight-dep])
     [:div (merge
            {:style {:opacity (if highlighted 1 0.8)}
-            :class (cond-> "flex flex-col grayscale"
-                     highlighted (str " !grayscale-0 !opacity-100 bg-gray-03-alpha dark:bg-gray-04-alpha")
-                     hoverable (str " transition-all duration-50 ease-in !opacity-75 hover:!opacity-100 hover:grayscale-0 hover:cursor-pointer hover:bg-gradient-to-r hover:from-gray-03-alpha hover:to-gray-01-alpha from-0% to-100%")
+            :class (cond-> "flex flex-col transition-opacity"
+                     highlighted (str " !opacity-100 bg-gray-03-alpha dark:bg-gray-04-alpha")
+                     hoverable (str " transition-all duration-50 ease-in !opacity-75 hover:!opacity-100 hover:cursor-pointer hover:bg-gradient-to-r hover:from-gray-03-alpha hover:to-gray-01-alpha from-0% to-100%")
                      (and hoverable rounded) (str " !rounded-lg")
-                     (not compact) (str  " py-4 px-6 gap-1")
+                     (not compact) (str " py-4 px-6 gap-1")
                      compact (str " py-1.5 px-3 gap-0.5")
                      (not highlighted) (str " "))
             :ref ref
@@ -143,5 +143,5 @@
            [:span.text-gray-11 (to-string value)])])
       (when shortcut
         [:div {:class "flex gap-1"
-               :style {:opacity (if (or highlighted hover?) 1 0.5)}}
+               :style {:opacity (if (or highlighted hover?) 1 0.9)}}
          (shortcut/root shortcut)])]]))

+ 19 - 14
deps/shui/src/logseq/shui/shortcut/v1.cljs

@@ -62,31 +62,36 @@
                            %)))))))
 
 (rum/defc part
-  [ks size]
-  (let [tiles (map print-shortcut-key ks)]
-    (ui/button {:variant     :default
-                :class       "bg-gray-03 text-gray-12 px-1.5 py-0 leading-4 h-5 hover:bg-gray-04 active:bg-gray-03 hover:text-gray-11"
-                :interactive false
-                :size        size}
+  [ks size {:keys [interactive?]}]
+  (let [tiles (map print-shortcut-key ks)
+        interactive? (true? interactive?)]
+    (ui/button {:variant (if interactive? :default :text)
+                :class   (str "bg-gray-03 text-gray-10 px-1.5 py-0 leading-4 h-5 rounded font-normal "
+                           (if interactive?
+                             "hover:bg-gray-04 active:bg-gray-03 hover:text-gray-12"
+                             "bg-transparent cursor-default active:bg-gray-03 hover:text-gray-11 opacity-80"))
+                :size    size}
       (for [[index tile] (map-indexed vector tiles)]
         [:<>
          (when (< 0 index)
-           [:div.ui__button__tile-separator])
-         [:div.ui__button__tile tile]]))))
+           [:span.ui__button__tile-separator])
+         [:span.ui__button__tile tile]]))))
 
 (rum/defc root
-  [shortcut & {:keys [size theme]
-                       :or   {size  :xs
-                              theme :gray}}]
+  [shortcut & {:keys [size theme interactive?]
+               :or   {size  :xs
+                      interactive? true
+                      theme :gray}}]
   (when (seq shortcut)
     (let [shortcuts (if (coll? shortcut)
                       [shortcut]
-                      (parse-shortcuts shortcut))]
+                      (parse-shortcuts shortcut))
+          opts {:interactive? interactive?}]
       (for [[index binding] (map-indexed vector shortcuts)]
         [:<>
          (when (< 0 index)
            [:div.text-gray-11.text-sm "|"])
          (if (coll? (first binding))   ; + included
            (for [ks binding]
-             (part ks size))
-           (part binding size))]))))
+             (part ks size opts))
+           (part binding size opts))]))))

+ 5 - 0
fastlane/metadata/android/en-US/full_description.txt

@@ -0,0 +1,5 @@
+Logseq is a knowledge management and collaboration platform. It focuses on privacy, longevity, and user control. Logseq offers a range of powerful tools for knowledge management, collaboration, PDF annotation, and task management with support for multiple file formats, including Markdown and Org-mode, and various features for organizing and structuring your notes.
+
+Logseq's Whiteboard feature lets you organize your knowledge and ideas using a spatial canvas with shapes, drawings, website embeds, and connectors. You can visually group and link your notes and external media (such as videos and images), enabling visual thinkers to compose, remix, annotate, and connect content from their knowledge base and emerging thoughts in a new way.
+
+In addition to its core features, Logseq has a growing ecosystem of plugins and themes that enable a wide range of workflows and customization options. Mobile apps are also available, providing access to most of the features of the desktop application. Whether you're a student, a professional, or anyone who values a clear and organized approach to managing your ideas and notes, Logseq is an excellent choice for anyone looking to improve their productivity and streamline their workflow.

+ 1 - 0
fastlane/metadata/android/en-US/short_description.txt

@@ -0,0 +1 @@
+A privacy-first, open-source platform for knowledge management and collaboration

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

@@ -519,7 +519,7 @@
 				INFOPLIST_FILE = App/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
-				MARKETING_VERSION = 0.10.5;
+				MARKETING_VERSION = 0.10.6;
 				OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -546,7 +546,7 @@
 				INFOPLIST_FILE = App/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
-				MARKETING_VERSION = 0.10.5;
+				MARKETING_VERSION = 0.10.6;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
@@ -571,7 +571,7 @@
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
-				MARKETING_VERSION = 0.10.5;
+				MARKETING_VERSION = 0.10.6;
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq.ShareViewController;
@@ -598,7 +598,7 @@
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
-				MARKETING_VERSION = 0.10.5;
+				MARKETING_VERSION = 0.10.6;
 				MTL_FAST_MATH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq.ShareViewController;
 				PRODUCT_NAME = "$(TARGET_NAME)";

+ 1 - 0
package.json

@@ -79,6 +79,7 @@
         "postinstall": "yarn tldraw:build && yarn amplify:build "
     },
     "dependencies": {
+        "@capacitor/action-sheet": "^5.0.7",
         "@capacitor/android": "^5.0.0",
         "@capacitor/app": "^5.0.0",
         "@capacitor/camera": "^5.0.0",

+ 26 - 7
packages/ui/@/components/ui/context-menu.tsx

@@ -27,7 +27,9 @@ const ContextMenuSubTrigger = React.forwardRef<
     ref={ref}
     className={cn(
       'ui__context-menu-sub-trigger',
-      'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground',
+      'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm ' +
+      'outline-none focus:bg-accent focus:text-accent-foreground ' +
+      'data-[state=open]:bg-accent data-[state=open]:text-accent-foreground',
       inset && 'pl-8',
       className
     )}
@@ -47,7 +49,10 @@ const ContextMenuSubContent = React.forwardRef<
     ref={ref}
     className={cn(
       'ui__context-menu-sub-content',
-      'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
+      'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in' +
+      ' data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95' +
+      ' data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2' +
+      ' data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
       className
     )}
     {...props}
@@ -64,7 +69,12 @@ const ContextMenuContent = React.forwardRef<
       ref={ref}
       className={cn(
         'ui__context-menu-content',
-        'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
+        'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 ' +
+        'text-popover-foreground shadow-md animate-in fade-in-80 ' +
+        'data-[state=open]:animate-in data-[state=closed]:animate-out ' +
+        'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 ' +
+        'data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 ' +
+        'data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
         className
       )}
       {...props}
@@ -82,7 +92,10 @@ const ContextMenuItem = React.forwardRef<
   <ContextMenuPrimitive.Item
     ref={ref}
     className={cn(
-      'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
+      'relative flex cursor-default select-none items-center rounded-sm px-2 ' +
+      'text-popover-foreground/75 hover:text-popover-foreground/100 py-1.5 text-sm ' +
+      'outline-none focus:bg-accent focus:text-accent-foreground ' +
+      'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
       inset && 'pl-8',
       className
     )}
@@ -98,7 +111,10 @@ const ContextMenuCheckboxItem = React.forwardRef<
   <ContextMenuPrimitive.CheckboxItem
     ref={ref}
     className={cn(
-      'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
+      'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm ' +
+      'text-popover-foreground/75 hover:text-popover-foreground/100 ' +
+      'outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none ' +
+      'data-[disabled]:opacity-50',
       className
     )}
     checked={checked}
@@ -122,7 +138,10 @@ const ContextMenuRadioItem = React.forwardRef<
   <ContextMenuPrimitive.RadioItem
     ref={ref}
     className={cn(
-      'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
+      'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 ' +
+      'text-popover-foreground/75 hover:text-popover-foreground/100 ' +
+      'pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground ' +
+      'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
       className
     )}
     {...props}
@@ -174,7 +193,7 @@ const ContextMenuShortcut = ({
   return (
     <span
       className={cn(
-        'ml-auto text-xs tracking-widest text-muted-foreground',
+        'ml-auto text-xs text-muted-foreground',
         className
       )}
       {...props}

+ 1 - 1
packages/ui/@/components/ui/dropdown-menu.tsx

@@ -178,7 +178,7 @@ const DropdownMenuShortcut = ({
 }: React.HTMLAttributes<HTMLSpanElement>) => {
   return (
     <span
-      className={cn('ml-auto text-xs tracking-widest opacity-60', className)}
+      className={cn('ml-auto text-xs opacity-60', className)}
       {...props}
     />
   )

+ 71 - 30
packages/ui/src/colors.css

@@ -12,6 +12,45 @@ html {
     --lx-accent-10: var(--rx-logseq-10);
     --lx-accent-11: var(--rx-logseq-11);
     --lx-accent-12: var(--rx-logseq-12);
+
+    &[data-theme=light] {
+      --primary: 200 97% 37%;
+      --primary-foreground: 255 92% 100%;
+      --accent: 200 97% 37%;
+      --accent-foreground: 255 92% 100%;
+      --ring: 200 97% 37%;
+
+      --lx-gray-01: var(--rx-gray-01);
+      --lx-gray-02: var(--rx-gray-02);
+      --lx-gray-03: var(--rx-gray-03);
+      --lx-gray-04: var(--rx-gray-04);
+      --lx-gray-05: var(--rx-gray-05);
+      --lx-gray-06: var(--rx-gray-06);
+      --lx-gray-07: var(--rx-gray-07);
+      --lx-gray-08: var(--rx-gray-08);
+      --lx-gray-09: var(--rx-gray-09);
+      --lx-gray-10: var(--rx-gray-10);
+      --lx-gray-11: var(--rx-gray-11);
+      --lx-gray-12: var(--rx-gray-12);
+    }
+
+    &[data-theme=dark] {
+      --background: 192 100% 11%;
+      --foreground: 0 0% 95%;
+      --accent: 192 80% 10%;
+      --accent-foreground: 255 92% 100%;
+      --primary: 200 97% 37%;
+      --primary-foreground: 255 92% 100%;
+      --ring: 200 97% 37%;
+      --secondary: 203 50% 20%;
+      --secondary-foreground: 0 0% 98%;
+      --muted: 192 100% 13%;
+      --border: 192 100% 14%;
+      --card: 192 100% 10%;
+      --card-foreground: 0 0% 95%;
+      --popover: 192 100% 11%;
+      --input: 203 35% 25%;
+    }
   }
 
   &[data-color=tomato] {
@@ -80,8 +119,8 @@ html {
       --ls-link-text-color: var(--rx-tomato-11);
       --ls-link-text-hover-color: var(--rx-tomato-12);
       --ls-block-ref-link-text-color: var(--rx-tomato-09);
-      --ls-secondary-text-color: var(--rx-mauve-12);
-      --ls-primary-text-color: var(--rx-mauve-11);
+      --ls-secondary-text-color: var(--rx-mauve-11);
+      --ls-primary-text-color: var(--rx-mauve-12);
       --ls-border-color: var(--rx-mauve-05);
       --ls-secondary-border-color: var(--rx-tomato-05);
       --ls-page-checkbox-color: var(--rx-mauve-07);
@@ -104,6 +143,7 @@ html {
     body, .dark-theme, .light-theme {
       --accent: 358 75.0% 59.0%;
       --primary: 358 75.0% 59.0%;
+      --primary-foreground: 190 43% 97%;
       --ring: 359 69.5% 74.3%;
       --accent-foreground: 190 43% 97%;
       --primary-accent-foreground: 190 43% 97%;
@@ -165,8 +205,8 @@ html {
       --ls-link-text-color: var(--rx-red-11);
       --ls-link-text-hover-color: var(--rx-red-12);
       --ls-block-ref-link-text-color: var(--rx-red-09);
-      --ls-secondary-text-color: var(--rx-mauve-12);
-      --ls-primary-text-color: var(--rx-mauve-11);
+      --ls-secondary-text-color: var(--rx-mauve-11);
+      --ls-primary-text-color: var(--rx-mauve-12);
       --ls-border-color: var(--rx-mauve-05);
       --ls-secondary-border-color: var(--rx-red-05);
       --ls-page-checkbox-color: var(--rx-mauve-07);
@@ -250,8 +290,8 @@ html {
       --ls-link-text-color: var(--rx-blue-11);
       --ls-link-text-hover-color: var(--rx-blue-12);
       --ls-block-ref-link-text-color: var(--rx-blue-09);
-      --ls-secondary-text-color: var(--rx-slate-12);
-      --ls-primary-text-color: var(--rx-slate-11);
+      --ls-secondary-text-color: var(--rx-slate-11);
+      --ls-primary-text-color: var(--rx-slate-12);
       --ls-border-color: var(--rx-slate-05);
       --ls-secondary-border-color: var(--rx-blue-05);
       --ls-page-checkbox-color: var(--rx-slate-07);
@@ -274,6 +314,7 @@ html {
     body, .dark-theme, .light-theme {
       --accent: 336 80.0% 57.8%;
       --primary: 336 80.0% 57.8%;
+      --primary-foreground: 190 43% 97%;
       --ring: 336 62.3% 72.9%;
       --accent-foreground: 190 43% 97%;
       --primary-accent-foreground: 190 43% 97%;
@@ -335,8 +376,8 @@ html {
       --ls-link-text-color: var(--rx-crimson-11);
       --ls-link-text-hover-color: var(--rx-crimson-12);
       --ls-block-ref-link-text-color: var(--rx-crimson-09);
-      --ls-secondary-text-color: var(--rx-mauve-12);
-      --ls-primary-text-color: var(--rx-mauve-11);
+      --ls-secondary-text-color: var(--rx-mauve-11);
+      --ls-primary-text-color: var(--rx-mauve-12);
       --ls-border-color: var(--rx-mauve-05);
       --ls-secondary-border-color: var(--rx-crimson-05);
       --ls-page-checkbox-color: var(--rx-mauve-07);
@@ -419,8 +460,8 @@ html {
       --ls-link-text-color: var(--rx-pink-11);
       --ls-link-text-hover-color: var(--rx-pink-12);
       --ls-block-ref-link-text-color: var(--rx-pink-09);
-      --ls-secondary-text-color: var(--rx-mauve-12);
-      --ls-primary-text-color: var(--rx-mauve-11);
+      --ls-secondary-text-color: var(--rx-mauve-11);
+      --ls-primary-text-color: var(--rx-mauve-12);
       --ls-border-color: var(--rx-mauve-05);
       --ls-secondary-border-color: var(--rx-pink-05);
       --ls-page-checkbox-color: var(--rx-mauve-07);
@@ -503,8 +544,8 @@ html {
       --ls-link-text-color: var(--rx-plum-11);
       --ls-link-text-hover-color: var(--rx-plum-12);
       --ls-block-ref-link-text-color: var(--rx-plum-09);
-      --ls-secondary-text-color: var(--rx-mauve-12);
-      --ls-primary-text-color: var(--rx-mauve-11);
+      --ls-secondary-text-color: var(--rx-mauve-11);
+      --ls-primary-text-color: var(--rx-mauve-12);
       --ls-border-color: var(--rx-mauve-05);
       --ls-secondary-border-color: var(--rx-plum-05);
       --ls-page-checkbox-color: var(--rx-mauve-07);
@@ -587,8 +628,8 @@ html {
       --ls-link-text-color: var(--rx-purple-11);
       --ls-link-text-hover-color: var(--rx-purple-12);
       --ls-block-ref-link-text-color: var(--rx-purple-09);
-      --ls-secondary-text-color: var(--rx-mauve-12);
-      --ls-primary-text-color: var(--rx-mauve-11);
+      --ls-secondary-text-color: var(--rx-mauve-11);
+      --ls-primary-text-color: var(--rx-mauve-12);
       --ls-border-color: var(--rx-mauve-05);
       --ls-secondary-border-color: var(--rx-purple-05);
       --ls-page-checkbox-color: var(--rx-mauve-07);
@@ -671,8 +712,8 @@ html {
       --ls-link-text-color: var(--rx-violet-11);
       --ls-link-text-hover-color: var(--rx-violet-12);
       --ls-block-ref-link-text-color: var(--rx-violet-09);
-      --ls-secondary-text-color: var(--rx-mauve-12);
-      --ls-primary-text-color: var(--rx-mauve-11);
+      --ls-secondary-text-color: var(--rx-mauve-11);
+      --ls-primary-text-color: var(--rx-mauve-12);
       --ls-border-color: var(--rx-mauve-05);
       --ls-secondary-border-color: var(--rx-violet-05);
       --ls-page-checkbox-color: var(--rx-mauve-07);
@@ -755,8 +796,8 @@ html {
       --ls-link-text-color: var(--rx-indigo-11);
       --ls-link-text-hover-color: var(--rx-indigo-12);
       --ls-block-ref-link-text-color: var(--rx-indigo-09);
-      --ls-secondary-text-color: var(--rx-slate-12);
-      --ls-primary-text-color: var(--rx-slate-11);
+      --ls-secondary-text-color: var(--rx-slate-11);
+      --ls-primary-text-color: var(--rx-slate-12);
       --ls-border-color: var(--rx-slate-05);
       --ls-secondary-border-color: var(--rx-indigo-05);
       --ls-page-checkbox-color: var(--rx-slate-07);
@@ -841,8 +882,8 @@ html {
       --ls-link-text-color: var(--rx-cyan-11);
       --ls-link-text-hover-color: var(--rx-cyan-12);
       --ls-block-ref-link-text-color: var(--rx-cyan-09);
-      --ls-secondary-text-color: var(--rx-slate-12);
-      --ls-primary-text-color: var(--rx-slate-11);
+      --ls-secondary-text-color: var(--rx-slate-11);
+      --ls-primary-text-color: var(--rx-slate-12);
       --ls-border-color: var(--rx-slate-05);
       --ls-secondary-border-color: var(--rx-cyan-05);
       --ls-page-checkbox-color: var(--rx-slate-07);
@@ -925,8 +966,8 @@ html {
       --ls-link-text-color: var(--rx-teal-11);
       --ls-link-text-hover-color: var(--rx-teal-12);
       --ls-block-ref-link-text-color: var(--rx-teal-09);
-      --ls-secondary-text-color: var(--rx-sage-12);
-      --ls-primary-text-color: var(--rx-sage-11);
+      --ls-secondary-text-color: var(--rx-sage-11);
+      --ls-primary-text-color: var(--rx-sage-12);
       --ls-border-color: var(--rx-sage-05);
       --ls-secondary-border-color: var(--rx-teal-05);
       --ls-page-checkbox-color: var(--rx-sage-07);
@@ -1010,8 +1051,8 @@ html {
       --ls-link-text-color: var(--rx-green-11);
       --ls-link-text-hover-color: var(--rx-green-12);
       --ls-block-ref-link-text-color: var(--rx-green-09);
-      --ls-secondary-text-color: var(--rx-sage-12);
-      --ls-primary-text-color: var(--rx-sage-11);
+      --ls-secondary-text-color: var(--rx-sage-11);
+      --ls-primary-text-color: var(--rx-sage-12);
       --ls-border-color: var(--rx-sage-05);
       --ls-secondary-border-color: var(--rx-green-05);
       --ls-page-checkbox-color: var(--rx-sage-07);
@@ -1094,8 +1135,8 @@ html {
       --ls-link-text-color: var(--rx-grass-11);
       --ls-link-text-hover-color: var(--rx-grass-12);
       --ls-block-ref-link-text-color: var(--rx-grass-09);
-      --ls-secondary-text-color: var(--rx-olive-12);
-      --ls-primary-text-color: var(--rx-olive-11);
+      --ls-secondary-text-color: var(--rx-olive-11);
+      --ls-primary-text-color: var(--rx-olive-12);
       --ls-border-color: var(--rx-olive-05);
       --ls-secondary-border-color: var(--rx-grass-05);
       --ls-page-checkbox-color: var(--rx-olive-07);
@@ -1178,8 +1219,8 @@ html {
       --ls-link-text-color: var(--rx-orange-11);
       --ls-link-text-hover-color: var(--rx-orange-12);
       --ls-block-ref-link-text-color: var(--rx-orange-09);
-      --ls-secondary-text-color: var(--rx-sand-12);
-      --ls-primary-text-color: var(--rx-sand-11);
+      --ls-secondary-text-color: var(--rx-sand-11);
+      --ls-primary-text-color: var(--rx-sand-12);
       --ls-border-color: var(--rx-sand-05);
       --ls-secondary-border-color: var(--rx-orange-05);
       --ls-page-checkbox-color: var(--rx-sand-07);
@@ -1263,8 +1304,8 @@ html {
       --ls-link-text-color: var(--rx-brown-11);
       --ls-link-text-hover-color: var(--rx-brown-12);
       --ls-block-ref-link-text-color: var(--rx-brown-09);
-      --ls-secondary-text-color: var(--rx-sand-12);
-      --ls-primary-text-color: var(--rx-sand-11);
+      --ls-secondary-text-color: var(--rx-sand-11);
+      --ls-primary-text-color: var(--rx-sand-12);
       --ls-border-color: var(--rx-sand-05);
       --ls-secondary-border-color: var(--rx-brown-05);
       --ls-page-checkbox-color: var(--rx-sand-07);

+ 3 - 3
packages/ui/src/index.css

@@ -20,7 +20,7 @@
     --secondary-foreground: 222.2 47.4% 11.2%;
 
     --muted: 210 40% 96.1%;
-    --muted-foreground: 215.4 16.3% 46.9%;
+    --muted-foreground: 0 0% 52.3%;
 
     --accent: 210 40% 96.1%;
     --accent-foreground: 222.2 47.4% 11.2%;
@@ -28,7 +28,7 @@
     --destructive: 0 84.2% 60.2%;
     --destructive-foreground: 210 40% 98%;
 
-    --border: 214.3 31.8% 91.4%;
+    --border: 300 1% 92%;
     --input: 214.3 31.8% 91.4%;
     --ring: 222.2 84% 4.9%;
 
@@ -52,7 +52,7 @@
     --secondary-foreground: 210 40% 98%;
 
     --muted: 217.2 32.6% 17.5%;
-    --muted-foreground: 215 20.2% 65.1%;
+    --muted-foreground: 0 0% 49.4%;
 
     --accent: 217.2 32.6% 17.5%;
     --accent-foreground: 210 40% 98%;

+ 174 - 0
packages/ui/src/vars-classic.css

@@ -0,0 +1,174 @@
+:root {
+  --ls-tag-text-opacity: 0.8;
+  --ls-tag-text-hover-opacity: 1;
+  --ls-page-text-size: 1em;
+  --ls-page-title-size: 36px;
+  --ls-main-content-max-width: 810px;
+  --ls-main-content-max-width-wide: 1280px;
+  --ls-font-family: Inter;
+  --ls-scrollbar-width: 6px;
+  --ls-border-radius-low: 4px;
+  --ls-border-radius-medium: 8px;
+  --ls-headbar-height: 3rem;
+  --ls-headbar-inner-top-padding: 0px;
+  --ls-left-sidebar-width: 246px;
+  --ls-left-sidebar-sm-width: 74vw;
+  --ls-left-sidebar-nav-btn-size: 38px;
+  --ls-native-kb-height: 0px;
+
+  --ls-highlight-color-gray: var(--rx-gray-05);
+  --ls-highlight-color-red: var(--rx-red-05);
+  --ls-highlight-color-yellow: var(--rx-yellow-05);
+  --ls-highlight-color-green: var(--rx-green-05);
+  --ls-highlight-color-blue: var(--rx-blue-05);
+  --ls-highlight-color-purple: var(--rx-purple-05);
+  --ls-highlight-color-pink: var(--rx-pink-05);
+
+  /* NOTE: For compatible. Will be deprecated*/
+  --ls-block-bullet-color: var(--lx-gray-08, var(--rx-gray-08));
+}
+
+.white-theme,
+.light-theme,
+html[data-theme='light'] {
+  --color-level-1: var(--rx-gray-02);
+  --color-level-2: var(--rx-gray-03);
+  --color-level-3: var(--rx-gray-04);
+  --color-level-4: var(--rx-gray-05);
+  --color-level-5: var(--rx-gray-06);
+  --color-level-6: var(--rx-gray-07);
+}
+
+html[data-theme=light][data-color=logseq] {
+  --ls-primary-background-color: #ffffff;
+  --ls-secondary-background-color: #f7f7f7;
+  --ls-tertiary-background-color: #eaeaea;
+  --ls-quaternary-background-color: #dcdcdc;
+  --ls-table-tr-even-background-color: var(--ls-secondary-background-color);
+  --ls-active-primary-color: rgb(0, 105, 182);
+  --ls-active-secondary-color: #00477c;
+  --ls-block-properties-background-color: var(--ls-secondary-background-color);
+  --ls-page-properties-background-color: var(--ls-secondary-background-color);
+  --ls-block-ref-link-text-color: #d8e1e8;
+  --ls-border-color: #ccc;
+  --ls-secondary-border-color: #e2e2e2;
+  --ls-tertiary-border-color: rgba(200, 200, 200, 0.3);
+  --ls-guideline-color: rgba(46, 27, 5, 0.08);
+  --ls-menu-hover-color: var(--ls-a-chosen-bg);
+  --ls-primary-text-color: #433f38;
+  --ls-secondary-text-color: #161e2e;
+  --ls-title-text-color: var(--ls-header-button-background);
+  --ls-link-text-color: #106ba3;
+  --ls-link-text-hover-color: #1a537c;
+  --ls-link-ref-text-color: var(--ls-link-text-color);
+  --ls-link-ref-text-hover-color: var(--ls-link-text-hover-color);
+  --ls-tag-text-color: var(--ls-link-ref-text-color);
+  --ls-tag-text-hover-color: var(--ls-link-ref-text-hover-color);
+  --ls-slide-background-color: #fff;
+  --ls-block-bullet-border-color: #dedede;
+  --ls-block-bullet-color: rgba(67, 63, 56, 0.25);
+  --ls-block-highlight-color: #c0e6fd;
+  --ls-selection-background-color: #e4f2ff;
+  --ls-selection-text-color: var(--ls-secondary-text-color);
+  --ls-page-checkbox-color: #9dbbd8;
+  --ls-page-checkbox-border-color: var(--ls-page-checkbox-color);
+  --ls-page-blockquote-color: var(--ls-primary-text-color);
+  --ls-page-blockquote-bg-color: var(--lx-gray-03, #fbfaf8);
+  --ls-page-blockquote-border-color: var(--lx-gray-08, #799bbc);
+  --ls-page-mark-color: #262626;
+  --ls-page-mark-bg-color: #fef3ac;
+  --ls-page-inline-code-bg-color: var(--ls-secondary-background-color);
+  --ls-page-inline-code-color: var(--ls-primary-text-color);
+  --ls-scrollbar-foreground-color: rgba(0, 0, 0, 0.1);
+  --ls-scrollbar-background-color: rgba(0, 0, 0, 0.05);
+  --ls-scrollbar-thumb-hover-color: rgba(0, 0, 0, 0.2);
+  --ls-cloze-text-color: #0000cd;
+  --ls-icon-color: #646464;
+  --ls-search-icon-color: var(--ls-primary-text-color);
+  --ls-search-icon-hover-color: var(--ls-secondary-text-color);
+  --ls-a-chosen-bg: var(--ls-quaternary-background-color);
+  --ls-pie-bg-color: #e1e1e1;
+  --ls-pie-fg-color: #0a4a5d;
+
+  --ls-error-text-color: var(--color-red-700);
+  --ls-error-background-color: var(--color-red-100);
+  --ls-warning-text-color: var(--color-yellow-700);
+  --ls-warning-background-color: var(--color-yellow-100);
+  --ls-success-text-color: var(--color-green-800);
+  --ls-success-background-color: var(--color-green-100);
+  --ls-focus-ring-color: rgba(66, 133, 244, 0.5);
+  --ls-header-button-background: rgba(15, 20, 25, 1);
+  --ls-left-sidebar-text-color: var(--lx-gray-12);
+  --ls-button-background-hsl: 200 98% 35%;
+  --ls-button-background: hsl(var(--ls-button-background-hsl));
+}
+
+html[data-theme=dark][data-color=logseq] {
+  --ls-primary-background-color: #002b36;
+  --ls-secondary-background-color: #023643;
+  --ls-tertiary-background-color: #08404f;
+  --ls-quaternary-background-color: #094b5a;
+  --ls-table-tr-even-background-color: #03333f;
+  --ls-active-primary-color: #8ec2c2;
+  --ls-active-secondary-color: #d0e8e8;
+  --ls-block-properties-background-color: #06323e;
+  --ls-page-properties-background-color: #06323e;
+  --ls-block-ref-link-text-color: #1a6376;
+  --ls-border-color: #0e5263;
+  --ls-secondary-border-color: #126277;
+  --ls-tertiary-border-color: rgba(0, 2, 0, 0.1);
+  --ls-guideline-color: #0b4a5a;
+  --ls-menu-hover-color: var(--ls-secondary-background-color);
+  --ls-primary-text-color: #a4b5b6;
+  --ls-secondary-text-color: #dfdfdf;
+  --ls-title-text-color: #93a1a1;
+  --ls-link-text-color: rgb(138, 187, 187);
+  --ls-link-text-hover-color: var(--ls-active-secondary-color);
+  --ls-link-ref-text-color: var(--ls-link-text-color);
+  --ls-link-ref-text-hover-color: var(--ls-link-text-hover-color);
+  --ls-tag-text-color: var(--ls-link-text-color);
+  --ls-tag-text-hover-color: var(--ls-link-text-hover-color);
+  --ls-slide-background-color: var(--ls-primary-background-color);
+  --ls-block-bullet-border-color: #0f4958;
+  --ls-block-bullet-color: #608e91;
+  --ls-block-highlight-color: #0a3d4b;
+  --ls-selection-background-color: #338fff;
+  --ls-selection-text-color: #fff;
+  --ls-page-checkbox-color: #6093a0;
+  --ls-page-checkbox-border-color: var(--ls-primary-background-color);
+  --ls-page-blockquote-color: var(--ls-primary-text-color);
+  --ls-page-blockquote-bg-color: var(--ls-secondary-background-color);
+  --ls-page-blockquote-border-color: var(--ls-border-color);
+  --ls-page-mark-color: #262626;
+  --ls-page-mark-bg-color: #fef3ac;
+  --ls-page-inline-code-color: var(--ls-primary-text-color);
+  --ls-page-inline-code-bg-color: #01222a;
+  --ls-scrollbar-foreground-color: #11505f;
+  --ls-scrollbar-background-color: rgba(30, 60, 67, 0.1);
+  --ls-scrollbar-thumb-hover-color: rgba(255, 255, 255, 0.2);
+  --ls-cloze-text-color: #8fbc8f;
+  --ls-icon-color: var(--ls-link-text-color);
+  --ls-search-icon-color: var(--ls-primary-text-color);
+  --ls-search-icon-hover-color: var(--ls-secondary-text-color);
+  --ls-a-chosen-bg: var(--ls-quaternary-background-color);
+  --ls-pie-bg-color: #01303b;
+  --ls-pie-fg-color: #0b5869;
+
+  --ls-error-text-color: var(--color-red-400);
+  --ls-error-background-color: var(--color-red-900);
+  --ls-warning-text-color: var(--color-yellow-400);
+  --ls-warning-background-color: var(--color-yellow-900);
+  --ls-success-text-color: var(--color-green-100);
+  --ls-success-background-color: var(--color-green-900);
+  --ls-focus-ring-color: rgba(18, 98, 119, 0.5);
+  --ls-header-button-background: #dee4ea;
+  --ls-left-sidebar-text-color: var(--lx-gray-11);
+  --ls-button-background-hsl: 200 98% 35%;
+  --ls-button-background: hsl(var(--ls-button-background-hsl));
+  --color-level-1: var(--ls-secondary-background-color);
+  --color-level-2: var(--ls-tertiary-background-color);
+  --color-level-3: var(--ls-quaternary-background-color);
+  --color-level-4: #195d6c;
+  --color-level-5: #266c7d;
+  --color-level-6: #3a7e8e;
+}

+ 2 - 2
packages/ui/tailwind.config.js

@@ -65,11 +65,11 @@ module.exports = {
           foreground: 'hsl(var(--muted-foreground))',
         },
         accent: {
-          DEFAULT: 'hsl(var(--accent))',
+          DEFAULT: 'var(--lx-gray-04, hsl(var(--accent)))',
           foreground: 'hsl(var(--accent-foreground))',
         },
         popover: {
-          DEFAULT: 'hsl(var(--popover))',
+          DEFAULT: 'var(--lx-gray-03, hsl(var(--popover)))',
           foreground: 'hsl(var(--popover-foreground))',
         },
         card: {

+ 9 - 16
resources/css/codemirror.lsradix.css

@@ -35,21 +35,17 @@ http://ethanschoonover.com/lsradix/img/lsradix-palette.png
   rendering-intent: auto;
 }
 .cm-s-lsradix.cm-s-dark {
-  /* color: or(--lx-gray-09, #839496); */
-  color: or(--lx-gray-11, #839496);
-  background-color: or(--lx-gray-01, #002b36);
-  text-shadow: #002b36 0 1px;
+  background-color: var(--lx-gray-01, hsl(var(--secondary)/.7));
+  color: var(--lx-gray-10, hsl(var(--secondary-foreground)));
 }
 
 .dark .cm-s-lsradix.cm-s-dark {
-  background-color: or(--lx-gray-02, #002b36);
+  background-color: var(--lx-gray-02, hsl(var(--secondary)/.7));
 }
 
 .cm-s-lsradix.cm-s-light {
-  /* background-color: or(--lx-gray-12, #fdf6e3); */
-  background-color: or(--lx-gray-02, #fdf6e3);
-  color: or(--lx-gray-10, #657b83);
-  text-shadow: #eee8d5 0 1px;
+  background-color: var(--lx-gray-02, hsl(var(--secondary)/.7));
+  color: var(--lx-gray-10, hsl(var(--secondary-foreground)));
 }
 
 .cm-s-lsradix .CodeMirror-widget {
@@ -124,23 +120,20 @@ http://ethanschoonover.com/lsradix/img/lsradix-palette.png
 
 /* Dark */
 .cm-s-lsradix.cm-s-dark .CodeMirror-gutters {
-  background-color: or(--lx-gray-01, #073642);
+  background-color: var(--lx-gray-03, hsl(var(--secondary)));
 }
 
 .cm-s-lsradix.cm-s-dark .CodeMirror-linenumber {
-  color: or(--lx-gray-09, #586e75);
-  /* color: or(--lx-gray-03, #586e75); */
-  text-shadow: #021014 0 -1px;
+  color: var(--lx-gray-09, #586e75);
 }
 
 /* Light */
 .cm-s-lsradix.cm-s-light .CodeMirror-gutters {
-  background-color: or(--lx-gray-03, #eee8d5);
-  /* background-color: or(--lx-gray-11, #eee8d5); */
+  background-color: var(--lx-gray-03, hsl(var(--secondary)));
 }
 
 .cm-s-lsradix.cm-s-light .CodeMirror-linenumber {
-  color: or(--lx-gray-09, #839496);
+  color: var(--lx-gray-09, #839496);
 }
 
 /* Common */

+ 147 - 40
resources/css/shui.css

@@ -2,15 +2,60 @@ html * {
   border-color: hsl(var(--border));
 }
 
+html[data-theme=light] {
+  --accent: var(--rx-gray-12-hsl);
+  --accent-foreground: var(--rx-gray-02-hsl);
+  --input: var(--rx-gray-03-hsl);
+  --secondary: 240 4.8% 95.9%;
+}
+
+html[data-theme=dark] {
+  --accent: var(--rx-gray-12-hsl);
+  --accent-foreground: var(--rx-gray-02-hsl);
+  --primary-foreground: 0 0% 22%;
+  --background: 0 0% 11%;
+  --foreground: 0 0% 95%;
+  --card: 0 0% 11%;
+  --card-foreground: 0 0% 95%;
+  --secondary: 0 0% 20%;
+  --secondary-foreground: 0 0% 98%;
+  --border: 0 0% 16%;
+  --muted: 0 0% 15%;
+  --popover: 0 0% 7%;
+  --popover-foreground: 0 0 95%;
+  --input: 0 0% 25%;
+}
+
 html {
   .ui__dropdown-menu-content,
   .ui__context-menu-content,
   .ui__select-content {
     --accent: var(--rx-gray-04-hsl);
     --accent-foreground: var(--rx-gray-12-hsl);
+
+    --muted: var(--rx-gray-05-hsl);
+  }
+
+  .ui__calendar {
+    --accent: var(--rx-gray-04-hsl);
+    --accent-foreground: var(--rx-gray-12-hsl);
   }
 
   &:not([data-color=logseq]) {
+    .ui__dropdown-menu-item,
+    div[data-radix-popper-content-wrapper] div[role=menuitem] {
+      &:focus, &:hover {
+        background-color: var(--lx-gray-04, hsl(var(--accent)));
+      }
+    }
+
+
+    div[data-radix-popper-content-wrapper] div[role=menu],
+    .menu-links-wrapper,
+    .menu-links-outer,
+    .absolute-modal[data-modal-name] {
+      background-color: var(--lx-popover-bg, var(--lx-gray-01, hsl(var(--popover))));
+    }
   }
 
   &[data-color=logseq] {
@@ -23,24 +68,48 @@ html {
     }
   }
 
-  .ui__calendar {
-    --accent: var(--rx-gray-04-hsl);
-    --accent-foreground: var(--rx-gray-12-hsl);
+  &[data-color=none] {
+    --ls-block-bullet-color: var(--rx-gray-08);
+    --ls-block-bullet-active-color: var(--rx-gray-12);
+
+    ::selection {
+      @apply bg-primary/20;
+    }
+
+    #ui__ac-inner {
+    }
+
+    .cp__cmdk {
+      --lx-gray-07: var(--rx-gray-07);
+    }
+
+    .ui__toggle {
+      &-background-off {
+        @apply bg-gray-08;
+
+        .switcher {
+          @apply bg-gray-10;
+        }
+      }
+
+      &-background-on {
+        .switcher {
+          @apply bg-gray-03;
+        }
+      }
+    }
+
+    .tl-container {
+      --ls-primary-background-color: hsl(var(--background));
+      --ls-secondary-background-color: var(--rx-gray-01);
+      --ls-tertiary-background-color: var(--rx-gray-03);
+      --ls-quaternary-background-color: var(--rx-gray-05);
+    }
   }
 }
 
 html[data-theme=light] {
-  --accent: var(--rx-gray-04-hsl);
-  --accent-foreground: var(--rx-gray-12-hsl);
-  --input: var(--rx-gray-03-hsl);
-
   &[data-color=logseq] {
-    --primary: 200 97% 37%;
-    --primary-foreground: 255 92% 100%;
-    --accent: 200 97% 37%;
-    --accent-foreground: 255 92% 100%;
-    --ring: 200 97% 37%;
-
     .references-blocks-item {
       --lx-bg-override: var(--rx-gray-03-alpha);
     }
@@ -52,35 +121,34 @@ html[data-theme=light] {
 }
 
 html[data-theme=dark] {
-  --primary-foreground: 255 92% 100%;
-  --background: 0 0% 11%;
-  --foreground: 0 0% 95%;
-  --card: 0 0% 11%;
-  --card-foreground: 0 0% 95%;
-  --secondary: 0 0% 20%;
-  --secondary-foreground: 0 0% 98%;
-  --border: 0 0% 16%;
-  --muted: 0 0% 15%;
-  --popover: 0 0% 7%;
-  --popover-foreground: 0 0 95%;
-  --input: 0 0% 25%;
+  background-color: #161616;
 
   &[data-color=logseq] {
-    --background: 192 100% 11%;
-    --foreground: 0 0% 95%;
-    --accent: 192 80% 10%;
-    --accent-foreground: 255 92% 100%;
-    --primary: 200 97% 37%;
-    --primary-foreground: 255 92% 100%;
-    --ring: 200 97% 37%;
-    --secondary: 203 50% 20%;
-    --secondary-foreground: 0 0% 98%;
-    --muted: 192 100% 13%;
-    --border: 192 100% 16%;
-    --card: 192 100% 10%;
-    --card-foreground: 0 0% 95%;
-    --popover: 192 100% 11%;
-    --input: 203 35% 25%;
+    @apply bg-[#002b36];
+
+    .ui__modal-panel {
+      @apply border-accent-01;
+    }
+
+    .tippy-tooltip {
+      @apply border-accent-02;
+    }
+
+    .references-blocks-item {
+      background-color: var(--lx-gray-03, var(--ls-secondary-background-color));
+    }
+
+    .cp__right-sidebar-inner {
+      .references-blocks-item {
+        background-color: var(--lx-gray-04, var(--ls-tertiary-background-color));
+      }
+    }
+
+    .cp__themes-installed {
+      .it.is-active, .it:hover {
+        @apply bg-accent-01;
+      }
+    }
 
     .ui__button {
       &.as-outline {
@@ -92,6 +160,17 @@ html[data-theme=dark] {
     .ui__context-menu-content,
     .ui__select-content {
       --accent: 190 100% 15%;
+      --muted: 192 100% 13%;
+    }
+
+    .ui__button {
+      &.as-ghost {
+        @apply hover:bg-accent-01;
+      }
+    }
+
+    .menu-separator {
+      @apply opacity-20;
     }
 
     .ui__calendar {
@@ -101,6 +180,34 @@ html[data-theme=dark] {
     .rc-datepicker {
       --accent: 200 97% 37%;
     }
+
+    .cp__cmdk {
+      .border-gray-06,
+      .border-gray-07 {
+        border-color: var(--ls-border-color, var(--lx-gray-06));
+        opacity: .6;
+      }
+
+      > .hints {
+        @apply border-accent-01 bg-gray-02;
+      }
+    }
+
+    .tl-button {
+      &:hover {
+        @apply bg-accent-02;
+      }
+    }
+
+    .cp__header {
+      > .r > div:not(.ui__dropdown-trigger) a.button, button.button {
+        @apply opacity-60 hover:opacity-90;
+      }
+    }
+
+    .form-input {
+      @apply border-accent-01;
+    }
   }
 }
 

+ 1 - 1
resources/forge.config.js

@@ -4,7 +4,7 @@ module.exports = {
   packagerConfig: {
     name: 'Logseq',
     icon: './icons/logseq_big_sur.icns',
-    buildVersion: 79,
+    buildVersion: 80,
     protocols: [
       {
         "protocol": "logseq",

File diff suppressed because it is too large
+ 0 - 0
resources/js/ui.js


+ 1 - 1
resources/package.json

@@ -1,7 +1,7 @@
 {
   "name": "Logseq",
   "productName": "Logseq",
-  "version": "0.10.5",
+  "version": "0.10.6",
   "main": "electron.js",
   "author": "Logseq",
   "license": "AGPL-3.0",

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

@@ -3,7 +3,7 @@
   (:require [clojure.string :as string]
             [frontend.util :as util]))
 
-(def color-list [:tomato :red :crimson :pink :plum :purple :violet :indigo :blue :cyan :teal :green :grass :orange :brown])
+(def color-list [:tomato :red :crimson :pink :plum :purple :violet :indigo :blue :cyan :teal :green :grass :orange])
 ;(def color-list [:tomato :red :blue])
 
 (defn variable

+ 26 - 225
src/main/frontend/common.css

@@ -1,26 +1,3 @@
-:root {
-  --ls-tag-text-opacity: 0.8;
-  --ls-tag-text-hover-opacity: 1;
-  --ls-page-text-size: 1em;
-  --ls-page-title-size: 36px;
-  --ls-main-content-max-width: 810px;
-  --ls-main-content-max-width-wide: 1280px;
-  --ls-font-family: Inter;
-  --ls-scrollbar-width: 6px;
-  --ls-border-radius-low: 4px;
-  --ls-border-radius-medium: 8px;
-  --ls-headbar-height: 3rem;
-  --ls-headbar-inner-top-padding: 0px;
-  --ls-left-sidebar-width: 246px;
-  --ls-left-sidebar-sm-width: 74vw;
-  --ls-left-sidebar-nav-btn-size: 38px;
-  --ls-native-kb-height: 0px;
-  --ls-error-color: var(--color-red-500);
-  --ls-warning-color: var(--color-orange-500);
-  --ls-success-color: var(--color-green-500);
-  --ls-highlight-color-default: var(--ls-secondary-background-color);
-}
-
 @media (prefers-color-scheme: dark) {
   html {
     background-color: #002b36;
@@ -37,164 +14,6 @@
   }
 }
 
-.dark-theme,
-html[data-theme='dark'] {
-  --ls-primary-background-color: #002b36;
-  --ls-secondary-background-color: #023643;
-  --ls-tertiary-background-color: #08404f;
-  --ls-quaternary-background-color: #094b5a;
-  --ls-table-tr-even-background-color: #03333f;
-  --ls-active-primary-color: #8ec2c2;
-  --ls-active-secondary-color: #d0e8e8;
-  --ls-block-properties-background-color: #06323e;
-  --ls-page-properties-background-color: #06323e;
-  --ls-block-ref-link-text-color: #1a6376;
-  --ls-border-color: #0e5263;
-  --ls-secondary-border-color: #126277;
-  --ls-tertiary-border-color: rgba(0, 2, 0, 0.1);
-  --ls-guideline-color: #0b4a5a;
-  --ls-menu-hover-color: var(--ls-secondary-background-color);
-  --ls-primary-text-color: #a4b5b6;
-  --ls-secondary-text-color: #dfdfdf;
-  --ls-title-text-color: #93a1a1;
-  --ls-link-text-color: rgb(138, 187, 187);
-  --ls-link-text-hover-color: var(--ls-active-secondary-color);
-  --ls-link-ref-text-color: var(--ls-link-text-color);
-  --ls-link-ref-text-hover-color: var(--ls-link-text-hover-color);
-  --ls-tag-text-color: var(--ls-link-text-color);
-  --ls-tag-text-hover-color: var(--ls-link-text-hover-color);
-  --ls-slide-background-color: var(--ls-primary-background-color);
-  --ls-block-bullet-border-color: #0f4958;
-  --ls-block-bullet-color: #608e91;
-  --ls-block-highlight-color: #0a3d4b;
-  --ls-selection-background-color: #338fff;
-  --ls-selection-text-color: #fff;
-  --ls-page-checkbox-color: #6093a0;
-  --ls-page-checkbox-border-color: var(--ls-primary-background-color);
-  --ls-page-blockquote-color: var(--ls-primary-text-color);
-  --ls-page-blockquote-bg-color: var(--ls-secondary-background-color);
-  --ls-page-blockquote-border-color: var(--ls-border-color);
-  --ls-page-mark-color: #262626;
-  --ls-page-mark-bg-color: #fef3ac;
-  --ls-page-inline-code-color: var(--ls-primary-text-color);
-  --ls-page-inline-code-bg-color: #01222a;
-  --ls-scrollbar-foreground-color: #11505f;
-  --ls-scrollbar-background-color: rgba(30, 60, 67, 0.1);
-  --ls-scrollbar-thumb-hover-color: rgba(255, 255, 255, 0.2);
-  --ls-cloze-text-color: #8fbc8f;
-  --ls-icon-color: var(--ls-link-text-color);
-  --ls-search-icon-color: var(--ls-primary-text-color);
-  --ls-search-icon-hover-color: var(--ls-secondary-text-color);
-  --ls-a-chosen-bg: var(--ls-quaternary-background-color);
-  --ls-pie-bg-color: #01303b;
-  --ls-pie-fg-color: #0b5869;
-  --ls-highlight-color-gray: var(--color-gray-900);
-  --ls-highlight-color-red: var(--color-red-900);
-  --ls-highlight-color-yellow: var(--color-yellow-900);
-  --ls-highlight-color-green: var(--color-green-900);
-  --ls-highlight-color-blue: var(--color-blue-900);
-  --ls-highlight-color-purple: var(--color-purple-900);
-  --ls-highlight-color-pink: var(--color-pink-900);
-  --ls-error-text-color: var(--color-red-400);
-  --ls-error-background-color: var(--color-red-900);
-  --ls-warning-text-color: var(--color-yellow-400);
-  --ls-warning-background-color: var(--color-yellow-900);
-  --ls-success-text-color: var(--color-green-100);
-  --ls-success-background-color: var(--color-green-900);
-  --ls-focus-ring-color: rgba(18, 98, 119, 0.5);
-  --ls-header-button-background: #dee4ea;
-  --ls-left-sidebar-text-color: var(--lx-gray-11);
-  --ls-button-background-hsl: 200 98% 35%;
-  --ls-button-background: hsl(var(--ls-button-background-hsl));
-  --color-level-1: var(--ls-secondary-background-color);
-  --color-level-2: var(--ls-tertiary-background-color);
-  --color-level-3: var(--ls-quaternary-background-color);
-  --color-level-4: #195d6c;
-  --color-level-5: #266c7d;
-  --color-level-6: #3a7e8e;
-}
-
-/* You should always use .light-theme for light mode, the .white-theme is just for backward compatibility.
-
-See: https://github.com/logseq/logseq/pull/4652. */
-.white-theme,
-.light-theme,
-html[data-theme='light'] {
-  --ls-primary-background-color: #ffffff;
-  --ls-secondary-background-color: #f7f7f7;
-  --ls-tertiary-background-color: #eaeaea;
-  --ls-quaternary-background-color: #dcdcdc;
-  --ls-table-tr-even-background-color: var(--ls-secondary-background-color);
-  --ls-active-primary-color: rgb(0, 105, 182);
-  --ls-active-secondary-color: #00477c;
-  --ls-block-properties-background-color: var(--ls-secondary-background-color);
-  --ls-page-properties-background-color: var(--ls-secondary-background-color);
-  --ls-block-ref-link-text-color: #d8e1e8;
-  --ls-border-color: #ccc;
-  --ls-secondary-border-color: #e2e2e2;
-  --ls-tertiary-border-color: rgba(200, 200, 200, 0.3);
-  --ls-guideline-color: rgba(46, 27, 5, 0.08);
-  --ls-menu-hover-color: var(--ls-a-chosen-bg);
-  --ls-primary-text-color: #433f38;
-  --ls-secondary-text-color: #161e2e;
-  --ls-title-text-color: var(--ls-header-button-background);
-  --ls-link-text-color: #106ba3;
-  --ls-link-text-hover-color: #1a537c;
-  --ls-link-ref-text-color: var(--ls-link-text-color);
-  --ls-link-ref-text-hover-color: var(--ls-link-text-hover-color);
-  --ls-tag-text-color: var(--ls-link-ref-text-color);
-  --ls-tag-text-hover-color: var(--ls-link-ref-text-hover-color);
-  --ls-slide-background-color: #fff;
-  --ls-block-bullet-border-color: #dedede;
-  --ls-block-bullet-color: rgba(67, 63, 56, 0.25);
-  --ls-block-highlight-color: #c0e6fd;
-  --ls-selection-background-color: #e4f2ff;
-  --ls-selection-text-color: var(--ls-secondary-text-color);
-  --ls-page-checkbox-color: #9dbbd8;
-  --ls-page-checkbox-border-color: var(--ls-page-checkbox-color);
-  --ls-page-blockquote-color: var(--ls-primary-text-color);
-  --ls-page-blockquote-bg-color: var(--lx-gray-03, #fbfaf8);
-  --ls-page-blockquote-border-color: var(--lx-gray-08, #799bbc);
-  --ls-page-mark-color: #262626;
-  --ls-page-mark-bg-color: #fef3ac;
-  --ls-page-inline-code-bg-color: var(--ls-secondary-background-color);
-  --ls-page-inline-code-color: var(--ls-primary-text-color);
-  --ls-scrollbar-foreground-color: rgba(0, 0, 0, 0.1);
-  --ls-scrollbar-background-color: rgba(0, 0, 0, 0.05);
-  --ls-scrollbar-thumb-hover-color: rgba(0, 0, 0, 0.2);
-  --ls-cloze-text-color: #0000cd;
-  --ls-icon-color: #646464;
-  --ls-search-icon-color: var(--ls-primary-text-color);
-  --ls-search-icon-hover-color: var(--ls-secondary-text-color);
-  --ls-a-chosen-bg: var(--ls-quaternary-background-color);
-  --ls-pie-bg-color: #e1e1e1;
-  --ls-pie-fg-color: #0a4a5d;
-  --ls-highlight-color-gray: var(--color-gray-100);
-  --ls-highlight-color-red: var(--color-red-100);
-  --ls-highlight-color-yellow: var(--color-yellow-100);
-  --ls-highlight-color-green: var(--color-green-100);
-  --ls-highlight-color-blue: var(--color-blue-100);
-  --ls-highlight-color-purple: var(--color-purple-100);
-  --ls-highlight-color-pink: var(--color-pink-100);
-  --ls-error-text-color: var(--color-red-600);
-  --ls-error-background-color: var(--color-red-100);
-  --ls-warning-text-color: var(--color-yellow-700);
-  --ls-warning-background-color: var(--color-yellow-100);
-  --ls-success-text-color: var(--color-green-800);
-  --ls-success-background-color: var(--color-green-100);
-  --ls-focus-ring-color: rgba(66, 133, 244, 0.5);
-  --ls-header-button-background: rgba(15, 20, 25, 1);
-  --ls-left-sidebar-text-color: var(--lx-gray-12);
-  --ls-button-background-hsl: 200 98% 35%;
-  --ls-button-background: hsl(var(--ls-button-background-hsl));
-  --color-level-1: var(--ls-secondary-background-color);
-  --color-level-2: var(--ls-tertiary-background-color);
-  --color-level-3: var(--ls-quaternary-background-color);
-  --color-level-4: #d0e6fa;
-  --color-level-5: #bbdaf6;
-  --color-level-6: #a7cef1;
-}
-
 html:not(.is-native-android) {
   font-family: var(--ls-font-family), sans-serif, system-ui, -apple-system,
   BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans',
@@ -207,9 +26,9 @@ html {
 }
 
 body {
-  color: or(--ls-default-text-color, --lx-gray-12, --ls-primary-text-color);
+  color: var(--lx-gray-12, var(--ls-primary-text-color, hsl(var(--foreground))));
+  background-color: var(--lx-gray-01, var(--ls-primary-background-color, hsl(var(--background))));
   line-height: 1.5;
-  background-color: transparent;
   min-height: 100%;
   word-break: break-word; /* compatible for overflow-wrap: anywhere */
 }
@@ -264,12 +83,12 @@ body {
 
   a {
     cursor: pointer;
-    color: or(--ls-anchor-link-text-color, --lx-accent-11, --ls-link-text-color, #045591);
+    color: var(--lx-accent-11, var(--ls-link-text-color, hsl(var(--primary)/.8)));
     text-decoration: none;
   }
 
   a:hover {
-    color: or(--ls-anchor-link-text-color-hover, --lx-accent-12, --ls-link-text-hover-color, #000);
+    color: var(--lx-accent-12, var(--ls-link-text-hover-color, hsl(var(--primary))));
   }
 
   code {
@@ -296,21 +115,13 @@ body {
     text-indent: 0;
     padding: 8px 20px;
     border-left: 4px solid;
-    border-left-color: var(--ls-page-blockquote-border-color, #d3d3d3);
-    background-color: var(--ls-page-blockquote-bg-color, #f7f7f7);
+    border-left-color: var(--ls-page-blockquote-border-color, hsl(var(--primary)/.4));
+    background-color: var(--ls-page-blockquote-bg-color, hsl(var(--secondary)));
+    color: var(--ls-page-blockquote-color, hsl(var(--secondary-foreground)));
     margin: 1rem 0;
-    color: var(--ls-page-blockquote-color, #24292e);
     font-size: 1rem;
   }
 
-
-  input[type='text'],
-  input[type='password'] {
-    color: var(--ls-primary-text-color);
-    background: transparent;
-    font-size: inherit;
-  }
-
   summary {
     outline: none;
   }
@@ -454,8 +265,7 @@ li p:last-child,
 }
 
 .admonition-icon {
-  border-right: 1px solid;
-  border-right-color: var(--ls-border-color, #ccc);
+  @apply border-r;
 }
 
 i.ti {
@@ -526,10 +336,10 @@ i.ti {
 
 /* region FIXME: override elements (?) */
 h1.title, h1.title input {
-  margin-bottom: 1.5rem;
-  color: or(--ls-journal-title-color, --lx-gray-12, --ls-title-text-color, #222);
-  font-size: var(--ls-page-title-size, 36px);
-  font-weight: 500;
+  @apply mb-4 font-medium;
+
+  color: var(--lx-gray-12, var(--ls-title-text-color, hsl(var(--foreground))));
+  font-size: var(--ls-page-title-size, 32px);
 }
 
 .title .page-icon {
@@ -539,7 +349,7 @@ h1.title, h1.title input {
 .block-highlight,
 .content .selected {
   transition: background-color 0.2s cubic-bezier(0, 1, 0, 1);
-  background-color: var(--ls-block-highlight-color);
+  background-color: var(--ls-block-highlight-color, var(--rx-gray-04));
   padding: -1px;
 }
 
@@ -573,16 +383,14 @@ button.menu {
 .menu-link:hover,
 button.pull:hover,
 button.menu:focus {
-  background-color: or(--ls-settings-list-item-hover-background-color, --lx-gray-05, --ls-menu-hover-color, #f4f5f7);
+  background-color: or(--lx-gray-05, --ls-menu-hover-color, --rx-gray-05);
 }
 
 .menu-links-wrapper,
 .menu-links-outer {
-  @apply py-2 rounded-md shadow-lg overflow-y-auto;
+  @apply py-2 rounded-md shadow-lg overflow-y-auto border bg-popover min-w-[12rem];
 
   max-height: calc(100vh - 100px) !important;
-  background-color: or(--ls-settings-dropdown-background, --lx-gray-03, --ls-primary-background-color, #fff);
-  min-width: 12rem;
 }
 
 .menu-backdrop {
@@ -592,17 +400,12 @@ button.menu:focus {
 }
 
 .menu-link {
-  background-color: or(--ls-settings-dropdown-link-item-background, --lx-gray-03, --ls-primary-background-color, #fff);
-  color: or(--ls-settings-dropdown-link-text-color, --lx-gray-11, --ls-primary-text-color);
-  user-select: none;
+  @apply text-popover-foreground/75 select-none hover:text-popover-foreground/100;
+  @apply text-sm px-2 py-1.5 mx-1 hover:rounded transition-opacity duration-150;
 }
 
 .menu-separator {
-  @apply my-1;
-
-  opacity: 0.5;
-  border-top-width: 1px;
-  border-color: var(--ls-border-color, #ccc);
+  @apply my-1 opacity-50 border-t;
 }
 
 a.login {
@@ -686,7 +489,7 @@ a.tag {
   cursor: pointer;
   padding: 0 2px;
   border-radius: 4px;
-  color: or(--ls-tag-text, --lx-accent-11, --ls-tag-text-color, #045591);
+  color: var(--lx-accent-11, var(--ls-tag-text-color, hsl(var(--primary))));
   opacity: var(--ls-tag-text-opacity, 0.7);
 }
 
@@ -696,11 +499,11 @@ a.tag:hover {
 }
 
 svg.note {
-  color: var(--ls-primary-text-color, #19407c);
+  color: var(--rx-yellow-08);
 }
 
 svg.tip {
-  color: var(--ls-active-primary-color);
+  color: var(--lx-accent-08, var(--rx-blue-08));
 }
 
 /* endregion */
@@ -723,7 +526,7 @@ svg.tip {
 
 hr {
   margin: 2rem 0;
-  border-color: var(--ls-border-color, #ccc);
+  border-color: var(--lx-gray-05, var(--ls-border-color, var(--rx-gray-05)));
 }
 
 .resize {
@@ -802,10 +605,8 @@ mark {
   font-family: MonoLisa, 'Fira Code', Monaco, Menlo, Consolas, 'COURIER NEW',
   monospace;
   letter-spacing: 0;
-  background-color: or(--ls-inline-code-background, --lx-gray-06, --ls-page-inline-code-bg-color, #eee);
-  color: or(--ls-inline-code-text, --lx-gray-11, --ls-page-inline-code-color);
-  background-color: var(--ls-page-inline-code-bg-color, #eee);
-  color: var(--ls-page-inline-code-color);
+  background-color: var(--lx-gray-06, var(--ls-page-inline-code-bg-color, var(--rx-gray-05)));
+  color: var(--lx-gray-11, var(--ls-page-inline-code-color, var(--rx-gray-11)));
   text-rendering: optimizeSpeed;
 }
 
@@ -840,11 +641,11 @@ a.tooltip-priority {
 }
 
 .page-reference:hover {
-  background: or(--ls-page-reference-hover-background, --lx-accent-04-alpha, --ls-secondary-background-color);
+  background: var(--lx-accent-04-alpha, var(--ls-secondary-background-color, hsl(var(--primary)/.4)));
 }
 
 .references-blocks .page-reference:hover {
-  background: or(--ls-page-reference-block-hover-background, --lx-accent-04-alpha, --ls-tertiary-background-color);
+  background: var(--lx-accent-04-alpha, var(--ls-tertiary-background-color, hsl(var(--primary)/.4)));
 }
 
 #head .fade-link {

+ 6 - 4
src/main/frontend/components/block.cljs

@@ -2,7 +2,6 @@
   (:refer-clojure :exclude [range])
   (:require-macros [hiccups.core])
   (:require ["/frontend/utils" :as utils]
-            ["@capacitor/share" :refer [^js Share]]
             [cljs-bean.core :as bean]
             [cljs.core.match :refer [match]]
             [cljs.reader :as reader]
@@ -48,6 +47,7 @@
             [frontend.handler.whiteboard :as whiteboard-handler]
             [frontend.handler.export.common :as export-common-handler]
             [frontend.mobile.util :as mobile-util]
+            [frontend.mobile.intent :as mobile-intent]
             [frontend.modules.outliner.tree :as tree]
             [frontend.security :as security]
             [frontend.shui :refer [get-shui-component-version make-shui-context]]
@@ -395,8 +395,7 @@
                          (let [[rel-dir basename] (util/get-dir-and-basename href)
                                rel-dir (string/replace rel-dir #"^/+" "")
                                asset-url (path/path-join repo-dir rel-dir basename)]
-                           (.share Share (clj->js {:url asset-url
-                                                   :title "Open file with your favorite app"})))))]
+                           (mobile-intent/open-or-share-file asset-url))))]
 
         (cond
           (contains? config/audio-formats ext)
@@ -1353,7 +1352,10 @@
                   url)]
         (if (and (coll? src)
                  (= (first src) "youtube-player"))
-          (youtube/youtube-video (last src) nil)
+          (let [t (re-find #"&t=(\d+)" url)
+                opts (when (seq t)
+                       {:start (nth t 1)})]
+            (youtube/youtube-video (last src) opts))
           (when src
             (let [width (min (- (util/get-width) 96) 560)
                   height (int (* width (/ (if (string/includes? src "player.bilibili.com")

+ 29 - 23
src/main/frontend/components/block.css

@@ -180,15 +180,21 @@
   position: absolute;
   border-radius: 2px;
   opacity: 0.6;
-}
+  border-left-color: var(--lx-gray-09, var(--ls-border-color, var(--rx-gray-09)));
+
+  &:hover {
+    background-color: var(--lx-gray-10, var(--ls-primary-text-color, var(--rx-gray-10)));
+    opacity: .7;
+  }
 
-.block-children-left-border:hover {
-  background-color: or(--ls-block-left-color, --lx-gray-11, --ls-primary-text-color);
+  &:active {
+    opacity: 1;
+  }
 }
 
 .block-children {
   border-left: 1px solid;
-  border-left-color: or(--lx-gray-04-alpha, --ls-guideline-color, #ddd) !important;
+  border-left-color: var(--lx-gray-04-alpha, var(--ls-guideline-color, var(--rx-gray-04-alpha))) !important;
 
   padding-top: 2px;
   padding-bottom: 3px;
@@ -296,12 +302,10 @@
 }
 
 .page-ref {
-  --lx-text-override: var(--ls-link-ref-text-color);
-  @apply text-accent-11;
+  color: var(--lx-accent-11, var(--ls-link-text-color, hsl(var(--primary)/.8)));
 
   &:hover {
-    --lx-text-override: var(--ls-link-ref-text-hover-color);
-    @apply text-accent-12;
+    color: var(--lx-accent-11, var(--ls-link-text-color, hsl(var(--primary))));
   }
 }
 
@@ -339,14 +343,10 @@
   }
 }
 
-.block-properties {
+.block-properties, .page-properties {
   margin: 4px 0;
   padding: 4px 8px;
-  background-color: var(--ls-block-properties-background-color, #f0f8ff);
-}
-
-.page-properties {
-  background-color: var(--ls-page-properties-background-color);
+  background-color: var(--lx-gray-03, var(--ls-block-properties-background-color, var(--rx-gray-03)));
 }
 
 .block-marker {
@@ -376,9 +376,9 @@
     border-radius: 2px;
   }
 
-  /* .bullet-container .selected { */
-  /*   border: 3px solid; */
-  /* } */
+  span.bullet-container:not(.as-order-list) .selected {
+    border: 3px solid;
+  }
 }
 
 .ls-block,
@@ -479,10 +479,10 @@
 }
 
 .color-level {
-  background-color: or(--ls-right-sidebar-content-background, --lx-gray-02, --color-level-1);
+  background-color: var(--lx-gray-02, var(--color-level-1, var(--rx-gray-02)));
 
   .dark & {
-    background-color: or(--ls-right-sidebar-content-background, --lx-gray-01, --color-level-1);
+    background-color: var(--lx-gray-01, var(--color-level-1, var(--rx-gray-01)));
   }
 
   & .color-level {
@@ -576,11 +576,11 @@
 
   &:not(.typed-list) {
     &.bullet-closed {
-      background-color: or(--ls-bullet-closed-background, --lx-gray-04-alpha, --ls-block-bullet-border-color, #ced9e0);
+      background-color: var(--lx-gray-04-alpha, var(--ls-block-bullet-border-color, var(--rx-gray-04-alpha)));
     }
 
     .bullet {
-      background-color: or(--lx-gray-08, --ls-block-bullet-color, #394b59);
+      background-color: var(--lx-gray-08, var(--ls-block-bullet-color, var(--rx-gray-08)));
       transition: transform 0.2s;
     }
   }
@@ -599,11 +599,11 @@
   color: var(--ls-primary-text-color);
 
   &:hover > .bullet-container:not(.typed-list) {
-    background-color: or(--lx-gray-04-alpha, --ls-block-bullet-border-color, #ced9e0);
+    background-color: var(--lx-gray-04-alpha, var(--ls-block-bullet-border-color, var(--rx-gray-04-alpha)));
 
     .bullet {
       transform: scale(1.2);
-      background-color: or(--lx-gray-08, --ls-block-bullet-color, inherit) !important;
+      background-color: var(--lx-gray-08, var(--ls-block-bullet-color, var(--rx-gray-08))) !important;
     }
   }
 }
@@ -758,3 +758,9 @@ html.is-mac {
     @apply bg-gray-03 rounded p-4;
   }
 }
+
+.cp__right-sidebar-inner {
+  .references-blocks-item {
+    @apply bg-gray-04;
+  }
+}

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

@@ -824,9 +824,7 @@
                       {:on-click #(handle-action action (assoc state :opts opts) %)
                        :muted    true}))]
     (when action
-      [:div {:class "flex w-full px-3 py-2 gap-2 justify-between"
-             :style {:background "var(--lx-gray-03)"
-                     :border-top "1px solid var(--lx-gray-07)"}}
+      [:div.hints
        [:div.text-sm.leading-6
         [:div.flex.flex-row.gap-1.items-center
          [:div.font-medium.text-gray-12 "Tip:"]

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

@@ -0,0 +1,6 @@
+.cp__cmdk {
+  > .hints {
+    @apply flex w-full px-3 py-2 gap-2 justify-between
+    bg-gray-03 border-t border-gray-05;
+  }
+}

+ 2 - 3
src/main/frontend/components/command_palette.css

@@ -16,7 +16,6 @@
     .menu-link {
       transition: none;
       border: none;
-      border-radius: unset !important;
       background: none;
 
       .type-icon {
@@ -40,13 +39,13 @@
 
       &.chosen,
       &.chosen p {
-        background-color: or(--ls-cp-chosen, --lx-gray-03, --ls-a-chosen-bg);
+        background-color: var(--lx-gray-03, var(--ls-a-chosen-bg, var(--rx-gray-03)));
         color: var(--ls-secondary-text-color);
       }
 
       .dark &.chosen,
       .dark &.chosen p {
-        background-color: or(--ls-cp-chosen, --lx-gray-02, --ls-a-chosen-bg);
+        background-color: var(--lx-gray-02, var(--ls-a-chosen-bg, var(--rx-gray-02)));
       }
 
       &:hover p {

+ 55 - 35
src/main/frontend/components/container.cljs

@@ -92,10 +92,46 @@
                            (state/sidebar-add-block!
                              (state/get-current-repo)
                              (:db/id page-entity)
-                             :page))]
+                             :page))
+        x-menu-content (fn [type opts]
+                         (let [dropdown? (= type :dropdown)
+                               x-menu-content (if dropdown? shui/dropdown-menu-content shui/context-menu-content)
+                               x-menu-item (if dropdown? shui/dropdown-menu-item shui/context-menu-item)
+                               x-menu-shortcut (if dropdown? shui/dropdown-menu-shortcut shui/context-menu-shortcut)]
+                           (x-menu-content
+                             (merge {:class "w-60"} opts)
+                             (when-not recent?
+                               (x-menu-item
+                                 {:on-click #(page-handler/unfavorite-page! original-name)}
+                                 (ctx-icon "star-off")
+                                 (t :page/unfavorite)
+                                 (x-menu-shortcut (when-let [binding (shortcut-dh/shortcut-binding :command/toggle-favorite)]
+                                                    (some-> binding
+                                                            (first)
+                                                            (shortcut-utils/decorate-binding))))))
+                             (when-let [page-fpath (and (util/electron?) file-rpath
+                                                     (config/get-repo-fpath (state/get-current-repo) file-rpath))]
+                               [:<>
+                                (x-menu-item
+                                  {:on-click #(ipc/ipc :openFileInFolder page-fpath)}
+                                  (ctx-icon "folder")
+                                  (t :page/open-in-finder))
+
+                                (x-menu-item
+                                  {:on-click #(js/window.apis.openPath page-fpath)}
+                                  (ctx-icon "file")
+                                  (t :page/open-with-default-app))])
+
+                             (x-menu-item
+                               {:on-click open-in-sidebar}
+                               (ctx-icon "layout-sidebar-right")
+                               (t :content/open-in-sidebar)
+                               (x-menu-shortcut (shortcut-utils/decorate-binding "shift+click"))))))]
+
+    ;; TODO: move to standalone component
     (shui/context-menu
       (shui/context-menu-trigger
-        [:a.flex.items-center
+        [:a.flex.items-center.justify-between.relative.group
          {:on-click
           (fn [e]
             (let [name (if (empty? source-page) name (:block/name source-page))]
@@ -107,34 +143,22 @@
          [:span.page-icon.ml-3.justify-center (if whiteboard-page? (ui/icon "whiteboard" {:extension? true}) icon)]
          [:span.page-title {:class (when untitled? "opacity-50")}
           (if untitled? (t :untitled)
-                        (pdf-utils/fix-local-asset-pagename original-name))]]
-        (shui/context-menu-content
-          {:class "w-60"}
-          (when-not recent?
-            (shui/context-menu-item
-              {:on-click #(page-handler/unfavorite-page! original-name)}
-              (ctx-icon "star-off")
-              (t :page/unfavorite)
-              (shui/context-menu-shortcut (some-> (shortcut-dh/shortcut-binding :command/toggle-favorite) (first)
-                                            (shortcut-utils/decorate-binding)))))
-          (when-let [page-fpath (and (util/electron?) file-rpath
-                                  (config/get-repo-fpath (state/get-current-repo) file-rpath))]
-            [:<>
-             (shui/context-menu-item
-               {:on-click #(ipc/ipc :openFileInFolder page-fpath)}
-               (ctx-icon "folder")
-               (t :page/open-in-finder))
-
-             (shui/context-menu-item
-               {:on-click #(js/window.apis.openPath page-fpath)}
-               (ctx-icon "file")
-               (t :page/open-with-default-app))])
-
-          (shui/context-menu-item
-            {:on-click open-in-sidebar}
-            (ctx-icon "layout-sidebar-right")
-            (t :content/open-in-sidebar)
-            (shui/context-menu-shortcut (shortcut-utils/decorate-binding "shift+click"))))))))
+                        (pdf-utils/fix-local-asset-pagename original-name))]
+
+         ;; dots trigger
+         (shui/dropdown-menu
+           (shui/dropdown-menu-trigger
+             (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"
+                :on-click #(util/stop %)}
+               [:i.relative {:style {:top "1px"}} (shui/tabler-icon "dots")]))
+           ;; menu content
+           (x-menu-content :dropdown {:align "start"}))]
+
+        ;; menu content
+        (x-menu-content :context nil)))))
 
 (defn get-page-icon [page-entity]
   (let [default-icon (ui/icon "page" {:extension? true})
@@ -755,11 +779,7 @@
         links (state/sub :custom-context-menu/links)
         position (state/sub :custom-context-menu/position)]
     (when (and show? links position)
-      (ui/css-transition
-       {:class-names "fade"
-        :timeout {:enter 500
-                  :exit 300}}
-       (render-custom-context-menu links position)))))
+      (render-custom-context-menu links position))))
 
 (rum/defc new-block-mode < rum/reactive
   []

+ 24 - 55
src/main/frontend/components/container.css

@@ -5,25 +5,12 @@
   }
 }
 
-#app-container {
-  background-color: or(--ls-top-bar-background, --lx-gray-01, --ls-primary-background-color, #fff);
-  position: relative;
-}
-
-.dark #app-container {
-  background-color: or(--ls-top-bar-background, --lx-gray-02, --ls-primary-background-color, #fff);
-}
-
 #root {
-  > div {
-    color: or(--ls-document-text-color, --lx-gray-12, --ls-primary-text-color, #24292e);
-    font-size: var(--ls-page-text-size);
-  }
+  font-size: var(--ls-page-text-size);
 }
 
 #app-container {
-  display: flex;
-  flex: 0 0 100%;
+  @apply flex basis-full;
 }
 
 #skip-to-main {
@@ -79,13 +66,7 @@
   overflow: auto;
 }
 
-.dark .left-sidebar-inner {
-  --left-sidebar-bg-color: or(--ls-left-sidebar-background, --lx-gray-02, --ls-secondary-background-color);
-}
-
 .left-sidebar-inner {
-  --left-sidebar-bg-color: or(--ls-left-sidebar-background-color, --lx-gray-02, --ls-primary-background-color);
-
   position: relative;
   height: 100%;
   padding-top: 12px;
@@ -182,8 +163,8 @@
     }
 
     &:hover, &.active {
-      background-color: or(--ls-left-sidebar-active-background, --lx-gray-04, --color-level-3);
-      color: or(--ls-left-sidebar-active-text-color, --lx-gray-12);
+      background-color: var(--lx-gray-04, var(--color-level-3, var(--rx-gray-04)));
+      color: var(--lx-gray-12, var(--rx-gray-12));
 
       .ui__icon {
         opacity: .9;
@@ -216,7 +197,6 @@
       @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);
 
       .ui__icon {
         @apply flex justify-center;
@@ -229,7 +209,7 @@
       }
 
       &:hover {
-        background-color: or(--ls-nav-item-hover, --lx-gray-04, --ls-tertiary-background-color);
+        background-color: var(--lx-gray-04, var(--ls-tertiary-background-color, var(--rx-gray-04)));
 
         * {
           opacity: 1 !important;
@@ -287,9 +267,8 @@
           }
 
           &:hover {
-            background-color: or(--ls-recessed-nav-item-hover, --lx-gray-04, --ls-quaternary-background-color);
+            background-color: var(--lx-gray-04, var(--ls-quaternary-background-color, var(--rx-gray-04)));
             opacity: 1;
-            background-color: var(--ls-quaternary-background-color);
           }
         }
       }
@@ -367,16 +346,14 @@
     }
   }
 
-  @screen sm {
-    --left-sidebar-bg-color: or(--ls-left-sidebar-background, --lx-gray-02, --ls-secondary-background-color);
+  .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);
 
-    .dark & {
-      --left-sidebar-bg-color: or(--ls-left-sidebar-background, --lx-gray-02, --ls-secondary-background-color);
-    }
-
     > .wrap {
       margin-top: 52px;
     }
@@ -390,18 +367,15 @@
 }
 
 .cp__sidebar-left-layout {
-  position: fixed;
-  top: 0;
+  @apply fixed top-0 left-0 w-[10px];
 
-  left: 0;
   z-index: var(--ls-z-index-level-5);
-  width: 10px;
 
   a {
     @apply opacity-90 hover:opacity-100;
     transition: all 120ms ease-out;
 
-    color: or(--ls-left-sidebar-text-color, --ls-header-button-background);
+    color: var(--ls-left-sidebar-text-color, var(--ls-header-button-background));
   }
 
   > .left-sidebar-inner {
@@ -560,14 +534,6 @@
   /* box-shadow: inset 0 0 0 1px var(--ls-border-color); */
 }
 
-.cp__sidebar-main-layout {
-  background-color: or(--ls-main-content-background, --lx-gray-01, --ls-primary-background-color);
-}
-
-.dark .cp__sidebar-main-layout {
-  background-color: or(--ls-main-content-background, --lx-gray-02, --ls-primary-background-color);
-}
-
 .cp__sidebar-main-content {
   width: 100%;
   max-width: var(--ls-main-content-max-width);
@@ -594,16 +560,14 @@
       @apply rounded-full h-8 w-8 flex items-center justify-center
       font-bold select-none cursor-pointer;
 
-      background-color: or(--ls-left-sidebar-help-background, --lx-gray-01, --ls-secondary-background-color);
+      background-color: var(--lx-gray-02, var(--ls-secondary-background-color, var(--rx-gray-02)));
     }
   }
 
   &-menu-popup {
-    @apply fixed bottom-14 right-8 z-10 border
-    rounded-lg min-w-[260px] shadow;
+    @apply fixed bottom-14 right-8 z-10 border rounded-lg min-w-[260px] shadow;
 
-    background-color: var(--ls-secondary-background-color);
-    border-color: var(--ls-border-color);
+    background-color: var(--ls-secondary-background-color, var(--rx-gray-03));
 
     > .list-wrap {
       @apply flex flex-col pt-3;
@@ -612,7 +576,7 @@
         color: var(--ls-primary-text-color);
 
         &:active, &:hover {
-          background-color: var(--ls-tertiary-background-color);
+          background-color: var(--ls-tertiary-background-color, var(--rx-gray-05));
         }
       }
     }
@@ -664,6 +628,7 @@
 
   &-inner {
     padding-top: 0;
+    background-color: var(--lx-gray-02, var(--ls-secondary-background-color));
   }
 
   &-settings {
@@ -734,9 +699,7 @@
   }
 
   .sidebar-item {
-    @apply relative;
-    flex: 1 1;
-    min-height: 100px;
+    @apply relative flex-1 min-h-[100px];
 
     .sidebar-item-header {
       .breadcrumb {
@@ -851,4 +814,10 @@ html[data-theme='dark'] {
       background-color: rgba(0, 0, 0, .15);
     }
   }
+
+  .cp__right-sidebar {
+    .sidebar-item {
+      background-color: var(--lx-gray-03, var(--ls-secondary-background-color));
+    }
+  }
 }

+ 1 - 6
src/main/frontend/components/content.css

@@ -12,13 +12,8 @@
 }
 
 #custom-context-menu {
-  @apply transition ease-out duration-100 transform
-  opacity-100 scale-100 absolute;
+  @apply absolute animate-in zoom-in-95;
 
   z-index: calc(var(--ls-z-index-level-1) + 1);
   width: 270px;
-
-  a:hover {
-    color: var(--ls-primary-text-color);
-  }
 }

+ 0 - 2
src/main/frontend/components/datepicker.css

@@ -49,10 +49,8 @@
   top: 100px;
   left: 20px;
   padding: 10px;
-  margin-top: 1px;
   line-height: 16px;
   border-radius: 4px;
-  background: #efefef;
 }
 
 .datepicker table {

+ 12 - 0
src/main/frontend/components/editor.css

@@ -41,6 +41,10 @@
     transform: translateY(calc(-100% - 2rem));
   }
 
+  &[data-modal-name] {
+    @apply bg-popover border overflow-x-hidden overflow-y-auto rounded-lg py-1;
+  }
+
   &[data-modal-name="commands"],
   &[data-modal-name="select-code-block-mode"] {
     @screen sm {
@@ -48,6 +52,14 @@
       max-width: 90vw !important;
     }
   }
+
+  &[data-modal-name="date-picker"] {
+    @apply py-0;
+
+    .rc-datepicker {
+      @apply my-0;
+    }
+  }
 }
 
 .is-mobile {

+ 42 - 39
src/main/frontend/components/file_sync.cljs

@@ -305,9 +305,10 @@
               (calc-time-left))]]])
 
        [:div.c
+        {:class (when waiting? "pt-2")}
         (second tip-b&p)
         (when (or history-files? (not no-active-files?))
-          [:span.inline-flex.ml-1.active:opacity-50
+          [:span.inline-flex.pl-2.active:opacity-50
            {:on-click #(set-list-active? (not list-active?))}
            (if list-active?
              (ui/icon "chevron-up" {:style {:font-size 24}})
@@ -522,47 +523,49 @@
 
 (rum/defc pick-local-graph-for-sync [graph]
   [:div.cp__file-sync-related-normal-modal
-   [:div.flex.justify-center.pb-4 [:span.icon-wrap (ui/icon "cloud-download")]]
+   [:div.flex.justify-center.pb-4
+    [:span.icon-wrap (ui/icon "cloud-download" {:size 22})]]
 
-   [:h1.mb-5.text-2xl.text-center.font-bold (util/format "Sync graph \"%s\" to local"
-                                                         (:GraphName graph))]
+   [:h1.mb-5.text-2xl.text-center.font-bold
+    (util/format "Sync graph \"%s\" to local" (:GraphName graph))]
 
    (ui/button
-    "Open a local directory"
-    :class "block w-full py-4 mt-4"
-    :on-click #(do
-                 (state/close-modal!)
-                 (fs-sync/<sync-stop)
-                 (->
-                  (page-handler/ls-dir-files!
-                   (fn [{:keys [url]}]
-                     (file-sync-handler/init-remote-graph url graph)
-                     (js/setTimeout (fn [] (repo-handler/refresh-repos!)) 200))
-
-                   {:on-open-dir
-                    (fn [result]
-                      (prn ::on-open-dir result)
-                      (let [empty-dir? (not (seq (:files result)))
-                            root (:path result)]
-                        (cond
-                          (string/blank? root)
-                          (p/rejected (js/Error. nil))  ;; cancel pick a directory
-
-                          empty-dir?
-                          (p/resolved nil)
-
-                          :else ; dir is not empty
-                          (-> (if (util/electron?)
-                                (ipc/ipc :readGraphTxIdInfo root)
-                                (fs-util/read-graphs-txid-info root))
-                              (p/then (fn [^js info]
-                                        (when (or (nil? info)
-                                                  (nil? (second info))
-                                                  (not= (second info) (:GraphUUID graph)))
-                                          (if (js/confirm "This directory is not empty, are you sure to sync the remote graph to it? Make sure to back up the directory first.")
-                                            (p/resolved nil)
-                                            (p/rejected (js/Error. nil))))))))))}) ;; cancel pick a non-empty directory
-                  (p/catch (fn [])))))
+     "Open a local directory"
+     :class "block w-full mt-4"
+     :size :lg
+     :on-click #(do
+                  (state/close-modal!)
+                  (fs-sync/<sync-stop)
+                  (->
+                    (page-handler/ls-dir-files!
+                      (fn [{:keys [url]}]
+                        (file-sync-handler/init-remote-graph url graph)
+                        (js/setTimeout (fn [] (repo-handler/refresh-repos!)) 200))
+
+                      {:on-open-dir
+                       (fn [result]
+                         (prn ::on-open-dir result)
+                         (let [empty-dir? (not (seq (:files result)))
+                               root (:path result)]
+                           (cond
+                             (string/blank? root)
+                             (p/rejected (js/Error. nil))   ;; cancel pick a directory
+
+                             empty-dir?
+                             (p/resolved nil)
+
+                             :else                          ; dir is not empty
+                             (-> (if (util/electron?)
+                                   (ipc/ipc :readGraphTxIdInfo root)
+                                   (fs-util/read-graphs-txid-info root))
+                               (p/then (fn [^js info]
+                                         (when (or (nil? info)
+                                                 (nil? (second info))
+                                                 (not= (second info) (:GraphUUID graph)))
+                                           (if (js/confirm "This directory is not empty, are you sure to sync the remote graph to it? Make sure to back up the directory first.")
+                                             (p/resolved nil)
+                                             (p/rejected (js/Error. nil))))))))))}) ;; cancel pick a non-empty directory
+                    (p/catch (fn [])))))
 
    [:div.text-xs.opacity-50.px-1.flex-row.flex.items-center.p-2
     (ui/icon "alert-circle")

+ 3 - 8
src/main/frontend/components/file_sync.css

@@ -1,7 +1,7 @@
 :root {
-  --ls-color-file-sync-error: var(--ls-error-color);
-  --ls-color-file-sync-pending: var(--color-yellow-500);
-  --ls-color-file-sync-idle: var(--ls-success-color);
+  --ls-color-file-sync-error: var(--color-red-600);
+  --ls-color-file-sync-pending: var(--color-yellow-600);
+  --ls-color-file-sync-idle: var(--color-green-600);
 }
 
 .cp__file-sync {
@@ -96,11 +96,6 @@
         font-size: 13px;
         padding: 6px 20px;
 
-        > div.flex > div {
-          margin: 0 !important;
-          flex: 1;
-        }
-
         &.is-first-placeholder {
           padding: 0 !important;
           user-select: none;

+ 39 - 19
src/main/frontend/components/git.cljs

@@ -1,6 +1,7 @@
 (ns frontend.components.git
   (:require [rum.core :as rum]
             [frontend.ui :as ui]
+            [promesa.core :as p]
             [frontend.util :as util]
             [clojure.string :as string]
             [frontend.handler.shell :as shell]
@@ -36,23 +37,42 @@
 
      [:div.mt-5.sm:mt-4.flex
       (ui/button
-        "Submit"
-        {:on-click (fn []
-                     (let [username @username
-                           email @email]
-                       (when (and (not (string/blank? username))
-                                  (not (string/blank? email)))
-                         (shell/set-git-username-and-email username email))))})]]))
+       "Submit"
+       {:on-click (fn []
+                    (let [username @username
+                          email @email]
+                      (when (and (not (string/blank? username))
+                                 (not (string/blank? email)))
+                        (shell/set-git-username-and-email username email))))})]]))
 
-(rum/defc file-specific-version
-  [path hash content]
-  [:div.w-full.sm:max-w-lg {:style {:width 700}}
-   [:div.font-bold.mb-4 (str path (util/format " (%s)" hash)) ]
-   [:pre content]
-   (ui/button "Revert"
-     :on-click (fn []
-                 (file/alter-file (state/get-current-repo)
-                                  path
-                                  content
-                                  {:re-render-root? true
-                                   :skip-compare? true})))])
+(rum/defc file-version-selector
+  [versions path get-content]
+  (let
+   [[content set-content!] (rum/use-state  nil)
+    [hash  set-hash!] (rum/use-state   "HEAD")]
+    (rum/use-effect! (fn [] (p/let [c (get-content hash path)] (set-content! c)) [hash path]))
+    [:div.flex
+     [:div.overflow-y-auto {:class "w-48 max-h-[calc(85vh_-_4rem)] "}
+      [:div.font-bold "File history - " path]
+      (for [line  versions]
+        (let [[hash title time] (string/split line "$$$")
+              hash (subs hash 8)]
+          [:div.my-4 {:key hash}
+           [:hr]
+           [:div.mb-2
+            [:a.font-medium.mr-1.block
+             {:on-click (fn []  (set-hash!  hash))}
+             hash]
+            title]
+           [:div.opacity-50 time]]))]
+     [:div.flex-1.p-4
+      [:div.w-full.sm:max-w-lg {:style {:width 700}}
+       [:div.font-bold.mb-4 (str path (util/format " (%s)" hash))]
+       [:pre content]
+       (ui/button "Revert"
+                  :on-click (fn []
+                              (file/alter-file (state/get-current-repo)
+                                               path
+                                               content
+                                               {:re-render-root? true
+                                                :skip-compare? true})))]]]))

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

@@ -28,7 +28,7 @@
   < {:key-fn #(identity "home-button")}
   []
   (ui/with-shortcut :go/home "left"
-    [:button.button.icon.inline
+    [:button.button.icon.inline.mx-1
      {:title (t :home)
       :on-click #(do
                    (when (mobile-util/native-iphone?)
@@ -47,7 +47,7 @@
                   logged?
                   (not sync-enabled?))
       [:span.flex.space-x-2
-       [:a.button.text-sm.font-medium.block
+       [:a.button.text-sm.font-medium.block.text-gray-11
         {:on-click #(state/pub-event! [:user/login])}
         [:span (t :login)]
         (when loading?

+ 3 - 12
src/main/frontend/components/header.css

@@ -178,22 +178,13 @@
 }
 
 .button {
-  @apply h-8 px-2.5 py-1 rounded-md opacity-60;
-  display: block;
-  user-select: none;
-
-  &:hover, &.active {
-    opacity: 1;
-    background: none;
+  @apply h-8 px-2.5 py-1 rounded-md opacity-90 block select-none hover:opacity-100 active:opacity-80;
 
+  &:hover {
     @screen md {
-      background: or(--ls-header-button-hover, --lx-gray-04, --ls-tertiary-background-color);
+      background: var(--lx-gray-04, var(--ls-tertiary-background-color, var(--rx-gray-04)));
     }
   }
-
-  &:active {
-    opacity: .7;
-  }
 }
 
 .button.icon {

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

@@ -7,7 +7,7 @@
 
   .journal-item {
     border-top: 1px solid;
-    border-top-color: or(--ls-journal-page-rule, --lx-gray-07, --ls-border-color, #738694);
+    border-top-color: var(--lx-gray-07, var(--ls-border-color, var(--rx-gray-06)));
     margin: 24px 0;
     padding: 24px 0;
     min-height: 250px;

+ 6 - 16
src/main/frontend/components/onboarding/index.css

@@ -32,7 +32,6 @@ body[data-page=import] {
           &:first-child {
             display: block;
             text-align: center;
-            padding-bottom: 15px;
           }
         }
       }
@@ -53,7 +52,7 @@ body[data-page=import] {
           flex: 1;
 
           &.a {
-            background-color: var(--ls-tertiary-background-color);
+            background-color: var(--ls-tertiary-background-color, var(--rx-gray-03));
             display: flex;
             flex-direction: column;
             align-items: center;
@@ -172,8 +171,7 @@ body[data-page=import] {
         }
 
         &.importer {
-          background-color: var(--ls-tertiary-background-color);
-          position: relative;
+          @apply relative bg-gray-03;
 
           > section {
             flex: unset;
@@ -298,16 +296,8 @@ body[data-page=import] {
     }
 
     label.action-input {
-      transition: none;
-      color: var(--ls-active-primary-color);
-      background-color: var(--ls-quaternary-background-color);
-      height: 68px;
-      width: 100%;
-      opacity: .8;
-      user-select: none;
-      border-radius: 12px;
-      overflow: hidden;
-      cursor: pointer;
+      @apply transition-none bg-gray-06 h-[68px] w-full opacity-80 select-none;
+      @apply rounded-xl overflow-hidden cursor-pointer;
 
       small {
         font-size: 11px;
@@ -413,7 +403,7 @@ body[data-page=import] {
 
     .shepherd-footer {
       button {
-        background-color: var(--lx-bg-override, var(--lx-accent-09, var(--ls-button-background)));
+        background-color: var(--lx-accent-09, var(--ls-button-background));
         padding: 4px 8px;
         border-radius: 6px;
         overflow: hidden;
@@ -431,7 +421,7 @@ body[data-page=import] {
       }
 
       button:hover {
-          background-color: var(--lx-bg-override, var(--lx-accent-10, var(--ls-button-background)));
+          background-color: var(--lx-accent-10, var(--ls-button-background));
       }
     }
   }

+ 129 - 127
src/main/frontend/components/onboarding/setups.cljs

@@ -30,10 +30,12 @@
 
       [:h1.text-xl
        (if picker?
-         [:span [:strong (ui/icon "heart")] (t :on-boarding/main-title)]
-         [:span [:strong (ui/icon "file-import")] (t :on-boarding/importing-main-title)])]
+         [:span.flex.items-center.gap-1
+          [:strong (ui/icon "heart" {:size 30})] (t :on-boarding/main-title)]
+         [:span.flex.items-center.gap-1
+          [:strong (ui/icon "file-import" {:size 30})] (t :on-boarding/importing-main-title)])]
 
-      [:h2
+      [:h2.opacity-60
        (if picker?
          (t :on-boarding/main-desc)
          (t :on-boarding/importing-main-desc))]
@@ -48,11 +50,11 @@
      [:div.px-4
       "You can save them in your local storage, and use Logseq Sync or any third-party sync service to keep your notes sync with other devices. "
       "If you prefer to use Dropbox to sync your notes, you can use "
-      [:a {:href "https://play.google.com/store/apps/details?id=com.ttxapps.dropsync"
+      [:a {:href   "https://play.google.com/store/apps/details?id=com.ttxapps.dropsync"
            :target "_blank"}
        "Dropsync"]
       ". Or you can use "
-      [:a {:href "https://play.google.com/store/apps/details?id=dk.tacit.android.foldersync.lite"
+      [:a {:href   "https://play.google.com/store/apps/details?id=dk.tacit.android.foldersync.lite"
            :target "_blank"}
        "FolderSync"]
       "."]
@@ -62,72 +64,72 @@
 
 (rum/defcs picker < rum/reactive
   [_state onboarding-and-home?]
-  (let [parsing?       (state/sub :repo/parsing-files?)
-        _              (state/sub :auth/id-token)
-        native-ios?    (mobile-util/native-ios?)
+  (let [parsing? (state/sub :repo/parsing-files?)
+        _ (state/sub :auth/id-token)
+        native-ios? (mobile-util/native-ios?)
         native-icloud? (not (string/blank? (state/sub [:mobile/container-urls :iCloudContainerUrl])))
-        logged?        (user-handler/logged-in?)]
+        logged? (user-handler/logged-in?)]
 
     (setups-container
-     :picker
-     [:article.flex.w-full
-      [:section.a.
-       (when (and (mobile-util/native-platform?) (not native-ios?))
-         (mobile-intro))
+      :picker
+      [:article.flex.w-full
+       [:section.a.
+        (when (and (mobile-util/native-platform?) (not native-ios?))
+          (mobile-intro))
 
-       (if native-ios?
-         ;; TODO: open for all native mobile platforms
-         (graph-picker/graph-picker-cp {:onboarding-and-home? onboarding-and-home?
-                                        :logged? logged?
-                                        :native-icloud? native-icloud?})
+        (if native-ios?
+          ;; TODO: open for all native mobile platforms
+          (graph-picker/graph-picker-cp {:onboarding-and-home? onboarding-and-home?
+                                         :logged?              logged?
+                                         :native-icloud?       native-icloud?})
 
-         (if (or (nfs/supported?) (mobile-util/native-platform?))
-           [:div.choose.flex.flex-col.items-center
-            {:on-click #(page-handler/ls-dir-files!
-                         (fn []
-                           (shortcut/refresh!)))}
-            [:i]
-            [:div.control
-             [:label.action-input.flex.items-center.justify-center.flex-col
-              {:disabled parsing?}
+          (if (or (nfs/supported?) (mobile-util/native-platform?))
+            [:div.choose.flex.flex-col.items-center
+             {:on-click #(page-handler/ls-dir-files!
+                           (fn []
+                             (shortcut/refresh!)))}
+             [:i]
+             [:div.control
+              [:label.action-input.flex.items-center.justify-center.flex-col
+               {:disabled parsing?}
 
-              (if parsing?
-                (ui/loading "")
-                [[:strong (t :on-boarding/section-btn-title)]
-                 [:small (t :on-boarding/section-btn-desc)]])]]]
-           [:div.px-5
-            (ui/admonition :warning
-                           (widgets/native-fs-api-alert))]))]
-      [:section.b.flex.items-center.flex-col
-       [:p.flex
-        [:i.as-flex-center (ui/icon "zoom-question" {:style {:fontSize "22px"}})]
-        [:span.flex-1.flex.flex-col
-         [:strong (t :on-boarding/section-title)]
-         [:small.opacity-60 (t :on-boarding/section-desc)]]]
+               (if parsing?
+                 (ui/loading "")
+                 [[:strong (t :on-boarding/section-btn-title)]
+                  [:small (t :on-boarding/section-btn-desc)]])]]]
+            [:div.px-5
+             (ui/admonition :warning
+               (widgets/native-fs-api-alert))]))]
+       [:section.b.flex.items-center.flex-col
+        [:p.flex
+         [:i.as-flex-center (ui/icon "zoom-question" {:style {:fontSize "22px"}})]
+         [:span.flex-1.flex.flex-col
+          [:strong (t :on-boarding/section-title)]
+          [:small.opacity-60 (t :on-boarding/section-desc)]]]
 
-       [:p.text-sm.pt-5.tracking-wide
-        [:span (str (t :on-boarding/section-tip-1 DEVICE))]
-        [:br]
-        [:span (t :on-boarding/section-tip-2)]]
+        [:p.text-sm.pt-5.tracking-wide
+         [:span (str (t :on-boarding/section-tip-1 DEVICE))]
+         [:br]
+         [:span (t :on-boarding/section-tip-2)]]
 
-       [:ul
-        (for [[title label icon]
-              [[(t :on-boarding/section-assets) "/assets" "whiteboard"]
-               [(t :on-boarding/section-journals) "/journals" "calendar-plus"]
-               [(t :on-boarding/section-pages) "/pages" "page"]
-               []
-               [(t :on-boarding/section-app) "/logseq" "tool"]
-               [(t :on-boarding/section-config) "/logseq/config.edn"]]]
-          (if-not title
-            [:li.hr]
-            [:li
-             {:key title}
-             [:i.as-flex-center
-              {:class (when (string/ends-with? label ".edn") "is-file")}
-              (when icon (ui/icon icon))]
-             [:span
-              [:strong.uppercase title]
-              [:small.opacity-50 label]]]))]]])))
+        [:ul
+         (for [[title label icon]
+               [[(t :on-boarding/section-assets) "/assets" "whiteboard"]
+                [(t :on-boarding/section-journals) "/journals" "calendar-plus"]
+                [(t :on-boarding/section-pages) "/pages" "page"]
+                []
+                [(t :on-boarding/section-app) "/logseq" "tool"]
+                [(t :on-boarding/section-config) "/logseq/config.edn"]]]
+           (if-not title
+             [:li.hr]
+             [:li
+              {:key title}
+              [:i.as-flex-center
+               {:class (when (string/ends-with? label ".edn") "is-file")}
+               (when icon (ui/icon icon))]
+              [:span
+               [:strong.uppercase title]
+               [:small.opacity-50 label]]]))]]])))
 
 (defonce *opml-imported-pages (atom nil))
 
@@ -139,29 +141,29 @@
 
 (defn- roam-import-handler
   [e]
-  (let [file      (first (array-seq (.-files (.-target e))))
+  (let [file (first (array-seq (.-files (.-target e))))
         file-name (gobj/get file "name")]
     (if (string/ends-with? file-name ".json")
       (do
         (state/set-state! :graph/importing :roam-json)
         (let [reader (js/FileReader.)]
           (set! (.-onload reader)
-                (fn [e]
-                  (let [text (.. e -target -result)]
-                    (external-handler/import-from-roam-json!
-                     text
-                     #(do
-                        (state/set-state! :graph/importing nil)
-                        (finished-cb))))))
+            (fn [e]
+              (let [text (.. e -target -result)]
+                (external-handler/import-from-roam-json!
+                  text
+                  #(do
+                     (state/set-state! :graph/importing nil)
+                     (finished-cb))))))
           (.readAsText reader file)))
       (notification/show! "Please choose a JSON file."
-                          :error))))
+        :error))))
 
 (defn- lsq-import-handler
   [e]
-  (let [file      (first (array-seq (.-files (.-target e))))
+  (let [file (first (array-seq (.-files (.-target e))))
         file-name (some-> (gobj/get file "name")
-                          (string/lower-case))
+                    (string/lower-case))
         edn? (string/ends-with? file-name ".edn")
         json? (string/ends-with? file-name ".json")]
     (if (or edn? json?)
@@ -172,36 +174,36 @@
                          external-handler/import-from-edn!
                          external-handler/import-from-json!)]
           (set! (.-onload reader)
-                (fn [e]
-                  (let [text (.. e -target -result)]
-                    (import-f
-                     text
-                     #(do
-                        (state/set-state! :graph/importing nil)
-                        (finished-cb))))))
+            (fn [e]
+              (let [text (.. e -target -result)]
+                (import-f
+                  text
+                  #(do
+                     (state/set-state! :graph/importing nil)
+                     (finished-cb))))))
           (.readAsText reader file)))
       (notification/show! "Please choose an EDN or a JSON file."
-                          :error))))
+        :error))))
 
 (defn- opml-import-handler
   [e]
-  (let [file      (first (array-seq (.-files (.-target e))))
+  (let [file (first (array-seq (.-files (.-target e))))
         file-name (gobj/get file "name")]
     (if (string/ends-with? file-name ".opml")
       (do
         (state/set-state! :graph/importing :opml)
         (let [reader (js/FileReader.)]
           (set! (.-onload reader)
-                (fn [e]
-                  (let [text (.. e -target -result)]
-                    (external-handler/import-from-opml! text
-                                                        (fn [pages]
-                                                          (reset! *opml-imported-pages pages)
-                                                          (state/set-state! :graph/importing nil)
-                                                          (finished-cb))))))
+            (fn [e]
+              (let [text (.. e -target -result)]
+                (external-handler/import-from-opml! text
+                  (fn [pages]
+                    (reset! *opml-imported-pages pages)
+                    (state/set-state! :graph/importing nil)
+                    (finished-cb))))))
           (.readAsText reader file)))
       (notification/show! "Please choose a OPML file."
-                          :error))))
+        :error))))
 
 (rum/defc importer < rum/reactive
   [{:keys [query-params]}]
@@ -218,43 +220,43 @@
                     (str current-idx "/" total))]
       (ui/progress-bar-with-label width left-label process))
     (setups-container
-     :importer
-     [:article.flex.flex-col.items-center.importer.py-16.px-8
-      [:section.c.text-center
-       [:h1 (t :on-boarding/importing-title)]
-       [:h2 (t :on-boarding/importing-desc)]]
-      [:section.d.md:flex
-       [:label.action-input.flex.items-center.mx-2.my-2
-        [:span.as-flex-center [:i (svg/roam-research 28)]]
-        [:div.flex.flex-col
-         [[:strong "RoamResearch"]
-          [:small (t :on-boarding/importing-roam-desc)]]]
-        [:input.absolute.hidden
-         {:id        "import-roam"
-          :type      "file"
-          :on-change roam-import-handler}]]
+      :importer
+      [:article.flex.flex-col.items-center.importer.py-16.px-8
+       [:section.c.text-center
+        [:h1 (t :on-boarding/importing-title)]
+        [:h2 (t :on-boarding/importing-desc)]]
+       [:section.d.md:flex
+        [:label.action-input.flex.items-center.mx-2.my-2
+         [:span.as-flex-center [:i (svg/roam-research 28)]]
+         [:div.flex.flex-col
+          [[:strong "RoamResearch"]
+           [:small (t :on-boarding/importing-roam-desc)]]]
+         [:input.absolute.hidden
+          {:id        "import-roam"
+           :type      "file"
+           :on-change roam-import-handler}]]
 
-       [:label.action-input.flex.items-center.mx-2.my-2
-        [:span.as-flex-center [:i (svg/logo 28)]]
-        [:span.flex.flex-col
-         [[:strong "EDN / JSON"]
-          [:small (t :on-boarding/importing-lsq-desc)]]]
-        [:input.absolute.hidden
-         {:id        "import-lsq"
-          :type      "file"
-          :on-change lsq-import-handler}]]
+        [:label.action-input.flex.items-center.mx-2.my-2
+         [:span.as-flex-center [:i (svg/logo 28)]]
+         [:span.flex.flex-col
+          [[:strong "EDN / JSON"]
+           [:small (t :on-boarding/importing-lsq-desc)]]]
+         [:input.absolute.hidden
+          {:id        "import-lsq"
+           :type      "file"
+           :on-change lsq-import-handler}]]
 
-       [:label.action-input.flex.items-center.mx-2.my-2
-        [:span.as-flex-center (ui/icon "sitemap" {:style {:fontSize "26px"}})]
-        [:span.flex.flex-col
-         [[:strong "OPML"]
-          [:small (t :on-boarding/importing-opml-desc)]]]
+        [:label.action-input.flex.items-center.mx-2.my-2
+         [:span.as-flex-center (ui/icon "sitemap" {:style {:fontSize "26px"}})]
+         [:span.flex.flex-col
+          [[:strong "OPML"]
+           [:small (t :on-boarding/importing-opml-desc)]]]
 
-        [:input.absolute.hidden
-         {:id        "import-opml"
-          :type      "file"
-          :on-change opml-import-handler}]]]
+         [:input.absolute.hidden
+          {:id        "import-opml"
+           :type      "file"
+           :on-change opml-import-handler}]]]
 
-      (when (= "picker" (:from query-params))
-        [:section.e
-         [:a.button {:on-click #(route-handler/redirect-to-home!)} "Skip"]])])))
+       (when (= "picker" (:from query-params))
+         [:section.e
+          [:a.button {:on-click #(route-handler/redirect-to-home!)} "Skip"]])])))

+ 110 - 24
src/main/frontend/components/page.cljs

@@ -41,7 +41,8 @@
             [medley.core :as medley]
             [promesa.core :as p]
             [reitit.frontend.easy :as rfe]
-            [rum.core :as rum]))
+            [rum.core :as rum]
+            [frontend.extensions.graph.pixi :as pixi]))
 
 (defn- get-page-name
   [state]
@@ -542,27 +543,56 @@
 (defonce *n-hops (atom nil))
 (defonce *focus-nodes (atom []))
 (defonce *graph-reset? (atom false))
+(defonce *graph-forcereset? (atom false))
 (defonce *journal? (atom nil))
 (defonce *orphan-pages? (atom true))
 (defonce *builtin-pages? (atom nil))
 (defonce *excluded-pages? (atom true))
 (defonce *show-journals-in-page-graph? (atom nil))
+(defonce *link-dist (atom 70))
+(defonce *charge-strength (atom -600))
+(defonce *charge-range (atom 600))
+
+(rum/defcs simulation-switch < rum/reactive
+  [state]
+  (let [*simulation-paused? pixi/*simulation-paused?]
+    [:div.flex.flex-col.mb-2
+     [:p {:title "Pause simulation"}
+      "Pause simulation"]
+     (ui/toggle
+      (rum/react *simulation-paused?)
+      (fn []
+        (let [paused? @*simulation-paused?]
+          (if paused?
+            (pixi/resume-simulation!)
+            (pixi/stop-simulation!))))
+      true)]))
 
 (rum/defc ^:large-vars/cleanup-todo graph-filters < rum/reactive
-  [graph settings n-hops]
+  [graph settings forcesettings n-hops]
   (let [{:keys [journal? orphan-pages? builtin-pages? excluded-pages?]
          :or {orphan-pages? true}} settings
+        {:keys [link-dist charge-strength charge-range]} forcesettings
         journal?' (rum/react *journal?)
         orphan-pages?' (rum/react *orphan-pages?)
         builtin-pages?' (rum/react *builtin-pages?)
         excluded-pages?' (rum/react *excluded-pages?)
+        link-dist'  (rum/react *link-dist)
+        charge-strength'  (rum/react *charge-strength)
+        charge-range'  (rum/react *charge-range)
         journal? (if (nil? journal?') journal? journal?')
         orphan-pages? (if (nil? orphan-pages?') orphan-pages? orphan-pages?')
         builtin-pages? (if (nil? builtin-pages?') builtin-pages? builtin-pages?')
         excluded-pages? (if (nil? excluded-pages?') excluded-pages? excluded-pages?')
+        link-dist (if (nil? link-dist') link-dist link-dist')
+        charge-strength (if (nil? charge-strength') charge-strength charge-strength')
+        charge-range (if (nil? charge-range') charge-range charge-range')
         set-setting! (fn [key value]
                        (let [new-settings (assoc settings key value)]
                          (config-handler/set-config! :graph/settings new-settings)))
+        set-forcesetting! (fn [key value]
+                            (let [new-forcesettings (assoc forcesettings key value)]
+                              (config-handler/set-config! :graph/forcesettings new-forcesettings)))
         search-graph-filters (state/sub :search/graph-filters)
         focus-nodes (rum/react *focus-nodes)]
     [:div.absolute.top-4.right-4.graph-filters
@@ -670,6 +700,57 @@
                [:a.opacity-70.opacity-100 {:on-click #(route-handler/go-to-search! :graph)}
                 "Click to search"])]))
          {:search-filters search-graph-filters})
+        (graph-filter-section
+         [:span.font-medium "Forces"]
+         (fn [open?]
+           (filter-expand-area
+            open?
+            [:div
+             [:p.text-sm.opacity-70.px-4
+              (let [c2 (count (:links graph))
+                    s2 (if (> c2 1) "s" "")]
+                (util/format "%d link%s" c2 s2))]
+             [:div.p-6
+              (simulation-switch)
+
+              [:div.flex.flex-col.mb-2
+               [:p {:title "Link Distance"}
+                "Link Distance"]
+               (ui/tippy {:html [:div.pr-3 link-dist]}
+                         (ui/slider (/ link-dist 10)
+                                    {:min 1   ;; 10
+                                     :max 18  ;; 180
+                                     :on-change #(let [value (int %)]
+                                                   (reset! *link-dist (* value 10))
+                                                   (set-forcesetting! :link-dist (* value 10)))}))]
+              [:div.flex.flex-col.mb-2
+               [:p {:title "Charge Strength"}
+                "Charge Strength"]
+               (ui/tippy {:html [:div.pr-3 charge-strength]}
+                         (ui/slider (/ charge-strength 100)
+                                    {:min -10  ;;-1000
+                                     :max 10   ;;1000
+                                     :on-change #(let [value (int %)]
+                                                   (reset! *charge-strength (* value 100))
+                                                   (set-forcesetting! :charge-strength (* value 100)))}))]
+              [:div.flex.flex-col.mb-2
+               [:p {:title "Charge Range"}
+                "Charge Range"]
+               (ui/tippy {:html [:div.pr-3 charge-range]}
+                         (ui/slider (/ charge-range 100)
+                                    {:min 5    ;;500
+                                     :max 40   ;;4000
+                                     :on-change #(let [value (int %)]
+                                                   (reset! *charge-range (* value 100))
+                                                   (set-forcesetting! :charge-range (* value 100)))}))]
+
+              [:a.opacity-70.opacity-100 {:on-click (fn []
+                                                      (swap! *graph-forcereset? not)
+                                                      (reset! *link-dist 70)
+                                                      (reset! *charge-strength -600)
+                                                      (reset! *charge-range 600))}
+               "Reset Forces"]]]))
+         {})
         (graph-filter-section
          [:span.font-medium "Export"]
          (fn [open?]
@@ -699,11 +780,15 @@
          (reset! last-node-position [node (.-x event) (.-y event)]))))
 
 (rum/defc global-graph-inner < rum/reactive
-  [graph settings theme]
+  [graph settings forcesettings theme]
   (let [[width height] (rum/react layout)
         dark? (= theme "dark")
         n-hops (rum/react *n-hops)
+        link-dist (rum/react *link-dist)
+        charge-strength (rum/react *charge-strength)
+        charge-range (rum/react *charge-range)
         reset? (rum/react *graph-reset?)
+        forcereset? (rum/react *graph-forcereset?)
         focus-nodes (when n-hops (rum/react *focus-nodes))
         graph (if (and (integer? n-hops)
                        (seq focus-nodes)
@@ -722,11 +807,15 @@
                       :width (- width 24)
                       :height (- height 48)
                       :dark? dark?
+                      :link-dist link-dist
+                      :charge-strength charge-strength
+                      :charge-range charge-range
                       :register-handlers-fn
                       (fn [graph]
                         (graph-register-handlers graph *focus-nodes *n-hops dark?))
-                      :reset? reset?})
-     (graph-filters graph settings n-hops)]))
+                      :reset? reset?
+                      :forcereset? forcereset?})
+     (graph-filters graph settings forcesettings n-hops)]))
 
 (defn- filter-graph-nodes
   [nodes filters]
@@ -741,21 +830,19 @@
      (mixins/listen state js/window "resize"
                     (fn [_e]
                       (reset! layout [js/window.innerWidth js/window.innerHeight])))))
-  {:will-mount (fn [state]
-                 (state/set-search-mode! :graph)
-                 state)
-   :will-unmount (fn [state]
+  {:will-unmount (fn [state]
                    (reset! *n-hops nil)
                    (reset! *focus-nodes [])
                    (state/set-search-mode! :global)
                    state)}
   [state]
   (let [settings (state/graph-settings)
+        forcesettings (state/graph-forcesettings)
         theme (state/sub :ui/theme)
         graph (graph-handler/build-global-graph theme settings)
         search-graph-filters (state/sub :search/graph-filters)
         graph (update graph :nodes #(filter-graph-nodes % search-graph-filters))]
-    (global-graph-inner graph settings theme)))
+    (global-graph-inner graph settings forcesettings theme)))
 
 (rum/defc page-graph-inner < rum/reactive
   [_page graph dark?]
@@ -1125,7 +1212,6 @@
                                               (doseq [{:block/keys [idx]} @*results]
                                                 (swap! *checks assoc idx (or indeterminate? (not all?))))))
                            :indeterminate (when (= -1 @*indeterminate) "indeterminate")})]
-           [:th.icon ""]
            (sortable-title (t :block/name) :block/name *sort-by-item *desc?)
            (when-not mobile?
              [(sortable-title (t :page/backlinks) :block/backlinks *sort-by-item *desc?)
@@ -1141,19 +1227,19 @@
                               (get @*checks idx)
                               {:on-change (fn []
                                             (swap! *checks update idx not))})]
-               [:td.icon.w-4.p-0.overflow-hidden
-                (when-let [icon (get-in page [:block/properties :icon])]
-                  icon)]
-               [:td.name [:a {:on-click (fn [e]
-                                          (.preventDefault e)
-                                          (let [repo (state/get-current-repo)]
-                                            (when (gobj/get e "shiftKey")
-                                              (state/sidebar-add-block!
-                                               repo
-                                               (:db/id page)
-                                               :page))))
-                              :href     (rfe/href :page {:name (:block/name page)})}
-                          (component-block/page-cp {} page)]]
+               [:td.name
+                [:a {:on-click (fn [e]
+                                 (.preventDefault e)
+                                 (let [repo (state/get-current-repo)]
+                                   (when (gobj/get e "shiftKey")
+                                     (state/sidebar-add-block!
+                                       repo
+                                       (:db/id page)
+                                       :page))))
+                     :href     (rfe/href :page {:name (:block/name page)})}
+                 (when-let [icon (get-in page [:block/properties :icon])]
+                   [:span.pr-1 icon])
+                 (component-block/page-cp {} page)]]
 
                (when-not mobile?
                  [[:td.backlinks [:span backlinks]]

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

@@ -1,7 +1,3 @@
-.page-references h2 {
-  color: var(--ls-title-text-color);
-}
-
 .cp__page {
   &-publish-actions {
     background-color: var(--ls-primary-background-color);
@@ -97,14 +93,14 @@
 
   .actions {
     position: sticky;
-    background-color: or(--ls-all-pages-table, --lx-gray-01, --ls-primary-background-color);
+    background-color: var(--lx-gray-01, var(--ls-primary-background-color, var(--rx-gray-01)));
     white-space: nowrap;
     top: -18px;
     padding-bottom: 10px;
     z-index: 1;
 
     .dark & {
-        background-color: or(--ls-all-pages-table, --lx-gray-02, --ls-primary-background-color);
+        background-color: var(--lx-gray-02, var(--ls-primary-background-color, var(--rx-gray-02)));
     }
 
     @screen md {
@@ -137,7 +133,6 @@
       @apply text-base space-x-2;
 
       a.button {
-        color: var(--ls-primary-text-color);
         margin-top: 1px;
         height: unset;
         padding: 4px;
@@ -146,7 +141,6 @@
 
         &.active {
           opacity: 1;
-          color: var(--ls-link-ref-text-color);
         }
       }
 
@@ -181,9 +175,7 @@
       }
 
       .form-input {
-        padding: 3px;
-        padding-left: 30px;
-        padding-right: 8px;
+        padding: 3px 8px 3px 30px;
       }
 
       .cancel {
@@ -247,11 +239,9 @@
   }
 
   .edit-input {
-    width: 100%;
-    border: none;
+    @apply w-full border-0 p-0 pr-1 bg-transparent outline-0;
+
     box-shadow: none;
-    padding: 0;
-    padding-right: 4px;
 
     &-wrapper {
       @apply rounded;

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

@@ -75,6 +75,8 @@
           _ (state/sub :auth/id-token)
           file-sync-graph-uuid (and (user-handler/logged-in?)
                                     (file-sync-handler/enable-sync?)
+                                    ;; FIXME: Sync state is not cleared when switching to a new graph
+                                    (file-sync-handler/current-graph-sync-on?)
                                     (file-sync-handler/get-current-graph-uuid))]
       (when (and page (not block?))
         (->>

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

@@ -606,7 +606,8 @@
                    :options {:on-click
                              #(p/let [root (plugin-handler/get-ls-dotdir-root)]
                                 (js/apis.openPath (str root "/preferences.json")))}}
-                  {:title   [:span.flex.items-center.whitespace-nowrap.space-x-1 (ui/icon "bug") (t :plugin/open-logseq-dir) [:code "~/.logseq"]]
+                  {:title   [:span.flex.items-center.whitespace-nowrap.gap-1
+                             (ui/icon "bug") (t :plugin/open-logseq-dir) [:code "~/.logseq"]]
                    :options {:on-click
                              #(p/let [root (plugin-handler/get-ls-dotdir-root)]
                                 (js/apis.openPath root))}}])
@@ -1043,7 +1044,7 @@
           {:title   key
            :item    [:div.flex.items-center.item-wrap
                      (ui-item-renderer pid :toolbar (assoc opts :prefix "pl-" :key (str "pl-" key)))
-                     [:span.opacity-80 {:style {:padding-left "2px"}} key]
+                     [:span {:style {:padding-left "2px"}} key]
                      [:span.pin.flex.items-center.opacity-60
                       {:class (util/classnames [{:pinned pinned?}])}
                       (ui/icon (if pinned? "pinned" "pin"))]]
@@ -1065,7 +1066,7 @@
 
          (when badge-updates?
            {:title   [:div.flex.items-center.space-x-5.leading-none
-                      [:span (t :plugin/found-updates)] (ui/point "bg-red-600" 5 {:style {:margin-top 2}})]
+                      [:span (t :plugin/found-updates)] (ui/point "bg-red-700" 5 {:style {:margin-top 2}})]
             :options {:on-click #(open-waiting-updates-modal!)
                       :class    "extra-item"}
             :icon    (ui/icon "download")})]

+ 20 - 17
src/main/frontend/components/plugins.css

@@ -42,7 +42,8 @@
           }
         }
 
-        &.active {}
+        &.active {
+        }
       }
     }
 
@@ -196,7 +197,7 @@
   &-item-card {
     @apply flex py-3 px-1 rounded-md;
 
-    background-color: var(--ls-tertiary-background-color);
+    background-color: var(--ls-tertiary-background-color, hsl(var(--muted)));
     height: 150px;
 
     li {
@@ -648,11 +649,10 @@
   &-installed {
     margin: -2rem;
     outline: none;
-    padding: 1rem;
+    padding: .5rem;
 
     .it {
       user-select: none;
-      background-color: var(--ls-secondary-background-color);
       border: 1px solid transparent;
       margin-bottom: 4px;
       cursor: pointer;
@@ -662,18 +662,12 @@
         font-weight: 600;
       }
 
-      &.is-active {
-        background-color: var(--ls-tertiary-background-color);
-        border: 1px solid var(--ls-quaternary-background-color);
-        opacity: 1;
+      &:hover, &.is-active {
+        @apply opacity-100 bg-gray-06;
       }
 
       &.is-selected {
-        opacity: 1;
-      }
-
-      &:hover {
-        opacity: 1;
+        @apply opacity-100;
       }
     }
   }
@@ -861,11 +855,10 @@
       }
 
       .menu-link {
-        padding: 3px 5px;
+        @apply px-[5px] py-1.5;
 
         &.extra-item {
-          padding: 4px 15px;
-          opacity: .8;
+          @apply opacity-80 px-4 py-1.5;
 
           .title-wrap {
             margin-left: 8px !important;
@@ -879,7 +872,7 @@
 
       .item-wrap {
         padding-right: 28px;
-        font-size: 13px;
+        font-size: 14px;
         position: relative;
 
         div[data-injected-ui] :is(.ti, .tie) {
@@ -900,6 +893,16 @@
           opacity: 90;
         }
       }
+
+      .menu-links-wrapper {
+        a {
+          @apply !text-popover-foreground/80;
+
+          &.button {
+            @apply h-auto hover:bg-transparent scale-95;
+          }
+        }
+      }
     }
   }
 }

+ 13 - 1
src/main/frontend/components/query/builder.css

@@ -21,7 +21,13 @@
     }
 
     .cp__select .input-wrap input {
-        border: none;
+        @apply border-0 outline-0 text-popover-foreground/90;
+
+        box-shadow: none;
+    }
+
+    .item-results-wrap {
+        @apply py-1;
     }
 
     .cp__select-input {
@@ -52,4 +58,10 @@
     .query-clause-btn {
         border-color: var(--ls-border-color);
     }
+
+    .query-builder-picker {
+       .form-input {
+           @apply focus:border-0;
+       }
+    }
 }

+ 69 - 58
src/main/frontend/components/right_sidebar.cljs

@@ -16,13 +16,13 @@
             [frontend.handler.ui :as ui-handler]
             [frontend.state :as state]
             [frontend.ui :as ui]
+            [logseq.shui.ui :as shui]
             [frontend.util :as util]
             [frontend.config :as config]
             [frontend.modules.editor.undo-redo :as undo-redo]
             [medley.core :as medley]
             [reitit.frontend.easy :as rfe]
-            [rum.core :as rum]
-            [frontend.handler.common :as common-handler]))
+            [rum.core :as rum]))
 
 (rum/defc toggle
   []
@@ -189,28 +189,33 @@
 (defonce *drag-from
   (atom nil))
 
-(rum/defc context-menu-content
-  [db-id idx type collapsed? block-count toggle-fn]
-  [:.menu-links-wrapper.text-left
-   {:on-click toggle-fn}
-   (ui/menu-link {:on-click #(state/sidebar-remove-block! idx)} (t :right-side-bar/pane-close))
-   (when (> block-count 1) (ui/menu-link {:on-click #(state/sidebar-remove-rest! db-id)} (t :right-side-bar/pane-close-others)))
-   (when (> block-count 1) (ui/menu-link {:on-click (fn []
-                                                      (state/clear-sidebar-blocks!)
-                                                      (state/hide-right-sidebar!))} (t :right-side-bar/pane-close-all)))
-   (when (or (not collapsed?) (> block-count 1)) [:hr.menu-separator])
-   (when-not collapsed? (ui/menu-link {:on-click #(state/sidebar-block-toggle-collapse! db-id)} (t :right-side-bar/pane-collapse)))
-   (when (> block-count 1) (ui/menu-link {:on-click #(state/sidebar-block-collapse-rest! db-id)} (t :right-side-bar/pane-collapse-others)))
-   (when (> block-count 1) (ui/menu-link {:on-click #(state/sidebar-block-set-collapsed-all! true)} (t :right-side-bar/pane-collapse-all)))
-   (when (or collapsed? (> block-count 1)) [:hr.menu-separator])
-   (when collapsed? (ui/menu-link {:on-click #(state/sidebar-block-toggle-collapse! db-id)} (t :right-side-bar/pane-expand)))
-   (when (> block-count 1) (ui/menu-link {:on-click #(state/sidebar-block-set-collapsed-all! false)}  (t :right-side-bar/pane-expand-all)))
-   (when (= type :page) [:hr.menu-separator])
-   (when (= type :page)
-     (let [name (:block/name (db/entity db-id))]
-       (ui/menu-link {:href (if (db-model/whiteboard-page? name)
+(rum/defc x-menu-content
+  [db-id idx type collapsed? block-count toggle-fn as-dropdown?]
+  (let [menu-content (if as-dropdown? shui/dropdown-menu-content shui/context-menu-content)
+        menu-item (if as-dropdown? shui/dropdown-menu-item shui/context-menu-content)
+        multi-items? (> block-count 1)]
+
+    (menu-content
+      {:on-click toggle-fn :class "w-48" :align "end"}
+
+      (menu-item {:on-click #(state/sidebar-remove-block! idx)} (t :right-side-bar/pane-close))
+      (when multi-items? (menu-item {:on-click #(state/sidebar-remove-rest! db-id)} (t :right-side-bar/pane-close-others)))
+      (when multi-items? (menu-item {:on-click (fn []
+                                                 (state/clear-sidebar-blocks!)
+                                                 (state/hide-right-sidebar!))} (t :right-side-bar/pane-close-all)))
+      (when (and (not collapsed?) multi-items?) [:hr.menu-separator])
+      (when-not collapsed? (menu-item {:on-click #(state/sidebar-block-toggle-collapse! db-id)} (t :right-side-bar/pane-collapse)))
+      (when multi-items? (menu-item {:on-click #(state/sidebar-block-collapse-rest! db-id)} (t :right-side-bar/pane-collapse-others)))
+      (when multi-items? (menu-item {:on-click #(state/sidebar-block-set-collapsed-all! true)} (t :right-side-bar/pane-collapse-all)))
+      (when (and collapsed? multi-items?) [:hr.menu-separator])
+      (when collapsed? (menu-item {:on-click #(state/sidebar-block-toggle-collapse! db-id)} (t :right-side-bar/pane-expand)))
+      (when multi-items? (menu-item {:on-click #(state/sidebar-block-set-collapsed-all! false)} (t :right-side-bar/pane-expand-all)))
+      (when (= type :page) [:hr.menu-separator])
+      (when (= type :page)
+        (let [name (:block/name (db/entity db-id))]
+          (menu-item {:href (if (db-model/whiteboard-page? name)
                               (rfe/href :whiteboard {:name name})
-                              (rfe/href :page {:name name}))} (t :right-side-bar/pane-open-as-page))))])
+                              (rfe/href :page {:name name}))} (t :right-side-bar/pane-open-as-page)))))))
 
 (rum/defc drop-indicator
   [idx drag-to]
@@ -246,56 +251,62 @@
       (let [collapsed? (state/sub [:ui/sidebar-collapsed-blocks db-id])]
         [:<>
          (when (zero? idx) (drop-indicator (dec idx) drag-to))
-         [:div.flex.sidebar-item.content.color-level.shadow-md.rounded-md
+         [:div.flex.sidebar-item.content.color-level.rounded-md.shadow-lg
           {:class [(str "item-type-" (name block-type))
                    (when collapsed? "collapsed")]}
           (let [[title component] item]
             [:div.flex.flex-col.w-full.relative
              [:.flex.flex-row.justify-between.pr-2.sidebar-item-header.color-level.rounded-t-md
-              {:class (when collapsed? "rounded-b-md")
-               :draggable true
+              {:class         (when collapsed? "rounded-b-md")
+               :draggable     true
                :on-drag-start (fn [event]
                                 (editor-handler/block->data-transfer! (:block/name (db/entity db-id)) event)
                                 (reset! *drag-from idx))
-               :on-drag-end (fn [_event]
-                              (when drag-to (state/sidebar-move-block! idx drag-to))
-                              (reset! *drag-to nil)
-                              (reset! *drag-from nil))
-               :on-mouse-up (fn [event]
-                              (when (= (.-which (.-nativeEvent event)) 2)
-                                (state/sidebar-remove-block! idx)))
-               :on-context-menu (fn [e]
-                                  (util/stop e)
-                                  (common-handler/show-custom-context-menu! e (context-menu-content db-id idx block-type collapsed? block-count #())))}
+               :on-drag-end   (fn [_event]
+                                (when drag-to (state/sidebar-move-block! idx drag-to))
+                                (reset! *drag-to nil)
+                                (reset! *drag-from nil))
+               :on-mouse-up   (fn [event]
+                                (when (= (.-which (.-nativeEvent event)) 2)
+                                  (state/sidebar-remove-block! idx)))}
+
               [:button.flex.flex-row.p-2.items-center.w-full.overflow-hidden
                {:aria-expanded (str (not collapsed?))
-                :id (str "sidebar-panel-header-" idx)
+                :id            (str "sidebar-panel-header-" idx)
                 :aria-controls (str "sidebar-panel-content-" idx)
-                :on-click (fn [event]
-                            (util/stop event)
-                            (state/sidebar-block-toggle-collapse! db-id))}
+                :on-click      (fn [event]
+                                 (util/stop event)
+                                 (state/sidebar-block-toggle-collapse! db-id))}
                [:span.opacity-50.hover:opacity-100.flex.items-center.pr-1
                 (ui/rotating-arrow collapsed?)]
-               [:div.ml-1.font-medium.overflow-hidden
+               [:div.ml-1.font-medium.overflow-hidden.whitespace-nowrap
                 title]]
               [:.item-actions.flex.items-center
-               (ui/dropdown (fn [{:keys [toggle-fn]}]
-                              [:button.button {:title (t :right-side-bar/pane-more)
-                                               :on-click (fn [e]
-                                                           (util/stop e)
-                                                           (toggle-fn))} (ui/icon "dots")])
-                            (fn [{:keys [close-fn]}]
-                              (context-menu-content db-id idx block-type collapsed? block-count close-fn)))
-               [:button.button.close {:title (t :right-side-bar/pane-close)
-                                      :on-click #(state/sidebar-remove-block! idx)} (ui/icon "x")]]]
-             [:div {:role "region"
-                    :id (str "sidebar-panel-content-" idx)
+               (shui/dropdown-menu
+                 (shui/dropdown-menu-trigger
+                   {:as-child true}
+                   (shui/button
+                     {:title   (t :right-side-bar/pane-more)
+                      :class   "px-3"
+                      :variant :text}
+                     (ui/icon "dots")))
+                 (x-menu-content db-id idx block-type collapsed? block-count #() true))
+
+               (shui/button
+                 {:title    (t :right-side-bar/pane-close)
+                  :variant  :text
+                  :class "px-3"
+                  :on-click #(state/sidebar-remove-block! idx)}
+                 (ui/icon "x"))]]
+
+             [:div {:role            "region"
+                    :id              (str "sidebar-panel-content-" idx)
                     :aria-labelledby (str "sidebar-panel-header-" idx)
-                    :class (util/classnames [{:hidden collapsed?
-                                              :initial (not collapsed?)
-                                              :p-4 (not (contains? #{:page :block :contents :search :shortcut-settings} block-type))
-                                              :pt-4 (not (contains? #{:search :shortcut-settings} block-type))
-                                              :p-1 (not (contains? #{:search :shortcut-settings} block-type))}])}
+                    :class           (util/classnames [{:hidden  collapsed?
+                                                        :initial (not collapsed?)
+                                                        :p-4     (not (contains? #{:page :block :contents :search :shortcut-settings} block-type))
+                                                        :pt-4    (not (contains? #{:search :shortcut-settings} block-type))
+                                                        :p-1     (not (contains? #{:search :shortcut-settings} block-type))}])}
               (inner-component component (not drag-from))]
              (when drag-from (drop-area idx))])]
          (drop-indicator idx drag-to)]))))
@@ -445,7 +456,7 @@
                                                                        (state/sidebar-add-block! repo "history" :history))}
             (t :right-side-bar/history)]])]]
 
-      [:.sidebar-item-list.flex-1.scrollbar-spacing
+      [:.sidebar-item-list.flex-1.scrollbar-spacing.px-2
        (if @*anim-finished?
          (for [[idx [repo db-id block-type]] (medley/indexed blocks)]
             (rum/with-key

+ 8 - 15
src/main/frontend/components/right_sidebar.css

@@ -19,33 +19,26 @@ html[data-theme=light] {
   @apply ml-1 mt-[-8px] pb-[150px];
 
   height: calc(100vh - 48px);
-  background-color: or(--ls-right-ridebar-color, --lx-gray-01, --ls-secondary-background-color, #d8e1e8);
 }
 
 html[data-theme=light] a.toggle:hover {
   color: var(--ls-primary-text-color);
 }
 
-.cp__header a, .cp__header button {
-    opacity: 1;
-    color: or(--ls-header-button-text-color, --lx-gray-11, --ls-header-button-background);
-}
+.cp__header {
+  > .r > div:not(.ui__dropdown-trigger) a, button {
+    color: var(--lx-gray-11, var(--ls-header-button-background, var(--rx-gray-11)));
 
-.cp__header a:hover, .cp__header button:hover, .cp__right-sidebar-topbar button:hover {
-    color: or(--ls-header-button-text-color-hover, --lx-gray-12, --ls-header-button-background);
+    &:hover {
+      color: var(--lx-gray-12, var(--ls-header-button-background, var(--rx-gray-12)));
+    }
+  }
 }
 
 .cp__right-sidebar-topbar {
-  @apply px-1 h-12;
-  background-color: var(--ls-primary-background-color);
+  @apply px-1 h-12 bg-transparent;
 
   button {
     @apply opacity-100;
   }
 }
-
-html[data-theme=dark] {
-    .cp__header a, .cp__header button {
-        opacity: 0.7;
-    }
-}

+ 44 - 33
src/main/frontend/components/settings.cljs

@@ -13,7 +13,6 @@
             [frontend.date :as date]
             [frontend.db :as db]
             [frontend.dicts :as dicts]
-            [frontend.handler :as handler]
             [frontend.handler.config :as config-handler]
             [frontend.handler.file-sync :as file-sync-handler]
             [frontend.handler.global-config :as global-config-handler]
@@ -333,40 +332,52 @@
                              :action     pick-theme
                              :desc       (ui/render-keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-theme))})))
 
-(defn accent-color-row []
+(defn accent-color-row [_in-modal?]
   (let [color-accent (state/sub :ui/radix-color)
-        pick-theme [:div.grid {:style {:grid-template-columns "repeat(5, 1fr)"
-                                       :gap "0.75rem"
-                                       :width "100%"
-                                       :max-width "16rem"}}
-                    (for [color colors/color-list
-                          :let [active? (= color color-accent)]]
+        pick-theme [:div.cp__accent-colors-list-wrap
+                    {:class (if _in-modal? "as-modal-picker" "")}
+                    (for [color (concat [:none :logseq] colors/color-list)
+                          :let [active? (= color color-accent)
+                                none? (= color :none)]]
                       [:div.flex.items-center {:style {:height 28}}
-                       [:div {:class "w-5 h-5 rounded-full flex justify-center items-center transition ease-in duration-100 hover:cursor-pointer hover:opacity-100"
-                              :title color
-                              :style {:background-color (colors/variable color :09)
-                                      :outline-color (colors/variable color (if active? :07 :06))
-                                      :outline-width (if active? "4px" "1px")
-                                      :outline-style :solid
-                                      :opacity (if active? 1 0.5)}
-                              :on-click (fn [_e] (state/set-color-accent! color))}
-                        [:div {:class "w-2 h-2 rounded-full transition ease-in duration-100"
-                               :style {:background-color (str "var(--rx-" (name color) "-07)")
-                                       :opacity (if active? 1 0)}}]]])
-                    (when color-accent
-                      [:div.col-span-5
-                       (shui-ui/button
-                         {:variant  :secondary
-                          :size :xs
-                          :on-click (fn [_e] (state/unset-color-accent!))}
-                         "Back to default color")])]]
+                       (ui/tippy
+                         {:html (case color
+                                  :none [:p {:style {:max-width "300px"}}
+                                         "Cancel accent color. This is currently in beta stage and mainly used for compatibility with custom themes."]
+                                  :logseq "Logseq classical color"
+                                  (str (name color) " color") )
+                          :delay [1000, 100]}
+                         (shui-ui/button
+                           {:class      "w-5 h-5 px-1 rounded-full flex justify-center items-center transition ease-in duration-100 hover:cursor-pointer hover:opacity-100"
+                            :auto-focus (and _in-modal? active?)
+                            :style      {:background-color (colors/variable color :09)
+                                         :outline-color    (colors/variable color (if active? :07 :06))
+                                         :outline-width    (if active? "4px" "1px")
+                                         :outline-style    :solid
+                                         :opacity          (if active? 1 0.5)}
+                            :variant    :text
+                            :on-click   (fn [_e] (state/set-color-accent! color))}
+                           [:strong
+                            {:class (if none? "h-0.5 w-full bg-red-700"
+                                              "w-2 h-2 rounded-full transition ease-in duration-100")
+                             :style {:background-color (if-not none? (str "var(--rx-" (name color) "-07)") "")
+                                     :opacity          (if (or none? active?) 1 0)}}]))
+                       ])]]
 
     [:<>
-     (row-with-button-action {:left-label "Accent color"
-                              :description "Choosing an accent color will override any theme you have selected."
-                              :-for       "toggle_radix_theme"
-                              :stretch    true
-                              :action     pick-theme})]))
+     (row-with-button-action {:left-label  "Accent color"
+                              :description "Choosing an accent color may override any theme you have selected."
+                              :-for        "toggle_radix_theme"
+                              :desc        (when-not _in-modal?
+                                             [:span.pl-6 (ui/render-keyboard-shortcut
+                                                           (shortcut-helper/gen-shortcut-seq :ui/accent-colors-picker))])
+                              :stretch     (boolean _in-modal?)
+                              :action      pick-theme})]))
+
+(rum/defc modal-accent-colors-inner
+  []
+  [:div.cp__settings-accent-colors-modal-inner
+   (accent-color-row true)])
 
 (defn file-format-row [t preferred-format]
   [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-center
@@ -569,7 +580,7 @@
 (defn clear-cache-row [t]
   (row-with-button-action {:left-label   (t :settings-page/clear-cache)
                            :button-label (t :settings-page/clear)
-                           :on-click     handler/clear-cache!
+                           :on-click     #(state/pub-event! [:graph/clear-cache!])
                            :-for         "clear_cache"}))
 
 (defn version-row [t version]
@@ -707,7 +718,7 @@
      (language-row t preferred-language)
      (theme-modes-row t switch-theme system-theme? dark?)
      (when (and (util/electron?) (not util/mac?)) (native-titlebar-row t))
-     (when show-radix-themes? (accent-color-row))
+     (when show-radix-themes? (accent-color-row false))
      (when (config/global-config-enabled?) (edit-global-config-edn))
      (when current-repo (edit-config-edn))
      (when current-repo (edit-custom-css))

+ 28 - 1
src/main/frontend/components/settings.css

@@ -14,7 +14,7 @@
     }
 
     aside {
-      @apply bg-gray-400/5 p-4 outline-0;
+      @apply bg-gray-03-alpha p-4;
 
       > ul > li {
         > a {
@@ -365,6 +365,17 @@
       pointer-events: none;
     }
   }
+
+  &-accent-colors-modal-inner {
+    & > .it {
+      grid-template-columns: auto;
+      margin: -8px;
+    }
+
+    label[for=toggle_radix_theme] {
+      @apply text-xl font-semibold opacity-90;
+    }
+  }
 }
 
 .cp__assets {
@@ -538,3 +549,19 @@ body[data-settings-tab=keymap] {
     }
   }
 }
+
+.ui__modal[label=accent-colors-picker] {
+  .panel-content {
+    @apply sm:min-w-[520px];
+  }
+}
+
+.cp__accent-colors {
+  &-list-wrap {
+    @apply grid grid-cols-8 gap-2 max-w-[250px];
+
+    &.as-modal-picker {
+      @apply grid-cols-8 gap-3 pt-1 pb-2 ml-8 max-w-none;
+    }
+  }
+}

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

@@ -475,5 +475,6 @@
 
                         (not unset?)
                         [:code.flex.items-center.bg-transparent
-                         (shui/shortcut-v1 (string/join " | " (map #(dh/binding-for-display id %) binding))
-                           nil {:size :md})])]]))))])])]]))
+                         (shui/shortcut
+                           (string/join " | " (map #(dh/binding-for-display id %) binding))
+                           {:size :md :interactive? true})])]]))))])])]]))

+ 3 - 3
src/main/frontend/components/svg.cljs

@@ -82,7 +82,7 @@
   []
   [:svg.h-8.w-8.important
    {:view-box "0 0 512 512"
-    :fill     "var(--ls-error-color)"}
+    :fill     "var(--color-red-600)"}
    [:path
     {:d
      "M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"}]])
@@ -91,7 +91,7 @@
   []
   [:svg.h-8.w-8.caution
    {:view-box "0 0 384 512"
-    :fill     "var(--ls-warning-color)"}
+    :fill     "var(--color-orange-600)"}
    [:path
     {:d
      "M216 23.86c0-23.8-30.65-32.77-44.15-13.04C48 191.85 224 200 224 288c0 35.63-29.11 64.46-64.85 63.99-35.17-.45-63.15-29.77-63.15-64.94v-85.51c0-21.7-26.47-32.23-41.43-16.5C27.8 213.16 0 261.33 0 320c0 105.87 86.13 192 192 192s192-86.13 192-192c0-170.29-168-193-168-296.14z"}]])
@@ -103,7 +103,7 @@
    [:svg.h-8.w-8.warning
     (merge
       {:view-box "0 0 576 512"
-       :fill     "var(--ls-warning-color)"}
+       :fill     "var(--color-orange-600)"}
       opts)
     [:path
      {:d

+ 3 - 3
src/main/frontend/components/table.css

@@ -14,7 +14,7 @@
     font-size: 14px;
     font-weight: 400;
     color: var(--ls-primary-text-color);
-    border-bottom: 2px solid var(--ls-border-color);
+    border-bottom: 2px solid var(--ls-border-color, var(--rx-gray-05));
     padding: 10px 8px;
   }
 
@@ -24,11 +24,11 @@
   }
 
   tr:nth-child(even) {
-    background: var(--ls-table-tr-even-background-color);
+    background: var(--ls-table-tr-even-background-color, var(--rx-gray-03));
   }
 
   tr:nth-child(odd) {
-    background: var(--ls-primary-background-color);
+    background: var(--ls-primary-background-color, var(--rx-gray-01));
   }
 
   caption {

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

@@ -24,12 +24,14 @@
         [restored-sidebar? set-restored-sidebar?] (rum/use-state false)]
 
     (rum/use-effect!
-     #(let [doc js/document.documentElement
-            cls (.-classList doc)]
+     #(let [^js doc js/document.documentElement
+            ^js cls (.-classList doc)
+            ^js cls-body (.-classList js/document.body)]
         (.setAttribute doc "data-theme" theme)
         (if (= theme "dark") ;; for tailwind dark mode
-          (.add cls "dark")
-          (.remove cls "dark"))
+          ; The white-theme is for backward compatibility. See: https://github.com/logseq/logseq/pull/4652.
+          (do (.add cls "dark") (doto cls-body (.remove "white-theme" "light-theme") (.add "dark-theme")))
+          (do (.remove cls "dark") (doto cls-body (.remove "dark-theme") (.add "white-theme" "light-theme"))))
         (ui/apply-custom-theme-effect! theme)
         (plugin-handler/hook-plugin-app :theme-mode-changed {:mode theme}))
      [theme])
@@ -116,11 +118,8 @@
      #(storage/set :file-sync/onboarding-state onboarding-state)
      [onboarding-state])
 
-    [:div
-     {:class    (util/classnames
-                 [(str theme "-theme")
-                  {:white-theme (= "light" theme)}]) ; The white-theme is for backward compatibility. See: https://github.com/logseq/logseq/pull/4652.
-      :on-click on-click}
+    [:div.theme-container
+     {:on-click on-click}
      child
 
      (pdf/default-embed-playground)]))

+ 27 - 37
src/main/frontend/components/theme.css

@@ -1,6 +1,6 @@
 :root {
   scrollbar-width: thin;
-  scrollbar-color: or(--lx-gray-05, --ls-scrollbar-foreground-color) or(--lx-gray-02, --ls-scrollbar-background-color);
+  scrollbar-color: var(--lx-gray-05, var(--ls-scrollbar-foreground-color)) var(--lx-gray-02, var(--ls-scrollbar-background-color));
 
   --ls-z-index-level-0: 0;
   --ls-z-index-level-1: 9;
@@ -10,19 +10,25 @@
   --ls-z-index-level-5: 99999;
 }
 
-html {
+@media (prefers-color-scheme: dark) {
+  .preboot-loading {
+    color: lightgray;
+  }
+}
+
+.visible-scrollbar, html:not(.is-mac) {
   ::-webkit-scrollbar-thumb {
-    background-color: or(--ls-scrollbar-thumb-color, --lx-gray-05, --ls-scrollbar-foreground-color);
+    background-color: var(--lx-gray-05, var(--ls-scrollbar-foreground-color, var(--rx-gray-05)));
   }
 
   ::-webkit-scrollbar {
-    background-color: or(--ls-scrollbar-color, --lx-gray-02, --ls-scrollbar-background-color);
-    width: var(--ls-scrollbar-width);
+    background-color: var(--lx-gray-02, var(--ls-scrollbar-background-color, var(--rx-gray-02)));
+    width: var(--ls-scrollbar-width, 6px);
     height: 8px;
   }
 
   ::-webkit-scrollbar-thumb:active {
-    background-color: or(--ls-scrollbar-thumb-color-active, --lx-gray-06, --ls-scrollbar-thumb-hover-color);
+    background-color: var(--lx-gray-06, var(--ls-scrollbar-thumb-hover-color, var(--rx-gray-06)));
   }
 
   ::-webkit-scrollbar-corner {
@@ -30,47 +36,34 @@ html {
   }
 }
 
-@media (prefers-color-scheme: dark) {
-  .preboot-loading {
-    color: lightgray;
+.hide-scrollbar {
+  -ms-overflow-style: none; /* IE and Edge */
+  scrollbar-width: none !important; /* Firefox */
+
+  &::-webkit-scrollbar {
+    display: none;
   }
 }
 
 .form-checkbox, .form-radio {
-  color: var(--ls-page-checkbox-color, #6093a0);
-  background-color: var(--ls-page-checkbox-color, #6093a0);
-  border-color: var(--ls-page-checkbox-border-color, #6093a0);
+  color: var(--ls-page-checkbox-color, hsl(var(--primary)/.4));
+  background-color: var(--ls-page-checkbox-color, var(--rx-gray-06));
+  border-color: var(--ls-page-checkbox-border-color, hsl(var(--border)));
   border: none;
   position: relative;
-  top: -1px;
-  margin-right: 2px;
 }
 
 .form-checkbox:hover {
   transform: scale(1.1);
 }
 
-html[data-theme='dark'] {
-  background-color: var(--ls-primary-background-color);
-
-  input.form-input {
-    background: none;
-  }
-}
-
-html[data-theme='light'] {
+html[data-theme='light'] .theme-container {
   .form-checkbox {
     &:focus {
       border-color: var(--ls-page-checkbox-border-color);
     }
   }
 
-  .cp__header {
-    a {
-      color: var(--ls-primary-text-color);
-    }
-  }
-
   a.right-sidebar-button {
     color: var(--ls-primary-text-color);
   }
@@ -80,14 +73,7 @@ html[data-theme='light'] {
   }
 }
 
-.hide-scrollbar {
-  -ms-overflow-style: none; /* IE and Edge */
-  scrollbar-width: none !important; /* Firefox */
-
-  &::-webkit-scrollbar {
-    display: none;
-  }
-}
+html[data-theme='dark'] .theme-container {}
 
 html.locked-scroll {
   overflow: hidden !important;
@@ -164,3 +150,7 @@ main.ls-fold-button-on-right {
     }
   }
 }
+
+main.theme-inner {
+  --left-sidebar-bg-color: var(--lx-gray-02, hsl(var(--secondary, var(--rx-gray-03-hsl))));
+}

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

@@ -188,7 +188,7 @@
     (fn [e]
       (util/stop e)
       (whiteboard-handler/create-new-whiteboard-and-redirect!))}
-   (ui/icon "plus")
+   (ui/icon "plus" {:size 32})
    [:span.dashboard-create-card-caption.select-none
     (t :whiteboard/dashboard-card-new-whiteboard)]])
 

+ 24 - 21
src/main/frontend/components/whiteboard.css

@@ -44,19 +44,30 @@ h1.title.whiteboard-dashboard-title {
 }
 
 .dashboard-preview-card {
-  @apply transition;
-  border: 1px solid var(--ls-border-color);
+  @apply transition border;
 }
 
 .dashboard-create-card {
-  @apply items-center justify-center relative;
-  background-color: or(--ls-create-whiteboard-background, --lx-gray-02, --ls-secondary-background-color);
+  @apply items-center justify-center relative border opacity-90 hover:shadow-lg;
+
+  background-color: var(--lx-gray-02, var(--ls-secondary-background-color, var(--rx-gray-02)));
   box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.06);
-  border: 1px solid transparent;
+
+  .ls-icon-plus, .dashboard-create-card-caption {
+    @apply opacity-60;
+  }
+
+  &:hover {
+    @apply opacity-100;
+
+    .ls-icon-plus, .dashboard-create-card-caption {
+      @apply opacity-90;
+    }
+  }
 }
 
 .dark .dashboard-create-card {
-  background-color: or(--ls-create-whiteboard-background, --lx-gray-03, --ls-secondary-background-color);
+  background-color: var(--lx-gray-03, var(--ls-secondary-background-color, var(--rx-gray-03)));
 }
 
 .dashboard-create-card i {
@@ -72,23 +83,14 @@ h1.title.whiteboard-dashboard-title {
   font-size: 14px;
 }
 
-.dashboard-create-card:hover {
-  background-color: var(--ls-selection-background-color);
-  box-shadow: 0 4px 8px -2px rgba(16, 24, 40, 0.1),
-  0 2px 4px -2px rgba(16, 24, 40, 0.06);
-  border: 1px solid var(--ls-page-blockquote-border-color);
-}
-
 .dashboard-card-title {
-  @apply px-4 py-3 flex flex-col;
-  gap: 4px;
-  border-bottom: 1px solid var(--ls-border-color);
-  background-color: or(--ls-whiteboard-card-title-background, --lx-gray-02, --ls-secondary-background-color);
-  font-size: 16px;
+  @apply px-4 py-3 flex flex-col gap-1 border-b;
+
+  background-color: var(--lx-gray-02, var(--ls-secondary-background-color, var(--rx-gray-02)));
 }
 
 .dark .dashboard-card-title {
-  background-color: or(--ls-whiteboard-card-title-background, --lx-gray-03, --ls-secondary-background-color);
+  background-color: var(--lx-gray-03, var(--ls-secondary-background-color, var(--rx-gray-03)));
 }
 
 .dashboard-card-title-name {
@@ -153,11 +155,11 @@ input.tl-text-input {
   z-index: 2000;
   gap: 4px;
   line-height: 1.4;
-  background: or(--ls-whiteboard-title-background, --lx-gray-01, --ls-primary-background-color);
+  background: var(--lx-gray-01, var(--ls-primary-background-color, hsl(var(--background))));
 }
 
 .dark .whiteboard-page-title-root {
-  background: or(--ls-whiteboard-title-background, --lx-gray-02, --ls-primary-background-color);
+  background: var(--lx-gray-02, var(--ls-primary-background-color, hsl(var(--background))));
 }
 
 .whiteboard-page-title {
@@ -186,6 +188,7 @@ input.tl-text-input {
     border: none;
     box-shadow: none;
     padding: 0;
+    background-color: transparent;
   }
 }
 

+ 3 - 1
src/main/frontend/dicts.cljc

@@ -59,6 +59,7 @@
    :pl      (edn-resource "dicts/pl.edn")
    :sk      (edn-resource "dicts/sk.edn")
    :uk      (edn-resource "dicts/uk.edn")
+   :fa      (edn-resource "dicts/fa.edn")
    :id      (edn-resource "dicts/id.edn")})
 
 (def languages
@@ -82,7 +83,8 @@
    {:label "Українська" :value :uk}
    {:label "한국어" :value :ko}
    {:label "Slovenčina" :value :sk}
+   {:label "فارسی" :value :fa}
    {:label "Bahasa Indonesia" :value :id}])
 
 (assert (= (set (keys dicts)) (set (map :value languages)))
-        "List of user-facing languages must match list of dictionaries")
+        "List of user-facing languages must match list of dictionaries")

+ 2 - 7
src/main/frontend/extensions/code.css

@@ -1,10 +1,5 @@
 .extensions__code {
-  @apply relative;
-  z-index: 0;
-  display: flex;
-  flex-direction: row;
-  flex-wrap: nowrap;
-  justify-content: space-between;
+  @apply relative z-0 flex flex-row flex-nowrap justify-between;
 
   &-lang {
     @apply p-1 text-sm;
@@ -51,7 +46,7 @@
   line-height: 1.45em;
 
   &-scroll {
-    padding-top: 18px;
+    padding-top: 14px;
     padding-bottom: 62px;
   }
 

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

@@ -51,9 +51,9 @@
   {:did-update pixi/render!
    :should-update (fn [old-state new-state]
                     (not= (select-keys (first (:rum/args old-state))
-                                       [:nodes :links :dark?])
+                                       [:nodes :links :dark? :link-dist :charge-strength :charge-range])
                           (select-keys (first (:rum/args new-state))
-                                       [:nodes :links :dark?])))
+                                       [:nodes :links :dark? :link-dist :charge-strength :charge-range])))
    :will-unmount (fn [state]
                    (reset! pixi/*graph-instance nil)
                    state)}

+ 44 - 11
src/main/frontend/extensions/graph/pixi.cljs

@@ -10,6 +10,8 @@
 
 (defonce *graph-instance (atom nil))
 (defonce *simulation (atom nil))
+(defonce *simulation-paused?
+  (atom false))
 
 (def Graph (gobj/get graphology "Graph"))
 
@@ -54,20 +56,34 @@
    :edge {:color "#A5B4FC"}})
 
 (defn layout!
-  [nodes links]
-  (let [nodes-count (count nodes)
-        simulation (forceSimulation nodes)]
+  "Node forces documentation can be read in more detail here https://d3js.org/d3-force"
+  [nodes links link-dist charge-strength charge-range]
+  (let [simulation (forceSimulation nodes)]
     (-> simulation
         (.force "link"
+                ;; The link force pushes linked nodes together or apart according to the desired link distance.
+                ;; The strength of the force is proportional to the difference between the linked nodes distance
+                ;; and the target distance, similar to a spring force.
                 (-> (forceLink)
                     (.id (fn [d] (.-id d)))
-                    (.distance 180)
+                    (.distance link-dist)
                     (.links links)))
         (.force "charge"
+                ;; The many-body (or n-body) force applies mutually amongst all nodes.
+                ;; It can be used to simulate gravity or electrostatic charge.
                 (-> (forceManyBody)
-                    (.distanceMax (if (> nodes-count 500) 4000 600))
+                    ;; The minimum distance between nodes over which this force is considered.
+                    ;; A minimum distance establishes an upper bound on the strength of the force between two nearby nodes, avoiding instability.
+                    (.distanceMin 1)
+                    ;; The maximum distance between nodes over which this force is considered.
+                    ;; Specifying a finite maximum distance improves performance and produces a more localized layout.
+                    (.distanceMax charge-range)
+                    ;; For a cluster of nodes that is far away, the charge force can be approximated by treating the cluster as a single, larger node.
+                    ;; The theta parameter determines the accuracy of the approximation
                     (.theta 0.5)
-                    (.strength -600)))
+                    ;; A positive value causes nodes to attract each other, similar to gravity,
+                    ;; while a negative value causes nodes to repel each other, similar to electrostatic charge.
+                    (.strength charge-strength)))
         (.force "collision"
                 (-> (forceCollide)
                     (.radius (+ 8 18))
@@ -75,7 +91,10 @@
         (.force "x" (-> (forceX 0) (.strength 0.02)))
         (.force "y" (-> (forceY 0) (.strength 0.02)))
         (.force "center" (forceCenter))
-        (.velocityDecay 0.8))
+        ;; The decay factor is akin to atmospheric friction; after the application of any forces during a tick,
+        ;; each node’s velocity is multiplied by 1 - decay. As with lowering the alpha decay rate,
+        ;; less velocity decay may converge on a better solution, but risks numerical instabilities and oscillation.
+        (.velocityDecay 0.5))
     (reset! *simulation simulation)
     simulation))
 
@@ -96,7 +115,20 @@
   (when-let [instance (:pixi @*graph-instance)]
     (.destroy instance)
     (reset! *graph-instance nil)
-    (reset! *simulation nil)))
+    (reset! *simulation nil))
+  (reset! *simulation-paused? false))
+
+(defn stop-simulation!
+  []
+  (when-let [^js simulation @*simulation]
+    (.stop simulation)
+    (reset! *simulation-paused? true)))
+
+(defn resume-simulation!
+  []
+  (when-let [^js simulation @*simulation]
+    (.restart simulation))
+  (reset! *simulation-paused? false))
 
 (defn- update-position!
   [node obj]
@@ -144,7 +176,8 @@
              #_:clj-kondo/ignore
              (when-let [node (.get nodes node-key)]
                (when-let [s @*simulation]
-                 (when-not (.-active event)
+                 (when-not (or (.-active event)
+                               @*simulation-paused?)
                    (-> (.alphaTarget s 0.3)
                        (.restart))
                    (js/setTimeout #(.alphaTarget s 0) 2000))
@@ -167,7 +200,7 @@
     (when @*graph-instance
       (clear-nodes! (:graph @*graph-instance))
       (destroy-instance!))
-    (let [{:keys [nodes links style hover-style height register-handlers-fn dark?]} (first (:rum/args state))
+    (let [{:keys [nodes links style hover-style height register-handlers-fn dark? link-dist charge-strength charge-range]} (first (:rum/args state))
           style                                                                     (or style (default-style dark?))
           hover-style                                                               (or hover-style (default-hover-style dark?))
           graph                                                                     (Graph.)
@@ -182,7 +215,7 @@
           links                                                                     (remove (fn [{:keys [source target]}] (or (nil? source) (nil? target))) links)
           nodes-js                                                                  (bean/->js nodes)
           links-js                                                                  (bean/->js links)
-          simulation                                                                (layout! nodes-js links-js)]
+          simulation                                                                (layout! nodes-js links-js link-dist charge-strength charge-range)]
       (doseq [node nodes-js]
         (try (.addNode graph (.-id node) node)
           (catch :default e

+ 27 - 22
src/main/frontend/extensions/handbooks/handbooks.css

@@ -13,7 +13,7 @@
       @apply dark:text-white px-3 pt-3 pb-2 sticky top-0 left-0 z-[4]
       transition-shadow duration-200;
 
-      background-color: var(--ls-tertiary-background-color);
+      background-color: var(--ls-tertiary-background-color1);
 
       .title {
         text-align: left;
@@ -33,11 +33,11 @@
       > .input-wrap {
         @apply mx-4 mb-2 flex rounded-lg mt-1.5;
 
-        border: 3px solid var(--ls-primary-background-color);
-        background-color: var(--ls-primary-background-color);
+        border: 3px solid var(--ls-primary-background-color1);
+        background-color: var(--ls-primary-background-color1);
 
         &:focus-within {
-          border: 3px solid var(--ls-secondary-border-color);
+          border: 3px solid var(--ls-secondary-border-color, var(--rx-gray-06));
         }
 
         > input {
@@ -76,7 +76,7 @@
       @apply text-sm px-3 py-2.5 rounded-lg cursor-pointer
       mb-2 active:opacity-90 select-none items-center;
 
-      background-color: var(--ls-secondary-background-color);
+      background-color: var(--ls-secondary-background-color1);
       border: 1px solid var(--ls-border-color);
       transition: background-color .3s;
 
@@ -104,7 +104,7 @@
       }
 
       &:hover, &.active {
-        background-color: var(--ls-primary-background-color);
+        background-color: var(--ls-primary-background-color1);
         border-color: var(--ls-secondary-border-color);
 
         > .l {
@@ -133,15 +133,15 @@
     .category-card {
       @apply flex rounded px-2 py-3 active:opacity-90 cursor-pointer transition-colors items-end;
 
-      border-left: 4px solid var(--ls-secondary-background-color);
-      background-color: var(--ls-secondary-background-color);
+      border-left: 4px solid var(--ls-secondary-background-color1);
+      background-color: var(--ls-secondary-background-color1);
 
       &[data-total="0"] {
         @apply hidden;
       }
 
       &:hover, &:active {
-        background-color: var(--ls-primary-background-color);
+        background-color: var(--ls-primary-background-color1);
       }
 
       > .icon-wrap {
@@ -282,7 +282,7 @@
           @apply relative flex flex-col rounded py-2 px-3 leading-5 select-none z-[1];
 
           color: var(--ls-primary-text-color);
-          background-color: var(--ls-secondary-background-color);
+          background-color: var(--ls-secondary-background-color1);
 
           small {
             @apply text-[11px] opacity-50 pl-0.5;
@@ -305,7 +305,7 @@
           ul {
             @apply absolute top-[58px] left-0 w-full list-none m-0 rounded-b py-2;
 
-            background-color: var(--ls-secondary-background-color);
+            background-color: var(--ls-secondary-background-color1);
             transform: translateY(-5px);
             max-height: 300px;
             overflow: auto;
@@ -314,7 +314,7 @@
               @apply list-none px-3 py-1 transition-colors text-sm;
 
               &:hover {
-                background-color: var(--ls-tertiary-background-color);
+                background-color: var(--ls-tertiary-background-color1);
               }
             }
           }
@@ -336,11 +336,16 @@
   }
 
   &-popup {
-    @apply fixed rounded-lg overflow-hidden
+    --ls-primary-background-color1: var(--ls-primary-background-color, var(--lx-gray-01, var(--rx-gray-02)));
+    --ls-secondary-background-color1: var(--ls-secondary-background-color, var(--lx-gray-03, var(--rx-gray-03)));
+    --ls-tertiary-background-color1: var(--ls-tertiary-background-color, var(--lx-gray-05, var(--rx-gray-05)));
+    --ls-quaternary-background-color1: var(--ls-quaternary-background-color, var(--lx-gray-08, var(--rx-gray-08)));
+
+    @apply fixed rounded-lg overflow-hidden border
     z-[19] shadow-lg flex justify-center flex-col;
 
-    background-color: var(--ls-tertiary-background-color);
-    border: 1px solid var(--ls-tertiary-background-color);
+    background-color: var(--ls-tertiary-background-color1);
+    border: 1px solid var(--ls-tertiary-background-color1);
     touch-action: none;
     height: 686px;
     max-height: 86vh;
@@ -352,7 +357,7 @@
 
 html[data-theme="light"] {
   .cp__handbooks-popup {
-    background-color: var(--ls-primary-background-color);
+    background-color: var(--ls-primary-background-color1);
 
     .input-wrap {
       background-color: #f1f1f1;
@@ -364,7 +369,7 @@ html[data-theme="light"] {
 
     .topic-card, :not(.as-primary).link-card {
       &:hover, &.active {
-        background-color: var(--ls-tertiary-background-color);
+        background-color: var(--ls-tertiary-background-color1);
         border-color: var(--ls-secondary-border-color);
       }
     }
@@ -372,27 +377,27 @@ html[data-theme="light"] {
 
   .cp__handbooks-content {
     .hd {
-      background-color: var(--ls-primary-background-color);
+      background-color: var(--ls-primary-background-color1);
     }
 
     .ft {
-      background-color: var(--ls-primary-background-color);
+      background-color: var(--ls-primary-background-color1);
     }
 
     .search {
-      background-color: var(--ls-primary-background-color);
+      background-color: var(--ls-primary-background-color1);
     }
 
     .chapters-select {
       .select-trigger {
-        background-color: var(--ls-tertiary-background-color);
+        background-color: var(--ls-tertiary-background-color1);
       }
     }
 
     .categories-list {
       .category-card {
         &:hover, &:active {
-          background-color: var(--ls-tertiary-background-color);
+          background-color: var(--ls-tertiary-background-color1);
         }
       }
     }

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

@@ -808,8 +808,8 @@
 
     (let [^js viewer        (:viewer state)
           in-system-window? (some-> viewer (.-$inSystemWindow))]
-      [:div.extensions__pdf-viewer-cnt
-       [:div.extensions__pdf-viewer
+      [:div.extensions__pdf-viewer-cnt.visible-scrollbar
+       [:div.extensions__pdf-viewer.overflow-x-auto
         {:ref *el-ref :class (util/classnames [{:is-area-dashed area-dashed?}])}
         [:div.pdfViewer "viewer pdf"]
         [:div.pp-holder]

+ 12 - 18
src/main/frontend/extensions/pdf/pdf.css

@@ -425,15 +425,16 @@ input::-webkit-inner-spin-button {
   }
 
   &-resizer {
-    position: absolute;
-    width: 3px;
-    height: 88vh;
-    background-color: rgba(0, 0, 0, 0);
-    overflow: hidden;
-    z-index: 4;
-    top: 10vh;
-    cursor: col-resize;
-    right: 0;
+    @apply absolute w-[3px] h-screen overflow-hidden;
+    @apply z-10 right-0 cursor-col-resize;
+
+    transition: background-color 300ms;
+    transition-delay: 300ms;
+
+    &.is-active, &:hover,
+    &:focus, &:active {
+      @apply bg-primary/90;
+    }
   }
 
   &-hls-text-region {
@@ -841,14 +842,7 @@ input::-webkit-inner-spin-button {
     }
 
     .hl-area {
-      @apply relative;
-
-      display: inline-block;
-      cursor: text;
-      border: 1px solid #eee;
-      border-radius: 4px;
-      overflow: hidden;
-      margin-top: 4px;
+      @apply relative inline-block cursor-text border rounded-md overflow-hidden mt-1;
 
       .actions {
         @apply absolute right-1 top-1 flex opacity-0 transition-opacity;
@@ -936,7 +930,7 @@ body[data-page] {
     }
 
     &-viewer-cnt {
-      display: flex;
+      @apply flex overflow-x-hidden;
     }
 
     &-highlights {

+ 10 - 6
src/main/frontend/extensions/tldraw.cljs

@@ -21,7 +21,8 @@
             [promesa.core :as p]
             [rum.core :as rum]
             [frontend.ui :as ui]
-            [frontend.components.whiteboard :as whiteboard]))
+            [frontend.components.whiteboard :as whiteboard]
+            [cljs-bean.core :as bean]))
 
 (def tldraw (r/adapt-class (gobj/get TldrawLogseq "App")))
 
@@ -79,11 +80,14 @@
          (map (fn [k] (js->clj (gobj/get props k) {:keywordize-keys true})) ["id" "className" "options"])))
 
 (rum/defc keyboard-shortcut
-  [props]
-  (let [shortcut (ui/keyboard-shortcut-from-config (keyword (gobj/get props "action")))]
-    (cond
-      (string? shortcut) (ui/render-keyboard-shortcut shortcut)
-      :else (interpose " | " (map ui/render-keyboard-shortcut shortcut)))))
+  [^js props]
+  (when-let [props (bean/->clj props)]
+    (let [{:keys [action shortcut opts]} props
+          shortcut (if (string? action) (ui/keyboard-shortcut-from-config (keyword action)) shortcut)
+          opts (merge {:interactive? false} opts)]
+      (cond
+        (string? shortcut) (ui/render-keyboard-shortcut shortcut opts)
+        :else (interpose " | " (map #(ui/render-keyboard-shortcut % opts) shortcut))))))
 
 (def tldraw-renderers {:Page page-cp
                        :Block block-cp

+ 7 - 3
src/main/frontend/extensions/video/youtube.cljs

@@ -51,16 +51,20 @@
        (<! (load-youtube-api))
        (register-player state))
      state)}
-  [state id {:keys [width height] :as _opts}]
+  [state id {:keys [width height start] :as _opts}]
   (let [width  (or width (min (- (util/get-width) 96)
                               560))
-        height (or height (int (* width (/ 315 560))))]
+        height (or height (int (* width (/ 315 560))))
+        url (str "https://www.youtube.com/embed/" id "?enablejsapi=1")
+        url (if start
+              (str url "&start=" start)
+              url)]
     [:iframe
      {:id                (str "youtube-player-" id)
       :allow-full-screen "allowfullscreen"
       :allow             "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope"
       :frame-border      "0"
-      :src               (str "https://www.youtube.com/embed/" id "?enablejsapi=1")
+      :src               url
       :height            height
       :width             width}]))
 

+ 93 - 62
src/main/frontend/handler/events.cljs

@@ -5,6 +5,8 @@
   (:refer-clojure :exclude [run!])
   (:require ["@capacitor/filesystem" :refer [Directory Filesystem]]
             ["@sentry/react" :as Sentry]
+            [electron.ipc :as ipc]
+            [frontend.idb :as idb]
             [cljs-bean.core :as bean]
             [clojure.core.async :as async]
             [clojure.core.async.interop :refer [p->c]]
@@ -13,6 +15,7 @@
             [frontend.commands :as commands]
             [frontend.components.cmdk :as cmdk]
             [frontend.components.conversion :as conversion-component]
+            [frontend.components.settings :as settings]
             [frontend.components.diff :as diff]
             [frontend.components.encryption :as encryption]
             [frontend.components.file-sync :as file-sync]
@@ -184,16 +187,16 @@
           (repo-handler/broadcast-persist-db! graph))))
      (repo-handler/restore-and-setup-repo! graph)
      (graph-switch graph)
-     state/set-state! :sync-graph/init? false)))
+     (state/set-state! :sync-graph/init? false))))
 
 (defmethod handle :graph/switch [[_ graph opts]]
   (let [opts (if (false? (:persist? opts)) opts (assoc opts :persist? true))]
     (if (or (not (false? (get @outliner-file/*writes-finished? graph)))
-           (:sync-graph/init? @state/state))
+            (:sync-graph/init? @state/state))
       (graph-switch-on-persisted graph opts)
-     (notification/show!
-      "Please wait seconds until all changes are saved for the current graph."
-      :warning))))
+      (notification/show!
+       "Please wait seconds until all changes are saved for the current graph."
+       :warning))))
 
 (defmethod handle :graph/pull-down-remote-graph [[_ graph dir-name]]
   (if (mobile-util/native-ios?)
@@ -270,18 +273,18 @@
   (when
    (and (not (util/electron?))
         (not (mobile-util/native-platform?)))
-   (fn [close-fn]
-     [:div
+    (fn [close-fn]
+      [:div
       ;; TODO: fn translation with args
-      [:p
-       "Grant native filesystem permission for directory: "
-       [:b (config/get-local-dir repo)]]
-      (ui/button
-       (t :settings-permission/start-granting)
-       :class "ui__modal-enter"
-       :on-click (fn []
-                   (nfs/check-directory-permission! repo)
-                   (close-fn)))])))
+       [:p
+        "Grant native filesystem permission for directory: "
+        [:b (config/get-local-dir repo)]]
+       (ui/button
+        (t :settings-permission/start-granting)
+        :class "ui__modal-enter"
+        :on-click (fn []
+                    (nfs/check-directory-permission! repo)
+                    (close-fn)))])))
 
 (defmethod handle :modal/nfs-ask-permission []
   (when-let [repo (get-local-repo)]
@@ -340,6 +343,17 @@
 (defmethod handle :modal/show-themes-modal [_]
   (plugin/open-select-theme!))
 
+(defmethod handle :modal/toggle-accent-colors-modal [_]
+  (let [label "accent-colors-picker"]
+    (if (or (= label (state/get-modal-id))
+          (= label (some-> (state/get-sub-modals) (first) :modal/id)))
+      (state/close-sub-modal! label)
+      (state/set-sub-modal!
+        #(settings/modal-accent-colors-inner)
+        {:center? true
+         :id      label
+         :label   label}))))
+
 (rum/defc modal-output
   [content]
   content)
@@ -368,8 +382,9 @@
       (state/set-modal! #(diff/local-file repo path disk-content db-content)
                         {:label "diff__cp"}))))
 
-(defmethod handle :modal/display-file-version [[_ path content hash]]
-  (state/set-modal! #(git-component/file-specific-version path hash content)))
+
+(defmethod handle :modal/display-file-version-selector  [[_ versions path  get-content]]
+  (state/set-modal! #(git-component/file-version-selector versions path get-content)))
 
 ;; Hook on a graph is ready to be shown to the user.
 ;; It's different from :graph/restored, as :graph/restored is for window reloaded
@@ -422,8 +437,8 @@
 
 (defmethod handle :go/proxy-settings [[_ agent-opts]]
   (state/set-sub-modal!
-    (fn [_] (plugin/user-proxy-settings-panel agent-opts))
-    {:id :https-proxy-panel :center? true}))
+   (fn [_] (plugin/user-proxy-settings-panel agent-opts))
+   {:id :https-proxy-panel :center? true}))
 
 
 (defmethod handle :redirect-to-home [_]
@@ -566,14 +581,14 @@
           (if-not error-code
             (plugin/set-updates-sub-content! (str title "...") 0)
             (notification/show!
-              (str "[Checked]<" title "> " error-code) :error)))))
+             (str "[Checked]<" title "> " error-code) :error)))))
 
     (if (and updated? downloading?)
       ;; try to start consume downloading item
       (if-let [next-coming (state/get-next-selected-coming-update)]
         (plugin-handler/check-or-update-marketplace-plugin!
-          (assoc next-coming :only-check false :error-code nil)
-          (fn [^js e] (js/console.error "[Download Err]" next-coming e)))
+         (assoc next-coming :only-check false :error-code nil)
+         (fn [^js e] (js/console.error "[Download Err]" next-coming e)))
         (plugin-handler/close-updates-downloading))
 
       ;; try to start consume pending item
@@ -581,11 +596,11 @@
         (do
           (println "Updates: take next pending - " (:id next-pending))
           (js/setTimeout
-            #(plugin-handler/check-or-update-marketplace-plugin!
-               (assoc next-pending :only-check true :auto-check auto-checking? :error-code nil)
-               (fn [^js e]
-                 (notification/show! (.toString e) :error)
-                 (js/console.error "[Check Err]" next-pending e))) 500))
+           #(plugin-handler/check-or-update-marketplace-plugin!
+             (assoc next-pending :only-check true :auto-check auto-checking? :error-code nil)
+             (fn [^js e]
+               (notification/show! (.toString e) :error)
+               (js/console.error "[Check Err]" next-pending e))) 500))
 
         ;; try to open waiting updates list
         (do (when (and prev-pending? (not auto-checking?)
@@ -610,7 +625,7 @@
         payload (js->clj event :keywordize-keys true)]
     (fs-watcher/handle-changed! type payload)
     (when (file-sync-handler/enable-sync?)
-     (sync/file-watch-handler type payload))))
+      (sync/file-watch-handler type payload))))
 
 (defmethod handle :rebuild-slash-commands-list [[_]]
   (page-handler/rebuild-slash-commands-list!))
@@ -631,7 +646,7 @@
       (t :yes)
       :autoFocus "on"
       :class "ui__modal-enter"
-       :on-click (fn []
+      :on-click (fn []
                   (state/close-modal!)
                   (nfs-handler/refresh! (state/get-current-repo) refresh-cb)))]]))
 
@@ -651,7 +666,7 @@
                                           :GraphName graph-name
                                           :remote? true)
                                    r))
-                            (state/get-repos)))))))
+                               (state/get-repos)))))))
 
 (defmethod handle :graph/re-index [[_]]
   ;; Ensure the graph only has ONE window instance
@@ -662,6 +677,22 @@
      #(do (page-handler/create-today-journal!)
           (file-sync-restart!)))))
 
+;; FIXME: move
+(defn- clear-cache!
+  []
+  (notification/show! "Clearing..." :warning false)
+  (p/let [_ (when (util/electron?)
+              (ipc/ipc "clearCache"))
+          _ (idb/clear-local-storage-and-idb!)]
+    (js/setTimeout
+      (fn [] (if (util/electron?)
+               (ipc/ipc :reloadWindowPage)
+               (js/window.location.reload)))
+      2000)))
+
+(defmethod handle :graph/clear-cache! [[_]]
+  (clear-cache!))
+
 (defmethod handle :graph/ask-for-re-index [[_ *multiple-windows? ui]]
   ;; *multiple-windows? - if the graph is opened in multiple windows, boolean atom
   ;; ui - custom message to show on asking for re-index
@@ -677,12 +708,12 @@
        (when (not (nil? ui)) ui)
        [:p (t :re-index-discard-unsaved-changes-warning)]
        (ui/button
-         (t :yes)
-         :autoFocus "on"
-         :class "ui__modal-enter"
-         :on-click (fn []
-                     (state/close-modal!)
-                     (state/pub-event! [:graph/re-index])))]])))
+        (t :yes)
+        :autoFocus "on"
+        :class "ui__modal-enter"
+        :on-click (fn []
+                    (state/close-modal!)
+                    (state/pub-event! [:graph/re-index])))]])))
 
 (defmethod handle :modal/remote-encryption-input-pw-dialog [[_ repo-url remote-graph-info type opts]]
   (state/set-modal!
@@ -868,17 +899,17 @@
          [:p
           "Or, let me"
           (ui/button "Fix"
-            :on-click (fn []
-                        (let [dir (config/get-repo-dir repo)]
-                          (p/let [content (fs/read-file dir file)]
-                            (let [new-content (string/replace content (str id) (str (random-uuid)))]
-                              (p/let [_ (fs/write-file! repo
-                                                        dir
-                                                        file
-                                                        new-content
-                                                        {})]
-                                (reset! resolved? true))))))
-            :class "inline mx-1")
+                     :on-click (fn []
+                                 (let [dir (config/get-repo-dir repo)]
+                                   (p/let [content (fs/read-file dir file)]
+                                     (let [new-content (string/replace content (str id) (str (random-uuid)))]
+                                       (p/let [_ (fs/write-file! repo
+                                                                 dir
+                                                                 file
+                                                                 new-content
+                                                                 {})]
+                                         (reset! resolved? true))))))
+                     :class "inline mx-1")
           "it."]])]]))
 
 (defmethod handle :file/parse-and-load-error [[_ repo parse-errors]]
@@ -890,18 +921,18 @@
                         (for [[file error] parse-errors]
                           (let [data (ex-data error)]
                             (cond
-                             (and (gp-config/whiteboard? file)
-                                  (= :transact/upsert (:error data))
-                                  (uuid? (last (:assertion data))))
-                             (rum/with-key (file-id-conflict-item repo file data) file)
-
-                             :else
-                             (do
-                               (state/pub-event! [:capture-error {:error error
-                                                                  :payload {:type :file/parse-and-load-error}}])
-                               [:li.my-1 {:key file}
-                                [:a {:on-click #(js/window.apis.openPath file)} file]
-                                [:p (.-message error)]]))))]
+                              (and (gp-config/whiteboard? file)
+                                   (= :transact/upsert (:error data))
+                                   (uuid? (last (:assertion data))))
+                              (rum/with-key (file-id-conflict-item repo file data) file)
+
+                              :else
+                              (do
+                                (state/pub-event! [:capture-error {:error error
+                                                                   :payload {:type :file/parse-and-load-error}}])
+                                [:li.my-1 {:key file}
+                                 [:a {:on-click #(js/window.apis.openPath file)} file]
+                                 [:p (.-message error)]]))))]
                        [:p "Don't forget to re-index your graph when all the conflicts are resolved."]]
                       :status :error}]))
 
@@ -924,8 +955,8 @@
 (defmethod handle :editor/toggle-own-number-list [[_ blocks]]
   (let [batch? (sequential? blocks)
         blocks (cond->> blocks
-                  batch?
-                  (map #(cond-> % (or (uuid? %) (string? %)) (db-model/get-block-by-uuid))))]
+                 batch?
+                 (map #(cond-> % (or (uuid? %) (string? %)) (db-model/get-block-by-uuid))))]
     (if (and batch? (> (count blocks) 1))
       (editor-handler/toggle-blocks-as-own-order-list! blocks)
       (when-let [block (cond-> blocks batch? (first))]

+ 9 - 30
src/main/frontend/handler/shell.cljs

@@ -1,14 +1,13 @@
 (ns frontend.handler.shell
   "Git related handler fns"
-  (:require [electron.ipc :as ipc]
-            [clojure.string :as string]
-            [logseq.graph-parser.util :as gp-util]
-            [frontend.handler.notification :as notification]
-            [promesa.core :as p]
+  (:require [clojure.string :as string]
+            [electron.ipc :as ipc]
             [frontend.db :as db]
+            [frontend.handler.notification :as notification]
             [frontend.state :as state]
-            [frontend.config :as config]
-            [frontend.util :as util]))
+            [frontend.util :as util]
+            [logseq.graph-parser.util :as gp-util]
+            [promesa.core :as p]))
 
 (defn run-git-command!
   [command]
@@ -29,7 +28,7 @@
   (p/let [result (f command args)]
     (notification/show!
      (if (string/blank? result)
-       [:p [:code.mr-1 (str command " " args) ]
+       [:p [:code.mr-1 (str command " " args)]
         "was executed successfully!"]
        result)
      :success
@@ -59,15 +58,6 @@
         :else
         (run-cli-command! command args)))))
 
-;; git show $REV:$FILE
-(defn- get-versioned-file-content
-  [hash path]
-  (when (and hash path)
-    (let [repo (state/get-current-repo)
-          local-dir (config/get-local-dir repo)
-          path (string/replace path (str local-dir "/") "")]
-      (p/let [content (run-git-command! ["show" (str hash ":" path)])]
-        (state/pub-event! [:modal/display-file-version path content hash])))))
 
 (defn get-file-latest-git-log
   [page n]
@@ -77,19 +67,8 @@
         (p/let [result (run-git-command! ["log" (str "-" n) "--pretty=format:Commit: %C(auto)%h$$$%s$$$%ad" "-p" path])
                 lines (->> (string/split-lines result)
                            (filter #(string/starts-with? % "Commit: ")))]
-          (notification/show! [:div
-                               [:div.font-bold "File history - " path]
-                               (for [line lines]
-                                 (let [[hash title time] (string/split line "$$$")
-                                       hash (subs hash 8)]
-                                   [:div.my-4 {:key hash}
-                                    [:hr]
-                                    [:div.mb-2
-                                     [:a.font-medium.mr-1.inline
-                                      {:on-click (fn [] (get-versioned-file-content hash path))}
-                                      hash]
-                                     title]
-                                    [:div.opacity-50 time]]))] :success false))))))
+          (state/pub-event! [:modal/display-file-version-selector  lines path  (fn [hash path] (run-git-command! ["show" (str hash ":" path)]))]))))))
+
 
 (defn set-git-username-and-email
   [username email]

+ 22 - 0
src/main/frontend/mobile/intent.cljs

@@ -1,5 +1,7 @@
 (ns frontend.mobile.intent
   (:require ["@capacitor/filesystem" :refer [Filesystem]]
+            ["@capacitor/share" :refer [^js Share]]
+            ["@capacitor/action-sheet" :refer [ActionSheet]]
             ["path" :as node-path]
             ["send-intent" :refer [^js SendIntent]]
             [clojure.pprint :as pprint]
@@ -22,6 +24,26 @@
             [logseq.graph-parser.util.page-ref :as page-ref]
             [promesa.core :as p]))
 
+(defn open-or-share-file
+  "Share file to mobile platform"
+  [uri]
+  (p/let [options [{:title "Open"
+                    :style "DEFAULT"}
+                   {:title "Share"}
+                   {:title "Cancel"
+                    :style "CANCEL"}]
+          result (.showActions ActionSheet (clj->js {:title "File Options"
+                                                     :message "Select an option to perform"
+                                                     :options options}))
+          index (.-index result)]
+
+    (when (not= index 2)
+      (if (and (= index 0) (mobile-util/native-android?))
+        (.openFile mobile-util/folder-picker (clj->js {:uri uri}))
+        (.share Share (clj->js {:url uri
+                                :dialogTitle "Open file with your favorite app"
+                                :title "Open file with your favorite app"}))))))
+
 (defn- is-link
   [url]
   (when (not-empty url)

+ 351 - 351
src/main/frontend/modules/shortcut/config.cljs

@@ -530,11 +530,11 @@
    :editor/toggle-open-blocks               {:binding "t o"
                                              :fn      editor-handler/toggle-open!}
 
-   :ui/cycle-color-off                      {:binding "c o"
+   :ui/accent-color-reset                   {:binding "c o"
                                              :fn      state/unset-color-accent!}
 
-   :ui/cycle-color                          {:binding "c c"
-                                             :fn      state/cycle-color!}
+   :ui/accent-colors-picker                 {:binding "c c"
+                                             :fn      #(state/pub-event! [:modal/toggle-accent-colors-modal])}
 
    :git/commit                              {:binding  "mod+g c"
                                              :inactive (not (util/electron?))
@@ -560,8 +560,8 @@
       {::commands       (set (keys all-built-in-keyboard-shortcuts))
        ::dicts/commands dicts/abbreviated-commands}]
   (assert (= (::commands keyboard-commands) (::dicts/commands keyboard-commands))
-          (str "Keyboard commands must have an english label"
-               (data/diff (::commands keyboard-commands) (::commands keyboard-commands)))))
+    (str "Keyboard commands must have an english label"
+      (data/diff (::commands keyboard-commands) (::commands keyboard-commands)))))
 
 (defn- resolve-fn
   "Converts a keyword fn to the actual fn. The fn to be resolved needs to be
@@ -569,7 +569,7 @@
   [keyword-fn]
   (fn []
     (if-let [resolved-fn (some-> (find-ns-obj (namespace keyword-fn))
-                                 (aget (munge (name keyword-fn))))]
+                           (aget (munge (name keyword-fn))))]
       (resolved-fn)
       (throw (ex-info (str "Unable to resolve " keyword-fn " to a fn") {})))))
 
@@ -577,361 +577,361 @@
   (->> (if (sequential? ks)
          ks (let [{:keys [ns includes excludes]} ks]
               (->> (keys all-built-in-keyboard-shortcuts)
-                   (filter (fn [k]
-                             (and (or (and ns (keyword? k)
-                                           (contains? (->> (if (seqable? ns) (seq ns) [ns]) (map #(name %)) (set))
-                                                      (namespace k)))
-                                      (and includes (contains? (set includes) k)))
-                                  (if (not (seq excludes)) true (not (contains? (set excludes) k)))))))))
-       (select-keys all-built-in-keyboard-shortcuts)
-       (remove (comp :inactive val))
-       ;; Convert keyword fns to real fns
-       (map (fn [[k v]]
-              [k (if (keyword? (:fn v))
-                   (assoc v :fn (resolve-fn (:fn v)))
-                   v)]))
-       (into {})))
+                (filter (fn [k]
+                          (and (or (and ns (keyword? k)
+                                     (contains? (->> (if (seqable? ns) (seq ns) [ns]) (map #(name %)) (set))
+                                       (namespace k)))
+                                 (and includes (contains? (set includes) k)))
+                            (if (not (seq excludes)) true (not (contains? (set excludes) k)))))))))
+    (select-keys all-built-in-keyboard-shortcuts)
+    (remove (comp :inactive val))
+    ;; Convert keyword fns to real fns
+    (map (fn [[k v]]
+           [k (if (keyword? (:fn v))
+                (assoc v :fn (resolve-fn (:fn v)))
+                v)]))
+    (into {})))
 
 ;; This is the only var that should be publicly expose :fn functionality
 (defonce ^:large-vars/data-var *config
-         (atom
-           {:shortcut.handler/date-picker
-            (build-category-map {:ns :date-picker})
-
-            :shortcut.handler/pdf
-            (-> (build-category-map {:ns :pdf})
-                (with-meta {:before m/enable-when-not-editing-mode!}))
-
-            :shortcut.handler/whiteboard
-            (-> (build-category-map {:ns :whiteboard})
-                (with-meta {:before m/enable-when-not-editing-mode!}))
-
-            :shortcut.handler/auto-complete
-            (build-category-map {:ns :auto-complete})
-
-            :shortcut.handler/cards
-            (-> (build-category-map {:ns :cards})
-                (with-meta {:before m/enable-when-not-editing-mode!}))
-
-            :shortcut.handler/block-editing-only
-            (-> (build-category-map
-                  [:editor/escape-editing
-                   :editor/backspace
-                   :editor/delete
-                   :editor/zoom-in
-                   :editor/zoom-out
-                   :editor/new-block
-                   :editor/new-line
-                   :editor/follow-link
-                   :editor/open-link-in-sidebar
-                   :editor/bold
-                   :editor/italics
-                   :editor/highlight
-                   :editor/strike-through
-                   :editor/clear-block
-                   :editor/kill-line-before
-                   :editor/kill-line-after
-                   :editor/beginning-of-block
-                   :editor/end-of-block
-                   :editor/forward-word
-                   :editor/backward-word
-                   :editor/forward-kill-word
-                   :editor/backward-kill-word
-                   :editor/replace-block-reference-at-point
-                   :editor/copy-embed
-                   :editor/paste-text-in-one-block-at-point
-                   :editor/insert-youtube-timestamp])
-                (with-meta {:before m/enable-when-editing-mode!}))
-
-            :shortcut.handler/editor-global
-            (-> (build-category-map
-                  [:graph/export-as-html
-                   :graph/open
-                   :graph/remove
-                   :graph/add
-                   :graph/save
-                   :graph/re-index
-                   :editor/cycle-todo
-                   :editor/up
-                   :editor/down
-                   :editor/left
-                   :editor/right
-                   :editor/select-up
-                   :editor/select-down
-                   :editor/move-block-up
-                   :editor/move-block-down
-                   :editor/open-edit
-                   :editor/select-block-up
-                   :editor/select-block-down
-                   :editor/select-parent
-                   :editor/delete-selection
-                   :editor/expand-block-children
-                   :editor/collapse-block-children
-                   :editor/indent
-                   :editor/outdent
-                   :editor/copy
-                   :editor/copy-text
-                   :editor/cut
-                   :command/toggle-favorite])
-                (with-meta {:before m/enable-when-not-component-editing!}))
-
-            :shortcut.handler/global-prevent-default
-            (-> (build-category-map
-                  [:editor/insert-link
-                   :editor/select-all-blocks
-                   :editor/toggle-undo-redo-mode
-                   :editor/toggle-number-list
-                   :editor/undo
-                   :editor/redo
-                   :ui/toggle-brackets
-                   :go/search
-                   :go/search-in-page
-                   :command-palette/toggle
-                   :go/electron-find-in-page
-                   :go/electron-jump-to-the-next
-                   :go/electron-jump-to-the-previous
-                   :go/backward
-                   :go/forward
-                   :search/re-index
-                   :sidebar/open-today-page
-                   :sidebar/clear
-                   :command/run
-                   :window/close])
-                (with-meta {:before m/prevent-default-behavior}))
-
-            :shortcut.handler/global-non-editing-only
-            (-> (build-category-map
-                  [:go/home
-                   :go/journals
-                   :go/all-pages
-                   :go/flashcards
-                   :go/graph-view
-                   :go/all-graphs
-                   :go/whiteboards
-                   :go/keyboard-shortcuts
-                   :go/tomorrow
-                   :go/next-journal
-                   :go/prev-journal
-                   :ui/toggle-document-mode
-                   :ui/toggle-settings
-                   :ui/toggle-right-sidebar
-                   :ui/toggle-left-sidebar
-                   :ui/toggle-help
-                   :ui/toggle-theme
-                   :ui/toggle-contents
-                   :editor/open-file-in-default-app
-                   :editor/open-file-in-directory
-                   :editor/copy-current-file
-                   :editor/copy-page-url
-                   :editor/new-whiteboard
-                   :ui/toggle-wide-mode
-                   :ui/select-theme-color
-                   :ui/goto-plugins
-                   :ui/install-plugins-from-file
-                   :editor/toggle-open-blocks
-                   :ui/clear-all-notifications
-                   :git/commit
-                   :sidebar/close-top
-                   :dev/show-block-data
-                   :dev/show-block-ast
-                   :dev/show-page-data
-                   :dev/show-page-ast
-                   :ui/cycle-color
-                   :ui/cycle-color-off])
-                (with-meta {:before m/enable-when-not-editing-mode!}))
-
-            :shortcut.handler/misc
-            ;; always overrides the copy due to "mod+c mod+s"
-            {:misc/copy (:misc/copy all-built-in-keyboard-shortcuts)}}))
+  (atom
+    {:shortcut.handler/date-picker
+     (build-category-map {:ns :date-picker})
+
+     :shortcut.handler/pdf
+     (-> (build-category-map {:ns :pdf})
+       (with-meta {:before m/enable-when-not-editing-mode!}))
+
+     :shortcut.handler/whiteboard
+     (-> (build-category-map {:ns :whiteboard})
+       (with-meta {:before m/enable-when-not-editing-mode!}))
+
+     :shortcut.handler/auto-complete
+     (build-category-map {:ns :auto-complete})
+
+     :shortcut.handler/cards
+     (-> (build-category-map {:ns :cards})
+       (with-meta {:before m/enable-when-not-editing-mode!}))
+
+     :shortcut.handler/block-editing-only
+     (-> (build-category-map
+           [:editor/escape-editing
+            :editor/backspace
+            :editor/delete
+            :editor/zoom-in
+            :editor/zoom-out
+            :editor/new-block
+            :editor/new-line
+            :editor/follow-link
+            :editor/open-link-in-sidebar
+            :editor/bold
+            :editor/italics
+            :editor/highlight
+            :editor/strike-through
+            :editor/clear-block
+            :editor/kill-line-before
+            :editor/kill-line-after
+            :editor/beginning-of-block
+            :editor/end-of-block
+            :editor/forward-word
+            :editor/backward-word
+            :editor/forward-kill-word
+            :editor/backward-kill-word
+            :editor/replace-block-reference-at-point
+            :editor/copy-embed
+            :editor/paste-text-in-one-block-at-point
+            :editor/insert-youtube-timestamp])
+       (with-meta {:before m/enable-when-editing-mode!}))
+
+     :shortcut.handler/editor-global
+     (-> (build-category-map
+           [:graph/export-as-html
+            :graph/open
+            :graph/remove
+            :graph/add
+            :graph/save
+            :graph/re-index
+            :editor/cycle-todo
+            :editor/up
+            :editor/down
+            :editor/left
+            :editor/right
+            :editor/select-up
+            :editor/select-down
+            :editor/move-block-up
+            :editor/move-block-down
+            :editor/open-edit
+            :editor/select-block-up
+            :editor/select-block-down
+            :editor/select-parent
+            :editor/delete-selection
+            :editor/expand-block-children
+            :editor/collapse-block-children
+            :editor/indent
+            :editor/outdent
+            :editor/copy
+            :editor/copy-text
+            :editor/cut
+            :command/toggle-favorite])
+       (with-meta {:before m/enable-when-not-component-editing!}))
+
+     :shortcut.handler/global-prevent-default
+     (-> (build-category-map
+           [:editor/insert-link
+            :editor/select-all-blocks
+            :editor/toggle-undo-redo-mode
+            :editor/toggle-number-list
+            :editor/undo
+            :editor/redo
+            :ui/toggle-brackets
+            :go/search
+            :go/search-in-page
+            :command-palette/toggle
+            :go/electron-find-in-page
+            :go/electron-jump-to-the-next
+            :go/electron-jump-to-the-previous
+            :go/backward
+            :go/forward
+            :search/re-index
+            :sidebar/open-today-page
+            :sidebar/clear
+            :command/run
+            :window/close])
+       (with-meta {:before m/prevent-default-behavior}))
+
+     :shortcut.handler/global-non-editing-only
+     (-> (build-category-map
+           [:go/home
+            :go/journals
+            :go/all-pages
+            :go/flashcards
+            :go/graph-view
+            :go/all-graphs
+            :go/whiteboards
+            :go/keyboard-shortcuts
+            :go/tomorrow
+            :go/next-journal
+            :go/prev-journal
+            :ui/toggle-document-mode
+            :ui/toggle-settings
+            :ui/toggle-right-sidebar
+            :ui/toggle-left-sidebar
+            :ui/toggle-help
+            :ui/toggle-theme
+            :ui/toggle-contents
+            :editor/open-file-in-default-app
+            :editor/open-file-in-directory
+            :editor/copy-current-file
+            :editor/copy-page-url
+            :editor/new-whiteboard
+            :ui/toggle-wide-mode
+            :ui/select-theme-color
+            :ui/goto-plugins
+            :ui/install-plugins-from-file
+            :editor/toggle-open-blocks
+            :ui/clear-all-notifications
+            :git/commit
+            :sidebar/close-top
+            :dev/show-block-data
+            :dev/show-block-ast
+            :dev/show-page-data
+            :dev/show-page-ast
+            :ui/accent-colors-picker
+            :ui/accent-color-reset])
+       (with-meta {:before m/enable-when-not-editing-mode!}))
+
+     :shortcut.handler/misc
+     ;; always overrides the copy due to "mod+c mod+s"
+     {:misc/copy (:misc/copy all-built-in-keyboard-shortcuts)}}))
 
 ;; To add a new entry to this map, first add it here and then
 ;; a description for it in frontend.dicts.en/dicts
 ;; Full list of categories for docs purpose
 (defonce ^:large-vars/data-var *category
   (atom
-   {:shortcut.category/basics
-    [:go/search
-     :editor/new-block
-     :editor/new-line
-     :editor/indent
-     :editor/outdent
-     :editor/select-all-blocks
-     :editor/select-parent
-     :go/search-in-page
-     :command-palette/toggle
-     :go/electron-find-in-page
-     :go/electron-jump-to-the-next
-     :go/electron-jump-to-the-previous
-     :editor/undo
-     :editor/redo
-     :editor/copy
-     :editor/copy-text
-     :editor/cut]
-
-    :shortcut.category/formatting
-    [:editor/bold
-     :editor/insert-link
-     :editor/italics
-     :editor/strike-through
-     :editor/highlight]
-
-    :shortcut.category/navigating
-    [:editor/up
-     :editor/down
-     :editor/left
-     :editor/right
-     :editor/collapse-block-children
-     :editor/expand-block-children
-     :editor/toggle-open-blocks
-     :go/backward
-     :go/forward
-     :go/home
-     :go/journals
-     :go/all-pages
-     :go/graph-view
-     :go/all-graphs
-     :go/whiteboards
-     :go/flashcards
-     :go/tomorrow
-     :go/next-journal
-     :go/prev-journal
-     :go/keyboard-shortcuts]
-
-    :shortcut.category/block-editing
-    [:editor/backspace
-     :editor/delete
-     :editor/indent
-     :editor/outdent
-     :editor/new-block
-     :editor/new-line
-     :editor/zoom-in
-     :editor/zoom-out
-     :editor/cycle-todo
-     :editor/follow-link
-     :editor/open-link-in-sidebar
-     :editor/move-block-up
-     :editor/move-block-down
-     :editor/escape-editing]
-
-    :shortcut.category/block-command-editing
-    [:editor/backspace
-     :editor/clear-block
-     :editor/kill-line-before
-     :editor/kill-line-after
-     :editor/beginning-of-block
-     :editor/end-of-block
-     :editor/forward-word
-     :editor/backward-word
-     :editor/forward-kill-word
-     :editor/backward-kill-word
-     :editor/replace-block-reference-at-point
-     :editor/copy-embed
-     :editor/paste-text-in-one-block-at-point
-     :editor/select-up
-     :editor/select-down]
-
-    :shortcut.category/block-selection
-    [:editor/open-edit
-     :editor/select-all-blocks
-     :editor/select-parent
-     :editor/select-block-up
-     :editor/select-block-down
-     :editor/delete-selection]
-
-    :shortcut.category/toggle
-    [:ui/toggle-help
-     :editor/toggle-open-blocks
-     :editor/toggle-undo-redo-mode
-     :editor/toggle-number-list
-     :ui/toggle-wide-mode
-     :ui/toggle-document-mode
-     :ui/toggle-brackets
-     :ui/toggle-theme
-     :ui/toggle-left-sidebar
-     :ui/toggle-right-sidebar
-     :ui/toggle-settings
-     :ui/toggle-contents
-     :ui/cycle-color-off
-     :ui/cycle-color]
-
-    :shortcut.category/whiteboard
-    [:editor/new-whiteboard
-     :whiteboard/select
-     :whiteboard/pan
-     :whiteboard/portal
-     :whiteboard/pencil
-     :whiteboard/highlighter
-     :whiteboard/eraser
-     :whiteboard/connector
-     :whiteboard/text
-     :whiteboard/rectangle
-     :whiteboard/ellipse
-     :whiteboard/reset-zoom
-     :whiteboard/zoom-to-fit
-     :whiteboard/zoom-to-selection
-     :whiteboard/zoom-out
-     :whiteboard/zoom-in
-     :whiteboard/send-backward
-     :whiteboard/send-to-back
-     :whiteboard/bring-forward
-     :whiteboard/bring-to-front
-     :whiteboard/lock
-     :whiteboard/unlock
-     :whiteboard/group
-     :whiteboard/ungroup
-     :whiteboard/toggle-grid
-     :whiteboard/clone-left
-     :whiteboard/clone-right
-     :whiteboard/clone-top
-     :whiteboard/clone-bottom]
-
-    :shortcut.category/others
-    [:pdf/previous-page
-     :pdf/next-page
-     :pdf/close
-     :pdf/find
-     :command/toggle-favorite
-     :command/run
-     :graph/export-as-html
-     :graph/open
-     :graph/remove
-     :graph/add
-     :graph/save
-     :graph/re-index
-     :sidebar/close-top
-     :sidebar/clear
-     :sidebar/open-today-page
-     :search/re-index
-     :editor/insert-youtube-timestamp
-     :editor/open-file-in-default-app
-     :editor/open-file-in-directory
-     :editor/copy-page-url
-     :window/close
-     :auto-complete/prev
-     :auto-complete/next
-     :auto-complete/complete
-     :auto-complete/shift-complete
-     :auto-complete/open-link
-     :date-picker/prev-day
-     :date-picker/next-day
-     :date-picker/prev-week
-     :date-picker/next-week
-     :date-picker/complete
-     :git/commit
-     :dev/show-block-data
-     :dev/show-block-ast
-     :dev/show-page-data
-     :dev/show-page-ast
-     :ui/clear-all-notifications]
-
-    :shortcut.category/plugins
-    []}))
+    {:shortcut.category/basics
+     [:go/search
+      :editor/new-block
+      :editor/new-line
+      :editor/indent
+      :editor/outdent
+      :editor/select-all-blocks
+      :editor/select-parent
+      :go/search-in-page
+      :command-palette/toggle
+      :go/electron-find-in-page
+      :go/electron-jump-to-the-next
+      :go/electron-jump-to-the-previous
+      :editor/undo
+      :editor/redo
+      :editor/copy
+      :editor/copy-text
+      :editor/cut]
+
+     :shortcut.category/formatting
+     [:editor/bold
+      :editor/insert-link
+      :editor/italics
+      :editor/strike-through
+      :editor/highlight]
+
+     :shortcut.category/navigating
+     [:editor/up
+      :editor/down
+      :editor/left
+      :editor/right
+      :editor/collapse-block-children
+      :editor/expand-block-children
+      :editor/toggle-open-blocks
+      :go/backward
+      :go/forward
+      :go/home
+      :go/journals
+      :go/all-pages
+      :go/graph-view
+      :go/all-graphs
+      :go/whiteboards
+      :go/flashcards
+      :go/tomorrow
+      :go/next-journal
+      :go/prev-journal
+      :go/keyboard-shortcuts]
+
+     :shortcut.category/block-editing
+     [:editor/backspace
+      :editor/delete
+      :editor/indent
+      :editor/outdent
+      :editor/new-block
+      :editor/new-line
+      :editor/zoom-in
+      :editor/zoom-out
+      :editor/cycle-todo
+      :editor/follow-link
+      :editor/open-link-in-sidebar
+      :editor/move-block-up
+      :editor/move-block-down
+      :editor/escape-editing]
+
+     :shortcut.category/block-command-editing
+     [:editor/backspace
+      :editor/clear-block
+      :editor/kill-line-before
+      :editor/kill-line-after
+      :editor/beginning-of-block
+      :editor/end-of-block
+      :editor/forward-word
+      :editor/backward-word
+      :editor/forward-kill-word
+      :editor/backward-kill-word
+      :editor/replace-block-reference-at-point
+      :editor/copy-embed
+      :editor/paste-text-in-one-block-at-point
+      :editor/select-up
+      :editor/select-down]
+
+     :shortcut.category/block-selection
+     [:editor/open-edit
+      :editor/select-all-blocks
+      :editor/select-parent
+      :editor/select-block-up
+      :editor/select-block-down
+      :editor/delete-selection]
+
+     :shortcut.category/toggle
+     [:ui/toggle-help
+      :editor/toggle-open-blocks
+      :editor/toggle-undo-redo-mode
+      :editor/toggle-number-list
+      :ui/toggle-wide-mode
+      :ui/toggle-document-mode
+      :ui/toggle-brackets
+      :ui/toggle-theme
+      :ui/toggle-left-sidebar
+      :ui/toggle-right-sidebar
+      :ui/toggle-settings
+      :ui/toggle-contents
+      :ui/accent-color-reset
+      :ui/accent-colors-picker]
+
+     :shortcut.category/whiteboard
+     [:editor/new-whiteboard
+      :whiteboard/select
+      :whiteboard/pan
+      :whiteboard/portal
+      :whiteboard/pencil
+      :whiteboard/highlighter
+      :whiteboard/eraser
+      :whiteboard/connector
+      :whiteboard/text
+      :whiteboard/rectangle
+      :whiteboard/ellipse
+      :whiteboard/reset-zoom
+      :whiteboard/zoom-to-fit
+      :whiteboard/zoom-to-selection
+      :whiteboard/zoom-out
+      :whiteboard/zoom-in
+      :whiteboard/send-backward
+      :whiteboard/send-to-back
+      :whiteboard/bring-forward
+      :whiteboard/bring-to-front
+      :whiteboard/lock
+      :whiteboard/unlock
+      :whiteboard/group
+      :whiteboard/ungroup
+      :whiteboard/toggle-grid
+      :whiteboard/clone-left
+      :whiteboard/clone-right
+      :whiteboard/clone-top
+      :whiteboard/clone-bottom]
+
+     :shortcut.category/others
+     [:pdf/previous-page
+      :pdf/next-page
+      :pdf/close
+      :pdf/find
+      :command/toggle-favorite
+      :command/run
+      :graph/export-as-html
+      :graph/open
+      :graph/remove
+      :graph/add
+      :graph/save
+      :graph/re-index
+      :sidebar/close-top
+      :sidebar/clear
+      :sidebar/open-today-page
+      :search/re-index
+      :editor/insert-youtube-timestamp
+      :editor/open-file-in-default-app
+      :editor/open-file-in-directory
+      :editor/copy-page-url
+      :window/close
+      :auto-complete/prev
+      :auto-complete/next
+      :auto-complete/complete
+      :auto-complete/shift-complete
+      :auto-complete/open-link
+      :date-picker/prev-day
+      :date-picker/next-day
+      :date-picker/prev-week
+      :date-picker/next-week
+      :date-picker/complete
+      :git/commit
+      :dev/show-block-data
+      :dev/show-block-ast
+      :dev/show-page-data
+      :dev/show-page-ast
+      :ui/clear-all-notifications]
+
+     :shortcut.category/plugins
+     []}))
 
 (let [category-maps {::category       (set (keys @*category))
                      ::dicts/category dicts/categories}]
   (assert (= (::category category-maps) (::dicts/category category-maps))
-          (str "Keys for category maps must have an english label "
-               (data/diff (::category category-maps) (::dicts/category category-maps)))))
+    (str "Keys for category maps must have an english label "
+      (data/diff (::category category-maps) (::dicts/category category-maps)))))
 
 (defn get-category-shortcuts
   "Active list of categories for docs purpose"
@@ -948,9 +948,9 @@
      (swap! *shortcut-cmds assoc id (:cmd shortcut-map))
      (let [plugin? (str/starts-with? (str id) ":plugin.")
            category (or (:category shortcut-map)
-                        (if plugin?
-                          :shortcut.category/plugins
-                          :shortcut.category/others))]
+                      (if plugin?
+                        :shortcut.category/plugins
+                        :shortcut.category/others))]
        (swap! *category update category #(conj % id))))))
 
 (defn remove-shortcut!

+ 1 - 0
src/main/frontend/schema/handler/common_config.cljc

@@ -63,6 +63,7 @@
     [:ref/default-open-blocks-level :int]
     [:ref/linked-references-collapsed-threshold :int]
     [:graph/settings [:map-of :keyword :boolean]]
+    [:graph/forcesettings [:map-of :keyword :int]]
     [:favorites [:vector :string]]
     ;; There isn't a :float yet
     [:srs/learning-fraction float?]

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

@@ -7,7 +7,6 @@
             [clojure.string :as string]
             [dommy.core :as dom]
             [electron.ipc :as ipc]
-            [frontend.colors :as colors]
             [frontend.mobile.util :as mobile-util]
             [frontend.spec.storage :as storage-spec]
             [frontend.storage :as storage]
@@ -645,6 +644,10 @@ Similar to re-frame subscriptions"
   []
   (:graph/settings (sub-config)))
 
+(defn graph-forcesettings
+  []
+  (:graph/forcesettings (sub-config)))
+
 ;; Enable by default
 (defn show-brackets?
   []
@@ -1390,14 +1393,15 @@ Similar to re-frame subscriptions"
            input (medley/filter-vals
                    #(not (nil? %1))
                    {:modal/id            id
-                    :modal/label         (or label (if center? "ls-modal-align-center" ""))
+                    :modal/label         (if label (name label) "")
+                    :modal/class         (if center? "as-center" "")
                     :modal/payload       payload
                     :modal/show?         (if (boolean? show?) show? true)
                     :modal/panel-content panel-content
                     :modal/close-btn?    close-btn?
                     :modal/close-backdrop? (if (boolean? close-backdrop?) close-backdrop? true)})]
        (swap! state update-in
-              [:modal/subsets (or idx (count modals))]
+         [:modal/subsets (or idx (count modals))]
               merge input)
        (:modal/subsets @state)))))
 
@@ -1433,7 +1437,8 @@ Similar to re-frame subscriptions"
          (<! (async/timeout 100)))
        (swap! state assoc
               :modal/id id
-              :modal/label (or label (if center? "ls-modal-align-center" ""))
+              :modal/label (if label (name label) "")
+              :modal/class (if center? "as-center" "")
               :modal/show? (boolean modal-panel-content)
               :modal/panel-content modal-panel-content
               :modal/payload payload
@@ -2236,19 +2241,10 @@ Similar to re-frame subscriptions"
   (util/set-android-theme))
 
 (defn unset-color-accent! []
-  (swap! state assoc :ui/radix-color nil)
+  (swap! state assoc :ui/radix-color :logseq)
   (storage/remove :ui/radix-color)
   (util/set-android-theme))
 
-(defn cycle-color! []
-  (let [current-color (get-color-accent)
-        next-color (->> (cons nil colors/color-list)
-                        (drop-while #(not= % current-color))
-                        (second))]
-    (if next-color
-      (set-color-accent! next-color)
-      (unset-color-accent!))))
-
 (defn handbook-open?
   []
   (:ui/handbooks-open? @state))

+ 11 - 10
src/main/frontend/tippy-tooltip.css

@@ -5,7 +5,7 @@
 .tippy-popper[x-placement^=top] [x-arrow],
 .tippy-popper[x-placement^=top] [x-arrow].arrow-small,
 .tippy-popper[x-placement^=top] [x-arrow].arrow-big {
-  border-top-color: var(--ls-quaternary-background-color);
+  border-top-color: var(--ls-tertiary-background-color);
 }
 
 .tippy-popper[x-placement^=top] .tippy-tooltip.transparent-theme [x-circle],
@@ -25,7 +25,7 @@
 .tippy-popper[x-placement^=bottom] [x-arrow],
 .tippy-popper[x-placement^=bottom] [x-arrow].arrow-small,
 .tippy-popper[x-placement^=bottom] [x-arrow].arrow-big {
-  border-bottom-color: var(--ls-quaternary-background-color);
+  border-bottom-color: var(--ls-tertiary-background-color);
 }
 
 .tippy-popper[x-placement^=bottom] .tippy-tooltip.transparent-theme [x-arrow],
@@ -37,7 +37,7 @@
 .tippy-popper[x-placement^=left] [x-arrow],
 .tippy-popper[x-placement^=left] [x-arrow].arrow-small,
 .tippy-popper[x-placement^=left] [x-arrow].arrow-big {
-  border-left-color: var(--ls-quaternary-background-color);
+  border-left-color: var(--ls-tertiary-background-color);
 }
 
 .tippy-popper[x-placement^=left] .tippy-tooltip.transparent-theme [x-arrow],
@@ -49,21 +49,21 @@
 .tippy-popper[x-placement^=right] [x-arrow],
 .tippy-popper[x-placement^=right] [x-arrow].arrow-small,
 .tippy-popper[x-placement^=right] [x-arrow].arrow-big {
-  border-right-color: var(--ls-quaternary-background-color);
+  border-right-color: var(--ls-tertiary-background-color);
 }
 
 .tippy-popper[x-placement^=right] .tippy-tooltip.transparent-theme [x-arrow],
 .tippy-popper[x-placement^=right] .tippy-tooltip.transparent-theme [x-arrow].arrow-small,
 .tippy-popper[x-placement^=right] .tippy-tooltip.transparent-theme [x-arrow].arrow-big {
-  border-right-color: var(--ls-secondary-background-color);
+  border-right-color: var(--ls-tertiary-background-color);
 }
 
 .tippy-tooltip {
-  color: var(--ls-primary-text-color);
-  font-size: inherit;
-  padding: 0;
+  @apply shadow border border-gray-07 dark:border-gray-05 px-2 py-1;
+
   will-change: auto;
-  background-color: var(--ls-quaternary-background-color);
+  color: var(--ls-primary-text-color, hsl(var(--foreground)));
+  background-color: var(--lx-gray-03, var(--ls-tertiary-background-color, var(--rx-gray-03)));
 }
 
 .tippy-tooltip [x-circle] {
@@ -75,7 +75,8 @@
 }
 
 .tippy-wrapper {
-  background-color: var(--ls-quaternary-background-color);
+  border-radius: 8px;
+  margin: 0 -6px;
 }
 
 .tippy-hover {

+ 29 - 16
src/main/frontend/ui.cljs

@@ -177,14 +177,14 @@
                        (string/split #" "))
                    sequence)]
     [:span.keyboard-shortcut
-     (shui/shortcut-v1 sequence opts)]))
+     (shui/shortcut sequence opts)]))
 
 (rum/defc menu-link
   [{:keys [only-child? no-padding? class shortcut] :as options} child]
   (if only-child?
     [:div.menu-link
      (dissoc options :only-child?) child]
-    [:a.flex.justify-between.px-4.py-2.text-sm.transition.ease-in-out.duration-150.cursor.menu-link
+    [:a.flex.justify-between.menu-link
      (cond-> options
              (true? no-padding?)
              (assoc :class (str class " no-padding"))
@@ -194,7 +194,7 @@
 
      [:span.flex-1 child]
      (when shortcut
-       [:span.ml-1 (render-keyboard-shortcut shortcut)])]))
+       [:span.ml-1 (render-keyboard-shortcut shortcut {:interactive? false})])]))
 
 (rum/defc dropdown-with-links
   [content-fn links
@@ -647,13 +647,16 @@
         close-backdrop? (state/sub :modal/close-backdrop?)
         show? (state/sub :modal/show?)
         label (state/sub :modal/label)
+        class (state/sub :modal/class)
         close-fn (fn []
                    (state/close-modal!)
                    (state/close-settings!))
         modal-panel-content (or modal-panel-content (fn [_close] [:div]))]
     [:div.ui__modal
-     {:style {:z-index (if show? 999 -1)}
-      :label label}
+     {:style {:z-index (if show? 999 -1)
+              :display (if show? "flex" "none")}
+      :label label
+      :class class}
      (css-transition
       {:in show? :timeout 0}
       (fn [state]
@@ -718,12 +721,14 @@
             close-backdrop? (:modal/close-backdrop? modal)
             show? (:modal/show? modal)
             label (:modal/label modal)
+            class (:modal/class modal)
             close-fn (fn []
                        (state/close-sub-modal! id))
             modal-panel-content (or modal-panel-content (fn [_close] [:div]))]
         [:div.ui__modal.is-sub-modal
          {:style {:z-index (if show? (+ 999 idx) -1)}
-          :label label}
+          :label label
+          :class class}
          (css-transition
           {:in show? :timeout 0}
           (fn [state]
@@ -986,16 +991,24 @@
                           [:div {:key "tippy"} ""])))
            (rum/fragment {:key "tippy-children"} child))))
 
-(rum/defc slider
-  [default-value {:keys [min max on-change]}]
-  [:input.cursor-pointer
-   {:type      "range"
-    :value     (int default-value)
-    :min       min
-    :max       max
-    :style     {:width "100%"}
-    :on-change #(let [value (util/evalue %)]
-                  (on-change value))}])
+(rum/defcs slider < rum/reactive
+  {:init (fn [state]
+           (assoc state ::value (atom (first (:rum/args state)))))}
+  [state _default-value {:keys [min max on-change]}]
+  (let [*value (::value state)
+        value (rum/react *value)
+        value' (int value)]
+    (assert (int? value'))
+    [:input.cursor-pointer
+     {:type      "range"
+      :value     value'
+      :min       min
+      :max       max
+      :style     {:width "100%"}
+      :on-change #(let [value (util/evalue %)]
+                    (reset! *value value))
+      :on-mouse-up #(let [value (util/evalue %)]
+                      (on-change value))}]))
 
 (rum/defcs tweet-embed < (rum/local true :loading?)
   [state id]

+ 32 - 34
src/main/frontend/ui.css

@@ -6,7 +6,7 @@
     -webkit-overflow-scrolling: touch;
 
     .menu-link {
-      word-break: break-all;
+      @apply break-all rounded;
 
       .has-help {
         display: flex;
@@ -14,20 +14,17 @@
         justify-content: space-between;
 
         small {
-          visibility: visible;
-          cursor: help;
+          @apply cursor-help visible flex;
 
           svg {
-            opacity: .3;
-          }
-
-          &:hover svg {
-            opacity: .9;
+            @apply opacity-30 hover:opacity-90;
           }
         }
       }
 
       &:hover, &.chosen {
+        background-color: var(--lx-gray-05, var(--ls-menu-hover-color, hsl(var(--secondary))));
+
         .has-help small {
           visibility: visible;
         }
@@ -35,7 +32,7 @@
 
       &:not(.chosen):hover {
         background-color: unset !important;
-        color: or(--ls-autocomplete-color-hover, --lx-gray-12, --ls-primary-text-color);
+        color: var(--lx-gray-12, var(--ls-primary-text-color, hsl(var(--secondary))));
       }
     }
   }
@@ -48,9 +45,7 @@
 }
 
 .ui__ac-group-name {
-  @apply p-2 text-xs;
-  color: or(--ls-dropdown-title-color, --lx-gray-11-alpha, --ls-block-ref-link-text-color);
-  background-color: or(--ls-dropdown-title-background, --lx-gray-03);
+  @apply p-2 text-xs text-popover-foreground/20 font-medium;
 }
 
 .search-all #ui__ac-inner {
@@ -71,8 +66,10 @@
   }
 
   .notification-area {
-    background-color: or(--logseq-of-notification-background, --lx-gray-04, --ls-tertiary-background-color, #fff);
-    color: or(--ls-notification-text-color, --lx-gray-11, --ls-primary-text-color);
+    @apply border;
+
+    background-color: or(--ls-notification-background, --lx-gray-03, --ls-tertiary-background-color, --rx-gray-03);
+    color: or(--ls-notification-text-color, --lx-gray-11, --ls-primary-text-color, --rx-gray-11);
   }
 }
 
@@ -103,7 +100,7 @@
   top-12 sm:top-[calc(3vh+50px)];
 
   &-overlay {
-    @apply fixed inset-0 transition-opacity;
+    @apply fixed inset-0;
   }
 
   &-overlay div {
@@ -113,10 +110,9 @@
   }
 
   &-panel {
-    @apply relative rounded-md shadow-xl border-none;
+    @apply relative rounded-md shadow-lg border border-gray-06 overflow-hidden;
 
-    overflow: hidden;
-    background: or(--ls-modal-panel-color, --lx-gray-04, --ls-secondary-background-color);
+    background: var(--lx-gray-02, var(--ls-secondary-background-color, hsl(var(--popover))));
 
     .panel-content {
       overflow-y: auto;
@@ -163,7 +159,8 @@
     }
   }
 
-  &[label="ls-modal-align-center"] {
+  &[label="ls-modal-align-center"],
+  &.as-center{
     top: 0;
 
     .ui__modal-panel {
@@ -283,10 +280,11 @@ html.is-mobile {
 }
 
 .dropdown-wrapper {
-  background-color: or(--ls-dropdown-background, --lx-gray-03, --ls-primary-background-color, #fff);
-  border: 1px solid or(--ls-dropdown-border-color, --lx-gray-05, --ls-tertiary-background-color);
-  min-width: 12rem;
-  border-radius: 6px;
+  @apply border min-w-[12rem] rounded-md bg-popover overflow-hidden;
+
+  .menu-links-wrapper {
+    @apply border-0;
+  }
 }
 
 .dropdown-caret {
@@ -306,10 +304,10 @@ html.is-mobile {
   @apply block w-full pl-3 pr-10 py-2 text-base leading-6 rounded
   border-gray-300 focus:outline-none sm:text-sm sm:leading-5;
 
-  background-color: or(--ls-form-select-background-color, --lx-gray-03, --ls-primary-background-color, transparent);
+  background-color: var(--lx-gray-03, var(--ls-primary-background-color, transparent));
   background-repeat: no-repeat;
   border-width: 1px;
-  border-color: or(--ls-form-select-border-color, --lx-gray-07, --ls-border-color);
+  border-color: var(--lx-gray-07, var(--ls-border-color));
 
   &.is-small {
     @apply pl-2 py-1.5 sm:leading-4 sm:text-xs;
@@ -317,11 +315,7 @@ html.is-mobile {
 }
 
 .form-input {
-  @apply block w-full pl-2
-  sm:text-sm sm:leading-5 rounded;
-
-  border-width: 1px;
-  border-color: or(--ls-form-input-border-color, --lx-gray-07, --ls-border-color);
+  @apply block w-full pl-2 sm:text-sm sm:leading-5 rounded bg-background border border-gray-07;
 
   &:focus {
     box-shadow: 0 0 0 2px rgba(164, 202, 254, 0.45);
@@ -354,9 +348,9 @@ html.is-mobile {
   width: 24px;
   height: 24px;
   flex-shrink: 0;
-  border-color: or(--ls-type-icon-border-color, --lx-gray-03, --ls-primary-background-color);
+  border-color: var(--lx-gray-03, var(--ls-primary-background-color));
   overflow: hidden;
-  color: or(--ls-type-icon-text-color, --lx-gray-12, --ls-primary-text-color);
+  color: var(--lx-gray-12, var(--ls-primary-text-color));
 
   .ti,
   .tie {
@@ -365,7 +359,7 @@ html.is-mobile {
 
   &:before {
     @apply block absolute inset-0 ;
-    background: or(--ls-type-icon-before-color, --lx-gray-03, --ls-primary-background-color);
+    background: var(--lx-gray-03, var(--ls-primary-background-color));
     content: " ";
   }
 }
@@ -379,5 +373,9 @@ html.is-mobile {
 }
 
 .ui__toggle-background-off {
-  background: or(--ls-toggle-off-background, --lx-gray-08, rgb(212, 212, 212));
+  background: var(--lx-gray-08, rgb(212, 212, 212));
 }
+
+input[type='range'] {
+  accent-color: var(--lx-accent-10, var(--rx-blue-10));
+}

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

@@ -1,3 +1,3 @@
 (ns ^:no-doc frontend.version)
 
-(defonce version "0.10.5")
+(defonce version "0.10.6")

+ 6 - 6
src/resources/dicts/en.edn

@@ -261,7 +261,7 @@
  :editor/expand-block-children "Expand all"
  :editor/collapse-block-children "Collapse all"
  :editor/delete-selection "Delete selected blocks"
- :editor/cycle-todo "Rotate the TODO state of the current item"
+ :editor/cycle-todo "Rotate the TODO state"
  :dev/show-page-data "(Dev) Show page data"
  :dev/show-block-data "(Dev) Show block data"
  :dev/show-block-ast "(Dev) Show block AST"
@@ -755,8 +755,8 @@
   :editor/backward-kill-word    "Delete a word backwards"
   :editor/replace-block-reference-at-point "Replace block reference with its content at point"
   :editor/paste-text-in-one-block-at-point "Paste text into one block at point"
-  :editor/insert-youtube-timestamp         "Insert youtube timestamp"
-  :editor/cycle-todo              "Rotate the TODO state of the current item"
+  :editor/insert-youtube-timestamp "Insert youtube timestamp"
+  :editor/cycle-todo              "Rotate the TODO state"
   :editor/up                      "Move cursor up / Select up"
   :editor/down                    "Move cursor down / Select down"
   :editor/left                    "Move cursor left / Open selected block at beginning"
@@ -852,9 +852,9 @@
   :ui/toggle-help                 "Toggle help"
   :ui/toggle-theme                "Toggle between dark/light theme"
   :ui/toggle-contents             "Toggle Contents in sidebar"
-  :ui/cycle-color-off             "Cycle color off"
-  :ui/cycle-color                 "Cycle color"
-  ;;  :ui/open-new-window             "Open another window"
+  :ui/accent-color-reset          "Reset accent color"
+  :ui/accent-colors-picker        "Pick  accent color"
+   ;;  :ui/open-new-window             "Open another window"
   :command/toggle-favorite        "Add to/remove from favorites"
   :editor/open-file-in-default-app "Open file in default app"
   :editor/open-file-in-directory   "Open file in parent directory"

+ 0 - 2
src/resources/dicts/es.edn

@@ -226,8 +226,6 @@
  :command.sidebar/close-top                         "Cierra el elemento superior en la barra lateral derecha"
  :command.sidebar/open-today-page                   "Abrir la página de hoy en barra lateral derecha"
  :command.ui/clear-all-notifications                "Borrar todas las notificaciones"
- :command.ui/cycle-color                            "Ciclo de color"
- :command.ui/cycle-color-off                        "Ciclo de color desactivado"
  :command.ui/goto-plugins                           "Ir al panel de extensiones"
  :command.ui/install-plugins-from-file              "Instalar extensiones de plugins.edn"
  :command.ui/select-theme-color                     "Seleccionar temas de colores disponibles"

+ 515 - 0
src/resources/dicts/fa.edn

@@ -0,0 +1,515 @@
+{:accessibility/skip-to-main-content "برو به محتوای اصلی"
+ :tutorial/text #resource "tutorials/tutorial-fa.md"
+ :tutorial/dummy-notes #resource "tutorials/dummy-notes-fa.md"
+ :on-boarding/demo-graph "این یک گراف نمایشی است و تا زمانی که یک پوشه محلی را باز نکنید، تغییرات آن ذخیره نخواهند شد."
+ :on-boarding/add-graph "افزودن یک گراف"
+ :on-boarding/open-local-dir "باز کردن یک پوشه محلی"
+ :on-boarding/new-graph-desc-1 "لاگ‌سیک هم از مارک‌داون و هم از حالت Org پیشتیبانی می‌کند. می‌توانید یک پوشه موجود را باز کرده و یا یکی روی دستگاه‌تان بسازید. داده‌های شما صرفا روی دستگاه شما ذخیره می‌شوند."
+ :on-boarding/new-graph-desc-2 "بعد از این که پوشه‌ای را باز کنید, سه پوشه در آن ساخته می‌شود:"
+ :on-boarding/new-graph-desc-3 "/journals - محل ذخیره برگه‌های روزنگارها"
+ :on-boarding/new-graph-desc-4 "/pages - محل ذخیره بقیه برگه‌ها"
+ :on-boarding/new-graph-desc-5 "/logseq - محل ذخیره تنظیمات، custom.css و برخی فراداده‌ها."
+ :on-boarding/welcome-whiteboard-modal-title "یک بوم جدید برای ایده‌های شما."
+ :on-boarding/welcome-whiteboard-modal-description "نخته‌سفید‌ها ابزار قوی برای طوفان فکری و سازمان‌دهی هستند. اکنون می‌توانید افکارتان را از پایگاه دانش و یا مستقیما در کنار هم قرار دهید تا بتوانید آن‌ها را به روشی جدید به هم وصل کرده، ارتباط دهید و بفهمید. "
+ :on-boarding/welcome-whiteboard-modal-skip "پرش"
+ :on-boarding/welcome-whiteboard-modal-start "شروع کار با تخته‌سفید"
+ :on-boarding/tour-whiteboard-home "{1} خانه‌ای برای تخته‌سفیدهای شما"
+ :on-boarding/tour-whiteboard-home-description "تخته‌سفیدها، بخش مختص به خود را در برنامه دارند؛ جایی که شما می‌توانید همه‌شان را در یک نگاه ببینید، تخته‌های جدیدی بسازید و یا به راحتی پاک‌شان کنید."
+ :on-boarding/tour-whiteboard-new "{1} ساختن تخته‌سفید جدید"
+ :on-boarding/tour-whiteboard-new-description "راه‌های متعددی برای ساخت یک نخته‌سفید جدید وجود دارد. یکی از آن‌ها همیشه درست در همین مکان روی پیشخوان است."
+ :handbook/title           "راهنما"
+ :handbook/topics          "موضوعات"
+ :handbook/popular-topics  "موضوعات محبوب"
+ :handbook/help-categories "دسته‌بندی‌های راهنما"
+ :handbook/search          "جستجو"
+ :handbook/home            "خانه"
+ :handbook/settings        "تنظیمات"
+ :handbook/close           "بستن"
+ :on-boarding/tour-whiteboard-btn-next "بعدی"
+ :on-boarding/tour-whiteboard-btn-back "قبلی"
+ :on-boarding/tour-whiteboard-btn-finish "پایان"
+ :on-boarding/quick-tour-btn-next "بعدی"
+ :on-boarding/quick-tour-btn-back "قبلی"
+ :on-boarding/quick-tour-btn-finish "پایان"
+ :on-boarding/quick-tour-btn-skip "پرش از گشت و گذار سریع"
+ :on-boarding/quick-tour-steps "گام "
+ :on-boarding/quick-tour-help-title "❓ راهنما"
+ :on-boarding/quick-tour-help-desc "همواره می‌توانید با کلیک روی اینجا، به راهنما و ساید اطلاعات درباره لاگ‌سیک دسترسی پیدا کنید."
+ :on-boarding/quick-tour-journal-page-title "📆 برگه روزنگار"
+ :on-boarding/quick-tour-journal-page-desc-1 "این، برگه روزنگار امروز است. می‌توانید افکار، ایده‌ها و چیزهایی را که یاد می‌گیرید را اینجا وارد کنید.نگران سازمان‌دهی نباشید. فقط بنویسید و"
+ :on-boarding/quick-tour-journal-page-desc-3 "افکار شما."
+ :on-boarding/quick-tour-left-sidebar-title "👀 نوار کناری چپ"
+ :on-boarding/quick-tour-left-sidebar-desc "نوار کنار چپ را باز کنید تا موارد مهم فهرست در لاگ‌سیک را ببینید."
+ :on-boarding/quick-tour-favorites-title "⭐️ برگزیده‌ها"
+ :on-boarding/quick-tour-favorites-desc-1 "با کمک فهرست `...` در هر برگه می‌توانید برگه‌های برگزیده را سنجاق کنید."
+ :on-boarding/quick-tour-favorites-desc-2 "ما همچنین تعدادی برگه قالب را برای کمک به شما برای شروع اضافه کرده‌ایم. هر زمان که شروع به ایجاد یادداشت‌های خود کردید، می‌توانید این‌ها را حذف کنید."
+ :on-boarding/command-palette-quick-tour "گشت و گذار سریع برای آماده‌سازی"
+ :on-boarding/importing-main-title "درون‌ریزی یادداشت‌های موجود"
+ :on-boarding/importing-main-desc "این کار را می‌توانید بعدا هم انجام دهید."
+ :on-boarding/importing-title "آیا یادداشت‌هایی از پیش دارید که بخواهد وارد برنامه کنید؟"
+ :on-boarding/importing-desc "اگر در قالب‌های JSON، EDN یا مارک‌داون هستند، لاگ‌سیک می‌تواند با آن‌ها کار کند."
+ :on-boarding/importing-roam-desc "درون‌ریزی یک خروجی JSON از گراف Roam."
+ :on-boarding/importing-lsq-desc "درون‌ریزی یک خروجی JSON یا END از گراف لاگ‌سیک."
+ :on-boarding/importing-opml-desc " درون‌ریزی پرونده‌های OPML"
+ :on-boarding/main-title (fn [] ["به " [:strong "لاگ‌سیک"] "خوش‌آمدید!"])
+ :on-boarding/main-desc "در ابتدا نیاز به انتخاب پوشه‌ای دارید که لاگ‌سیک، افکار، ایده‌ها و یادداشت‌های شما را در آن‌جا ذخیره کند."
+ :on-boarding/section-btn-title "انتخاب یک پوشه"
+ :on-boarding/section-btn-desc  "یک پوشه از پیش موجود را باز کرده و یا یکی بسازید"
+ :on-boarding/section-title     "چگونه لاگ‌سیک کار شما را ذخیره می‌کند"
+ :on-boarding/section-desc      "داخل پوشه‌ای که انتخاب می‌کنید، لاگ‌سیک چهار پوشه خواهد ساخت."
+ :on-boarding/section-tip-1     "هر برگه  یک پرونده است که تنها روی {1} شما ذخیره می‌شود."
+ :on-boarding/section-tip-2     "بعدا می‌توانید انتخاب کنید که آن را هم‌گام کنید."
+ :on-boarding/section-assets    "گ‍رافیک‌ها و اسناد"
+ :on-boarding/section-computer  "رایانه"
+ :on-boarding/section-journals  "یادداشت‌های روزانه"
+ :on-boarding/section-pages     "برگه‌ها"
+ :on-boarding/section-phone     "گوشی"
+ :on-boarding/section-app       "فرایندهای داخلی برنامه"
+ :on-boarding/section-config    "پرونده پیکربندی"
+ :help/title-usage            "استفاده"
+ :help/title-community        "اجتماع"
+ :help/title-development      "توسعه"
+ :help/title-about            "درباره"
+ :help/title-terms            "قوانین"
+ :help/start                  "شروع"
+ :help/about                  "درباره لاگ‌سیک"
+ :help/roadmap                "مسیر توسعه"
+ :help/bug                    "گزارش مشکل"
+ :help/feature                "درخواست ویژگی"
+ :help/changelog              "فهرست تغییرات"
+ :help/blog                   "بلاگ لاگ‌سیک"
+ :help/docs                   "مستندات"
+ :help/privacy                "سیاست حریم شخصی"
+ :help/terms                  "قوانین"
+ :help/forum-community        "تالار گفتگو"
+ :help/shortcuts              "کلیدهای میان‌بر"
+ :help/shortcuts-triggers     "محرک‌ها"
+ :help/shortcut               "میان‌بر"
+ :help/search                 "جستجو در برگه‌ها/بلوک‌ها/نظرات"
+ :help/block-reference        "ارجاع بلوک"
+ :help/open-link-in-sidebar   "باز کردن پیوند در نوار کناری"
+ :search/page-names      "جستجو در نام برگه‌ها"
+ :search-item/whiteboard "تخته‌سفید"
+ :search-item/page       "برگه"
+ :search-item/no-result  "نتایجی یافت نشد"
+ :help/context-menu    "فهرست زمینه‌ای بلوک"
+ :help/markdown-syntax "نحو مارک‌داون"
+ :help/org-mode-syntax "نحو حالت Org"
+ :bold "ضخیم"
+ :italics "کج"
+ :strikethrough "خط‌زده"
+ :code "کد"
+ :untitled "بدون عنوان"
+ :right-side-bar/help                 "راهنما"
+ :right-side-bar/switch-theme         "پوسته"
+ :right-side-bar/contents             "محتوا"
+ :right-side-bar/page-graph           "گراف برگه"
+ :right-side-bar/history-global       "سراسری"
+ :right-side-bar/history-pageonly     "فقط برگه"
+ :right-side-bar/block-ref            "ارجاعات بلوک"
+ :right-side-bar/graph-view           "نمای گراف"
+ :right-side-bar/all-pages            "همه برگه‌ها"
+ :right-side-bar/whiteboards          "تخته‌سفیدها"
+ :right-side-bar/flashcards           "فلش کارت‌ها"
+ :right-side-bar/new-page             "برگه جدید"
+ :right-side-bar/show-journals        "نمایش روزنگارها"
+ :right-side-bar/separator            "دستگیره تغییر اندازه نوار سمت راست"
+ :right-side-bar/toggle-right-sidebar "باز و بسته کردن نوار سمت راست"
+ :right-side-bar/pane-close           "بستن"
+ :right-side-bar/pane-close-others    "بسته بقیه"
+ :right-side-bar/pane-close-all       "بستن همه"
+ :right-side-bar/pane-collapse        "جمع کردن"
+ :right-side-bar/pane-collapse-others "جمع کردن بقیه"
+ :right-side-bar/pane-collapse-all    "جمع کردن همه"
+ :right-side-bar/pane-expand          "گسترش"
+ :right-side-bar/pane-expand-all      "گسترش همه"
+ :right-side-bar/pane-open-as-page    "باز کردن به عنوان صفحه"
+ :right-side-bar/pane-more            "بیشتر"
+ :left-side-bar/switch           "تغییر به:"
+ :left-side-bar/journals         "روزنگارها"
+ :left-side-bar/create           "ساخت"
+ :left-side-bar/new-page         "برگه جدید"
+ :left-side-bar/new-whiteboard   "تخته‌سفید جدید"
+ :left-side-bar/nav-favorites    "برگزیده‌ها"
+ :left-side-bar/nav-recent-pages "اخیر"
+ :page/something-went-wrong "اشتباهی رخ داد"
+ :page/logseq-is-having-a-problem "لاگ‌سیک دچار مشکلی شده است. برای بازگرداندن آن به حالت قابل استفاده لطفا گام‌های زیر را به ترتیب انجام دهید:"
+ :page/step "گام {1}"
+ :page/try "امتحان کنید"
+ :page/slide-view "نمایش به صورت اسلاید"
+ :page/slide-view-tip-go-fullscreen (fn [] [[:span.opacity-70 "نکته: دکمه "] [:code "f"] [:span.opacity-70 " را برای رفتن به حالت تمام صفحه بفشارید"]])
+ :page/delete-confirmation "مطمئنید که می‌خواید این برگه به همراه پرونده‌اش را پاک کنید؟"
+ :page/open-in-finder "باز کردن در پوشه"
+ :page/open-with-default-app "باز کردن با برنامه پیش‌فرض"
+ :page/make-public "عمومی ساختن برای انتشار"
+ :page/version-history "نمایش تاریخچهٔ برگه"
+ :page/open-backup-directory "باز کردن پوشه نسخه پشتیبان برگه"
+ :page/make-private "تبدیل به خصوصی"
+ :page/delete "حذف برگه"
+ :page/add-to-favorites "افزودن به برگزیده‌ها"
+ :page/unfavorite "برداشتن از برگزیده‌ها"
+ :page/show-journals "نمایش روزنگارها"
+ :page/show-whiteboards "نمایش تخته‌سفید"
+ :block/name "نام برگه"
+ :page/earlier "پیش‌تر"
+ :page/copy-page-url "رونوشت از نشانی برگه"
+ :page/illegal-page-name "نام برگه غیرمجاز!"
+ :page/page-already-exists "برگه «{1}» از پیش موجود است!"
+ :page/whiteboard-to-journal-error "برگه‌های تخته‌سفید را نمی‌توان به عناوین روزنگار تغییر نام داد!"
+ :file/name "نام پرونده"
+ :file/last-modified-at "آخرین تغییر در"
+ :file/no-data "بدون داده"
+ :file/format-not-supported "قالب .{1} پشتیبانی نمی‌شود."
+ :file/validate-existing-file-error "این برگه از پیش در پروندهٔ دیگری موجود است: {1}, پرونده جاری: {2}. لطفا یکی از آن‌ها را نگه داشته و سپس گراف خود را بازفهرست‌بندی کنید."
+ :file-rn/re-index "اعمال بازفهرست‌بندی پس از تغییر نام پرونده‌ها و یا پس از هم‌گام‌سازی روی دستگاه‌های دیگر قویا پیشنهاد می‌شود."
+ :file-rn/need-action "عمل تغییر نام به منظور انطباق با قالب جدید پیشنهاد می‌شود. انجام بازفهرست‌بندی روی همه دستگاه‌ها پس از هم‌گام‌سازی پرونده‌های تغییر نام یافته ضروری است."
+ :file-rn/or-select-actions " یا پرونده‌های زیر را جدا جدا تغییر نام دهید، سپس "
+ :file-rn/or-select-actions-2 ". این کارها پس از آن که این قاب را ببندید دیگر در دسترس نخواهند بود."
+ :file-rn/legend "🟢 اعمال تغییر نام اختیاری؛ 🟡 عمل تغییر نام نیازمند آن است که عنوان تغییر نکند؛ 🔴 تغییر شکننده."
+ :file-rn/close-panel "بستن قاب"
+ :file-rn/all-action "اعمال همه کارها! ({1})"
+ :file-rn/select-format "(گزینه حالت توسعه‌دهنده، خطرناک!) انتخاب قالب پرونده"
+ :file-rn/rename "تغییر نام پرونده «{1}» به «{2}»"
+ :file-rn/apply-rename "اعمال تغییر نام‌ها"
+ :file-rn/suggest-rename "نیازمند اقدام: "
+ :file-rn/otherwise-breaking "یا عنوان تبدیل می‌شود به"
+ :file-rn/no-action "بسیار خوب! اقدام دیگری مورد نیاز نیست."
+ :file-rn/confirm-proceed "به‌روزرسانی قالب!"
+ :file-rn/select-confirm-proceed "توسعه‌دهنده: قالب نوشتن"
+ :file-rn/unreachable-title "هشدار! نام برگه به {1} و با قالب نام جاری تغییر می‌کند، مگر آن که خصوصیت `title::` به طور دستی تنظیم شود"
+ :file-rn/optional-rename "پیشنهاد: "
+ :file-rn/format-deprecated "در حال حاضر از یک قالب تاریخ‌گذشته استفاده می‌کنید. به‌روزرسانی به آخرین قالب قویا توصیه می‌شود. لطفا پیش از هر اقدامی، از داده‌های خود نسخه پشتیبان بگیرید و لاگ‌سیک را روی همه دستگاه ببندید."
+ :file-rn/filename-desc-1 "این تنظیمات، شیوه ذخیره برگه‌ها در پرونده‌ها را پیکربندی می‌کند. لاگ‌سیک یک برگه را در پرونده‌ای با نام مشابه ذخیره می‌کند."
+ :file-rn/filename-desc-2 "برخی نویسه‌ها مانند «/» یا «?» برای نام پرونده‌ها نامعتبرند."
+ :file-rn/filename-desc-3 "لاگ‌سیک نویسه‌های نامعتبر را با معادل‌شان از طریق کدگذاری URL جایگزین می‌کند تا معتبر شوند (مثلا. «?» به «%3F» تبدیل می‌شود)."
+ :file-rn/filename-desc-4 "جداساز فضانام «/» نیز به منظور زیبایی‌شناسانه با «___» (سه زیرخط) جایگزین می‌شود."
+ :file-rn/instruct-1 "به‌روزرسانی قالب نام پرونده یک فرایند دو مرحله‌ای است:"
+ :file-rn/instruct-2 "1. کلیک "
+ :file-rn/instruct-3 "2. از دستورالعمل زیر برای تغییر نام پرونده‌ها به قالب جدید استفاده کنید:"
+ :page/created-at "ساخته شده در"
+ :page/updated-at "به‌روز شده در"
+ :linked-references/filter-search "جستجو در برگه‌های پیوند شده"
+ :linked-references/unexpected-error "ارجاعات پیوندی: خطای غیر منتظره. لطفا ابتدا گراف خود را بازفهرست‌بندی کنید."
+ :linked-references/filter-heading "پالایه"
+ :linked-references/filter-directions "برای شمول کلیک و برای استثناء کردن، شیفت کلیک کنید. دوباره کلیک کنید تا پاک شود."
+ :linked-references/filter-includes "شامل شده‌ها: "
+ :linked-references/filter-excludes "استثناء شده‌ها: "
+ :linked-references/reference-count (fn [filtered-count total]
+                                      ;; 1 Linked Reference
+                                      ;; 1 of 1 Linked Reference
+                                      ;; 2 of 5 Linked References
+                                      (str
+                                       (when filtered-count
+                                         (str filtered-count " از "))
+                                       total
+                                       (if (= total 1) " ارجاع پیوندی" " ارجاع پیوندی")))
+ :unlinked-references/reference-count (fn [total]
+                                        ;; 1 Unlinked Reference
+                                        ;; 5 Unlinked References
+                                        (str total
+                                         (if (= total 1) " ارجاع غیر پیوندی" " ارجاع غیر پیوندی")))
+ :editor/block-search "جستجو برای بلوک"
+ :text/image "تصویر"
+ :asset/show-in-folder "نمایش تصویر در پوشه"
+ :asset/open-in-browser "باز کردن تصویر در مرورگر"
+ :asset/delete "حذف تصویر"
+ :asset/copy "رونوشت تصویر"
+ :asset/maximize "بیشینه کردن تصویر"
+ :asset/confirm-delete "آیا مطمئنید که می‌خواید این {1} را پاک کنید؟"
+ :asset/physical-delete "به همراه حذف پرونده (توجه داشته باشید که این کار بازگشت‌پذیر نیست)"
+ :color/gray "خاکستری"
+ :color/red "قرمز"
+ :color/yellow "زرد"
+ :color/green "سبز"
+ :color/blue "آبی"
+ :color/purple "بنفش"
+ :color/pink "صورتی"
+ :editor/copy "رونوشت"
+ :editor/cut "برش"
+ :editor/expand-block-children "گسترش همه"
+ :editor/collapse-block-children "جمع کردن همه"
+ :editor/delete-selection "حذف بلوک‌های انتخاب شده"
+ :editor/cycle-todo "چرخش وضعیت TODO مورد جاری"
+ :content/copy-export-as "رونوشت / برون‌ریزی به صورت.."
+ :content/copy-block-url "رونوشت نشانی بلوک"
+ :content/copy-block-ref "رونوشت ارجاع بلوک"
+ :content/copy-block-emebed "رونوشت جاساز بلوک"
+ :content/copy-ref "رونوشت از این ارجاع"
+ :content/delete-ref "حذف این ارجاع"
+ :content/replace-with-text "جایگزینی با متن"
+ :content/replace-with-embed "جایگزینی با جاساز"
+ :content/open-in-sidebar "باز کردن در نوار کناری"
+ :content/click-to-edit "برای ویرایش، کلیک کنید"
+ :context-menu/make-a-flashcard "ساخت یک فلش کارت"
+ :context-menu/toggle-number-list "تغییر فهرست شمارشی"
+ :context-menu/preview-flashcard "پیش‌نمایش فلش کارت"
+ :context-menu/make-a-template "ساخت یک قالب"
+ :context-menu/input-template-name "نام قالب چیست؟"
+ :context-menu/template-include-parent-block "بلوک والد نیز در قالب شامل شود؟"
+ :context-menu/template-exists-warning "این قالب از پیش وجود دارد!"
+:settings-page/alpha-features "ویژگی‌های آلفا"
+:settings-page/app-updated "برنامه شما به روز است 🎉"
+:settings-page/auto-chmod "تغییر خودکار سطوح دسترسی پرونده"
+:settings-page/auto-chmod-desc "غیر فعال کنید تا اجازه ویرایش توسط چند کاربر با دسترسی‌هایی که از طریق عضویت گروه اعطا شده است را بدهید."
+:settings-page/auto-expand-block-refs "گسترش خودکار ارجاعات بلوک در زمان بزرگ‌نمایی"
+:settings-page/auto-expand-block-refs-tip "این گزینه تعیین می‌کند که آیا ارجاعات بلوک در زمان بزرگ‌نمایی به طور خودکار گسترش یابد یا خیر."
+:settings-page/auto-updater "به‌روزرسان خودکار"
+:settings-page/beta-features "ویژگی‌های بتا"
+:settings-page/changelog "چه خبر؟"
+:settings-page/check-for-updates "بررسی برای به‌روزرسانی"
+:settings-page/checking "در حال بررسی ..."
+:settings-page/clear "پاک کردن"
+:settings-page/clear-cache "پاک کردن حافظه نهان"
+:settings-page/clear-cache-warning "عملیات پاک کردن حافظه نهان، گراف‌های باز را نادیده می‌گیرد. تغییرات ذخیره نشده را از دست خواهید داد."
+:settings-page/current-version "نسخه جاری"
+:settings-page/custom-configuration "پیکربندی سفارشی"
+:settings-page/custom-date-format "قالب تاریخ ترجیحی"
+:settings-page/custom-date-format-notification "برای این که این تغییر اعمال شود لازم است که گراف‌تان را بازفهرست‌بندی کنید"
+:settings-page/custom-date-format-warning "بازفهرست‌بندی ضروری است! پیوند ارجاعات روزنگار قطع خواهند شد!"
+:settings-page/custom-global-configuration "پیکربندی سراسری سفارشی"
+:settings-page/custom-theme "پوسته سفارشی"
+:settings-page/developer-mode "حالت توسعه‌دهنده"
+:settings-page/developer-mode-desc "حالت توسعه‌دهنده به مشارکت‌کنندگان و توسعه‌دهندگان افزونه‌ها امکان می‌دهد تا به شکل مؤثرتری بتوانند یکپارچگی کارشان با لاگ‌سیک را به آزمون بگذارند."
+:settings-page/disable-sentry "فرستادن داده‌های استفاده و ارزیابی به لاگ‌سیک"
+:settings-page/disable-sentry-desc "لاگ‌سیک هرگز پایگاه داده گراف محلی شما را گردآوری نکرده و داده‌های شما را نمی‌فروشد."
+:settings-page/edit-config-edn "ویرایش config.edn"
+:settings-page/edit-custom-css "ویرایش custom.css"
+:settings-page/edit-export-css "ویرایش export.css"
+:settings-page/edit-global-config-edn "ویرایش سراسری config.edn"
+:settings-page/edit-setting "ویرایش"
+:settings-page/enable-all-pages-public "عمومی کردن همه صفحه‌ها در زمانی انتشار"
+:settings-page/enable-block-time "مهر زمان بلوک"
+:settings-page/enable-flashcards "فلش‌کارت‌ها"
+:settings-page/enable-journals "روزنگارها"
+:settings-page/enable-whiteboards "تخته‌سفیدها"
+:settings-page/export-theme "برون‌ریزی پوسته"
+:settings-page/filename-format "قالب نام پرونده‌ها"
+:settings-page/git-commit-delay "فواصل ذخیره خودکار گیت به ثانیه"
+:settings-page/git-commit-on-close "ذخیره گیت در زمان خروج"
+:settings-page/git-desc-1 "برای مشاهده تاریخچهٔ ویرایش برگه، روی سه نقطه افقی گوشهٔ بالا سمت راست کلیک کرده و «نمایش تاریخچهٔ برگه» را انتخاب کنید."
+:settings-page/git-desc-2 "همچنین برای کاربران حرفه‌ای، لاگ‌سیک از "
+:settings-page/git-desc-3 " برای مدیریت نسخه پیشتیبانی می‌کند. به مسئولیت خودتان از گیت استفاده کنید زیرا مسائل عمومی گیت توسط تیم لاگ‌سیک پشتیبانی نمی‌شود."
+:settings-page/git-switcher-label "فعال‌سازی ذخیره خودکار گیت"
+:settings-page/git-tip "اگر هم‌گام‌سازی لاگ‌سیک را فعال کرده باشید می‌توانید مستقیما تاریخچهٔ ویرایش‌های برگه را مشاهده کنید. این بخش فقط برای خوره‌های فناوری است."
+:settings-page/home-default-page "تنظیم صفحه خانگی پیش‌فرض"
+:settings-page/login-prompt "برای آن که پیش از دیگران به ویژگی‌های جدید دسترسی پیدا کنید لازم است حامی لاگ‌سیک روی اوپن کالکتیو باشید و بنابراین لازم است که ابتدا وارد حساب‌تان بشوید."
+:settings-page/native-titlebar "نوار عنوان بومی"
+:settings-page/native-titlebar-desc "نوار عنوان پنجره بومی را روی گنولینوکس و ویندوز فعال می‌کند."
+:settings-page/network-proxy "پروکسی شبکه"
+:settings-page/plugin-system "افزونه‌ها"
+:settings-page/preferred-file-format "قالب پرونده ترجیحی"
+:settings-page/preferred-outdenting "برون‌رفتگی منطقی"
+:settings-page/preferred-outdenting-tip "سمت چپ، فرون‌رفتگی با تنظیمات پیش‌فرض را نمایش می‌دهد و سمت راست، برون‌رفتگی در حالت فعال بودن برون‌رفتگی منطقی را "
+:settings-page/preferred-outdenting-tip-more "← بیشتر بدانید"
+:settings-page/preferred-pasting-file "شیوه ترجیحی چسباندن پرونده"
+:settings-page/preferred-pasting-file-hint "وقتی فعال باشد، چسباندن یک تصویر از اینترنت باعث بارگیری و درج تصویر می‌شود. وقتی غیر فعال باشد، تنها پیوند به تصویر چسبانده خواهد شد."
+:settings-page/preferred-workflow "گردش کار ترجیحی"
+:settings-page/revision "بازبینی: "
+:settings-page/show-brackets "نمایش براکت"
+:settings-page/show-full-blocks "نمایش تمام خطوط یک ارجاع بلوکی"
+:settings-page/spell-checker "بررسی کننده نگارش"
+:settings-page/sync "هم‌گام‌سازی"
+:settings-page/sync-desc-1 "برای دستورالعمل شیوه تنظیمات و استفاده از هم‌گام‌سازی"
+:settings-page/sync-desc-2 "اینجا"
+:settings-page/sync-desc-3 "کلیک کنید."
+:settings-page/sync-diff-merge "فعال‌سازی ادغام هوشمند در زمان هم‌گام‌سازی"
+:settings-page/sync-diff-merge-desc "در صورت بروز تعارض، به جای نوشتن روی پرونده راه‌دور، تغییرات محلی با پرونده راه‌دو به صورت خودکار ادغام شود"
+:settings-page/tab-account "حساب"
+:settings-page/tab-advanced "پیشرفته"
+:settings-page/tab-assets "دارایی‌ها"
+:settings-page/tab-editor "ویرایش‌گر"
+:settings-page/tab-features "ویژگی‌ها"
+:settings-page/tab-general "عمومی"
+:settings-page/tab-keymap "نگاشت کلید"
+:settings-page/tab-version-control "کنترل نسخه"
+:settings-page/theme-dark "تیره"
+:settings-page/theme-light "روشن"
+:settings-page/theme-system "سامانه"
+:settings-page/update-available "انتشار جدید پیدا شد "
+:settings-page/update-error-1 "⚠️ اوه، مشکلی پیش آمد!"
+
+ :yes "بله"
+
+ :submit "ارسال"
+ :cancel "لغو"
+ :close "بستن"
+ :delete "پاک کردن"
+ :save "ذخیره"
+ :type "نوع"
+ :host "میزبان"
+ :port "درگاه"
+ :re-index "بازفهرست‌بندی"
+ :re-index-detail "ساخت دوباره گراف"
+ :re-index-multiple-windows-warning "پیش از بازفهرست‌بندی این گراف لازم است این پنجره را ببندید."
+ :re-index-discard-unsaved-changes-warning "بازفهرست‌بندی، گراف جاری را نادیده گرفته و سپس همهٔ پرونده‌ها را مجددا به همان شکلی که در حال حاضر روی دستگاه ذخیره شده‌اند پردازش می‌کند. شما تغییرات ذخیره نشده را از دست خواهید دید و این فرایند می‌تواند قدری طول بکشد. ادامه می‌دهید؟"
+ :open-new-window "پنجره جدید"
+ :sync-from-local-files "تازه‌سازی"
+ :sync-from-local-files-detail "وارد کردن تغییرات از پرونده‌های محلی"
+ :sync-from-local-changes-detected "تازه‌سازی پرونده‌های تغییر یافته روی دستگاه را که نسبت به محتوای برگهٔ لاگ‌سیک جاری انحراف دارند را شناسایی و پردازش می‌کند. ادامه می‌دهید؟"
+
+ :home "خانه"
+ :new-page "برگه جدید:"
+ :whiteboard "تخته‌سفید"
+ :whiteboards "تخته‌سفیدها"
+ :new-graph "افزودن گراف جدید"
+ :graph "گراف"
+ :graph/persist "لاگ‌سیک در حال هم‌گام‌سازی وضیعت فرایندهای داخلی است. چند ثانیه شکیبایی کنید."
+ :graph/persist-error "هم‌گام‌سازی فرایندهای داخلی به شکست انجامید."
+ :graph/save "در حال ذخیره‌سازی..."
+ :graph/save-success "با موفقیت ذخیره شد"
+ :graph/save-error "ذخیره‌سازی به شکست انجامید"
+ :graph/all-graphs "همه گراف‌ها"
+ :graph/local-graphs "گراف‌های محلی:"
+ :graph/remote-graphs "گراف‌های راه‌دور:"
+ :export "برون‌ریزی"
+ :export-graph "برون‌ریزی گراف"
+ :export-page "برون‌ریزی برگه"
+ :export-markdown "برون‌ریزی به صورت مارک‌داون معیاری (بدون خصوصیات بلوک‌ها)"
+ :export-opml "برون‌ریزی به صورت OPML"
+ :export-public-pages "برون‌ریزی برگه‌های عمومی"
+ :export-json "برون‌ریزی به صورت JSON"
+ :export-roam-json "برون‌ریزی به صورت Roam JSON"
+ :export-edn "برون‌ریزی به صورت EDN"
+ :export-transparent-background "پس‌زمینه شفاف"
+ :export-copy-to-clipboard "رونوشت در بریده‌دان"
+ :export-copied-to-clipboard "در بریده‌دان رونوشت شد!"
+ :export-save-to-file "ذخیره در پرونده"
+ :all-graphs "همهٔ گراف‌ها"
+ :all-pages "همهٔ برگه‌ها"
+ :all-whiteboards "همه تخته‌سفیدها"
+ :all-files "همهٔ پرونده‌ها"
+ :all-journals "همهٔ روزنگارها"
+ :settings "تنظیمات"
+ :settings-of-plugins "افزونه‌ها"
+ :plugins "افزونه‌ها"
+ :themes "پوسته‌ها"
+ :relaunch-confirm-to-work "برای آن که کار کند لازم است که برنامه از نو راه‌اندازی شود. می‌خواید هم‌اکنون بازراه‌اندازی کنید؟"
+ :import "درون‌ریزی"
+ :importing "در حال درون‌ریزی"
+ :join-community "به اجتماع بپیوندید"
+ :discourse-title "تالار گفتگوی ما!"
+ :help-shortcut-title "برای بررسی میان‌برها و دیگر نکات کلیک کنید"
+ :loading "در حال بار شدن..."
+ :parsing-files "تجزیه پرونده‌ها"
+ :loading-files "بار کردن پرونده‌ها"
+ :login "ورود"
+ :logout "خروج"
+ :logout-user "خروج ({1})"
+ :download "بارگیری"
+ :language "زبان"
+ :remove-background "حذف پس‌زمینه"
+ :open-a-directory "باز کردن یک پوشه محلی"
+ :toggle-theme      "تغییر پوسته"
+
+ :help/shortcut-page-title "کلیدهای میان‌بر"
+
+ :plugin/installed "نصب شده"
+ :plugin/installed-plugin "افزونه نصب شده: {1}"
+ :plugin/not-installed "نصب نشده"
+ :plugin/installing "در حال نصب"
+ :plugin/install "نصب"
+ :plugin/reload "بار کردن مجدد"
+ :plugin/update "به‌روزرسانی"
+ :plugin/update-plugin "به‌روزرسانی افزونه: {1} - {2}"
+ :plugin/check-update "بررسی به‌روزرسانی"
+ :plugin/check-all-updates "بررسی همه به‌روزرسانی‌ها"
+ :plugin/found-updates "به‌روزرسانی‌های جدید"
+ :plugin/found-n-updates "{1} به‌روزرسانی پیدا شد"
+ :plugin/update-all-selected "به‌روزرسانی همه انتخاب شده‌ها"
+ :plugin/all-updated "همگی به‌روز شدند!"
+ :plugin/updates-downloading "بارگیری به‌روزرسانی‌ها"
+ :plugin/refresh-lists "تازه‌سازی فهرست‌ها"
+ :plugin/enabled "فعال"
+ :plugin/disabled "غیرفعال"
+ :plugin/update-available "به‌روزرسانی موجود است"
+ :plugin/updating "در حال به‌روزرسانی"
+ :plugin/uninstall "حذف نصب"
+ :plugin/marketplace "بازارچه"
+ :plugin/downloads "بارگیری‌ها"
+ :plugin/stars "ستاره‌ها"
+ :plugin/title "عنوان ({1})"
+ :plugin/all "همه"
+ :plugin/unpacked "بسته‌بندی نشده"
+ :plugin/delete-alert "برای حذف افزونهٔ [{1}] اطمینان دارید؟"
+ :plugin/open-settings "باز کردن تنظیمات"
+ :plugin/open-package "باز کردن بسته"
+ :plugin/load-unpacked "بار کردن افزونه بسته‌بندی نشده"
+ :plugin/restart "بازراه‌اندازی برنامه"
+ :plugin/unpacked-tips "انتخاب پوشهٔ افزونه"
+ :plugin/contribute "✨ افزونهٔ جدید نوشته و بفرستید"
+ :plugin/up-to-date "به روز است {1}"
+ :plugin/custom-js-alert "پرونده‌ای به نام custom.js پیدا کردم. مجازم که اجرایش کنم؟ (اگر از محتوای این پرونده سر در نمی‌آورید، پیشنهاد می‌شود که اجازه اجرا را ندهید چون می‌تواند مخاطرات امنیتی داشته باشد.)"
+ :plugin/security-warning "افزونه‌ها می‌توانند به گراف و پرونده‌های محلی شما دسترسی داشته و درخواست‌هایی روی شبکه بفرستند.
+       آن‌ها همجنین می‌توانند باعت تخریب و یا از دست رفتن داده‌ها شوند. ما مشغول کار روی قوانین دسترسی مناسب برای گراف شما هستیم.
+       در این بین، اطمینان حاصل کنید که به طور منظم از گراف‌های خود نسخه پشتیبان تهیه کرده و تنها زمانی از افزونه‌ها استفاده کنید
+       که می‌توانید کد منبع آن‌ها مطالعه و درک کنید."
+ :plugin/search-plugin "جستجوی افزونه‌ها"
+ :plugin/open-preferences "باز کردن ترجیحات"
+ :plugin/open-logseq-dir "باز کردن"
+ :plugin/remote-error "خطای راه دور: "
+ :plugin/checking-for-updates "در حال بررسی به‌روزرسانی افزونه‌ها..."
+ :plugin/list-of-updates "افزونه‌های دارای به‌روزرسانی: "
+ :plugin/auto-check-for-updates "بررسی خودکار برای به‌روزرسانی"
+ :plugin.install-from-file/menu-title "نصب از plugins.edn"
+ :plugin.install-from-file/title "نصب افزونه‌ها از plugins.edn"
+ :plugin.install-from-file/notice "این افزونه‌ها جایگزین افزونه‌های شما می‌‌شوند:"
+ :plugin.install-from-file/success "همه افزونه‌ها نصب شدند!"
+
+ :updater/new-version-install "نسخه‌ای جدید بارگیری شده است."
+ :updater/quit-and-install "بازراه‌اندازی برای نصب"
+
+ :paginates/pages "مجموعا {1} برگه"
+ :paginates/prev "قبلی"
+ :paginates/next "بعدی"
+
+ :tips/all-done "همه انجام شدند!"
+
+ :select/default-prompt "یکی را انتخاب کنید"
+ :select/default-select-multiple "یک یا چند مورد را انتخاب کنید"
+ :select.graph/prompt "یک گراف را انتخاب کنید"
+ :select.graph/empty-placeholder-description "گرافی یافت نشد. می‌خواهید مورد دیگری را اضافه کنید؟"
+ :select.graph/add-graph "بله، گراف دیگری را اضافه کن"
+
+ :file-sync/other-user-graph "گراف محلی جاری به گراف راه‌دور کاربر دیگری متصل است. به همین دلیل شروع هم‌گام‌سازی ممکن نیست."
+ :file-sync/graph-deleted "گراف راه‌دور جاری پاک شده است"
+ :file-sync/rsapi-cannot-upload-err "شروع هم‌گام‌سازی ممکن نیست. لطفا از درستی زمان محلی‌تان اطمینان حاصل کنید."
+ :file-sync/connectivity-testing-failed "بررسی اتصال شبکه به شکست انجامید. لطفا تنظیمات شبکه خود را بررسی کنید. نشانی‌های بررسی: "
+
+ :notification/clear-all "پاک کردن همه"
+
+ :shortcut.category/basics "مقدماتی"
+ :shortcut.category/formatting "قالب‌بندی"
+ :shortcut.category/navigating "ناوبری"
+ :shortcut.category/block-editing "تنظیمات عمومی ویرایش بلوک"
+ :shortcut.category/block-command-editing "ویرایش دستوری بلوک"
+ :shortcut.category/block-selection "انتخاب بلوک (با دکمه Esc از حالت انتخاب خارج می‌شوید)"
+ :shortcut.category/toggle "تغییر حالت"
+ :shortcut.category/others "سایر"
+ :shortcut.category/plugins "افزونه‌ها"
+ :shortcut.category/whiteboard "تخته‌سفید"
+
+ :keymap/all "همه"
+ :keymap/disabled "غیر فعال"
+ :keymap/unset "تنظیم نشده"
+ :keymap/custom "سفارشی"
+ :keymap/search "جستجو"
+ :keymap/total "تعداد کل میان‌برها"
+ :keymap/keystroke-filter "پالایهٔ تاج‌مهره"
+ :keymap/keystroke-record-desc "سلسله‌ای از کلیدها را برای پالایش میان‌برها بفشارید"
+ :keymap/keystroke-record-setup-label "سلسله‌ای از دکمه‌ها را برای تنظیم میان‌بر بفشارید"
+ :keymap/restore-to-default "بازگردانی به پیش‌فرض سامانه"
+ :keymap/customize-for-label "سفارشی‌سازی میان‌برها"
+ :keymap/conflicts-for-label "تعارض میان‌بر برای"
+
+ :window/minimize "کمینه‌سازی"
+ :window/maximize "بیشینه‌سازی"
+ :window/restore "بازگردانی"
+ :window/close "بستن"
+ :window/exit-fullscreen "خروج از حالت تمام صفحه"
+
+ :header/toggle-left-sidebar         "تغییر وضعیت نوار چپ"
+ :header/search                      "جستجو"
+ :header/more                        "بیشتر"
+ :header/go-back                     "برو عقب"
+ :header/go-forward                  "برو جلو"
+}

+ 0 - 2
src/resources/dicts/fr.edn

@@ -408,8 +408,6 @@
     :command.search/re-index  "Reconstruire l'index de recherche"
     :command.sidebar/clear  "Vider intégralement la barre latérale droite"
     :command.sidebar/open-today-page  "Ouvrir la page du jour dans la barre latérale droite"
-    :command.ui/cycle-color "Bascule de la couleur"
-    :command.ui/cycle-color-off "Bascule de la couleur désactivée"
     :command.ui/goto-plugins  "Aller vers le panneau des extensions"
     :command.ui/install-plugins-from-file  "Installer les extensions depuis plugins.edn"
     :command.ui/select-theme-color  "Sélectionner les couleurs disponibles du thème"

+ 0 - 2
src/resources/dicts/it.edn

@@ -396,8 +396,6 @@
  :command.pdf/find "PDF: cerca testo nel documento"
  :command.sidebar/close-top "Chiudi l'elemento in alto nel pannello laterale destra"
  :command.ui/clear-all-notifications "Cancella tutte le notifiche"
- :command.ui/cycle-color "Cicla colore"
- :command.ui/cycle-color-off "Disattiva cicla colore"
  :command.ui/install-plugins-from-file "Installa plugin da plugins.edn"
  :command.whiteboard/bring-forward "Sposta in avanti"
  :command.whiteboard/bring-to-front "Sposta in fronte"

+ 0 - 2
src/resources/dicts/ja.edn

@@ -826,8 +826,6 @@
  :command.ui/toggle-help "ヘルプの表示/非表示"
  :command.ui/toggle-theme "テーマの切り替え"
  :command.ui/toggle-contents "目次の開閉"
- :command.ui/cycle-color-off "循環させた色を元に戻す"
- :command.ui/cycle-color "色を循環させる"
 
  :command.command/toggle-favorite "お気に入りへ追加/削除"
  :command.editor/open-file-in-default-app "ファイルを既定のアプリで開く"

+ 0 - 2
src/resources/dicts/nb-no.edn

@@ -792,8 +792,6 @@
 
  :command.command-palette/toggle "Søk kommandoer"
  :command.go/search-in-page "Søk blokker på side"
- :command.ui/cycle-color "Veksle farge"
- :command.ui/cycle-color-off "Veksle farge av"
  :command.whiteboard/clone-down "Klon ned"
  :command.whiteboard/clone-left "Klon venstre"
  :command.whiteboard/clone-right "Klon høyre"

+ 0 - 2
src/resources/dicts/pt-br.edn

@@ -763,8 +763,6 @@
  :command.sidebar/close-top "Fechar o item superior na barra lateral direita"
  :command.sidebar/open-today-page "Abrir a página de hoje na barra lateral direita"
  :command.ui/clear-all-notifications "Limpar todas as notificações"
- :command.ui/cycle-color "Mudar cor"
- :command.ui/cycle-color-off "Desativar mudança de cor"
  :command.ui/goto-plugins "Ir para o painel de plugins"
  :command.ui/install-plugins-from-file "Instalar plugins do arquivo plugins.edn"
  :command.ui/select-theme-color "Selecionar cores de tema disponíveis"

+ 0 - 2
src/resources/dicts/sk.edn

@@ -783,8 +783,6 @@
  :command.ui/toggle-help                           "Zobraziť/Skryť pomocníka"
  :command.ui/toggle-theme                          "Prepínať medzi tmavým/svetlým motívom"
  :command.ui/toggle-contents                       "Zobraziť/Skryť obsah na bočnom paneli"
- :command.ui/cycle-color-off                       "Vypnúť zmenu farieb"
- :command.ui/cycle-color                           "Zmeniť farbu"
  :command.command/toggle-favorite                  "Pridať/Odstrániť z obľúbených"
  :command.editor/open-file-in-default-app          "Otvoriť súbor v predvolenej aplikácii"
  :command.editor/open-file-in-directory            "Otvoriť súbor v nadradenom adresári"

+ 1 - 2
src/resources/dicts/tr.edn

@@ -287,6 +287,7 @@
  :settings-page/git-desc-1 "Sayfanın düzenleme geçmişini görüntülemek için sağ üst köşedeki üç yatay noktaya basın ve \"Sayfa geçmişini görüntüle\" ögesini seçin."
  :settings-page/git-desc-2 "Profesyonel kullanıcılar için Logseq, sürüm kontrolü için "
  :settings-page/git-desc-3 " kullanımını da destekler. Genel Git sorunları Logseq ekibi tarafından desteklenmediğinden Git'i kullanmanın riski size aittir."
+ :settings-page/git-commit-on-close "Pencere kapatıldığında git commit'i çalıştır"
  :settings-page/git-switcher-label "Otomatik git commit'i etkinleştir"
  :settings-page/git-commit-delay "Otomatik git commit saniyesi"
  :settings-page/edit-config-edn "config.edn dosyasını düzenle"
@@ -817,8 +818,6 @@
  :command.ui/toggle-help                 "Yardımı aç/kapat"
  :command.ui/toggle-theme                "Koyu ve açık tema arasında geçiş yap"
  :command.ui/toggle-contents             "Kenar çubuğundaki içeriği aç/kapat"
- :command.ui/cycle-color-off             "Renk değiştirmeyi kapat"
- :command.ui/cycle-color                 "Renk değiştir"
  :command.command/toggle-favorite        "Sık kullanılanlara ekle/çıkar"
  :command.editor/open-file-in-default-app "Dosyayı varsayılan uygulamada aç"
  :command.editor/open-file-in-directory   "Dosyayı üst dizinde aç"

+ 7 - 0
src/resources/templates/config.edn

@@ -291,6 +291,13 @@
  ;;  :excluded-pages? false  ; Default value: false
  ;;  :journal?        false} ; Default value: false
 
+ ;; Graph view configuration.
+ ;; Example usage:
+ ;; :graph/forcesettings
+ ;; {:link-dist       180    ; Default value: 180
+ ;;  :charge-strength -600   ; Default value: -600
+ ;;  :charge-range    600}   ; Default value: 600
+
  ;; Favorites to list on the left sidebar
  :favorites []
 

+ 14 - 0
src/resources/tutorials/dummy-notes-fa.md

@@ -0,0 +1,14 @@
+---
+title: چگونه یادداشت‌برداری کنیم؟
+---
+
+- درود. من یک بلوک هستم!
+:PROPERTIES:
+:id: 5f713e91-8a3c-4b04-a33a-c39482428e2d
+:END:
+    - من یک زیربلوک هستم!
+    - من هم یک زیر بلوک دیگه هستم!
+- آهای! من هم یک بلوک هستم!
+:PROPERTIES:
+:id: 5f713ea8-8cba-403d-ac00-9964b1ec7190
+:END:

+ 25 - 0
src/resources/tutorials/tutorial-fa.md

@@ -0,0 +1,25 @@
+## درود. به لاگ‌سیک خوش آمدید!
+- لاگ‌سیک یک سکوی [آزاد](https://github.com/logseq/logseq) برای مدیریت _دانش_ و همکاری است که _حریم شخصی_ را در اولویت اول قرار می‌دهد.
+- این مطلب، یک آموزش ۳ دقیقه‌ای برای شروع کار با لاگ‌سیک است. بگذارید شروع کنیم!
+- بگذارید سه نکته که می‌توانند مفید باشند را ببینیم.
+#+BEGIN_TIP
+برای ویرایش هر بلوک، روی آن کلید کنید.
+با فشردن دکمه `Enter‍` یک بلوک جدید بسازید.
+با فشردن `Shift+Enter` یک خط جدید بسازید.
+با فشردن `/` همه دستورات را ببینید.
+#+END_TIP
+- 1. بیایید برگه‌ای به نام [[چگونه یادداشت‌برداری کنیم؟]] بسازیم. می‌توانید روی آن کلید کنید تا به برگه مربوطه بروید و یا می‌توانید با فشردن `Shift+Click`، آن را در نوار کناری باز کنید! حالا باید بتوانید هم _ارجاعات پیوندی_ و هم _ارجاعات غیر پیوندی_ را ببینید.
+- 2. بیایید به چند بلوک در [[چگونه یادداشت‌برداری کنیم؟]] ارجاع دهیم. می‌توانید با `Shift-Click` روی هر ارجاع، آن را در نوار کناری باز کنید. سعی کنید تغییراتی در نوار کناری بدهید. می‌بینید که تغییرات در بلوک‌های ارجاع داده شده نیز تغییر مي‌کنند!
+    - ((5f713e91-8a3c-4b04-a33a-c39482428e2d)) : این یک ارجاع بوک است.
+    - ((5f713ea8-8cba-403d-ac00-9964b1ec7190)) : این یک ارجاع بلوک دیگر است.
+- 3. آیا از برچسب‌ها پشتیبانی مي‌کنید؟
+    - قطعا! این یک برچسب #dummy است.
+- 4. آیا از کلمات کلیدی تعریف وظایف مثل todo/doing/done و اولویت‌بندی‌ها پیشتیبانی می‌کنید؟
+    - بله. `/` را بنویسید و کلیدواژه وظایف یا اولویت‌بندی (A/B/C) مورد نظرتان را انتخاب کنید.
+    - NOW [#A] آموزشی درباره «چگونه یادداشت برداری کنیم؟» بسازم.
+    - LATER [#A] این ویدئوی فوق‌العاده، ساختهٔ [:a {:href "https://twitter.com/shuomi3" :target "_blank"} "@shuomi3"] درباره این که چگونه از لاگ‌سیک برای یادداشت برداری و سازمان‌دهی زندگی را ببینم.
+    {{youtube https://www.youtube.com/watch?v=BhHfF0P9A80&ab_channel=ShuOmi}}
+    - DONE ساخت یک برگه
+    - CANCELED [#C] نوشتن یک برگه با بیش از هزار بلوک
+- همین و بس! می‌توانید مواردی دیگری را اضافه کنید و یا پوشه محلی را برای وارد کردن تعدادی یادداشت جدید باز کنید!
+- همچنین می‌توانید برنامهٔ میزکار را از این نشانی دریافت کنید: https://github.com/logseq/logseq/releases

+ 1 - 0
tailwind.all.css

@@ -5,6 +5,7 @@
 
 @import "packages/ui/src/radix.css";
 @import "packages/ui/src/radix-hsl.css";
+@import "packages/ui/src/vars-classic.css";
 @import "packages/ui/src/colors.css";
 @import "packages/ui/src/index.css";
 @import "resources/css/shui.css";

+ 49 - 98
tailwind.config.js

@@ -2,73 +2,22 @@ const colors = require('tailwindcss/colors')
 const plugin = require('tailwindcss/plugin')
 const radix = require('@radix-ui/colors')
 
-const lx = override => ({
-  'accent-01': 'or(' + override + ', --lx-accent-01, --ls-page-properties-background-color)',
-  'accent-02': 'or(' + override + ', --lx-accent-02, --ls-page-properties-background-color)',
-  'accent-03': 'or(' + override + ', --lx-accent-03, --ls-page-properties-background-color)',
-  'accent-04': 'or(' + override + ', --lx-accent-04, --ls-page-properties-background-color)',
-  'accent-05': 'or(' + override + ', --lx-accent-05, --color-blue-900)',
-  'accent-06': 'or(' + override + ', --lx-accent-06, --color-blue-800)',
-  'accent-07': 'or(' + override + ', --lx-accent-07, --color-blue-700)',
-  'accent-08': 'or(' + override + ', --lx-accent-08, --color-blue-600)',
-  'accent-09': 'or(' + override + ', --lx-accent-09, --color-blue-500)',
-  'accent-10': 'or(' + override + ', --lx-accent-10, --color-blue-400)',
-  'accent-11': 'or(' + override + ', --lx-accent-11, --color-blue-200)',
-  'accent-12': 'or(' + override + ', --lx-accent-12, --color-blue-50)',
-  'accent-01-alpha': 'or(' + override + ', --lx-accent-01-alpha, --ls-page-properties-background-color)',
-  'accent-02-alpha': 'or(' + override + ', --lx-accent-02-alpha, --ls-page-properties-background-color)',
-  'accent-03-alpha': 'or(' + override + ', --lx-accent-03-alpha, --ls-page-properties-background-color)',
-  'accent-04-alpha': 'or(' + override + ', --lx-accent-04-alpha, --ls-page-properties-background-color)',
-  'accent-05-alpha': 'or(' + override + ', --lx-accent-05-alpha, --color-blue-900)',
-  'accent-06-alpha': 'or(' + override + ', --lx-accent-06-alpha, --color-blue-800)',
-  'accent-07-alpha': 'or(' + override + ', --lx-accent-07-alpha, --color-blue-700)',
-  'accent-08-alpha': 'or(' + override + ', --lx-accent-08-alpha, --color-blue-600)',
-  'accent-09-alpha': 'or(' + override + ', --lx-accent-09-alpha, --color-blue-500)',
-  'accent-10-alpha': 'or(' + override + ', --lx-accent-10-alpha, --color-blue-400)',
-  'accent-11-alpha': 'or(' + override + ', --lx-accent-11-alpha, --color-blue-200)',
-  'accent-12-alpha': 'or(' + override + ', --lx-accent-12-alpha, --color-blue-50)',
-  'gray-01': 'or(' + override + ', --lx-gray-01, --ls-primary-background-color)',
-  'gray-02': 'or(' + override + ', --lx-gray-02, --ls-secondary-background-color)',
-  'gray-03': 'or(' + override + ', --lx-gray-03, --ls-tertiary-background-color)',
-  'gray-04': 'or(' + override + ', --lx-gray-04, --ls-quaternary-background-color)',
-  'gray-05': 'or(' + override + ', --lx-gray-05, --color-level-4)',
-  'gray-06': 'or(' + override + ', --lx-gray-06, --ls-block-bullet-border-color)',
-  'gray-07': 'or(' + override + ', --lx-gray-07, --ls-border-color)',
-  'gray-08': 'or(' + override + ', --lx-gray-08, --ls-secondary-border-color)',
-  'gray-09': 'or(' + override + ', --lx-gray-09, --color-level-5)',
-  'gray-10': 'or(' + override + ', --lx-gray-10, --ls-title-text-color)',
-  'gray-11': 'or(' + override + ', --lx-gray-11, --ls-primary-text-color)',
-  'gray-12': 'or(' + override + ', --lx-gray-12, --ls-secondary-text-color)',
-  'gray-01-alpha': 'or(' + override + ', --lx-gray-01-alpha, --ls-primary-background-color)',
-  'gray-02-alpha': 'or(' + override + ', --lx-gray-02-alpha, --ls-secondary-background-color)',
-  'gray-03-alpha': 'or(' + override + ', --lx-gray-03-alpha, --ls-tertiary-background-color)',
-  'gray-04-alpha': 'or(' + override + ', --lx-gray-04-alpha, --ls-quaternary-background-color)',
-  'gray-05-alpha': 'or(' + override + ', --lx-gray-05-alpha, --color-level-4)',
-  'gray-06-alpha': 'or(' + override + ', --lx-gray-06-alpha, --ls-block-bullet-color)',
-  'gray-07-alpha': 'or(' + override + ', --lx-gray-07-alpha, --ls-border-color)',
-  'gray-08-alpha': 'or(' + override + ', --lx-gray-08-alpha, --ls-secondary-border-color)',
-  'gray-09-alpha': 'or(' + override + ', --lx-gray-09-alpha, --color-level-5)',
-  'gray-10-alpha': 'or(' + override + ', --lx-gray-10-alpha, --color-level-6)',
-  'gray-11-alpha': 'or(' + override + ', --lx-gray-11-alpha, --ls-primary-text-color)',
-  'gray-12-alpha': 'or(' + override + ', --lx-gray-12-alpha, --ls-secondary-text-color)',
-})
-
 const accent = {
   'DEFAULT': 'hsl(var(--accent))',
   'base': 'hsl(var(--accent))',
   'foreground': 'hsl(var(--accent-foreground))',
-  '01': 'var(--lx-accent-01)',
-  '02': 'var(--lx-accent-02)',
-  '03': 'var(--lx-accent-03)',
-  '04': 'var(--lx-accent-04)',
-  '05': 'var(--lx-accent-05)',
-  '06': 'var(--lx-accent-06)',
-  '07': 'var(--lx-accent-07)',
-  '08': 'var(--lx-accent-08)',
-  '09': 'var(--lx-accent-09)',
-  '10': 'var(--lx-accent-10)',
-  '11': 'var(--lx-accent-11)',
-  '12': 'var(--lx-accent-12)',
+  '01': 'var(--lx-accent-01, --rx-gray-01)',
+  '02': 'var(--lx-accent-02, hsl(var(--accent)/.1))',
+  '03': 'var(--lx-accent-03, hsl(var(--accent)/.15))',
+  '04': 'var(--lx-accent-04, hsl(var(--accent)/.2))',
+  '05': 'var(--lx-accent-05, hsl(var(--accent)/.3))',
+  '06': 'var(--lx-accent-06, hsl(var(--accent)/.4))',
+  '07': 'var(--lx-accent-07, hsl(var(--accent)/.5))',
+  '08': 'var(--lx-accent-08, hsl(var(--accent)/.6))',
+  '09': 'var(--lx-accent-09, hsl(var(--accent)/.7))',
+  '10': 'var(--lx-accent-10, hsl(var(--accent)/.8))',
+  '11': 'var(--lx-accent-11, hsl(var(--accent)/.9))',
+  '12': 'var(--lx-accent-12, --rx-gray-12)',
   '01-alpha': 'var(--lx-accent-01-alpha)',
   '02-alpha': 'var(--lx-accent-02-alpha)',
   '03-alpha': 'var(--lx-accent-03-alpha)',
@@ -85,30 +34,30 @@ const accent = {
 
 const gray = {
   ...colors.gray,
-  '01': 'var(--lx-gray-01)',
-  '02': 'var(--lx-gray-02)',
-  '03': 'var(--lx-gray-03)',
-  '04': 'var(--lx-gray-04)',
-  '05': 'var(--lx-gray-05)',
-  '06': 'var(--lx-gray-06)',
-  '07': 'var(--lx-gray-07)',
-  '08': 'var(--lx-gray-08)',
-  '09': 'var(--lx-gray-09)',
-  '10': 'var(--lx-gray-10)',
-  '11': 'var(--lx-gray-11)',
-  '12': 'var(--lx-gray-12)',
-  '01-alpha': 'var(--lx-gray-01-alpha)',
-  '02-alpha': 'var(--lx-gray-02-alpha)',
-  '03-alpha': 'var(--lx-gray-03-alpha)',
-  '04-alpha': 'var(--lx-gray-04-alpha)',
-  '05-alpha': 'var(--lx-gray-05-alpha)',
-  '06-alpha': 'var(--lx-gray-06-alpha)',
-  '07-alpha': 'var(--lx-gray-07-alpha)',
-  '08-alpha': 'var(--lx-gray-08-alpha)',
-  '09-alpha': 'var(--lx-gray-09-alpha)',
-  '10-alpha': 'var(--lx-gray-10-alpha)',
-  '11-alpha': 'var(--lx-gray-11-alpha)',
-  '12-alpha': 'var(--lx-gray-12-alpha)',
+  '01': 'var(--lx-gray-01, var(--ls-primary-background-color, var(--rx-gray-01)))',
+  '02': 'var(--lx-gray-02, var(--ls-secondary-background-color, var(--rx-gray-02)))',
+  '03': 'var(--lx-gray-03, var(--ls-tertiary-background-color, var(--rx-gray-03)))',
+  '04': 'var(--lx-gray-04, var(--ls-quaternary-background-color, var(--rx-gray-04)))',
+  '05': 'var(--lx-gray-05, var(--rx-gray-05))',
+  '06': 'var(--lx-gray-06, var(--rx-gray-06))',
+  '07': 'var(--lx-gray-07, var(--rx-gray-07))',
+  '08': 'var(--lx-gray-08, var(--rx-gray-08))',
+  '09': 'var(--lx-gray-09, var(--rx-gray-09))',
+  '10': 'var(--lx-gray-10, var(--rx-gray-10))',
+  '11': 'var(--lx-gray-11, var(--rx-gray-11))',
+  '12': 'var(--lx-gray-12, var(--rx-gray-12))',
+  '01-alpha': 'var(--lx-gray-01-alpha, var(--rx-gray-01-alpha))',
+  '02-alpha': 'var(--lx-gray-02-alpha, var(--rx-gray-02-alpha))',
+  '03-alpha': 'var(--lx-gray-03-alpha, var(--rx-gray-03-alpha))',
+  '04-alpha': 'var(--lx-gray-04-alpha, var(--rx-gray-04-alpha))',
+  '05-alpha': 'var(--lx-gray-05-alpha, var(--rx-gray-05-alpha))',
+  '06-alpha': 'var(--lx-gray-06-alpha, var(--rx-gray-06-alpha))',
+  '07-alpha': 'var(--lx-gray-07-alpha, var(--rx-gray-07-alpha))',
+  '08-alpha': 'var(--lx-gray-08-alpha, var(--rx-gray-08-alpha))',
+  '09-alpha': 'var(--lx-gray-09-alpha, var(--rx-gray-09-alpha))',
+  '10-alpha': 'var(--lx-gray-10-alpha, var(--rx-gray-10-alpha))',
+  '11-alpha': 'var(--lx-gray-11-alpha, var(--rx-gray-11-alpha))',
+  '12-alpha': 'var(--lx-gray-12-alpha, var(--rx-gray-12-alpha))',
 }
 
 function exposeColorsToCssVars ({ addBase, theme }) {
@@ -194,8 +143,7 @@ module.exports = {
     require('@tailwindcss/line-clamp'),
     require('tailwind-capitalize-first-letter'),
     require('tailwindcss-animate'),
-    exposeColorsToCssVars,
-    withOverride,
+    exposeColorsToCssVars
   ],
   theme: {
     extend: {
@@ -227,15 +175,15 @@ module.exports = {
         'lmd': '728px',
         'llg': '960px'
       },
-      backgroundColor: {
-        ...lx('--lx-bg-override'),
-      },
-      textColor: {
-        ...lx('--lx-text-override'),
-      },
-      borderColor: {
-        ...lx('--lx-border-override'),
-      },
+      // backgroundColor: {
+      //   ...lx('--lx-bg-override'),
+      // },
+      // textColor: {
+      //   ...lx('--lx-text-override'),
+      // },
+      // borderColor: {
+      //   ...lx('--lx-border-override'),
+      // },
       borderRadius: {
         lg: 'var(--radius)',
         md: 'calc(var(--radius) - 2px)',
@@ -279,6 +227,9 @@ module.exports = {
         DEFAULT: 'hsl(var(--popover))',
         foreground: 'hsl(var(--popover-foreground))',
       },
+      popovelx: {
+        DEFAULT: 'var(--lx-gray-03, hsl(var(--popover)))',
+      },
       card: {
         DEFAULT: 'hsl(var(--card))',
         foreground: 'hsl(var(--card-foreground))',

+ 110 - 100
tldraw/apps/tldraw-logseq/src/components/ContextMenu/ContextMenu.tsx

@@ -1,16 +1,24 @@
 import { useApp } from '@tldraw/react'
 import { LogseqContext } from '../../lib/logseq-context'
-import { MOD_KEY, AlignType, DistributeType, isDev, EXPORT_PADDING } from '@tldraw/core'
+import {
+  MOD_KEY,
+  AlignType,
+  DistributeType,
+  isDev,
+  EXPORT_PADDING
+} from '@tldraw/core'
 import { observer } from 'mobx-react-lite'
 import { TablerIcon } from '../icons'
 import { Button } from '../Button'
 import { KeyboardShortcut } from '../KeyboardShortcut'
 import * as React from 'react'
 
-import * as ReactContextMenu from '@radix-ui/react-context-menu'
 import * as Separator from '@radix-ui/react-separator'
 import { toJS } from 'mobx'
 
+// @ts-ignore
+const LSUI = window.LSUI
+
 interface ContextMenuProps {
   children: React.ReactNode
   collisionRef: React.RefObject<HTMLDivElement>
@@ -35,8 +43,8 @@ export const ContextMenu = observer(function ContextMenu({
   }, [])
 
   return (
-    <ReactContextMenu.Root
-      onOpenChange={open => {
+    <LSUI.ContextMenu
+      onOpenChange={(open: boolean) => {
         if (open && !app.isIn('select.contextMenu')) {
           app.transition('select').selectedTool.transition('contextMenu')
         } else if (!open && app.isIn('select.contextMenu')) {
@@ -44,12 +52,12 @@ export const ContextMenu = observer(function ContextMenu({
         }
       }}
     >
-      <ReactContextMenu.Trigger
+      <LSUI.ContextMenuTrigger
         disabled={app.editingShape && Object.keys(app.editingShape).length !== 0}
       >
         {children}
-      </ReactContextMenu.Trigger>
-      <ReactContextMenu.Content
+      </LSUI.ContextMenuTrigger>
+      <LSUI.ContextMenuContent
         className="tl-menu tl-context-menu"
         ref={rContent}
         onEscapeKeyDown={() => app.transition('select')}
@@ -62,34 +70,35 @@ export const ContextMenu = observer(function ContextMenu({
             !app.readOnly &&
             app.selectedShapesArray?.some(s => !s.props.isLocked) && (
               <>
-                <ReactContextMenu.Item>
+                <LSUI.ContextMenuItem className={'tl-menu-button-row-wrap'}>
                   <div className="tl-menu-button-row pb-0">
                     <Button
                       tooltip={t('whiteboard/align-left')}
                       onClick={() => runAndTransition(() => app.align(AlignType.Left))}
                     >
-                      <TablerIcon name="layout-align-left" />
+                      <TablerIcon name="layout-align-left"/>
                     </Button>
                     <Button
                       tooltip={t('whiteboard/align-center-horizontally')}
                       onClick={() => runAndTransition(() => app.align(AlignType.CenterHorizontal))}
                     >
-                      <TablerIcon name="layout-align-center" />
+                      <TablerIcon name="layout-align-center"/>
                     </Button>
                     <Button
                       tooltip={t('whiteboard/align-right')}
                       onClick={() => runAndTransition(() => app.align(AlignType.Right))}
                     >
-                      <TablerIcon name="layout-align-right" />
+                      <TablerIcon name="layout-align-right"/>
                     </Button>
-                    <Separator.Root className="tl-toolbar-separator" orientation="vertical" />
+                    <Separator.Root className="tl-toolbar-separator"
+                                    orientation="vertical"/>
                     <Button
                       tooltip={t('whiteboard/distribute-horizontally')}
                       onClick={() =>
                         runAndTransition(() => app.distribute(DistributeType.Horizontal))
                       }
                     >
-                      <TablerIcon name="layout-distribute-vertical" />
+                      <TablerIcon name="layout-distribute-vertical"/>
                     </Button>
                   </div>
                   <div className="tl-menu-button-row pt-0">
@@ -97,135 +106,130 @@ export const ContextMenu = observer(function ContextMenu({
                       tooltip={t('whiteboard/align-top')}
                       onClick={() => runAndTransition(() => app.align(AlignType.Top))}
                     >
-                      <TablerIcon name="layout-align-top" />
+                      <TablerIcon name="layout-align-top"/>
                     </Button>
                     <Button
                       tooltip={t('whiteboard/align-center-vertically')}
                       onClick={() => runAndTransition(() => app.align(AlignType.CenterVertical))}
                     >
-                      <TablerIcon name="layout-align-middle" />
+                      <TablerIcon name="layout-align-middle"/>
                     </Button>
                     <Button
                       tooltip={t('whiteboard/align-bottom')}
                       onClick={() => runAndTransition(() => app.align(AlignType.Bottom))}
                     >
-                      <TablerIcon name="layout-align-bottom" />
+                      <TablerIcon name="layout-align-bottom"/>
                     </Button>
-                    <Separator.Root className="tl-toolbar-separator" orientation="vertical" />
+                    <Separator.Root className="tl-toolbar-separator"
+                                    orientation="vertical"/>
                     <Button
                       tooltip={t('whiteboard/distribute-vertically')}
                       onClick={() =>
                         runAndTransition(() => app.distribute(DistributeType.Vertical))
                       }
                     >
-                      <TablerIcon name="layout-distribute-horizontal" />
+                      <TablerIcon name="layout-distribute-horizontal"/>
                     </Button>
                   </div>
-                </ReactContextMenu.Item>
-                <ReactContextMenu.Separator className="menu-separator" />
-                <ReactContextMenu.Item
+                </LSUI.ContextMenuItem>
+                <LSUI.ContextMenuSeparator className="menu-separator"/>
+                <LSUI.ContextMenuItem
                   className="tl-menu-item"
                   onClick={() => runAndTransition(app.packIntoRectangle)}
                 >
-                  <TablerIcon className="tl-menu-icon" name="layout-grid" />
+                  <TablerIcon className="tl-menu-icon" name="layout-grid"/>
                   {t('whiteboard/pack-into-rectangle')}
-                </ReactContextMenu.Item>
-                <ReactContextMenu.Separator className="menu-separator" />
+                </LSUI.ContextMenuItem>
+                <LSUI.ContextMenuSeparator className="menu-separator"/>
               </>
             )}
           {app.selectedShapes?.size > 0 && (
             <>
-              <ReactContextMenu.Item
+              <LSUI.ContextMenuItem
                 className="tl-menu-item"
                 onClick={() => runAndTransition(app.api.zoomToSelection)}
               >
+                <TablerIcon className="tl-menu-icon" name="circle-dotted"/>
                 {t('whiteboard/zoom-to-fit')}
-                <KeyboardShortcut action="whiteboard/zoom-to-fit" />
-              </ReactContextMenu.Item>
-              <ReactContextMenu.Separator className="menu-separator" />
+                <KeyboardShortcut action="whiteboard/zoom-to-fit"/>
+              </LSUI.ContextMenuItem>
+              <LSUI.ContextMenuSeparator className="menu-separator"/>
             </>
           )}
           {(app.selectedShapesArray.some(s => s.type === 'group' || app.getParentGroup(s)) ||
-            app.selectedShapesArray.length > 1) &&
+              app.selectedShapesArray.length > 1) &&
             app.selectedShapesArray?.some(s => !s.props.isLocked) &&
             !app.readOnly && (
               <>
                 {app.selectedShapesArray.some(s => s.type === 'group' || app.getParentGroup(s)) && (
-                  <ReactContextMenu.Item
+                  <LSUI.ContextMenuItem
                     className="tl-menu-item"
                     onClick={() => runAndTransition(app.api.unGroup)}
                   >
-                    <TablerIcon className="tl-menu-icon" name="ungroup" />
+                    <TablerIcon className="tl-menu-icon" name="ungroup"/>
                     {t('whiteboard/ungroup')}
-                    <KeyboardShortcut action="whiteboard/ungroup" />
-                  </ReactContextMenu.Item>
+                    <KeyboardShortcut action="whiteboard/ungroup"/>
+                  </LSUI.ContextMenuItem>
                 )}
                 {app.selectedShapesArray.length > 1 &&
                   app.selectedShapesArray?.some(s => !s.props.isLocked) && (
-                    <ReactContextMenu.Item
+                    <LSUI.ContextMenuItem
                       className="tl-menu-item"
                       onClick={() => runAndTransition(app.api.doGroup)}
                     >
-                      <TablerIcon className="tl-menu-icon" name="group" />
+                      <TablerIcon className="tl-menu-icon" name="group"/>
                       {t('whiteboard/group')}
-                      <KeyboardShortcut action="whiteboard/group" />
-                    </ReactContextMenu.Item>
+                      <KeyboardShortcut action="whiteboard/group"/>
+                    </LSUI.ContextMenuItem>
                   )}
-                <ReactContextMenu.Separator className="menu-separator" />
+                <LSUI.ContextMenuSeparator className="menu-separator"/>
               </>
             )}
           {app.selectedShapes?.size > 0 && app.selectedShapesArray?.some(s => !s.props.isLocked) && (
             <>
               {!app.readOnly && (
-                <ReactContextMenu.Item
+                <LSUI.ContextMenuItem
                   className="tl-menu-item"
                   onClick={() => runAndTransition(app.cut)}
                 >
-                  <TablerIcon className="tl-menu-icon" name="cut" />
+                  <TablerIcon className="tl-menu-icon" name="cut"/>
                   {t('whiteboard/cut')}
-                </ReactContextMenu.Item>
+                </LSUI.ContextMenuItem>
               )}
-              <ReactContextMenu.Item
+              <LSUI.ContextMenuItem
                 className="tl-menu-item"
                 onClick={() => runAndTransition(app.copy)}
               >
-                <TablerIcon className="tl-menu-icon" name="copy" />
+                <TablerIcon className="tl-menu-icon" name="copy"/>
                 {t('whiteboard/copy')}
-                <KeyboardShortcut action="editor/copy" />
-              </ReactContextMenu.Item>
+                <KeyboardShortcut action="editor/copy"/>
+              </LSUI.ContextMenuItem>
             </>
           )}
           {!app.readOnly && (
-            <ReactContextMenu.Item
+            <LSUI.ContextMenuItem
               className="tl-menu-item"
               onClick={() => runAndTransition(app.paste)}
             >
-              <TablerIcon className="tl-menu-icon" name="clipboard" />
+              <TablerIcon className="tl-menu-icon" name="clipboard"/>
               {t('whiteboard/paste')}
-              <div className="tl-menu-right-slot">
-                <span className="keyboard-shortcut">
-                  <code>{MOD_KEY}+v</code>
-                </span>
-              </div>
-            </ReactContextMenu.Item>
+              <KeyboardShortcut shortcut={`${MOD_KEY}+v`}/>
+            </LSUI.ContextMenuItem>
           )}
           {app.selectedShapes?.size === 1 && !app.readOnly && (
-            <ReactContextMenu.Item
+            <LSUI.ContextMenuItem
               className="tl-menu-item"
               onClick={() => runAndTransition(() => app.paste(undefined, true))}
             >
+              <TablerIcon className="tl-menu-icon" name="circle-dotted"/>
               {t('whiteboard/paste-as-link')}
-              <div className="tl-menu-right-slot">
-                <span className="keyboard-shortcut">
-                  <code>{MOD_KEY}+⇧+v</code>
-                </span>
-              </div>
-            </ReactContextMenu.Item>
+              <KeyboardShortcut shortcut={`${MOD_KEY}+⇧+v`}/>
+            </LSUI.ContextMenuItem>
           )}
           {app.selectedShapes?.size > 0 && (
             <>
-              <ReactContextMenu.Separator className="menu-separator" />
-              <ReactContextMenu.Item
+              <LSUI.ContextMenuSeparator className="menu-separator"/>
+              <LSUI.ContextMenuItem
                 className="tl-menu-item"
                 onClick={() =>
                   runAndTransition(() =>
@@ -239,107 +243,113 @@ export const ContextMenu = observer(function ContextMenu({
                   )
                 }
               >
-                <TablerIcon className="tl-menu-icon" name="file-export" />
+                <TablerIcon className="tl-menu-icon" name="file-export"/>
                 {t('whiteboard/export')}
                 <div className="tl-menu-right-slot">
                   <span className="keyboard-shortcut"></span>
                 </div>
-              </ReactContextMenu.Item>
+              </LSUI.ContextMenuItem>
             </>
           )}
-          <ReactContextMenu.Separator className="menu-separator" />
-          <ReactContextMenu.Item
+          <LSUI.ContextMenuSeparator className="menu-separator"/>
+          <LSUI.ContextMenuItem
             className="tl-menu-item"
             onClick={() => runAndTransition(app.api.selectAll)}
           >
+            <TablerIcon className="tl-menu-icon" name="circle-dotted"/>
             {t('whiteboard/select-all')}
-            <KeyboardShortcut action="editor/select-parent" />
-          </ReactContextMenu.Item>
+            <KeyboardShortcut action="editor/select-parent"/>
+          </LSUI.ContextMenuItem>
           {app.selectedShapes?.size > 1 && (
-            <ReactContextMenu.Item
+            <LSUI.ContextMenuItem
               className="tl-menu-item"
               onClick={() => runAndTransition(app.api.deselectAll)}
             >
+              <TablerIcon className="tl-menu-icon" name="circle-dotted"/>
               {t('whiteboard/deselect-all')}
-            </ReactContextMenu.Item>
+            </LSUI.ContextMenuItem>
           )}
           {!app.readOnly &&
             app.selectedShapes?.size > 0 &&
             app.selectedShapesArray?.some(s => !s.props.isLocked) && (
-              <ReactContextMenu.Item
+              <LSUI.ContextMenuItem
                 className="tl-menu-item"
                 onClick={() => runAndTransition(() => app.setLocked(true))}
               >
-                <TablerIcon className="tl-menu-icon" name="lock" />
+                <TablerIcon className="tl-menu-icon" name="lock"/>
                 {t('whiteboard/lock')}
-                <KeyboardShortcut action="whiteboard/lock" />
-              </ReactContextMenu.Item>
+                <KeyboardShortcut action="whiteboard/lock"/>
+              </LSUI.ContextMenuItem>
             )}
           {!app.readOnly &&
             app.selectedShapes?.size > 0 &&
             app.selectedShapesArray?.some(s => s.props.isLocked) && (
-              <ReactContextMenu.Item
+              <LSUI.ContextMenuItem
                 className="tl-menu-item"
                 onClick={() => runAndTransition(() => app.setLocked(false))}
               >
-                <TablerIcon className="tl-menu-icon" name="lock-open" />
+                <TablerIcon className="tl-menu-icon" name="lock-open"/>
                 {t('whiteboard/unlock')}
-                <KeyboardShortcut action="whiteboard/unlock" />
-              </ReactContextMenu.Item>
+                <KeyboardShortcut action="whiteboard/unlock"/>
+              </LSUI.ContextMenuItem>
             )}
           {app.selectedShapes?.size > 0 &&
             !app.readOnly &&
             app.selectedShapesArray?.some(s => !s.props.isLocked) && (
               <>
-                <ReactContextMenu.Item
+                <LSUI.ContextMenuItem
                   className="tl-menu-item"
                   onClick={() => runAndTransition(app.api.deleteShapes)}
                 >
-                  <TablerIcon className="tl-menu-icon" name="backspace" />
+                  <TablerIcon className="tl-menu-icon" name="backspace"/>
                   {t('whiteboard/delete')}
-                  <KeyboardShortcut action="editor/delete" />
-                </ReactContextMenu.Item>
+                  <KeyboardShortcut action="editor/delete"/>
+                </LSUI.ContextMenuItem>
                 {app.selectedShapes?.size > 1 && !app.readOnly && (
                   <>
-                    <ReactContextMenu.Separator className="menu-separator" />
-                    <ReactContextMenu.Item
+                    <LSUI.ContextMenuSeparator className="menu-separator"/>
+                    <LSUI.ContextMenuItem
                       className="tl-menu-item"
                       onClick={() => runAndTransition(app.flipHorizontal)}
                     >
-                      <TablerIcon className="tl-menu-icon" name="flip-horizontal" />
+                      <TablerIcon className="tl-menu-icon"
+                                  name="flip-horizontal"/>
                       {t('whiteboard/flip-horizontally')}
-                    </ReactContextMenu.Item>
-                    <ReactContextMenu.Item
+                    </LSUI.ContextMenuItem>
+                    <LSUI.ContextMenuItem
                       className="tl-menu-item"
                       onClick={() => runAndTransition(app.flipVertical)}
                     >
-                      <TablerIcon className="tl-menu-icon" name="flip-vertical" />
+                      <TablerIcon className="tl-menu-icon"
+                                  name="flip-vertical"/>
                       {t('whiteboard/flip-vertically')}
-                    </ReactContextMenu.Item>
+                    </LSUI.ContextMenuItem>
                   </>
                 )}
                 {!app.readOnly && (
                   <>
-                    <ReactContextMenu.Separator className="menu-separator" />
-                    <ReactContextMenu.Item
+                    <LSUI.ContextMenuSeparator className="menu-separator"/>
+                    <LSUI.ContextMenuItem
                       className="tl-menu-item"
                       onClick={() => runAndTransition(app.bringToFront)}
                     >
+                      <TablerIcon className="tl-menu-icon" name="circle-dotted"/>
                       {t('whiteboard/move-to-front')}
-                      <KeyboardShortcut action="whiteboard/bring-to-front" />
-                    </ReactContextMenu.Item>
-                    <ReactContextMenu.Item
+                      <KeyboardShortcut action="whiteboard/bring-to-front"/>
+                    </LSUI.ContextMenuItem>
+                    <LSUI.ContextMenuItem
                       className="tl-menu-item"
                       onClick={() => runAndTransition(app.sendToBack)}
                     >
+                      <TablerIcon className="tl-menu-icon" name="circle-dotted"/>
                       {t('whiteboard/move-to-back')}
-                      <KeyboardShortcut action="whiteboard/send-to-back" />
-                    </ReactContextMenu.Item>
+                      <KeyboardShortcut action="whiteboard/send-to-back"/>
+                    </LSUI.ContextMenuItem>
                   </>
                 )}
 
                 {developerMode && (
-                  <ReactContextMenu.Item
+                  <LSUI.ContextMenuItem
                     className="tl-menu-item"
                     onClick={() => {
                       if (app.selectedShapesArray.length === 1) {
@@ -350,12 +360,12 @@ export const ContextMenu = observer(function ContextMenu({
                     }}
                   >
                     {t('whiteboard/dev-print-shape-props')}
-                  </ReactContextMenu.Item>
+                  </LSUI.ContextMenuItem>
                 )}
               </>
             )}
         </div>
-      </ReactContextMenu.Content>
-    </ReactContextMenu.Root>
+      </LSUI.ContextMenuContent>
+    </LSUI.ContextMenu>
   )
 })

+ 3 - 3
tldraw/apps/tldraw-logseq/src/components/KeyboardShortcut/KeyboardShortcut.tsx

@@ -2,15 +2,15 @@ import { LogseqContext } from '../../lib/logseq-context'
 import * as React from 'react'
 
 export const KeyboardShortcut = ({
-  action,
+  action, shortcut, opts,
   ...props
-}: { action: string } & React.HTMLAttributes<HTMLElement>) => {
+}: Partial<{ action: string, shortcut: string, opts: any }> & React.HTMLAttributes<HTMLElement>) => {
   const { renderers } = React.useContext(LogseqContext)
   const Shortcut = renderers?.KeyboardShortcut
 
   return (
     <div className="tl-menu-right-slot" {...props}>
-      <Shortcut action={action} />
+      <Shortcut action={action} shortcut={shortcut} opts={opts} />
     </div>
   )
 }

+ 3 - 1
tldraw/apps/tldraw-logseq/src/lib/logseq-context.ts

@@ -38,7 +38,9 @@ export interface LogseqContextValue {
       }
     }>
     KeyboardShortcut: React.FC<{
-      action: string
+      action?: string,
+      shortcut?: string,
+      opts?: any
     }>
   }
   handlers: {

+ 39 - 59
tldraw/apps/tldraw-logseq/src/styles.css

@@ -6,19 +6,19 @@
   --ls-wb-stroke-color-blue: var(--color-blue-500, blue);
   --ls-wb-stroke-color-purple: var(--color-purple-500, purple);
   --ls-wb-stroke-color-pink: var(--color-pink-500, pink);
-  --ls-wb-stroke-color-default: var(--ls-secondary-border-color);
-  --ls-wb-text-color-default: var(--ls-primary-text-color);
-  --ls-wb-background-color-default: var(--ls-tertiary-background-color);
+  --ls-wb-stroke-color-default: var(--ls-secondary-border-color, hsl(var(--border, var(--rx-gray-05-hsl))));
+  --ls-wb-text-color-default: var(--ls-primary-text-color, hsl(var(--primary)));
+  --ls-wb-background-color-default: var(--ls-tertiary-background-color, var(--rx-gray-03));
 }
 
 .logseq-tldraw {
-  --color-panel: var(--ls-tertiary-background-color);
+  --color-panel: var(--ls-tertiary-background-color, hsl(var(--secondary)));
   --color-panel-inverted: var(--ls-secondary-text-color);
   --color-text: var(--ls-primary-text-color);
   --color-text-inverted: var(--ls-tertiary-background-color);
-  --color-hover: var(--ls-secondary-background-color);
-  --color-selectedStroke: var(--ls-button-background);
-  --color-selectedFill: hsl(var(--ls-button-background-hsl) / 0.9);
+  --color-hover: var(--ls-secondary-background-color, hsl(var(--secondary)));
+  --color-selectedStroke: var(--ls-button-background, hsl(var(--accent) / 0.9));
+  --color-selectedFill: hsl(var(--ls-button-background-hsl, var(--accent)) / 0.1);
   --color-selectedContrast: #fff;
   --shadow-small: 0 1px 2px 0 rgb(0 0 0 / 0.05);
   --shadow-medium: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
@@ -98,60 +98,50 @@ html[data-theme='light'] {
   background-color: transparent !important;
 }
 
-.tl-menu-item {
-  @apply flex items-center pl-10 pr-4 py-1 text-sm relative !important;
-
-  min-width: 220px;
-  all: unset;
-  line-height: 1;
-  height: 25px;
-  padding: 0 5px;
-  position: relative;
-  user-select: none;
-  color: var(--ls-primary-text-color);
-
-  &:hover,
-  &:focus {
-    cursor: pointer;
-    background-color: var(--ls-primary-background-color) !important;
-  }
-
-  .tl-menu-icon {
-    @apply absolute left-4 text-base opacity-50;
-  }
-
-  &[data-disabled] {
-    @apply opacity-50 pointer-events-none;
-  }
-}
-
 #tl-zoom {
   @apply w-auto !important;
 }
 
+.tl-menu-item {
+  @apply leading-[20px];
+}
+
 #zoomPopup .tl-menu-item {
   @apply pl-4 !important;
 }
 
+.tl-menu-button-row-wrap {
+  @apply flex flex-col;
+}
+
 .tl-menu-button-row {
-  @apply flex justify-between px-4 py-1;
+  @apply flex justify-between py-1 gap-[26px] opacity-80 hover:opacity-100;
+
+  .ti {
+    @apply text-lg;
+  }
 }
 
 .tl-menu {
-  @apply relative py-2 flex bottom-0 flex border-0 rounded shadow-lg;
+  @apply relative flex flex-col opacity-100 select-none;
 
-  opacity: 100%;
-  user-select: none;
-  flex-direction: column;
   z-index: 180;
   min-width: 220px;
   pointer-events: 'all';
-  background: var(--ls-secondary-background-color);
+}
+
+.tl-menu-icon {
+  @apply text-[16px] leading-none opacity-70 mr-1;
+}
+
+.tl-menu-icon.ti-circle-dotted {
+  @apply invisible;
 }
 
 .tl-menu-right-slot {
   margin-left: auto;
   padding-left: 20px;
+  line-height: 1;
 
   .keyboard-shortcut > code {
     padding: 4px !important;
@@ -177,7 +167,7 @@ html[data-theme='light'] {
 
   pointer-events: all;
   position: relative;
-  background-color: or(--logseq-whiteboard-toolbar-background, --lx-gray-03, --ls-secondary-background-color);
+  background-color: var(--lx-gray-03, var(--ls-secondary-background-color, var(--rx-gray-03)));
   border-radius: 8px;
   white-space: nowrap;
   gap: 8px;
@@ -271,22 +261,11 @@ html[data-theme='light'] {
 }
 
 .tl-button {
-  @apply relative flex items-center justify-center rounded border-0 gap-1;
-
-  height: 32px;
-  min-width: 32px;
-  font-family: var(--ls-font-family);
-  background: none;
-  cursor: pointer;
-  color: var(--ls-primary-text-color);
-
-  &:hover {
-    background-color: or(--ls-whiteboard-button-background-hover, --lx-gray-06, --ls-primary-background-color);
-  }
+  @apply relative flex items-center justify-center rounded border-0 gap-1 bg-none cursor-pointer;
+  @apply w-[32px] h-[32px] hover:bg-gray-06;
 
   &[data-selected='true'] {
-    background-color: or(--ls-whiteboard-button-background-selected, --lx-accent-09, --color-selectedFill);
-    color: or(--ls-whiteboard-button-text-selected, --lx-gray-12, --color-selectedContrast);
+    @apply bg-accent text-accent-foreground;
   }
 }
 
@@ -575,7 +554,7 @@ button.tl-select-input-trigger {
   @apply flex items-center rounded-lg text-base max-w-full;
 
   min-height: 40px;
-  background-color: var(--ls-secondary-background-color);
+  background-color: var(--ls-secondary-background-color, hsl(var(--background)));
   padding: 6px 16px;
   box-shadow: var(--shadow-small);
 }
@@ -623,6 +602,7 @@ button.tl-select-input-trigger {
 }
 
 .tl-quick-search .tl-text-input {
+  background-color: transparent;
   border: none;
 }
 
@@ -641,7 +621,7 @@ button.tl-select-input-trigger {
   @apply absolute left-0 w-full flex z-10;
 
   top: calc(100% + 12px);
-  background-color: var(--ls-primary-background-color);
+  background-color: var(--ls-primary-background-color, hsl(var(--background)));
   max-height: 300px;
   min-width: 460px;
   box-shadow: var(--shadow-large);
@@ -871,9 +851,9 @@ html[data-theme='dark'] {
 }
 
 .tl-toolbar-separator {
-  background-color: var(--ls-border-color);
+  background-color: var(--ls-border-color, var(--rx-gray-06));
   width: 1px;
-  opacity: 0.5;
+  opacity: 0.9;
 
   &[data-orientation='horizontal'] {
     height: 1px;

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