Преглед изворни кода

Merge branch 'master' into feat/db

Gabriel Horner пре 2 година
родитељ
комит
b66bde0230
82 измењених фајлова са 876 додато и 780 уклоњено
  1. 1 1
      .github/workflows/build-android.yml
  2. 3 2
      android/app/build.gradle
  3. 3 3
      android/app/capacitor.build.gradle
  4. 7 5
      android/app/src/main/AndroidManifest.xml
  5. 2 2
      android/app/src/main/assets/capacitor.plugins.json
  6. 1 1
      android/app/src/main/java/com/logseq/app/FolderPicker.java
  7. 2 2
      android/build.gradle
  8. 2 2
      android/capacitor.settings.gradle
  9. 1 2
      android/gradle.properties
  10. 1 1
      android/gradle/wrapper/gradle-wrapper.properties
  11. 10 10
      android/variables.gradle
  12. 4 0
      capacitor.config.ts
  13. 1 1
      deps.edn
  14. 1 0
      e2e-tests/accessibility.spec.ts
  15. 1 1
      ios/.gitignore
  16. 4 4
      ios/App/App.xcodeproj/project.pbxproj
  17. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  18. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  19. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  20. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  21. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  22. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  23. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  24. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  25. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  26. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  27. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  28. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  29. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  30. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  31. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  32. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  33. BIN
      ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]
  34. 11 113
      ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json
  35. 1 1
      ios/App/Podfile
  36. 27 19
      package.json
  37. 1 1
      resources/forge.config.js
  38. 11 5
      resources/js/preload.js
  39. 3 2
      resources/package.json
  40. 7 7
      src/electron/electron/configs.cljs
  41. 4 0
      src/electron/electron/core.cljs
  42. 13 2
      src/electron/electron/handler.cljs
  43. 8 2
      src/electron/electron/utils.cljs
  44. 1 1
      src/electron/electron/window.cljs
  45. 1 1
      src/main/frontend/components/block.cljs
  46. 2 2
      src/main/frontend/components/bug_report.cljs
  47. 1 1
      src/main/frontend/components/container.css
  48. 22 18
      src/main/frontend/components/editor.cljs
  49. 6 3
      src/main/frontend/components/header.cljs
  50. 34 12
      src/main/frontend/components/page.cljs
  51. 2 1
      src/main/frontend/components/plugins.cljs
  52. 1 0
      src/main/frontend/components/right_sidebar.cljs
  53. 15 0
      src/main/frontend/components/settings.cljs
  54. 6 1
      src/main/frontend/components/theme.cljs
  55. 1 1
      src/main/frontend/components/whiteboard.css
  56. 6 22
      src/main/frontend/components/window_controls.cljs
  57. 14 10
      src/main/frontend/config.cljs
  58. 1 1
      src/main/frontend/extensions/code.cljs
  59. 18 15
      src/main/frontend/extensions/pdf/core.cljs
  60. 4 10
      src/main/frontend/extensions/video/youtube.cljs
  61. 1 1
      src/main/frontend/handler/dnd.cljs
  62. 5 2
      src/main/frontend/handler/editor.cljs
  63. 2 11
      src/main/frontend/handler/web/nfs.cljs
  64. 2 2
      src/main/frontend/handler/whiteboard.cljs
  65. 19 0
      src/main/frontend/handler/window.cljs
  66. 7 0
      src/main/frontend/modules/shortcut/config.cljs
  67. 22 18
      src/main/frontend/ui.cljs
  68. 3 3
      src/main/frontend/util.cljc
  69. 1 1
      src/main/frontend/version.cljs
  70. 5 1
      src/resources/dicts/en.edn
  71. 38 23
      src/resources/dicts/es.edn
  72. 29 1
      src/resources/dicts/tr.edn
  73. 29 5
      static/yarn.lock
  74. 21 3
      tldraw/apps/tldraw-logseq/src/components/ActionBar/ActionBar.tsx
  75. 1 1
      tldraw/apps/tldraw-logseq/src/styles.css
  76. 7 0
      tldraw/packages/core/src/lib/TLApi/TLApi.ts
  77. 2 0
      tldraw/packages/core/src/lib/TLSettings.ts
  78. 1 0
      tldraw/packages/react/src/components/AppCanvas.tsx
  79. 1 0
      tldraw/packages/react/src/components/Canvas/Canvas.tsx
  80. 20 0
      tldraw/packages/react/src/hooks/useCanvasEvents.ts
  81. 22 0
      tldraw/packages/react/src/hooks/useShapeEvents.ts
  82. 373 420
      yarn.lock

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

@@ -43,7 +43,7 @@ on:
 env:
   CLOJURE_VERSION: '1.10.1.763'
   NODE_VERSION: '18'
-  JAVA_VERSION: '11'
+  JAVA_VERSION: '17'
 
 jobs:
   build-apk:

+ 3 - 2
android/app/build.gradle

@@ -1,13 +1,14 @@
 apply plugin: 'com.android.application'
 
 android {
+    namespace "com.logseq.app"
     compileSdkVersion rootProject.ext.compileSdkVersion
     defaultConfig {
         applicationId "com.logseq.app"
         minSdkVersion rootProject.ext.minSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
-        versionCode 70
-        versionName "0.9.17"
+        versionCode 71
+        versionName "0.9.18"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         aaptOptions {
              // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.

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

@@ -2,8 +2,8 @@
 
 android {
   compileOptions {
-      sourceCompatibility JavaVersion.VERSION_11
-      targetCompatibility JavaVersion.VERSION_11
+      sourceCompatibility JavaVersion.VERSION_17
+      targetCompatibility JavaVersion.VERSION_17
   }
 }
 
@@ -19,7 +19,7 @@ dependencies {
     implementation project(':capacitor-splash-screen')
     implementation project(':capacitor-status-bar')
     implementation project(':capawesome-capacitor-background-task')
-    implementation project(':hugotomazi-capacitor-navigation-bar')
+    implementation project(':capgo-capacitor-navigation-bar')
     implementation project(':logseq-capacitor-file-sync')
     implementation project(':capacitor-voice-recorder')
     implementation project(':send-intent')

+ 7 - 5
android/app/src/main/AndroidManifest.xml

@@ -1,7 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.logseq.app">
-
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
     <application
         android:allowBackup="true"
         android:icon="@mipmap/ic_launcher"
@@ -37,7 +35,7 @@
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.BROWSABLE" />
-                <data android:scheme="logseq" android:host="auth-callback" />
+                <data android:scheme="logseq" />
             </intent-filter>
 
         </activity>
@@ -56,7 +54,11 @@
     <!-- Permissions -->
 
     <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
+    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
+    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
 </manifest>

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

@@ -40,8 +40,8 @@
 		"classpath": "io.capawesome.capacitorjs.plugins.backgroundtask.BackgroundTaskPlugin"
 	},
 	{
-		"pkg": "@hugotomazi/capacitor-navigation-bar",
-		"classpath": "br.com.tombus.capacitor.plugin.navigationbar.NavigationBarPlugin"
+		"pkg": "@capgo/capacitor-navigation-bar",
+		"classpath": "ee.forgr.capacitor_navigation_bar.NavigationBarPlugin"
 	},
 	{
 		"pkg": "@logseq/capacitor-file-sync",

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

@@ -43,7 +43,7 @@ public class FolderPicker extends Plugin {
             startActivityForResult(call, i, "folderPickerResult");
         } else {
             Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
-            Uri uri = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null);
+            Uri uri = Uri.fromParts("package", this.getContext().getPackageName(), null);
             intent.setData(uri);
             startActivityForResult(call, intent, 20);
         }

+ 2 - 2
android/build.gradle

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

+ 2 - 2
android/capacitor.settings.gradle

@@ -32,8 +32,8 @@ project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacit
 include ':capawesome-capacitor-background-task'
 project(':capawesome-capacitor-background-task').projectDir = new File('../node_modules/@capawesome/capacitor-background-task/android')
 
-include ':hugotomazi-capacitor-navigation-bar'
-project(':hugotomazi-capacitor-navigation-bar').projectDir = new File('../node_modules/@hugotomazi/capacitor-navigation-bar/android')
+include ':capgo-capacitor-navigation-bar'
+project(':capgo-capacitor-navigation-bar').projectDir = new File('../node_modules/@capgo/capacitor-navigation-bar/android')
 
 include ':logseq-capacitor-file-sync'
 project(':logseq-capacitor-file-sync').projectDir = new File('../node_modules/@logseq/capacitor-file-sync/android')

+ 1 - 2
android/gradle.properties

@@ -20,5 +20,4 @@ org.gradle.jvmargs=-Xmx4096m
 # Android operating system, and which are packaged with your app's APK
 # https://developer.android.com/topic/libraries/support-library/androidx-rn
 android.useAndroidX=true
-# Automatically convert third-party libraries to use AndroidX
-android.enableJetifier=true
+

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

@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-all.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists

+ 10 - 10
android/variables.gradle

@@ -1,16 +1,16 @@
 ext {
     minSdkVersion = 22
-    compileSdkVersion = 32
-    targetSdkVersion = 32
-    androidxActivityVersion = '1.4.0'
-    androidxAppCompatVersion = '1.4.2'
+    compileSdkVersion = 33
+    targetSdkVersion = 33
+    androidxActivityVersion = '1.7.0'
+    androidxAppCompatVersion = '1.6.1'
     androidxCoordinatorLayoutVersion = '1.2.0'
-    androidxCoreVersion = '1.8.0'
-    androidxFragmentVersion = '1.4.1'
+    androidxCoreVersion = '1.10.0'
+    androidxFragmentVersion = '1.5.6'
     junitVersion = '4.13.2'
-    androidxJunitVersion = '1.1.3'
-    androidxEspressoCoreVersion = '3.4.0'
+    androidxJunitVersion = '1.1.5'
+    androidxEspressoCoreVersion = '3.5.1'
     cordovaAndroidVersion = '10.1.1'
-    coreSplashScreenVersion = '1.0.0-rc01'
-    androidxWebkitVersion = '1.4.0'
+    coreSplashScreenVersion = '1.0.0'
+    androidxWebkitVersion = '1.6.1'
 }

+ 4 - 0
capacitor.config.ts

@@ -9,6 +9,10 @@ const config: CapacitorConfig = {
   bundledWebRuntime: false,
   webDir: 'public',
   loggingBehavior: 'debug',
+  server: {
+    // https://capacitorjs.com/docs/updating/5-0#update-androidscheme
+    androidScheme: 'http',
+  },
   plugins: {
     SplashScreen: {
       launchShowDuration: 500,

+ 1 - 1
deps.edn

@@ -4,7 +4,7 @@
   rum/rum                               {:mvn/version "0.12.9"}
   datascript/datascript                 {:mvn/version "1.3.8"}
   datascript-transit/datascript-transit {:mvn/version "0.3.0"}
-  borkdude/rewrite-edn                  {:mvn/version "0.4.6"}
+  borkdude/rewrite-edn                  {:mvn/version "0.4.7"}
   funcool/promesa                       {:mvn/version "4.0.2"}
   medley/medley                         {:mvn/version "1.4.0"}
   metosin/reitit-frontend               {:mvn/version "0.3.10"}

+ 1 - 0
e2e-tests/accessibility.spec.ts

@@ -8,6 +8,7 @@ test('should not have any automatically detectable accessibility issues', async
     await page.waitForTimeout(2000)
     const accessibilityScanResults = await new AxeBuilder({ page })
         .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
+        .disableRules(['meta-viewport'])
         .setLegacyMode()
         .analyze()
 

+ 1 - 1
ios/.gitignore

@@ -1,9 +1,9 @@
 App/build
 App/Pods
-App/Podfile.lock
 App/App/public
 DerivedData
 xcuserdata
 
 # Cordova plugins for Capacitor
 capacitor-cordova-ios-plugins
+

+ 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.9.17;
+				MARKETING_VERSION = 0.9.18;
 				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.9.17;
+				MARKETING_VERSION = 0.9.18;
 				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.9.17;
+				MARKETING_VERSION = 0.9.18;
 				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.9.17;
+				MARKETING_VERSION = 0.9.18;
 				MTL_FAST_MATH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq.ShareViewController;
 				PRODUCT_NAME = "$(TARGET_NAME)";

BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/[email protected]


+ 11 - 113
ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json

@@ -1,116 +1,14 @@
 {
-  "images" : [
-    {
-      "size" : "20x20",
-      "idiom" : "iphone",
-      "filename" : "[email protected]",
-      "scale" : "2x"
-    },
-    {
-      "size" : "20x20",
-      "idiom" : "iphone",
-      "filename" : "[email protected]",
-      "scale" : "3x"
-    },
-    {
-      "size" : "29x29",
-      "idiom" : "iphone",
-      "filename" : "[email protected]",
-      "scale" : "2x"
-    },
-    {
-      "size" : "29x29",
-      "idiom" : "iphone",
-      "filename" : "[email protected]",
-      "scale" : "3x"
-    },
-    {
-      "size" : "40x40",
-      "idiom" : "iphone",
-      "filename" : "[email protected]",
-      "scale" : "2x"
-    },
-    {
-      "size" : "40x40",
-      "idiom" : "iphone",
-      "filename" : "[email protected]",
-      "scale" : "3x"
-    },
-    {
-      "size" : "60x60",
-      "idiom" : "iphone",
-      "filename" : "[email protected]",
-      "scale" : "2x"
-    },
-    {
-      "size" : "60x60",
-      "idiom" : "iphone",
-      "filename" : "[email protected]",
-      "scale" : "3x"
-    },
-    {
-      "size" : "20x20",
-      "idiom" : "ipad",
-      "filename" : "[email protected]",
-      "scale" : "1x"
-    },
-    {
-      "size" : "20x20",
-      "idiom" : "ipad",
-      "filename" : "[email protected]",
-      "scale" : "2x"
-    },
-    {
-      "size" : "29x29",
-      "idiom" : "ipad",
-      "filename" : "[email protected]",
-      "scale" : "1x"
-    },
-    {
-      "size" : "29x29",
-      "idiom" : "ipad",
-      "filename" : "[email protected]",
-      "scale" : "2x"
-    },
-    {
-      "size" : "40x40",
-      "idiom" : "ipad",
-      "filename" : "[email protected]",
-      "scale" : "1x"
-    },
-    {
-      "size" : "40x40",
-      "idiom" : "ipad",
-      "filename" : "[email protected]",
-      "scale" : "2x"
-    },
-    {
-      "size" : "76x76",
-      "idiom" : "ipad",
-      "filename" : "[email protected]",
-      "scale" : "1x"
-    },
-    {
-      "size" : "76x76",
-      "idiom" : "ipad",
-      "filename" : "[email protected]",
-      "scale" : "2x"
-    },
-    {
-      "size" : "83.5x83.5",
-      "idiom" : "ipad",
-      "filename" : "[email protected]",
-      "scale" : "2x"
-    },
-    {
-      "size" : "1024x1024",
-      "idiom" : "ios-marketing",
-      "filename" : "[email protected]",
-      "scale" : "1x"
+    "images" : [
+      {
+        "filename" : "[email protected]",
+        "idiom" : "universal",
+        "platform" : "ios",
+        "size" : "1024x1024"
+      }
+    ],
+    "info" : {
+      "author" : "xcode",
+      "version" : 1
     }
-  ],
-  "info" : {
-    "version" : 1,
-    "author" : "xcode"
-  }
 }

+ 1 - 1
ios/App/Podfile

@@ -21,7 +21,7 @@ def capacitor_pods
   pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen'
   pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
   pod 'CapawesomeCapacitorBackgroundTask', :path => '../../node_modules/@capawesome/capacitor-background-task'
-  pod 'HugotomaziCapacitorNavigationBar', :path => '../../node_modules/@hugotomazi/capacitor-navigation-bar'
+  pod 'CapgoCapacitorNavigationBar', :path => '../../node_modules/@capgo/capacitor-navigation-bar'
   pod 'LogseqCapacitorFileSync', :path => '../../node_modules/@logseq/capacitor-file-sync'
   pod 'CapacitorVoiceRecorder', :path => '../../node_modules/capacitor-voice-recorder'
   pod 'SendIntent', :path => '../../node_modules/send-intent'

+ 27 - 19
package.json

@@ -5,7 +5,7 @@
     "main": "static/electron.js",
     "devDependencies": {
         "@axe-core/playwright": "=4.4.4",
-        "@capacitor/cli": "^4.0.0",
+        "@capacitor/cli": "^5.0.0",
         "@playwright/test": "=1.31.0",
         "@tailwindcss/aspect-ratio": "0.4.2",
         "@tailwindcss/forms": "0.5.3",
@@ -76,26 +76,26 @@
         "postinstall": "yarn tldraw:build && yarn amplify:build "
     },
     "dependencies": {
-        "@capacitor/android": "^4.0.0",
-        "@capacitor/app": "^4.0.0",
-        "@capacitor/camera": "^4.0.0",
-        "@capacitor/clipboard": "^4.0.0",
-        "@capacitor/core": "^4.0.0",
-        "@capacitor/filesystem": "^4.0.0",
-        "@capacitor/haptics": "^4.0.0",
-        "@capacitor/ios": "^4.0.0",
-        "@capacitor/keyboard": "^4.0.0",
-        "@capacitor/share": "^4.0.0",
-        "@capacitor/splash-screen": "^4.0.0",
-        "@capacitor/status-bar": "^4.0.0",
-        "@capawesome/capacitor-background-task": "^2.0.0",
+        "@capacitor/android": "^5.0.0",
+        "@capacitor/app": "^5.0.0",
+        "@capacitor/camera": "^5.0.0",
+        "@capacitor/clipboard": "^5.0.0",
+        "@capacitor/core": "^5.0.0",
+        "@capacitor/filesystem": "^5.0.0",
+        "@capacitor/haptics": "^5.0.0",
+        "@capacitor/ios": "^5.0.0",
+        "@capacitor/keyboard": "^5.0.0",
+        "@capacitor/share": "^5.0.0",
+        "@capacitor/splash-screen": "^5.0.0",
+        "@capacitor/status-bar": "^5.0.0",
+        "@capawesome/capacitor-background-task": "^5.0.0",
         "@emoji-mart/data": "^1.1.2",
         "@emoji-mart/react": "^1.1.1",
         "@excalidraw/excalidraw": "0.15.3",
         "@highlightjs/cdn-assets": "10.4.1",
-        "@hugotomazi/capacitor-navigation-bar": "^2.0.0",
+        "@capgo/capacitor-navigation-bar": "^6.0.0",
         "@isomorphic-git/lightning-fs": "^4.6.0",
-        "@logseq/capacitor-file-sync": "0.0.35",
+        "@logseq/capacitor-file-sync": "5.0.0",
         "@logseq/diff-merge": "0.2.2",
         "@logseq/react-tweet-embed": "1.3.1-1",
         "@radix-ui/colors": "^0.1.8",
@@ -104,11 +104,11 @@
         "@tabler/icons": "^1.96.0",
         "@tippyjs/react": "4.2.5",
         "bignumber.js": "^9.0.2",
-        "capacitor-voice-recorder": "4.0.0",
+        "capacitor-voice-recorder": "^5.0.0",
         "check-password-strength": "2.0.7",
         "chokidar": "3.5.1",
         "chrono-node": "2.2.4",
-        "codemirror": "5.58.1",
+        "codemirror": "5.65.13",
         "comlink": "^4.4.1",
         "d3-force": "3.0.0",
         "diff": "5.0.0",
@@ -147,7 +147,7 @@
         "remove-accents": "0.4.2",
         "reveal.js": "^4.5.0",
         "sanitize-filename": "1.6.3",
-        "send-intent": "3.0.11",
+        "send-intent": "^5.0.0",
         "shepherd.js": "^9.1.0",
         "tailwind-capitalize-first-letter": "^1.0.4",
         "threads": "1.6.5",
@@ -155,6 +155,14 @@
         "yargs-parser": "20.2.4"
     },
     "resolutions": {
+        "**/postcss": "8.4.17",
+        "**/postcss-colormin": "5.3.0",
+        "**/postcss-convert-values": "5.1.2",
+        "**/postcss-js": "4.0.0",
+        "**/postcss-merge-longhand": "5.1.6",
+        "**/postcss-merge-rules": "5.1.2",
+        "**/postcss-minify-params": "5.1.3",
+        "**/postcss-reduce-initial": "5.1.0",
         "pixi-graph-fork/@pixi/app": "6.2.0",
         "pixi-graph-fork/@pixi/constants": "6.2.0",
         "pixi-graph-fork/@pixi/core": "6.2.0",

+ 1 - 1
resources/forge.config.js

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

+ 11 - 5
resources/js/preload.js

@@ -116,15 +116,21 @@ contextBridge.exposeInMainWorld('apis', {
 
     const dest = path.join(repoPathRoot, to)
     const assetsRoot = path.dirname(dest)
-    
+
     await fs.promises.mkdir(assetsRoot, { recursive: true })
 
-    from = from && decodeURIComponent(from || getFilePathFromClipboard())
+    from = from || getFilePathFromClipboard()
 
     if (from) {
-      // console.debug('copy file: ', from, dest)
-      await fs.promises.copyFile(from, dest)
-      return path.basename(from)
+      try {
+        // console.debug('copy file: ', from, dest)
+        await fs.promises.copyFile(from, dest)
+        return path.basename(from)
+      } catch (e) {
+        from = decodeURIComponent(from)
+        await fs.promises.copyFile(from, dest)
+        return path.basename(from)
+      }
     }
 
     // support image

+ 3 - 2
resources/package.json

@@ -1,7 +1,7 @@
 {
   "name": "Logseq",
   "productName": "Logseq",
-  "version": "0.9.17",
+  "version": "0.9.18",
   "main": "electron.js",
   "author": "Logseq",
   "license": "AGPL-3.0",
@@ -34,7 +34,8 @@
     "update-electron-app": "2.0.1",
     "extract-zip": "2.0.1",
     "diff-match-patch": "1.0.5",
-    "https-proxy-agent": "5.0.0",
+    "https-proxy-agent": "7.0.2",
+    "socks-proxy-agent": "8.0.2",
     "@sentry/electron": "2.5.1",
     "posthog-js": "1.10.2",
     "@logseq/rsapi": "0.0.73",

+ 7 - 7
src/electron/electron/configs.cljs

@@ -1,9 +1,9 @@
 (ns electron.configs
-  (:require
-    ["fs-extra" :as ^js fs]
-    ["path" :as ^js node-path]
-    ["electron" :refer [^js app] :as electron]
-    [cljs.reader :as reader]))
+  (:require ["electron" :refer [^js app] :as electron]
+            ["fs-extra" :as ^js fs]
+            ["path" :as ^js node-path]
+            [cljs.reader :as reader]
+            [electron.logger :as logger]))
 
 ;; FIXME: move configs.edn to where it should be
 (defonce dot-root (.join node-path (.getPath app "home") ".logseq"))
@@ -17,14 +17,14 @@
     (let [body (.toString (.readFileSync fs cfg-path))]
       (if (seq body) (reader/read-string body) {}))
     (catch :default e
-      (js/console.error :cfg-error e))))
+      (logger/error :cfg-error e))))
 
 (defn- write-cfg!
   [cfg]
   (try
     (.writeFileSync fs cfg-path (pr-str cfg)) cfg
     (catch :default e
-      (js/console.error :cfg-error e))))
+      (logger/error :cfg-error e))))
 
 (defn set-item!
   [k v]

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

@@ -208,6 +208,10 @@
                                     {:role "about"
                                      :label "About Logseq"
                                      :click about-fn}]}))
+        ;; Enable Cmd/Ctrl+= Zoom In
+        template (conj template 
+                       {:role "zoomin"
+                        :accelerator "CommandOrControl+="})
         menu (.buildFromTemplate Menu (clj->js template))]
     (.setApplicationMenu Menu menu)))
 

+ 13 - 2
src/electron/electron/handler.cljs

@@ -6,6 +6,7 @@
             ["buffer" :as buffer]
             ["diff-match-patch" :as google-diff]
             ["electron" :refer [app autoUpdater dialog ipcMain shell]]
+            ["electron-window-state" :as windowStateKeeper]
             ["fs" :as fs]
             ["fs-extra" :as fs-extra]
             ["os" :as os]
@@ -98,6 +99,12 @@
     (catch :default _e
       false)))
 
+(defn chmod-enabled?
+  []
+  (if (= nil (cfgs/get-item :feature/enable-automatic-chmod?))
+    true
+    (cfgs/get-item :feature/enable-automatic-chmod?)))
+
 (defmethod handle :copyFile [_window [_ _repo from-path to-path]]
   (logger/info ::copy-file from-path to-path)
   (fs-extra/copy from-path to-path))
@@ -108,7 +115,7 @@
                       (.from Buf content)
                       content)]
     (try
-      (when (and (fs/existsSync path) (not (writable? path)))
+      (when (and (chmod-enabled?) (fs/existsSync path) (not (writable? path)))
         (fs/chmodSync path "644"))
       (fs/writeFileSync path content)
       (fs/statSync path)
@@ -269,7 +276,7 @@
                                   (.toString (.readFileSync fs txid-path)))]
           (reader/read-string sync-meta))))
     (catch :default e
-      (js/console.debug "[read txid meta] #" root (.-message e)))))
+      (logger/error "[read txid meta] #" root (.-message e)))))
 
 (defmethod handle :inflateGraphsInfo [_win [_ graphs]]
   (if (seq graphs)
@@ -671,6 +678,10 @@
 (defmethod handle :window-close [^js win]
   (.close win))
 
+(defmethod handle :theme-loaded [^js win]
+  (.manage (windowStateKeeper) win)
+  (.show win))
+
 ;;;;;;;;;;;;;;;;;;;;;;;
 ;; file-sync-rs-apis ;;
 ;;;;;;;;;;;;;;;;;;;;;;;

+ 8 - 2
src/electron/electron/utils.cljs

@@ -26,7 +26,8 @@
 (defonce *fetchAgent (atom nil))
 
 (defonce open (js/require "open"))
-(defonce HttpsProxyAgent (js/require "https-proxy-agent"))
+(defonce HttpsProxyAgent (.-HttpsProxyAgent (js/require "https-proxy-agent")))
+(defonce SocksProxyAgent (.-SocksProxyAgent (js/require "socks-proxy-agent")))
 (defonce _fetch (js/require "node-fetch"))
 (defonce extract-zip (js/require "extract-zip"))
 
@@ -67,7 +68,12 @@
   [{:keys [protocol host port]}]
   (if (and protocol host port (or (= protocol "http") (= protocol "socks5")))
     (let [proxy-url (str protocol "://" host ":" port)]
-      (reset! *fetchAgent (new HttpsProxyAgent proxy-url)))
+      (condp = protocol
+        "http"
+        (reset! *fetchAgent (new HttpsProxyAgent proxy-url))
+        "socks5"
+        (reset! *fetchAgent (new SocksProxyAgent proxy-url))
+        (logger/error "Unknown proxy protocol:" protocol)))
     (reset! *fetchAgent nil)))
 
 (defn- set-rsapi-proxy

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

@@ -35,6 +35,7 @@
                       :titleBarStyle        "hiddenInset"
                       :trafficLightPosition {:x 16 :y 16}
                       :autoHideMenuBar      (not mac?)
+                      :show                 false
                       :webPreferences
                       {:plugins                 true        ; pdf
                        :nodeIntegration         false
@@ -55,7 +56,6 @@
                      linux?
                      (assoc :icon (node-path/join js/__dirname "icons/logseq.png")))
          win       (BrowserWindow. (clj->js win-opts))]
-     (.manage win-state win)
      (.onBeforeSendHeaders (.. session -defaultSession -webRequest)
                            (clj->js {:urls (array "*://*.youtube.com/*")})
                            (fn [^js details callback]

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

@@ -1351,7 +1351,7 @@
                   (str "https://player.vimeo.com/video/" id)
 
                   [_ _ _ "bilibili.com" _ id & query]
-                  (str "https://player.bilibili.com/player.html?bvid=" id "&high_quality=1"
+                  (str "https://player.bilibili.com/player.html?bvid=" id "&high_quality=1&autoplay=0"
                        (when-let [page (second query)]
                          (str "&page=" page)))
 

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

@@ -73,7 +73,7 @@
          (ui/button (t :bug-report/inspector-page-btn-copy) :on-click #(copy-result-to-clipboard! (js/JSON.stringify (clj->js result) nil 2)))]
         [:div.flex.justify-between.items-center.mt-2
          [:div (t :bug-report/inspector-page-desc-create-issue)]
-         (ui/button (t :bug-report/inspector-page-btn-create-issue) :href header/bug-report-url)]
+         (ui/button (t :bug-report/inspector-page-btn-create-issue) :href (header/bug-report-url))]
         [:div.flex.justify-between.items-center.mt-2
          [:div (t :bug-report/inspector-page-tip)]
          (ui/button (t :bug-report/inspector-page-btn-back) :on-click reset-step!)]
@@ -118,4 +118,4 @@
     [:div.flex.flex-col
      [:h1.text-2xl (t :bug-report/section-issues-title)]
      [:div.opacity-60 (t :bug-report/section-issues-desc)]
-     (report-item-button (t :bug-report/section-issues-btn-title) (t :bug-report/section-issues-btn-desc) "message-report" {:on-click #(util/open-url header/bug-report-url)})]]])
+     (report-item-button (t :bug-report/section-issues-btn-title) (t :bug-report/section-issues-btn-desc) "message-report" {:on-click #(util/open-url (header/bug-report-url))})]]])

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

@@ -524,7 +524,7 @@ html[data-theme='dark'] {
   }
 
   &-btn {
-    @apply fixed bottom-4 right-8;
+    @apply fixed bottom-4 right-4 sm:right-8;
 
     > .inner {
       @apply font-bold

+ 22 - 18
src/main/frontend/components/editor.cljs

@@ -479,16 +479,6 @@
         right-sidebar? (:ui/sidebar-open? @state/state)
         editing-key    (state/get-edit-input-id)
         *el (rum/use-ref nil)
-        _ (rum/use-effect! (fn []
-                             (when-let [^js/HTMLElement cnt
-                                        (and right-sidebar? editing-key
-                                             (js/document.querySelector "#main-content-container"))]
-                               (when (.contains cnt (js/document.querySelector (str "#" editing-key)))
-                                 (let [el  (rum/deref *el)
-                                       ofx (- (.-scrollWidth cnt) (.-clientWidth cnt))]
-                                   (when (> ofx 0)
-                                     (set! (.-transform (.-style el)) (str "translateX(-" (+ ofx 20) "px)")))))))
-                           [right-sidebar? editing-key])
         y-overflow-vh? (or (< to-max-height Y-BOUNDARY-HEIGHT)
                            (> (- max-height' to-max-height) Y-BOUNDARY-HEIGHT))
         to-max-height (if y-overflow-vh? max-height' to-max-height)
@@ -498,22 +488,36 @@
         style (merge
                {:top        (+ top offset-top (if (int? y-diff) y-diff 0))
                 :max-height to-max-height
-                :max-width 700
+                :max-width  700
                 ;; TODO: auto responsive fixed size
-                :width "fit-content"
+                :width      "fit-content"
                 :z-index    11}
                (when set-default-width?
                  {:width max-width})
                (if (<= vw-max-width (+ left (if set-default-width? max-width 500)))
                  {:right 0}
-                 {:left (if (or (nil? y-diff) (and y-diff (= y-diff 0))) left 0)}))]
+                 {:left 0}))]
+
+    (rum/use-effect!
+     (fn []
+       (when-let [^js/HTMLElement cnt
+                  (and right-sidebar? editing-key
+                       (js/document.querySelector "#main-content-container"))]
+         (when (.contains cnt (js/document.querySelector (str "#" editing-key)))
+           (let [el  (rum/deref *el)
+                 ofx (- (.-scrollWidth cnt) (.-clientWidth cnt))]
+             (when (> ofx 0)
+               (set! (.-transform (.-style el))
+                     (util/format "translate(-%spx, %s)" (+ ofx 20) (if y-overflow-vh? "calc(-100% - 2rem)" 0))))))))
+     [right-sidebar? editing-key y-overflow-vh?])
+
     [:div.absolute.rounded-md.shadow-lg.absolute-modal
-     {:ref *el
+     {:ref             *el
       :data-modal-name modal-name
-      :class (if y-overflow-vh? "is-overflow-vh-y" "")
-      :on-mouse-down (fn [e]
-                       (.stopPropagation e))
-      :style style}
+      :class           (if y-overflow-vh? "is-overflow-vh-y" "")
+      :on-mouse-down   (fn [e]
+                         (.stopPropagation e))
+      :style           style}
      cp]))
 
 (rum/defc transition-cp < rum/reactive

+ 6 - 3
src/main/frontend/components/header.cljs

@@ -62,13 +62,16 @@
       :on-click on-click}
      (ui/icon "menu-2" {:size ui/icon-size})]))
 
-(def bug-report-url
+(defn bug-report-url []
   (let [ua (.-userAgent js/navigator)
         safe-ua (string/replace ua #"[^_/a-zA-Z0-9\.\(\)]+" " ")
         platform (str "App Version: " version "\n"
                       "Git Revision: " config/REVISION "\n"
                       "Platform: " safe-ua "\n"
-                      "Language: " (.-language js/navigator))]
+                      "Language: " (.-language js/navigator) "\n"
+                      "Plugins: " (string/join ", " (map (fn [[k v]]
+                                                           (str (name k) " (" (:version v) ")"))
+                                                         (:plugin/installed-plugins @state/state))))]
     (str "https://github.com/logseq/logseq/issues/new?"
          "title=&"
          "template=bug_report.yaml&"
@@ -114,7 +117,7 @@
           :options {:href (rfe/href :import)}
           :icon (ui/icon "file-upload")})
 
-       (when-not config/publishing? 
+       (when-not config/publishing?
          {:title [:div.flex-row.flex.justify-between.items-center
                   [:span (t :join-community)]]
           :options {:href "https://discuss.logseq.com"

+ 34 - 12
src/main/frontend/components/page.cljs

@@ -2,18 +2,18 @@
   (:require ["/frontend/utils" :as utils]
             [clojure.string :as string]
             [frontend.components.block :as component-block]
-            [frontend.components.query :as query]
             [frontend.components.content :as content]
             [frontend.components.editor :as editor]
             [frontend.components.hierarchy :as hierarchy]
             [frontend.components.plugins :as plugins]
+            [frontend.components.query :as query]
             [frontend.components.reference :as reference]
-            [frontend.components.svg :as svg]
             [frontend.components.scheduled-deadlines :as scheduled]
             [frontend.components.property :as property]
             [frontend.components.property.value :as pv]
             [frontend.handler.property.util :as pu]
             [frontend.handler.property :as property-handler]
+            [frontend.components.svg :as svg]
             [frontend.config :as config]
             [frontend.context.i18n :refer [t]]
             [frontend.date :as date]
@@ -25,6 +25,7 @@
             [frontend.format.block :as block]
             [frontend.handler.common :as common-handler]
             [frontend.handler.config :as config-handler]
+            [frontend.handler.dnd :as dnd]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.graph :as graph-handler]
             [frontend.handler.notification :as notification]
@@ -38,13 +39,14 @@
             [frontend.util :as util]
             [frontend.util.text :as text-util]
             [goog.object :as gobj]
+            [logseq.graph-parser.mldoc :as gp-mldoc]
             [logseq.graph-parser.util :as gp-util]
+            [logseq.graph-parser.util.page-ref :as page-ref]
+            [logseq.db.property :as db-property]
             [medley.core :as medley]
+            [promesa.core :as p]
             [reitit.frontend.easy :as rfe]
-            [rum.core :as rum]
-            [logseq.graph-parser.util.page-ref :as page-ref]
-            [logseq.graph-parser.mldoc :as gp-mldoc]
-            [logseq.db.property :as db-property]))
+            [rum.core :as rum]))
 
 (defn- get-page-name
   [state]
@@ -106,10 +108,26 @@
 
   (rum/defc dummy-block
     [page-name]
-    (let [handler-fn (fn []
-                       (let [block (editor-handler/insert-first-page-block-if-not-exists! page-name {:redirect? false})]
-                         (js/setTimeout #(editor-handler/edit-block! block :max (:block/uuid block)) 0)))]
-      [:div.ls-block.flex-1.flex-col.rounded-sm {:style {:width "100%"}}
+    (let [[hover set-hover!] (rum/use-state false)
+          click-handler-fn (fn []
+                             (let [block (editor-handler/insert-first-page-block-if-not-exists! page-name {:redirect? false})]
+                               (js/setTimeout #(editor-handler/edit-block! block :max (:block/uuid block)) 0)))
+          drop-handler-fn (fn [^js event]
+                            (util/stop event)
+                            (p/let [block-uuids (state/get-selection-block-ids)
+                                    lookup-refs (map (fn [id] [:block/uuid id]) block-uuids)
+                                    selected (db/pull-many (state/get-current-repo) '[*] lookup-refs)
+                                    blocks (if (seq selected) selected [@component-block/*dragging-block])
+                                    _ (editor-handler/insert-first-page-block-if-not-exists! page-name {:redirect? false})]
+                              (js/setTimeout #(let [target-block (db/pull (:db/id (db/get-page page-name)))]
+                                                (dnd/move-blocks event blocks target-block nil :sibling))
+                                             0)))]
+      [:div.ls-block.flex-1.flex-col.rounded-sm
+       {:style {:width "100%"
+              ;; The same as .dnd-separator
+                :border-top (if hover
+                              "3px solid #ccc"
+                              nil)}}
        [:div.flex.flex-row
         [:div.flex.flex-row.items-center.mr-2.ml-1 {:style {:height 24}}
          [:span.bullet-container.cursor
@@ -117,8 +135,12 @@
         [:div.flex.flex-1 {:tabIndex 0
                            :on-key-press (fn [e]
                                            (when (= "Enter" (util/ekey e))
-                                             (handler-fn)))
-                           :on-click handler-fn}
+                                             (click-handler-fn)))
+                           :on-click click-handler-fn
+                           :on-drag-enter #(set-hover! true)
+                           :on-drag-over #(util/stop %)
+                           :on-drop drop-handler-fn
+                           :on-drag-leave #(set-hover! false)}
          [:span.opacity-70
           "Click here to edit..."]]]])))
 

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

@@ -447,9 +447,10 @@
                                  (-> (p/let [result (ipc/ipc :testProxyUrl val opts)]
                                        (js->clj result :keywordize-keys true))
                                      (p/then (fn [{:keys [code response-ms]}]
+                                               (notification/clear! :proxy-net-check)
                                                (notification/show! (str "Success! Status " code " in " response-ms "ms.") :success)))
                                      (p/catch (fn [e]
-                                                (notification/show! (str e) :error)))
+                                                (notification/show! (str e) :error false :proxy-net-check)))
                                      (p/finally (fn [] (set-testing?! false)))))))]
 
       [:p.pt-2

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

@@ -383,6 +383,7 @@
                 :aria-label (t :right-side-bar/separator)
                 :aria-valuemin (* min-ratio 100)
                 :aria-valuemax (* max-ratio 100)
+                :aria-valuenow 50
                 :tabIndex "0"
                 :data-expanded sidebar-open?}]))
 

+ 15 - 0
src/main/frontend/components/settings.cljs

@@ -593,6 +593,20 @@
    {:left-label (t :settings-page/network-proxy)
     :action (user-proxy-settings agent-opts)}))
 
+(rum/defcs auto-chmod-row < rum/reactive
+  [state t]
+  (let [enabled? (if (= nil (state/sub [:electron/user-cfgs :feature/enable-automatic-chmod?]))
+                   true
+                   (state/sub [:electron/user-cfgs :feature/enable-automatic-chmod?]))]
+    (toggle
+     "automatic-chmod"
+     (t :settings-page/auto-chmod)
+     enabled?
+     #(do
+       (state/set-state! [:electron/user-cfgs :feature/enable-automatic-chmod?] (not enabled?))
+       (ipc/ipc :userAppCfgs :feature/enable-automatic-chmod? (not enabled?)))
+     [:span.text-sm.opacity-50 (t :settings-page/auto-chmod-desc)])))
+
 (defn filename-format-row []
   (row-with-button-action
    {:left-label (t :settings-page/filename-format)
@@ -700,6 +714,7 @@
      (usage-diagnostics-row t instrument-disabled?)
      (when-not (mobile-util/native-platform?) (developer-mode-row t developer-mode?))
      (when (util/electron?) (https-user-agent-row https-agent-opts))
+     (when (util/electron?) (auto-chmod-row t))
      (when (and (util/electron?) (not (config/demo-graph? current-repo))) (filename-format-row))
      (clear-cache-row t)
 

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

@@ -1,5 +1,6 @@
 (ns frontend.components.theme
-  (:require [frontend.extensions.pdf.core :as pdf]
+  (:require [electron.ipc :as ipc]
+            [frontend.extensions.pdf.core :as pdf]
             [frontend.config :as config]
             [frontend.handler.plugin :as plugin-handler]
             [frontend.handler.plugin-config :as plugin-config-handler]
@@ -35,6 +36,10 @@
      #(let [doc js/document.documentElement]
         (.setAttribute doc "lang" preferred-language)))
 
+    (rum/use-effect!
+     #(js/setTimeout (fn [] (ipc/ipc "theme-loaded")) 100) ; Wait for the theme to be applied
+     [])
+
     (rum/use-effect!
      #(when (and restored-sidebar?
                  (mounted-fn))

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

@@ -205,7 +205,7 @@ input.tl-text-input {
 
   .tl-action-bar {
     left: 0.5rem;
-    bottom: 0.5rem;
+    bottom: 0;
   }
 
   .tl-primary-tools {

+ 6 - 22
src/main/frontend/components/window_controls.cljs

@@ -1,27 +1,11 @@
 (ns frontend.components.window-controls
-  (:require [electron.ipc :as ipc]
-            [frontend.components.svg :as svg]
+  (:require [frontend.components.svg :as svg]
             [frontend.context.i18n :refer [t]]
+            [frontend.handler.window :as window-handler]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [rum.core :as rum]))
 
-(defn minimize
-  []
-  (ipc/ipc "window-minimize"))
-
-(defn toggle-maximized
-  []
-  (ipc/ipc "window-toggle-maximized"))
-
-(defn close
-  []
-  (ipc/ipc "window-close"))
-
-(defn toggle-fullscreen
-  []
-  (ipc/ipc "window-toggle-fullscreen"))
-
 (rum/defc container < rum/reactive
   []
   (let [maximized?  (state/sub :electron/window-maximized?)
@@ -30,23 +14,23 @@
      (if fullscreen?
        [:button.button.icon.fullscreen-toggle
         {:title (t :window/exit-fullscreen)
-         :on-click toggle-fullscreen}
+         :on-click window-handler/toggle-fullscreen!}
         (ui/icon "arrows-minimize")]
        [:<>
         [:button.button.icon.minimize
          {:title (t :window/minimize)
-          :on-click minimize}
+          :on-click window-handler/minimize!}
          (svg/window-minimize)]
 
         [:button.button.icon.maximize-toggle
          {:title (if maximized? (t :window/restore) (t :window/maximize))
           :class (if maximized? "restore" "maximize")
-          :on-click toggle-maximized}
+          :on-click window-handler/toggle-maximized!}
          (if maximized?
            (svg/window-restore)
            (svg/window-maximize))]
 
         [:button.button.icon.close
          {:title (t :window/close)
-          :on-click close}
+          :on-click window-handler/close!}
          (svg/window-close)]])]))

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

@@ -496,16 +496,20 @@
   "Resolve all relative links in custom.css to assets:// URL"
   ;; ../assets/xxx -> {assets|file}://{current-graph-root-path}/xxx
   [source]
-  (let [protocol (and (string? source)
-                      (not (string/blank? source))
-                      (if (util/electron?) "assets://" "file://"))
-        ;; BUG: use "assets" as fake current directory
-        assets-link-fn (fn [_]
-                         (str (path/path-join protocol
-                                              (get-repo-dir (state/get-current-repo)) "assets") "/"))]
-    (when (not-empty source)
-      (string/replace source #"\.\./assets/"
-                      assets-link-fn))))
+  (when-not (string/blank? source)
+    (let [protocol (and (string? source)
+                        (not (string/blank? source))
+                        (if (util/electron?) "assets://" "file://"))
+          ;; BUG: use "assets" as fake current directory
+          assets-link-fn (fn [_]
+                           (let [graph-root (get-repo-dir (state/get-current-repo))
+                                 protocol (if (string/starts-with? graph-root "file:") "" protocol)
+                                 full-path (path/path-join protocol graph-root "assets")]
+                             (str (cond-> full-path
+                                          (mobile-util/native-platform?)
+                                          (mobile-util/convert-file-src))
+                                  "/")))]
+      (string/replace source #"\.\./assets/" assets-link-fn))))
 
 (defn get-current-repo-assets-root
   []

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

@@ -485,7 +485,7 @@
                  (when-not (:file? (first (:rum/args state)))
                    (let [code (nth (:rum/args state) 3)
                          editor @(:editor-atom state)]
-                     (when (not= (.getValue editor) code)
+                     (when (and editor (not= (.getValue editor) code))
                        (.setValue editor code))))
                  state)}
   [state _config id attr code _theme _options]

+ 18 - 15
src/main/frontend/extensions/pdf/core.cljs

@@ -886,21 +886,24 @@
      [hls-file])
 
     ;; cache highlights
-    (rum/use-effect!
-     (fn []
-       (when (= :completed (:status loader-state))
-         (p/catch
-          (when-not (:error hls-state)
-            (p/do!
-              (p/delay 100)
-              (pdf-assets/persist-hls-data$
-                pdf-current (:latest-hls hls-state) (:extra hls-state))))
-
-          ;; write hls file error
-          (fn [e]
-            (js/console.error "[write hls error]" e)))))
-
-     [(:latest-hls hls-state) (:extra hls-state)])
+    (let [persist-hls-data!
+          (rum/use-callback
+            (util/debounce
+              4000 (fn [latest-hls extra]
+                    (pdf-assets/persist-hls-data$
+                      pdf-current latest-hls extra))) [pdf-current])]
+      (rum/use-effect!
+        (fn []
+          (when (= :completed (:status loader-state))
+            (p/catch
+              (when-not (:error hls-state)
+                (p/do! (persist-hls-data! (:latest-hls hls-state) (:extra hls-state))))
+
+              ;; write hls file error
+              (fn [e]
+                (js/console.error "[write hls error]" e)))))
+
+        [(:latest-hls hls-state) (:extra hls-state)]))
 
     ;; load document
     (rum/use-effect!

+ 4 - 10
src/main/frontend/extensions/video/youtube.cljs

@@ -119,13 +119,13 @@ Remember: You can paste a raw YouTube url as embedded video on mobile."
 
 
 (defn parse-timestamp [timestamp]
-  (let [reg #"^(?:(\d+):)?([0-5]\d):([0-5]\d)$"
+  (let [reg #"^(?:(\d+):)?([0-5]?\d):([0-5]?\d)$"
         reg-number #"^\d+$"
         timestamp (str timestamp)
         total-seconds (some-> (re-matches reg-number timestamp)
                               util/safe-parse-int)
         [_ hours minutes seconds] (re-matches reg timestamp)
-        [hours minutes seconds] (map util/safe-parse-int (remove nil? [hours minutes seconds]))]
+        [hours minutes seconds] (map #(if (nil? %) 0 (util/safe-parse-int %)) [hours minutes seconds])]
     (cond
       total-seconds
       total-seconds
@@ -133,19 +133,13 @@ Remember: You can paste a raw YouTube url as embedded video on mobile."
       (and minutes seconds)
       (+ (* 3600 hours) (* 60 minutes) seconds)
 
-      minutes
-      (+ (* 3600 hours) (* 60 minutes))
-
-      hours
-      (* 3600 hours)
-
       :else
       nil)))
 
 (comment
   ;; hh:mm:ss
-  (re-matches #"^(?:(\d+):)?([0-5]\d):([0-5]\d)$" "123:22:23") ;; => ["123:22:23" "123" "22" "23"]
-  (re-matches #"^(?:(\d+):)?([0-5]\d):([0-5]\d)$" "30:23") ;; => ["30:23" nil "30" "23"]
+  (re-matches #"^(?:(\d+):)?([0-5]?\d):([0-5]?\d)$" "123:22:23") ;; => ["123:22:23" "123" "22" "23"]
+  (re-matches #"^(?:(\d+):)?([0-5]?\d):([0-5]?\d)$" "30:23") ;; => ["30:23" nil "30" "23"]
 
   (parse-timestamp "01:23") ;; => 83
 

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

@@ -1,5 +1,5 @@
 (ns frontend.handler.dnd
-  "Provides fns for drag n drop"
+  "Provides fns for drag and drop"
   (:require [frontend.handler.editor :as editor-handler]
             [frontend.handler.property :as property-handler]
             [frontend.modules.outliner.core :as outliner-core]

+ 5 - 2
src/main/frontend/handler/editor.cljs

@@ -2526,7 +2526,7 @@
             :down util/get-next-block-non-collapsed)
         sibling-block (f (gdom/getElement (state/get-editing-block-dom-id)))
         {:block/keys [uuid content format]} (state/get-edit-block)]
-    (when sibling-block
+    (if sibling-block
       (when-let [sibling-block-id (dom/attr sibling-block "blockid")]
         (let [value (state/get-edit-content)]
           (when (not= (clean-content! repo format content)
@@ -2540,7 +2540,10 @@
                        (or (:pos move-opts)
                         [direction line-pos])
                        new-id
-                       {:direction direction}))))))
+                       {:direction direction})))
+      (case direction
+        :up (cursor/move-cursor-to input 0)
+        :down (cursor/move-cursor-to-end input)))))
 
 (defn keydown-up-down-handler
   [direction {:keys [_pos] :as move-opts}]

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

@@ -21,7 +21,6 @@
             [lambdaisland.glogi :as log]
             [logseq.graph-parser.util :as gp-util]
             [promesa.core :as p]
-            [logseq.common.path :as path]
             [frontend.db.listener :as db-listener]))
 
 (defn remove-ignore-files
@@ -91,18 +90,10 @@
 (defn- precheck-graph-dir
   "Check graph dir, notify user if:
 
-   - Grame dir name is `logseq`, the same as app, which might cause confusion
    - Graph dir contains a nested graph, which should be avoided
    - Over 10000 files found in graph dir, which might cause performance issues"
-  [dir files]
-  (when (= (string/lower-case (path/basename dir))
-           "logseq")
-    (state/pub-event!
-     [:notification/show {:content [:div "The folder name "
-                                    [:code "logseq"]
-                                    " is not suitable for a graph name. Please unlink this graph and choose a different name."]
-                          :status :warning
-                          :clear?  false}]))
+  [_dir files]
+  ;; disable this check for now
   (when (some #(string/ends-with? (:path %) "/logseq/config.edn") files)
     (state/pub-event!
      [:notification/show {:content "It seems that you are trying to open a Logseq graph folder with nested graph. Please unlink this graph and choose a correct folder."

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

@@ -252,8 +252,8 @@
           point (-> (.getShapeById app source-shape)
                     (.-bounds)
                     ((fn [bounds] (if bottom?
-                                    [(.-minX bounds) (+ 64 (.-maxY bounds))]
-                                    [(+ 64 (.-maxX bounds)) (.-minY bounds)]))))
+                                    [(.-minX ^js bounds) (+ 64 (.-maxY ^js bounds))]
+                                    [(+ 64 (.-maxX ^js bounds)) (.-minY ^js bounds)]))))
           shape (->logseq-portal-shape block-uuid point)]
       (when (uuid? block-uuid) (editor-handler/set-blocks-id! [block-uuid]))
       (.createShapes api (clj->js shape))

+ 19 - 0
src/main/frontend/handler/window.cljs

@@ -0,0 +1,19 @@
+(ns frontend.handler.window
+  "Window management ns"
+  (:require [electron.ipc :as ipc]))
+
+(defn minimize!
+  []
+  (ipc/ipc "window-minimize"))
+
+(defn toggle-maximized!
+  []
+  (ipc/ipc "window-toggle-maximized"))
+
+(defn close!
+  []
+  (ipc/ipc "window-close"))
+
+(defn toggle-fullscreen!
+  []
+  (ipc/ipc "window-toggle-fullscreen"))

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

@@ -16,6 +16,7 @@
             [frontend.handler.export :as export-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
             [frontend.handler.plugin-config :as plugin-config-handler]
+            [frontend.handler.window :as window-handler]
             [frontend.modules.editor.undo-redo :as undo-redo]
             [frontend.dicts :as dicts]
             [frontend.modules.shortcut.before :as m]
@@ -513,6 +514,10 @@
                                              :inactive (not (util/electron?))
                                              :fn       #(page-handler/copy-page-url)}
 
+   :window/close                            {:binding  "mod+w"
+                                             :inactive (not (util/electron?))
+                                             :fn       window-handler/close!}
+
    :ui/toggle-wide-mode                     {:binding "t w"
                                              :fn      ui-handler/toggle-wide-mode!}
 
@@ -724,6 +729,7 @@
            :editor/open-file-in-directory
            :editor/copy-current-file
            :editor/copy-page-url
+           :window/close
            :editor/new-whiteboard
            :ui/toggle-wide-mode
            :ui/select-theme-color
@@ -902,6 +908,7 @@
      :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

+ 22 - 18
src/main/frontend/ui.cljs

@@ -93,20 +93,24 @@
 (rum/defc ls-textarea
   < rum/reactive
   {:did-mount (fn [state]
-                (let [^js el (rum/dom-node state)]
+                (let [^js el (rum/dom-node state)
+                      *mouse-point (volatile! nil)]
                   ;; Passing aria-label as a prop to TextareaAutosize removes the dash
                   (.setAttribute el "aria-label" "editing block")
-                  (. el addEventListener "select"
-                     #(let [start (util/get-selection-start el)
-                            end (util/get-selection-end el)]
-                        (when (and start end)
-                          (when-let [e (and (not= start end)
-                                            {:caret (cursor/get-caret-pos el)
-                                             :start start :end end
-                                             :text  (. (.-value el) substring start end)
-                                             :point {:x (.-x %) :y (.-y %)}})]
-
-                            (plugin-handler/hook-plugin-editor :input-selection-end (bean/->js e)))))))
+                  (doto el
+                    (.addEventListener "select"
+                       #(let [start (util/get-selection-start el)
+                              end (util/get-selection-end el)]
+                          (when (and start end)
+                            (when-let [e (and (not= start end)
+                                              (let [caret-pos (cursor/get-caret-pos el)]
+                                                {:caret caret-pos
+                                                 :start start :end end
+                                                 :text  (. (.-value el) substring start end)
+                                                 :point (select-keys (or @*mouse-point caret-pos) [:x :y])}))]
+                              (plugin-handler/hook-plugin-editor :input-selection-end (bean/->js e))
+                              (vreset! *mouse-point nil)))))
+                    (.addEventListener "mouseup" #(vreset! *mouse-point {:x (.-x %) :y (.-y %)}))))
                 state)}
   [{:keys [on-change] :as props}]
   (let [skip-composition? (state/sub :editor/action)
@@ -119,16 +123,16 @@
                                                 (on-change e))
                              (state/set-editor-in-composition! true))))
         props (assoc props
-                     :on-change (fn [e] (when-not (state/editor-in-composition?)
-                                          (on-change e)))
-                     :on-composition-start on-composition
-                     :on-composition-update on-composition
-                     :on-composition-end on-composition)]
+                :on-change (fn [e] (when-not (state/editor-in-composition?)
+                                     (on-change e)))
+                :on-composition-start on-composition
+                :on-composition-update on-composition
+                :on-composition-end on-composition)]
     (textarea props)))
 
 (rum/defc dropdown-content-wrapper
   < {:did-mount    (fn [state]
-                     (let [k    (inc (count (state/sub :modal/dropdowns)))
+                     (let [k (inc (count (state/sub :modal/dropdowns)))
                            args (:rum/args state)]
                        (state/set-state! [:modal/dropdowns k] (second args))
                        (assoc state ::k k)))

+ 3 - 3
src/main/frontend/util.cljc

@@ -6,7 +6,7 @@
             ["/frontend/selection" :as selection]
             ["/frontend/utils" :as utils]
             ["@capacitor/status-bar" :refer [^js StatusBar Style]]
-            ["@hugotomazi/capacitor-navigation-bar" :refer [^js NavigationBar]]
+            ["@capgo/capacitor-navigation-bar" :refer [^js NavigationBar]]
             ["grapheme-splitter" :as GraphemeSplitter]
             ["remove-accents" :as removeAccents]
             ["sanitize-filename" :as sanitizeFilename]
@@ -215,7 +215,7 @@
      (p/do!
       (.setStyle StatusBar (clj->js {:style (.-Light Style)}))
       (when (mobile-util/native-android?)
-        (.setColor NavigationBar (clj->js {:color "#ffffff"}))
+        (.setNavigationBarColor NavigationBar (clj->js {:color "#ffffff"}))
         (.setBackgroundColor StatusBar (clj->js {:color "#ffffff"}))))))
 
 #?(:cljs
@@ -224,7 +224,7 @@
      (p/do!
       (.setStyle StatusBar (clj->js {:style (.-Dark Style)}))
       (when (mobile-util/native-android?)
-        (.setColor NavigationBar (clj->js {:color "#002b36"}))
+        (.setNavigationBarColor NavigationBar (clj->js {:color "#002b36"}))
         (.setBackgroundColor StatusBar (clj->js {:color "#002b36"}))))))
 
 (defn find-first

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

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

+ 5 - 1
src/resources/dicts/en.edn

@@ -353,6 +353,8 @@
  :settings-page/update-error-2 " Please check out the "
  :settings-permission/start-granting "Grant"
 
+ :settings-page/auto-chmod "Automatically change file permissions"
+ :settings-page/auto-chmod-desc "Disable to allow editing by multiple users with permissions granted by group membership."
  :yes "Yes"
 
  :submit "Submit"
@@ -465,6 +467,7 @@
  :whiteboard/dashboard-card-edited "Edited "
  :whiteboard/toggle-grid "Toggle grid"
  :whiteboard/snap-to-grid "Snap to grid"
+ :whiteboard/toggle-pen-mode "Toggle pen mode"
  :flashcards/modal-welcome-title "Time to create a card!"
  :flashcards/modal-welcome-desc-1 "You can add \"#card\" to any block to turn it into a card or trigger \"/cloze\" to add some clozes."
  :flashcards/modal-welcome-desc-2 "You can "
@@ -828,4 +831,5 @@
   :dev/show-block-data             "(Dev) Show block data"
   :dev/show-block-ast              "(Dev) Show block AST"
   :dev/show-page-data              "(Dev) Show page data"
-  :dev/show-page-ast               "(Dev) Show page AST"}}
+  :dev/show-page-ast               "(Dev) Show page AST"
+  :window/close                    "Close window"}}

+ 38 - 23
src/resources/dicts/es.edn

@@ -13,10 +13,10 @@
  :delete                                            "Eliminar"
  :discourse-title                                   "¡Nuestro foro!"
  :download                                          "Descargar"
+ :export                                            "Exportar"
  :export-copied-to-clipboard                        "¡Copiado al portapapeles!"
  :export-copy-to-clipboard                          "Copiar al portapapeles"
  :export-edn                                        "Exportar a EDN"
- :export                                            "Exportar"
  :export-graph                                      "Exportar grafo"
  :export-json                                       "Exportar a JSON"
  :export-markdown                                   "Exportar como Markdown estándar (sin propiedades de bloque)"
@@ -53,23 +53,24 @@
  :parsing-files                                     "Analizando archivos"
  :plugins                                           "Extensiones"
  :port                                              "Puerto"
+ :re-index                                          "Reindexar"
  :re-index-detail                                   "Reconstruir el grafo"
  :re-index-discard-unsaved-changes-warning          "Al reindexar se descartará el grafo actual y se procesarán nuevamente todos los archivos según como están actualmente almacenados en disco. Perderá los cambios no guardados y puede tardar un poco. ¿Continuar?"
  :re-index-multiple-windows-warning                 "Debe cerrar las otras ventanas antes de reindexar este grafo."
- :re-index                                          "Reindexar"
  :relaunch-confirm-to-work                          "Debe relanzar la aplicación para hacer que funcione. ¿Desea reiniciarla ahora?"
  :remove-background                                 "Eliminar el fondo"
  :remove-heading                                    "Eliminar encabezado"
  :remove-orphaned-pages                             "¿Eliminar páginas huérfanas?"
+ :reset                                             "Reiniciar"
  :save                                              "Guardar"
  :search                                            "Buscar o Crear Página"
- :settings-of-plugins                               "Opciones de Extensiones"
  :settings                                          "Opciones"
+ :settings-of-plugins                               "Opciones de Extensiones"
  :strikethrough                                     "Tachado"
  :submit                                            "Enviar"
  :sync-from-local-changes-detected                  "Refrescar detecta y procesa los archivos modificados en su disco que difieren del contenido actual de la página en Logseq. ¿Continuar?"
- :sync-from-local-files-detail                      "Importar cambios de los archivos locales"
  :sync-from-local-files                             "Refrescar"
+ :sync-from-local-files-detail                      "Importar cambios de los archivos locales"
  :themes                                            "Temas"
  :toggle-theme                                      "Alternar tema"
  :type                                              "Tipo"
@@ -151,8 +152,8 @@
  :command.editor/copy-text                          "Copiar selección como texto"
  :command.editor/cut                                "Pegar"
  :command.editor/cycle-todo                         "Rotar estado TODO del elemento"
- :command.editor/delete-selection                   "Eliminar bloques seleccionados"
  :command.editor/delete                             "Suprimir / Eliminar hacia delante"
+ :command.editor/delete-selection                   "Eliminar bloques seleccionados"
  :command.editor/down                               "Mover cursor abajo / Seleccionar abajo"
  :command.editor/end-of-block                       "Mover cursor al final del bloque"
  :command.editor/escape-editing                     "Escapar de edición"
@@ -317,8 +318,8 @@
  :file-rn/need-action                               "Se sugieren acciones de cambio de nombre de archivo para que coincidan con el nuevo formato. Cuando se sincronicen los archivos renombrados se requiere volver a indexar en todos los dispositivos."
  :file-rn/no-action                                 "¡Bien hecho! No es necesario realizar más acciones."
  :file-rn/optional-rename                           "Sugerencia: "
- :file-rn/or-select-actions-2                       ". Estas acciones no estarán disponibles una vez cierres este panel."
  :file-rn/or-select-actions                         " o cambie el nombre de los archivos a continuación individualmente, luego "
+ :file-rn/or-select-actions-2                       ". Estas acciones no estarán disponibles una vez cierres este panel."
  :file-rn/otherwise-breaking                        "O el título se convertirá"
  :file-rn/re-index                                  "Se recomienda encarecidamente volver a indexar después de cambiar el nombre de los archivos y en otros dispositivos después de la sincronización."
  :file-rn/rename                                    "Renombrar \"{1}\" a \"{2}\""
@@ -326,6 +327,7 @@
  :file-rn/select-format                             "(Opción modo desarrollador, ¡peligroso!) Seccione el formato de nombre de archivo"
  :file-rn/suggest-rename                            "Acción necesaria: "
  :file-rn/unreachable-title                         "¡Advertencia! El nombre de la página se convertirá en {1} en el formato de nombre de archivo actual, a no ser que la propiedad `title::` se establezca manualmente"
+ :file-sync/connectivity-testing-failed             "Fallaron las pruebas de conexión de red. Consulte la configuración de su red. URLs de prueba: "
  :file-sync/graph-deleted                           "El gráfico remoto actual se ha eliminado"
  :file-sync/other-user-graph                        "El gráfico local actual está unido al gráfico remoto de otro usuario, así que no se puede empezar a sincronizar"
  :file-sync/rsapi-cannot-upload-err                 "Incapaz de comenzar la sincronización, comprueba si el tiempo local es correcto."
@@ -353,11 +355,11 @@
  :flashcards/modal-welcome-title                    "¡Hora de crear una tarjeta!"
  :graph/all-graphs                                  "Todos los grafos"
  :graph/local-graphs                                "Grafos locales:"
- :graph/persist-error                               "Falló la sincronización del estado interno."
  :graph/persist                                     "Logseq está sincronizando su estado interno, por favor espere unos segundos."
+ :graph/persist-error                               "Falló la sincronización del estado interno."
  :graph/remote-graphs                               "Grafos remotos:"
- :graph/save-error                                  "Falló el guardado"
  :graph/save                                        "Guardando..."
+ :graph/save-error                                  "Falló el guardado"
  :graph/save-success                                "Guardado satisfactoriamente"
  :header/go-back                                    "Ir hacia atrás"
  :header/go-forward                                 "Ir haca adelante"
@@ -393,6 +395,18 @@
  :help/title-development                            "Desarrollo"
  :help/title-terms                                  "Términos"
  :help/title-usage                                  "Uso"
+ :keymap/all                                        "Todo"
+ :keymap/conflicts-for-label                        "Conflicto de combinación de teclas para"
+ :keymap/custom                                     "Personalizado"
+ :keymap/customize-for-label                        "Personalizar atajos"
+ :keymap/disabled                                   "Deshabilitado"
+ :keymap/keystroke-filter                           "Filtro de pulsación de teclas"
+ :keymap/keystroke-record-desc                      "Presione cualquier secuencia de teclas para filtrar atajos"
+ :keymap/keystroke-record-setup-label               "Presione cualquier secuencia de teclas para establecer un atajo"
+ :keymap/restore-to-default                         "Reiniciar a valor predeterminado del sistema"
+ :keymap/search                                     "Buscar"
+ :keymap/total                                      "Atajos de teclado totales"
+ :keymap/unset                                      "Sin asignar"
  :left-side-bar/create                              "Crear"
  :left-side-bar/journals                            "Diarios"
  :left-side-bar/nav-favorites                       "Favoritos"
@@ -464,8 +478,8 @@
  :page/backlinks                                    "Vínculos de retroceso"
  :page/copy-page-url                                "Copiar URL de la página"
  :page/created-at                                   "Creada el"
- :page/delete-confirmation                          "¿Está seguro que desea eliminar esta página y su archivo?"
  :page/delete                                       "Eliminar página"
+ :page/delete-confirmation                          "¿Está seguro que desea eliminar esta página y su archivo?"
  :page/earlier                                      "Anteriormente"
  :page/illegal-page-name                            "¡Nombre de página ilegal!"
  :page/logseq-is-having-a-problem                   "Logseq está teniendo un problema. Para intentar volver a un estado de trabajo, intenta los siguientes pasos seguros en orden:"
@@ -477,8 +491,8 @@
  :page/page-already-exists                          "¡La página “{1}” ya existe!"
  :page/show-journals                                "Mostrar diarios"
  :page/show-whiteboards                             "Mostrar pizarras"
- :page/slide-view-tip-go-fullscreen (fn [] [[:span.opacity-70 "Consejo: presiona "] [:code "f"] [:span.opacity-70 " para ir a pantalla completa"]])
  :page/slide-view                                   "Ver como diapositivas"
+ :page/slide-view-tip-go-fullscreen (fn [] [[:span.opacity-70 "Consejo: presiona "] [:code "f"] [:span.opacity-70 " para ir a pantalla completa"]])
  :page/something-went-wrong                         "Algo malió sal"
  :page/step                                         "Paso {1}"
  :page/try                                          "Intentar"
@@ -509,6 +523,7 @@
  :plugin/enabled                                    "Habilitado"
  :plugin/found-n-updates                            "Se encontraron {1} actualizaciones"
  :plugin/found-updates                              "Nuevas actualizaciones"
+ :plugin/install                                    "Instalar"
  :plugin/installed                                  "Instalado"
  :plugin/installed-plugin                           "Extensión instalada: {1}"
  :plugin.install-from-file/menu-title               "Instalar desde plugins.edn"
@@ -516,7 +531,6 @@
  :plugin.install-from-file/success                  "¡Todas las extensiones fueron instaladas!"
  :plugin.install-from-file/title                    "Instalar extensiones desde  plugins.edn"
  :plugin/installing                                 "Instalando"
- :plugin/install                                    "Instalar"
  :plugin/list-of-updates                            "Actualizaciones de extensiones: "
  :plugin/load-unpacked                              "Cargar extensión desempaquetada"
  :plugin/marketplace                                "Tienda"
@@ -557,14 +571,14 @@
  :right-side-bar/history-undos                      "Deshacer"
  :right-side-bar/new-page                           "Nueva página"
  :right-side-bar/page-graph                         "Grafo de página"
+ :right-side-bar/pane-close                         "Cerrar"
  :right-side-bar/pane-close-all                     "Cerrar todo"
  :right-side-bar/pane-close-others                  "Cerrar otros"
- :right-side-bar/pane-close                         "Cerrar"
- :right-side-bar/pane-collapse-all                  "Colapsar todo"
  :right-side-bar/pane-collapse                      "Colapsar"
+ :right-side-bar/pane-collapse-all                  "Colapsar todo"
  :right-side-bar/pane-collapse-others               "Colapsar otros"
- :right-side-bar/pane-expand-all                    "Expandir todo"
  :right-side-bar/pane-expand                        "Expandir"
+ :right-side-bar/pane-expand-all                    "Expandir todo"
  :right-side-bar/pane-more                          "Más"
  :right-side-bar/pane-open-as-page                  "Abrir como página"
  :right-side-bar/separator                          "Right sidebar resize handler "
@@ -600,9 +614,9 @@
  :settings-page/changelog                           "¿Qué hay de nuevo?"
  :settings-page/check-for-updates                   "Comprobar actualizaciones"
  :settings-page/checking                            "Comprobando ..."
+ :settings-page/clear                               "Limpiar"
  :settings-page/clear-cache                         "Limpiar caché"
  :settings-page/clear-cache-warning                 "Limpiar la caché va a desechar los grafos abiertos. Vas a perder los cambios no guardados."
- :settings-page/clear                               "Limpiar"
  :settings-page/current-version                     "Versión actual"
  :settings-page/custom-configuration                "Configuración personalizada"
  :settings-page/custom-date-format                  "Formato de fecha preferido"
@@ -610,10 +624,10 @@
  :settings-page/custom-date-format-warning          "¡Se requiere reindexar! ¡Las referencias existentes del diario podrían estar rotas!"
  :settings-page/custom-global-configuration         "Configuración global personalizada"
  :settings-page/custom-theme                        "Tema personalizado"
- :settings-page/developer-mode-desc                 "El modo desarrollador permite a los colaboradores y desarrolladores de extensiones probar sus integraciones con Logseq más eficientemente."
  :settings-page/developer-mode                      "Modo desarrollador"
- :settings-page/disable-sentry-desc                 " Logseq nunca va a recolectar tu base de datos de grafos local o vender tus datos."
+ :settings-page/developer-mode-desc                 "El modo desarrollador permite a los colaboradores y desarrolladores de extensiones probar sus integraciones con Logseq más eficientemente."
  :settings-page/disable-sentry                      "Enviar datos de uso y diagnósticos a Logseq"
+ :settings-page/disable-sentry-desc                 " Logseq nunca va a recolectar tu base de datos de grafos local o vender tus datos."
  :settings-page/edit-config-edn                     "Editar config.edn (para este repositorio)"
  :settings-page/edit-custom-css                     "Editar custom.css"
  :settings-page/edit-export-css                     "Editar export.css"
@@ -645,26 +659,27 @@
  :settings-page/preferred-outdenting                "Disminución lógica de sangría"
  :settings-page/preferred-outdenting-tip            "El lado izquierdo muestra  sangría con la configuración por defecto, y el derecho muestra con sangría lógica habilitada"
  :settings-page/preferred-outdenting-tip-more       "→ Aprende más"
- :settings-page/preferred-pasting-file-hint         "Cuando está habilitado, al pegar una imagen de internet la imagen será descargada e insertada. Cuando está deshabilitado, el enlace a la imagen será pegado."
  :settings-page/preferred-pasting-file              "Preferir pegar archivo"
+ :settings-page/preferred-pasting-file-hint         "Cuando está habilitado, al pegar una imagen de internet la imagen será descargada e insertada. Cuando está deshabilitado, el enlace a la imagen será pegado."
  :settings-page/preferred-workflow                  "Flujo de trabajo preferido"
  :settings-page/revision                            "Revisión: "
  :settings-page/show-brackets                       "Mostrar corchetes"
  :settings-page/show-full-blocks                    "Mostrar todas las líneas de una referencia a bloque"
  :settings-page/spell-checker                       "Corrector ortográfico"
+ :settings-page/sync                                "Sincronizar"
  :settings-page/sync-desc-1                         "Clic"
  :settings-page/sync-desc-2                         "aquí"
  :settings-page/sync-desc-3                         "por instrucciones de cómo configurar y usar Sync."
- :settings-page/sync-diff-merge-desc                "Unir actualizaciones locales con archivos remotos automáticamente cuando ocurre un conflicto, en lugar de sobreescribir los archivos remotos"
  :settings-page/sync-diff-merge                     "Habilitar unión inteligente al sincronizar"
+ :settings-page/sync-diff-merge-desc                "Unir actualizaciones locales con archivos remotos automáticamente cuando ocurre un conflicto, en lugar de sobreescribir los archivos remotos"
  :settings-page/sync-diff-merge-warn                "La capacidad de unión inteligente solo se activa en un cliente después de la primera sincronización exitosa con el servidor remoto en el grafo en la nueva versión de LogSeq. Habilita esto en todos los dispositivos para alcanzar la mejor experiencia."
- :settings-page/sync                                "Sincronizar"
  :settings-page/tab-account                         "Cuenta"
  :settings-page/tab-advanced                        "Avanzado"
  :settings-page/tab-assets                          "Recursos"
  :settings-page/tab-editor                          "Editor"
  :settings-page/tab-features                        "Características"
  :settings-page/tab-general                         "General"
+ :settings-page/tab-keymap                          "Mapa de teclado"
  :settings-page/tab-version-control                 "Control de versiones"
  :settings-page/theme-dark                          "oscuro"
  :settings-page/theme-light                         "claro"
@@ -734,8 +749,8 @@
  :whiteboard/medium                                 "Medio"
  :whiteboard/move-to-back                           "Mover al fondo"
  :whiteboard/move-to-front                          "Mover al frente"
- :whiteboard/new-block-no-colon                     "Nuevo bloque"
  :whiteboard/new-block                              "Nuevo bloque:"
+ :whiteboard/new-block-no-colon                     "Nuevo bloque"
  :whiteboard/new-page                               "Nueva página:"
  :whiteboard/new-whiteboard                         "Nueva pizarra"
  :whiteboard/opacity                                "Opacidad"
@@ -746,8 +761,8 @@
  :whiteboard/open-youtube-url                       "Abrir url de YouTube"
  :whiteboard/pack-into-rectangle                    "Empacar en un rectángulo"
  :whiteboard/pan                                    "Mover"
- :whiteboard/paste-as-link                          "Pegar como enlace"
  :whiteboard/paste                                  "Pegar"
+ :whiteboard/paste-as-link                          "Pegar como enlace"
  :whiteboard/rectangle                              "Recargar"
  :whiteboard/redo                                   "Rehacer"
  :whiteboard/references                             "Referencias"
@@ -756,9 +771,9 @@
  :whiteboard/scale-level                            "Escalar nivel"
  :whiteboard/search-only-blocks                     "Buscar solo bloques"
  :whiteboard/search-only-pages                      "Buscar solo páginas"
+ :whiteboard/select                                 "Seleccionar"
  :whiteboard/select-all                             "Seleccionar todo"
  :whiteboard/select-custom-color                    "Seleccionar color personalizado"
- :whiteboard/select                                 "Seleccionar"
  :whiteboard/shape                                  "Forma"
  :whiteboard/shape-quick-links                      "Enlaces rápidos de forma"
  :whiteboard/small                                  "Pequeño"

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

@@ -148,6 +148,16 @@
  :right-side-bar/show-journals "Günlükleri Göster"
  :right-side-bar/separator "Sağ kenar çubuğu yeniden boyutlandırma işleyicisi"
  :right-side-bar/toggle-right-sidebar "Sağ kenar çubuğunu aç/kapat"
+ :right-side-bar/pane-close "Kapat"
+ :right-side-bar/pane-close-others "Diğerlerini kapat"
+ :right-side-bar/pane-close-all "Tümünü kapat"
+ :right-side-bar/pane-collapse "Daralt"
+ :right-side-bar/pane-collapse-others "Diğerlerini daralt"
+ :right-side-bar/pane-collapse-all "Tümünü daralt"
+ :right-side-bar/pane-expand "Genişlet"
+ :right-side-bar/pane-expand-all "Tümünü genişlet"
+ :right-side-bar/pane-open-as-page "Sayfa olarak aç"
+ :right-side-bar/pane-more "Diğer"
  :left-side-bar/switch "Geçiş yap:"
  :left-side-bar/journals "Günlük"
  :left-side-bar/create "Oluştur"
@@ -307,6 +317,7 @@
  :settings-page/current-version "Geçerli sürüm"
  :settings-page/tab-general "Genel"
  :settings-page/tab-editor "Düzenleyici"
+ :settings-page/tab-keymap "Tuş haritası"
  :settings-page/tab-version-control "Sürüm denetimi"
  :settings-page/tab-account "Hesap"
  :settings-page/tab-advanced "Gelişmiş"
@@ -337,6 +348,8 @@
  :settings-page/update-available "Yeni sürüm bulundu: "
  :settings-page/update-error-1 "⚠️ Ne yazık ki bir sorun oluştu!"
  :settings-page/update-error-2 " Lütfen bu bağlantıyı denetleyin: "
+ :settings-permission/start-granting "İzin ver"
+
  :yes "Evet"
 
  :submit "Onayla"
@@ -344,6 +357,7 @@
  :close "Kapat"
  :delete "Sil"
  :save "Kaydet"
+ :reset "Sıfırla"
  :type "Tür"
  :host "Ana Bilgisayar"
  :port "Bağlantı Noktası"
@@ -611,6 +625,7 @@
  :file-sync/other-user-graph "Geçerli yerel graf, diğer kullanıcının uzak grafına bağlıdır. Bu yüzden eşitleme başlatılamıyor."
  :file-sync/graph-deleted "Geçerli uzak graf silindi"
  :file-sync/rsapi-cannot-upload-err "Eşitleme başlatılamıyor, lütfen yerel saatin doğru olup olmadığını kontrol edin."
+ :file-sync/connectivity-testing-failed "Ağ bağlantısı testi başarısız oldu. Lütfen ağ ayarlarınızı kontrol edin. Test URL'si: "
 
  :notification/clear-all "Tümünü temizle"
 
@@ -621,9 +636,22 @@
  :shortcut.category/block-command-editing "Blok düzenleme komutuları"
  :shortcut.category/block-selection "Blok seçimi (seçimden çıkmak için Esc tuşuna basın)"
  :shortcut.category/toggle "Aç/Kapat"
- :shortcut.category/whiteboard "Beyaz tahta"
  :shortcut.category/others "Diğer"
  :shortcut.category/plugins "Eklentiler"
+ :shortcut.category/whiteboard "Beyaz tahta"
+
+ :keymap/all "Tümü"
+ :keymap/disabled "Devre dışı"
+ :keymap/unset "Ayarlanmadı"
+ :keymap/custom "Özel"
+ :keymap/search "Ara"
+ :keymap/total "Toplam kısayol sayısı"
+ :keymap/keystroke-filter "Tuş filtresi"
+ :keymap/keystroke-record-desc "Kısayolları filtrelemek için herhangi bir tuş dizisine basın"
+ :keymap/keystroke-record-setup-label "Kısayolu ayarlamak için herhangi bir tuş dizisine basın"
+ :keymap/restore-to-default "Sistem varsayılanına geri yükle"
+ :keymap/customize-for-label "Kısayolları özelleştir"
+ :keymap/conflicts-for-label "Tuş haritası çakışması:"
  :window/minimize "Simge durumuna küçült"
  :window/maximize "Ekranı kapla"
  :window/restore "Pencere durumuna dön"

+ 29 - 5
static/yarn.lock

@@ -757,6 +757,13 @@ agent-base@6, agent-base@^6.0.2:
   dependencies:
     debug "4"
 
+agent-base@^7.0.2:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434"
+  integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==
+  dependencies:
+    debug "^4.3.4"
+
 agentkeepalive@^4.2.1:
   version "4.2.1"
   resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717"
@@ -2840,12 +2847,12 @@ http2-wrapper@^1.0.0-beta.5.2:
     quick-lru "^5.1.1"
     resolve-alpn "^1.0.0"
 
-https-proxy-agent@5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2"
-  integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==
+https-proxy-agent@7.0.2:
+  version "7.0.2"
+  resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz#e2645b846b90e96c6e6f347fb5b2e41f1590b09b"
+  integrity sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==
   dependencies:
-    agent-base "6"
+    agent-base "^7.0.2"
     debug "4"
 
 https-proxy-agent@^5.0.0:
@@ -4553,6 +4560,15 @@ smart-buffer@^4.0.2, smart-buffer@^4.2.0:
   resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
   integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
 
[email protected]:
+  version "8.0.2"
+  resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz#5acbd7be7baf18c46a3f293a840109a430a640ad"
+  integrity sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==
+  dependencies:
+    agent-base "^7.0.2"
+    debug "^4.3.4"
+    socks "^2.7.1"
+
 socks-proxy-agent@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6"
@@ -4570,6 +4586,14 @@ socks@^2.6.2:
     ip "^2.0.0"
     smart-buffer "^4.2.0"
 
+socks@^2.7.1:
+  version "2.7.1"
+  resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55"
+  integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==
+  dependencies:
+    ip "^2.0.0"
+    smart-buffer "^4.2.0"
+
 sonic-boom@^3.1.0:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.2.1.tgz#972ceab831b5840a08a002fa95a672008bda1c38"

+ 21 - 3
tldraw/apps/tldraw-logseq/src/components/ActionBar/ActionBar.tsx

@@ -41,10 +41,14 @@ export const ActionBar = observer(function ActionBar(): JSX.Element {
     app.api.toggleSnapToGrid()
   }, [app])
 
+  const togglePenMode = React.useCallback(() => {
+    app.api.togglePenMode()
+  }, [app])
+
   return (
     <div className="tl-action-bar" data-html2canvas-ignore="true">
       {!app.readOnly && (
-        <div className="tl-toolbar tl-history-bar">
+        <div className="tl-toolbar tl-history-bar mr-2 mb-2">
           <Button tooltip={t('whiteboard/undo')} onClick={undo}>
             <TablerIcon name="arrow-back-up" />
           </Button>
@@ -54,7 +58,7 @@ export const ActionBar = observer(function ActionBar(): JSX.Element {
         </div>
       )}
 
-      <div className={`tl-toolbar tl-zoom-bar ${app.readOnly ? '' : 'ml-4'}`}>
+      <div className={'tl-toolbar tl-zoom-bar mr-2 mb-2'}>
         <Button tooltip={t('whiteboard/zoom-in')} onClick={zoomIn} id="tl-zoom-in">
           <TablerIcon name="plus" />
         </Button>
@@ -65,7 +69,7 @@ export const ActionBar = observer(function ActionBar(): JSX.Element {
         <ZoomMenu />
       </div>
 
-      <div className={'tl-toolbar tl-grid-bar ml-4'}>
+      <div className={'tl-toolbar tl-grid-bar mr-2 mb-2'}>
         <ToggleInput
             tooltip={t('whiteboard/toggle-grid')}
             className="tl-button"
@@ -88,6 +92,20 @@ export const ActionBar = observer(function ActionBar(): JSX.Element {
           </ToggleInput>
         )}
       </div>
+
+      {!app.readOnly && (
+        <div className="tl-toolbar tl-pen-mode-bar mb-2">
+          <ToggleInput
+            tooltip={t('whiteboard/toggle-pen-mode')}
+            className="tl-button"
+            pressed={app.settings.penMode}
+            id="tl-toggle-pen-mode"
+            onPressedChange={togglePenMode}
+          >
+          <TablerIcon name={app.settings.penMode ? "pencil" : "pencil-off"} />
+        </ToggleInput>
+        </div>
+      )}
     </div>
   )
 })

+ 1 - 1
tldraw/apps/tldraw-logseq/src/styles.css

@@ -166,7 +166,7 @@ html[data-theme='light'] {
 }
 
 .tl-action-bar {
-  @apply absolute bottom-0 flex items-center border-0 left-10 bottom-10;
+  @apply absolute flex items-center border-0 left-10 bottom-8 flex-wrap-reverse pr-12;
 
   z-index: 100000;
   user-select: none;

+ 7 - 0
tldraw/packages/core/src/lib/TLApi/TLApi.ts

@@ -178,6 +178,13 @@ export class TLApi<S extends TLShape = TLShape, K extends TLEventMap = TLEventMa
     return this
   }
 
+
+  togglePenMode = (): this => {
+    const { settings } = this.app
+    settings.update({ penMode: !settings.penMode })
+    return this
+  }
+
   setColor = (color: string): this => {
     const { settings } = this.app
 

+ 2 - 0
tldraw/packages/core/src/lib/TLSettings.ts

@@ -4,6 +4,7 @@ import { observable, makeObservable, action } from 'mobx'
 export interface TLSettingsProps {
   mode: 'light' | 'dark'
   showGrid: boolean
+  penMode: boolean
   snapToGrid: boolean
   color: string
   scaleLevel: string
@@ -17,6 +18,7 @@ export class TLSettings implements TLSettingsProps {
   @observable mode: 'dark' | 'light' = 'light'
   @observable showGrid = true
   @observable snapToGrid = true
+  @observable penMode = false
   @observable scaleLevel = 'md'
   @observable color = ''
 

+ 1 - 0
tldraw/packages/react/src/components/AppCanvas.tsx

@@ -27,6 +27,7 @@ export const AppCanvas = observer(function InnerApp<S extends TLReactShape>(
       shapes={app.shapes} // TODO: use shapes in viewport later?
       assets={app.assets}
       showGrid={app.settings.showGrid}
+      penMode={app.settings.penMode}
       showSelection={app.showSelection}
       showSelectionRotation={app.showSelectionRotation}
       showResizeHandles={app.showResizeHandles}

+ 1 - 0
tldraw/packages/react/src/components/Canvas/Canvas.tsx

@@ -58,6 +58,7 @@ export interface TLCanvasProps<S extends TLReactShape> {
   cursorRotation: number
   selectionRotation: number
   onEditingEnd: () => void
+  penMode: boolean
   showGrid: boolean
   showSelection: boolean
   showHandles: boolean

+ 20 - 0
tldraw/packages/react/src/hooks/useCanvasEvents.ts

@@ -13,11 +13,19 @@ export function useCanvasEvents() {
 
   const events = React.useMemo(() => {
     const onPointerMove: TLReactCustomEvents['pointer'] = e => {
+      if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+        return
+      }
+
       const { order = 0 } = e
       callbacks.onPointerMove?.({ type: TLTargetType.Canvas, order }, e)
     }
 
     const onPointerDown: TLReactCustomEvents['pointer'] = e => {
+      if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+        return
+      }
+
       const { order = 0 } = e
       if (!order) e.currentTarget?.setPointerCapture(e.pointerId)
 
@@ -41,17 +49,29 @@ export function useCanvasEvents() {
     }
 
     const onPointerUp: TLReactCustomEvents['pointer'] = e => {
+      if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+        return
+      }
+
       const { order = 0 } = e
       if (!order) e.currentTarget?.releasePointerCapture(e.pointerId)
       callbacks.onPointerUp?.({ type: TLTargetType.Canvas, order }, e)
     }
 
     const onPointerEnter: TLReactCustomEvents['pointer'] = e => {
+      if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+        return
+      }
+
       const { order = 0 } = e
       callbacks.onPointerEnter?.({ type: TLTargetType.Canvas, order }, e)
     }
 
     const onPointerLeave: TLReactCustomEvents['pointer'] = e => {
+      if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+        return
+      }
+
       const { order = 0 } = e
       callbacks.onPointerLeave?.({ type: TLTargetType.Canvas, order }, e)
     }

+ 22 - 0
tldraw/packages/react/src/hooks/useShapeEvents.ts

@@ -1,23 +1,33 @@
 import * as React from 'react'
 import { TLTargetType } from '@tldraw/core'
+import { useApp } from './useApp'
 import { useRendererContext } from '.'
 import { DOUBLE_CLICK_DURATION } from '../constants'
 import type { TLReactShape } from '../lib'
 import type { TLReactCustomEvents } from '../types'
 
 export function useShapeEvents<S extends TLReactShape>(shape: S) {
+  const app = useApp()
   const { inputs, callbacks } = useRendererContext()
 
   const rDoubleClickTimer = React.useRef<number>(-1)
 
   const events = React.useMemo(() => {
     const onPointerMove: TLReactCustomEvents['pointer'] = e => {
+      if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+        return
+      }
+
       const { order = 0 } = e
       callbacks.onPointerMove?.({ type: TLTargetType.Shape, shape, order }, e)
       e.order = order + 1
     }
 
     const onPointerDown: TLReactCustomEvents['pointer'] = e => {
+      if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+        return
+      }
+
       const { order = 0 } = e
       if (!order) e.currentTarget?.setPointerCapture(e.pointerId)
       callbacks.onPointerDown?.({ type: TLTargetType.Shape, shape, order }, e)
@@ -25,6 +35,10 @@ export function useShapeEvents<S extends TLReactShape>(shape: S) {
     }
 
     const onPointerUp: TLReactCustomEvents['pointer'] = e => {
+      if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+        return
+      }
+
       const { order = 0 } = e
       if (!order) e.currentTarget?.releasePointerCapture(e.pointerId)
       callbacks.onPointerUp?.({ type: TLTargetType.Shape, shape, order }, e)
@@ -42,12 +56,20 @@ export function useShapeEvents<S extends TLReactShape>(shape: S) {
     }
 
     const onPointerEnter: TLReactCustomEvents['pointer'] = e => {
+      if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+        return
+      }
+
       const { order = 0 } = e
       callbacks.onPointerEnter?.({ type: TLTargetType.Shape, shape, order }, e)
       e.order = order + 1
     }
 
     const onPointerLeave: TLReactCustomEvents['pointer'] = e => {
+      if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+        return
+      }
+
       const { order = 0 } = e
       callbacks.onPointerLeave?.({ type: TLTargetType.Shape, shape, order }, e)
       e.order = order + 1

Разлика између датотеке није приказан због своје велике величине
+ 373 - 420
yarn.lock


Неке датотеке нису приказане због велике количине промена