Просмотр исходного кода

Merge branch 'master' into chore/i18n-refactoring

Konstantinos Kaloutas 2 лет назад
Родитель
Сommit
4d9549314d
35 измененных файлов с 1392 добавлено и 785 удалено
  1. 1 0
      e2e-tests/history.spec.ts
  2. 2 0
      e2e-tests/page-rename.spec.ts
  3. 0 1
      e2e-tests/util/page.ts
  4. 63 1
      e2e-tests/whiteboards.spec.ts
  5. 2 2
      src/main/frontend/components/block.cljs
  6. 2 1
      src/main/frontend/components/container.css
  7. 2 2
      src/main/frontend/components/file_sync.cljs
  8. 13 14
      src/main/frontend/components/page.cljs
  9. 1 1
      src/main/frontend/components/right_sidebar.cljs
  10. 224 22
      src/main/frontend/components/settings.cljs
  11. 62 35
      src/main/frontend/components/settings.css
  12. 90 44
      src/main/frontend/components/shortcut.cljs
  13. 28 0
      src/main/frontend/components/shortcut.css
  14. 5 1
      src/main/frontend/config.cljs
  15. 17 5
      src/main/frontend/date.cljs
  16. 1 2
      src/main/frontend/handler/editor.cljs
  17. 6 0
      src/main/frontend/handler/events.cljs
  18. 76 69
      src/main/frontend/handler/paste.cljs
  19. 3 2
      src/main/frontend/handler/plugin.cljs
  20. 10 0
      src/main/frontend/handler/user.cljs
  21. 1 0
      src/main/frontend/handler/whiteboard.cljs
  22. 16 9
      src/main/frontend/modules/shortcut/config.cljs
  23. 20 17
      src/main/frontend/modules/shortcut/core.cljs
  24. 20 25
      src/main/frontend/modules/shortcut/data_helper.cljs
  25. 2 0
      src/main/frontend/quick_capture.cljs
  26. 1 1
      src/main/frontend/routes.cljs
  27. 15 0
      src/main/frontend/state.cljs
  28. 4 3
      src/main/frontend/ui.css
  29. 12 0
      src/main/frontend/util.cljc
  30. 3 3
      src/main/logseq/api.cljs
  31. 6 1
      src/resources/dicts/en.edn
  32. 630 511
      src/resources/dicts/ru.edn
  33. 48 12
      src/test/frontend/handler/paste_test.cljs
  34. 5 0
      tailwind.config.js
  35. 1 1
      tldraw/apps/tldraw-logseq/src/components/QuickSearch/QuickSearch.tsx

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

@@ -43,6 +43,7 @@ test('undo/redo of a renamed page should be preserved', async ({ page, block })
   await page.waitForTimeout(500) // Wait for 500ms autosave period to expire
 
   await renamePage(page, randomString(10))
+  await page.click('.ui__confirm-modal button')
 
   await page.keyboard.press(modKey + '+z')
   await page.waitForTimeout(100)

+ 2 - 0
e2e-tests/page-rename.spec.ts

@@ -15,6 +15,7 @@ async function page_rename_test(page: Page, original_page_name: string, new_page
 
   // Rename page in UI
   await renamePage(page, new_name)
+  await page.click('.ui__confirm-modal button')
 
   expect(await page.innerText('.page-title .title')).toBe(new_name)
 
@@ -45,6 +46,7 @@ async function homepage_rename_test(page: Page, original_page_name: string, new_
   expect(await page.locator('.home-nav span.flex-1').innerText()).toBe(original_name);
 
   await renamePage(page, new_name)
+  await page.click('.ui__confirm-modal button')
 
   expect(await page.locator('.home-nav span.flex-1').innerText()).toBe(new_name);
 

+ 0 - 1
e2e-tests/util/page.ts

@@ -11,5 +11,4 @@ export async function renamePage(page: Page, new_name: string) {
   await page.fill('input[type="text"]', '')
   await page.type('.title input', new_name)
   await page.keyboard.press('Enter')
-  await page.click('.ui__confirm-modal button')
 }

+ 63 - 1
e2e-tests/whiteboards.spec.ts

@@ -1,6 +1,6 @@
 import { expect } from '@playwright/test'
 import { test } from './fixtures'
-import { modKey } from './utils'
+import { modKey, renamePage } from './utils'
 
 test('enable whiteboards', async ({ page }) => {
   if (await page.$('.nav-header .whiteboard') === null) {
@@ -437,3 +437,65 @@ test('go to another board and check reference', async ({ page }) => {
   const pageRefCount$ = page.locator('.whiteboard-page-refs-count')
   await expect(pageRefCount$.locator('.open-page-ref-link')).toContainText('1')
 })
+
+test('Create an embedded whiteboard', async ({ page }) => {
+  const canvas = await page.waitForSelector('.logseq-tldraw')
+  await canvas.dblclick({
+    position: {
+      x: 150,
+      y: 150,
+    },
+  })
+
+  const quickAdd$ = page.locator('.tl-quick-search')
+  await expect(quickAdd$).toBeVisible()
+
+  await page.fill('.tl-quick-search input', 'My embedded whiteboard')
+  await quickAdd$
+    .locator('div[data-index="2"] .tl-quick-search-option')
+    .first()
+    .click()
+
+  await expect(quickAdd$).toBeHidden()
+  await expect(page.locator('.tl-logseq-portal-header a')).toContainText('My embedded whiteboard')
+})
+
+test('New whiteboard should have the correct name', async ({ page }) => {
+  page.locator('.tl-logseq-portal-header a').click()
+
+  await expect(page.locator('.whiteboard-page-title')).toContainText('My embedded whiteboard')
+})
+
+test('Create an embedded page', async ({ page }) => {
+  const canvas = await page.waitForSelector('.logseq-tldraw')
+  await canvas.dblclick({
+    position: {
+      x: 150,
+      y: 150,
+    },
+  })
+
+  const quickAdd$ = page.locator('.tl-quick-search')
+  await expect(quickAdd$).toBeVisible()
+
+  await page.fill('.tl-quick-search input', 'My page')
+  await quickAdd$
+    .locator('div[data-index="1"] .tl-quick-search-option')
+    .first()
+    .click()
+
+  await expect(quickAdd$).toBeHidden()
+  await expect(page.locator('.tl-logseq-portal-header a')).toContainText('My page')
+})
+
+test('New page should have the correct name', async ({ page }) => {
+  page.locator('.tl-logseq-portal-header a').click()
+
+  await expect(page.locator('.ls-page-title')).toContainText('My page')
+})
+
+test('Renaming a page to an existing whiteboard name should be prohibited', async ({ page }) => {
+  await renamePage(page, "My embedded whiteboard")
+
+  await expect(page.locator('.page-title input')).toHaveValue('My page')
+})

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

@@ -551,7 +551,7 @@
                untitled? (str " opacity-50"))
       :data-ref page-name
       :draggable true
-      :on-drag-start (fn [e] (editor-handler/block->data-transfer! page-name e))
+      :on-drag-start (fn [e] (editor-handler/block->data-transfer! page-name-in-block e))
       :on-mouse-down (fn [_e] (reset! *mouse-down? true))
       :on-mouse-up (fn [e]
                      (when @*mouse-down?
@@ -1675,7 +1675,7 @@
        :block)
       (util/stop e))
 
-    (whiteboard-handler/inside-portal? (.-target e))
+    (and (util/meta-key? e) (whiteboard-handler/inside-portal? (.-target e)))
     (do (whiteboard-handler/add-new-block-portal-shape!
          uuid
          (whiteboard-handler/closest-shape (.-target e)))

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

@@ -488,7 +488,8 @@ html[data-theme='dark'] {
 }
 
 .settings-modal {
-  margin: -15px;
+  @apply -m-8 rounded-lg;
+  /* box-shadow: inset 0 0 0 1px var(--ls-border-color); */
 }
 
 .cp__sidebar-main-layout {

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

@@ -789,7 +789,7 @@
    [:div.cloud-tip.rounded-md.mt-6.py-4
     [:div.items-center.opacity-90.flex.justify-center
      [:span.pr-2.flex (ui/icon "bell-ringing" {:class "font-semibold"})]
-     [:strong "Logseq Sync is still in Beta and we're working on a Pro plan!"]]
+     [:strong "Logseq Sync is still in Beta and we're working on a Pro plan!"]]]
 
     ;; [:ul.flex.py-6.px-4
     ;;  [:li.it
@@ -802,7 +802,7 @@
     ;;  [:li.it
     ;;   [:h1.dark:text-white "50G"]
     ;;   [:h2 "Total Storage"]]]
-    ]
+    
 
    [:div.pt-6.flex.justify-end.space-x-2
     (ui/button "Done" :on-click close-fn)]])

+ 13 - 14
src/main/frontend/components/page.cljs

@@ -218,6 +218,11 @@
                              (util/page-name-sanity-lc @*title-value))
                        (db/page-exists? page-name)
                        (db/page-exists? @*title-value))
+        rollback-fn #(do
+                       (reset! *title-value old-name)
+                       (gobj/set (rum/deref input-ref) "value" old-name)
+                       (reset! *edit? true)
+                       (.focus (rum/deref input-ref)))
         confirm-fn (fn []
                      (let [new-page-name (string/trim @*title-value)]
                        (ui/make-confirm-modal
@@ -228,16 +233,7 @@
                                           (close-fn)
                                           (page-handler/rename! (or title page-name) @*title-value)
                                           (reset! *edit? false))
-                         :on-cancel     (fn []
-                                          (reset! *title-value old-name)
-                                          (gobj/set (rum/deref input-ref) "value" old-name)
-                                          (reset! *edit? true)
-                                          (.focus (rum/deref input-ref)))})))
-        rollback-fn #(do
-                       (reset! *title-value old-name)
-                       (gobj/set (rum/deref input-ref) "value" old-name)
-                       (reset! *edit? false)
-                       (when-not untitled? (notification/show! "Illegal page name, can not rename!" :warning)))
+                         :on-cancel     rollback-fn})))
         blur-fn (fn [e]
                   (when (gp-util/wrapped-by-quotes? @*title-value)
                     (swap! *title-value gp-util/unquote-string)
@@ -247,13 +243,16 @@
                     (reset! *edit? false)
 
                     (string/blank? @*title-value)
-                    (rollback-fn)
+                    (do (when-not untitled? (notification/show! (t :page/illegal-page-name) :warning))
+                        (rollback-fn))
 
-                    (and (collide?) whiteboard-page?)
-                    (notification/show! (str "Page “" @*title-value "” already exists!") :error)
+                    (and (collide?) (or whiteboard-page? (model/whiteboard-page? @*title-value)))
+                    (do (notification/show! (t :page/page-already-exists @*title-value) :error)
+                        (rollback-fn))
 
                     (and (date/valid-journal-title? @*title-value) whiteboard-page?)
-                    (notification/show! (str "Whiteboard page cannot be renamed with journal titles!") :error)
+                    (do (notification/show! (t :page/whiteboard-to-journal-error) :error)
+                        (rollback-fn))
 
                     untitled?
                     (page-handler/rename! (or title page-name) @*title-value)

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

@@ -56,7 +56,7 @@
 (rum/defc shortcut-settings
   []
   [:div.contents.flex-col.flex.ml-3
-   (shortcut/shortcut {:show-title? false})])
+   (shortcut/shortcut-page {:show-title? false})])
 
 (defn- block-with-breadcrumb
   [repo block idx sidebar-key ref?]

+ 224 - 22
src/main/frontend/components/settings.cljs

@@ -1,37 +1,39 @@
 (ns frontend.components.settings
   (:require [clojure.string :as string]
-            [frontend.components.svg :as svg]
-            [frontend.components.plugins :as plugins]
+            [electron.ipc :as ipc]
             [frontend.components.assets :as assets]
+            [frontend.components.conversion :as conversion-component]
+            [frontend.components.file-sync :as fs]
+            [frontend.components.plugins :as plugins]
+            [frontend.components.svg :as svg]
             [frontend.config :as config]
             [frontend.context.i18n :refer [t]]
-            [frontend.storage :as storage]
-            [frontend.spec.storage :as storage-spec]
             [frontend.date :as date]
+            [frontend.db :as db]
             [frontend.dicts :as dicts]
             [frontend.handler :as handler]
             [frontend.handler.config :as config-handler]
+            [frontend.handler.file-sync :as file-sync-handler]
+            [frontend.handler.global-config :as global-config-handler]
             [frontend.handler.notification :as notification]
+            [frontend.handler.plugin :as plugin-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.user :as user-handler]
-            [frontend.handler.plugin :as plugin-handler]
-            [frontend.handler.file-sync :as file-sync-handler]
-            [frontend.handler.global-config :as global-config-handler]
+            [frontend.mobile.util :as mobile-util]
             [frontend.modules.instrumentation.core :as instrument]
             [frontend.modules.shortcut.data-helper :as shortcut-helper]
+            [frontend.spec.storage :as storage-spec]
             [frontend.state :as state]
+            [frontend.storage :as storage]
             [frontend.ui :as ui]
-            [electron.ipc :as ipc]
-            [promesa.core :as p]
             [frontend.util :refer [classnames web-platform?] :as util]
             [frontend.version :refer [version]]
             [goog.object :as gobj]
+            [goog.string :as gstring]
+            [promesa.core :as p]
             [reitit.frontend.easy :as rfe]
-            [rum.core :as rum]
-            [frontend.mobile.util :as mobile-util]
-            [frontend.db :as db]
-            [frontend.components.conversion :as conversion-component]))
+            [rum.core :as rum]))
 
 (defn toggle
   [label-for name state on-toggle & [detail-text]]
@@ -59,13 +61,13 @@
                (ui/button
                 "Check for updates"
                 :class "text-sm p-1 mr-1"
-                :href "https://github.com/logseq/logseq/releases" )
+                :href "https://github.com/logseq/logseq/releases")
 
                (mobile-util/native-ios?)
                (ui/button
                 "Check for updates"
                 :class "text-sm p-1 mr-1"
-                :href "https://apps.apple.com/app/logseq/id1601013908" )
+                :href "https://apps.apple.com/app/logseq/id1601013908")
 
                (util/electron?)
                (ui/button
@@ -750,6 +752,196 @@
    {:left-label (t :settings-page/enable-whiteboards)
     :action (whiteboards-enabled-switcher enabled?)}))
 
+(rum/defc settings-account-usage-description [pro-account? graph-usage]
+  (let [count-usage (count graph-usage)
+        count-limit (if pro-account? 10 1)
+        count-percent (js/Math.round (/ count-usage count-limit 0.01))
+        storage-usage (->> (map :used-gbs graph-usage)
+                           (reduce + 0)) 
+        storage-usage-formatted (cond 
+                                  (zero? storage-usage) "0.0"
+                                  (< storage-usage 0.01) "Less than 0.01"
+                                  :else (gstring/format "%.2f" storage-usage))
+        ;; TODO: check logic on this. What are the rules around storage limits?  
+        ;; do we, and should we be able to, give individual users more storage?
+        ;; should that be on a per graph or per user basis?
+        default-storage-limit (if pro-account? 10 0.05)
+        storage-limit (->> (range 0 count-limit)
+                           (map #(get-in graph-usage [% :limit-gbs] default-storage-limit))
+                           (reduce + 0))
+        storage-percent (/ storage-usage storage-limit 0.01)
+        storage-percent-formatted (gstring/format "%.1f" storage-percent)]
+    [:div.text-sm
+     (when pro-account?
+       [:<>
+        (gstring/format "%s of %s synced graphs " count-usage count-limit)
+        [:strong.text-white (gstring/format "(%s%%)" count-percent)]
+        ", "]) 
+     (gstring/format "%sGB of %sGB total storage " storage-usage-formatted storage-limit)
+     [:strong.text-white (gstring/format "(%s%%)" storage-percent-formatted)]]))
+     ; storage-usage-formatted "GB of " storage-limit "GB total storage"
+     ; [:strong.text-white " (" storage-percent-formatted "%)"]]))
+
+
+(rum/defc settings-account-usage-graphs [_pro-account? graph-usage]
+  (when (< 0 (count graph-usage))
+   [:div.grid.gap-3 {:style {:grid-template-columns (str "repeat(" (count graph-usage) ", 1fr)")}}
+    (for [{:keys [name used-percent]} graph-usage
+          :let [color (if (<= 100 used-percent) "bg-red-500" "bg-blue-500")]]
+     [:div.rounded-full.w-full.h-2 {:class "bg-black/50" 
+                                    :tooltip name}
+      [:div.rounded-full.h-2 {:class color
+                              :style {:width (str used-percent "%") 
+                                      :min-width "0.5rem" 
+                                      :max-width "100%"}}]])]))
+  
+(rum/defc ^:large-vars/cleanup-todo settings-account < rum/reactive
+  []
+  (let [current-graph-uuid (state/sub-current-file-sync-graph-uuid)
+        graph-usage (state/get-remote-graph-usage)
+        current-graph-is-remote? ((set (map :uuid graph-usage)) current-graph-uuid)
+        logged-in? (user-handler/logged-in?)
+        user-info (state/get-user-info)
+        paid-user? (#{"active" "on_trial" "cancelled"} (:LemonStatus user-info))
+        gift-user? (some #{"pro"} (:UserGroups user-info))
+        pro-account? (or paid-user? gift-user?)
+        expiration-date (some-> user-info :LemonEndsAt date/parse-iso)
+        renewal-date (some-> user-info :LemonRenewsAt date/parse-iso)
+        has-subscribed? (some? (:LemonStatus user-info))]
+    [:div.panel-wrap.is-features.mb-8
+     [:div.mt-1.sm:mt-0.sm:col-span-2
+      (cond
+        logged-in?
+        [:div.grid.grid-cols-3.gap-8.pt-2
+         [:div "Current plan"]
+         [:div.col-span-2 
+          [:div {:class "w-full bg-gray-500/10 rounded-lg p-4 flex flex-col gap-4"}
+           [:div.flex.gap-4.items-center
+            (if pro-account?
+              [:div.flex-1 "Pro"]
+              [:div.flex-1 "Free"])
+            (cond 
+              has-subscribed?
+              (ui/button "Manage plan" {:class "p-1 h-8 justify-center"
+                                        :disabled true
+                                        :icon "upload"})
+                                         ; :on-click user-handler/upgrade})
+              (not pro-account?)
+              (ui/button "Upgrade plan" {:class "p-1 h-8 justify-center"
+                                         :icon "upload"
+                                         :on-click user-handler/upgrade})
+              :else nil)]
+           (settings-account-usage-graphs pro-account? graph-usage)
+           (settings-account-usage-description pro-account? graph-usage)
+           (if current-graph-is-remote?
+             (ui/button "Deactivate syncing" {:class "p-1 h-8 justify-center"
+                                              :disabled true
+                                              :background "gray"
+                                              :icon "cloud-off"})
+             (ui/button "Activate syncing" {:class "p-1 h-8 justify-center"
+                                            :background "blue"
+                                            :icon "cloud"
+                                            :on-click #(fs/maybe-onboarding-show :sync-initiate)}))]]
+         (when has-subscribed?
+          [:<>
+           [:div "Billing"]
+           [:div.col-span-2.flex.flex-col.gap-4
+            (cond 
+              ;; If there is no expiration date, print the renewal date
+              (and renewal-date (nil? expiration-date)) 
+              [:div 
+               [:strong.font-semibold "Next billing date: " 
+                (date/get-locale-string renewal-date)]]
+              ;; If the expiration date is in the future, word it as such
+              (< (js/Date.) expiration-date) 
+              [:div
+               [:strong.font-semibold "Pro plan expires on: " 
+                (date/get-locale-string expiration-date)]]
+              ;; Otherwise, ind
+              :else 
+              [:div 
+               [:strong.font-semibold "Pro plan expired on: " 
+                (date/get-locale-string expiration-date)]])
+                               
+            [:div (ui/button "Open invoices" {:class "w-full h-8 p-1 justify-center" 
+                                              :disabled true 
+                                              :background "gray" 
+                                              :icon "receipt"})]]])
+         [:div "Profile"]
+         [:div.col-span-2.grid.grid-cols-2.gap-4
+          [:div.flex.flex-col.gap-2.box-border {:class "basis-1/2"}
+           [:label.text-sm.font-semibold "First name"]
+           [:input.rounded.border.px-2.py-1.box-border {:class "border-blue-500 bg-black/25 w-full"}]]
+          [:div.flex.flex-col.gap-2 {:class "basis-1/2"}
+           [:label.text-sm.font-semibold "Last name"]
+           [:input.rounded.border.px-2.py-1.box-border {:class "border-blue-500 bg-black/25 w-full"}]]
+          [:div.flex-1.flex.flex-col.gap-2.col-span-2
+           [:label.text-sm.font-semibold "Username"]
+           [:input.rounded.border.px-2.py-1.box-border {:class "border-blue-500 bg-black/25" 
+                                                        :value (user-handler/email)}]]]
+         [:div "Authentication"]
+         [:div.col-span-2
+          [:div.grid.grid-cols-2.gap-4
+           [:div (ui/button (t :logout) {:class "p-1 h-8 justify-center w-full"
+                                         :background "gray"
+                                         :icon "logout"
+                                         :on-click user-handler/logout})]
+           [:div (ui/button "Reset password" {:class "p-1 h-8 justify-center w-full"
+                                              :disabled true
+                                              :background "gray"
+                                              :icon "key"
+                                              :on-click user-handler/logout})]
+           [:div.col-span-2 (ui/button "Delete Account" {:class "p-1 h-8 justify-center w-full" 
+                                                         :disabled true
+                                                         :background "red"})]]]] 
+                                            
+        (not logged-in?)
+        [:div.grid.grid-cols-3.gap-8.pt-2
+         [:div "Authentication"]
+         [:div.col-span-2.flex.flex-wrap.gap-4
+          [:div.w-full.text-white "With a Logseq account, you can access cloud-based services like Logseq Sync and alpha/beta features."]
+          [:div.flex-1 (ui/button "Sign up" {:class "h-8 w-full text-center justify-center"
+                                             :on-click (fn []
+                                                         (state/close-settings!)
+                                                         (state/pub-event! [:user/login]))})]
+          [:div.flex-1 (ui/button (t :login) {:icon "login" 
+                                              :class "h-8 w-full text-center justify-center" 
+                                              :background "gray"
+                                              :on-click (fn []
+                                                          (state/close-settings!)
+                                                          (state/pub-event! [:user/login]))})]]
+         [:div.col-span-3.flex.flex-col.gap-4 {:class "bg-black/20 p-4 rounded-lg"}
+          [:div.flex.w-full.items-center
+           [:div {:class "w-1/2 text-lg"} 
+            "Discover the power of " 
+            [:strong {:class "text-white/80"} "Logseq Sync"]]
+           [:div {:class "w-1/2 bg-gradient-to-r from-white/10 to-transparent p-3 rounded-lg flex items-center gap-2 px-5 ml-5"} 
+            [:div.w-3.h-3.rounded-full.bg-green-500]
+            "Synced"]]
+          [:div.flex.w-full.gap-4
+           [:div {:class "w-1/2 bg-black/50 rounded-lg p-4 pt-10 relative flex flex-col gap-4"}
+            [:div.absolute.top-0.left-4.bg-gray-700.uppercase.px-2.py-1.rounded-b-lg.font-bold.text-xs "Free"]
+            [:div
+             [:strong.text-white.text-xl.font-normal "$0"]] 
+            [:div.text-white.font-bold {:class "h-[2.5rem] "} "Get started with basic syncing"]
+            [:ul.text-xs.list-none.m-0.flex.flex-col.gap-0.5
+             [:li "Unlimited unsynced graphs"]
+             [:li "1 synced graph (up to 50MB, notes only)"]
+             [:li "No asset syncing"]
+             [:li "Access to core Logseq features"]]]
+           [:div {:class "w-1/2 bg-black/50 rounded-lg p-4 pt-10 relative flex flex-col gap-4"}
+            [:div.absolute.top-0.left-4.bg-blue-700.uppercase.px-2.py-1.rounded-b-lg.font-bold.text-xs "Pro"]
+            [:div
+             [:strong.text-white.text-xl.font-normal "$10"] 
+             [:span.text-xs.font-base {:class "ml-0.5"} "/ month"]]
+            [:div.text-white.font-bold {:class "h-[2.5rem]"} "Unlock advanced syncing and more"]
+            [:ul.text-xs.list-none.m-0.flex.flex-col.gap-0.5
+             [:li "Unlimited unsynced graphs"]
+             [:li "10 synced graphs (up to 5GB each)"]
+             [:li "Sync assets up to 100MB per file"]
+             [:li "Early access to alpha/beta features"]
+             [:li "Upcoming cloud-based features, including Logseq Publish"]]]]]])]]))
+
 (rum/defc settings-features < rum/reactive
   []
   (let [current-repo (state/get-current-repo)
@@ -809,7 +1001,7 @@
           [:a.mx-1 {:href "https://blog.logseq.com/how-to-setup-and-use-logseq-sync/"
                     :target "_blank"}
            "here"]
-          "for instructions on how to set up and use Sync."]]])
+          "for instructions on how to set up and use Sync."]]])]))
 
      ;; (when-not web-platform?
      ;;   [:<>
@@ -820,10 +1012,12 @@
      ;;     {:class (when-not user-handler/alpha-user? "opacity-50 pointer-events-none cursor-not-allowed")}
      ;;     ;; features
      ;;     ]])
-     ]))
+     
+
+(def DEFAULT-ACTIVE-TAB-STATE (if config/ENABLE-SETTINGS-ACCOUNT-TAB [:account :account] [:general :general]))
 
 (rum/defcs settings
-  < (rum/local [:general :general] ::active)
+  < (rum/local DEFAULT-ACTIVE-TAB-STATE ::active)
     {:will-mount
      (fn [state]
        (state/load-app-user-cfgs)
@@ -841,15 +1035,18 @@
         *active (::active state)]
 
     [:div#settings.cp__settings-main
-     [:header
-      [:h1.title (t :settings)]]
 
      [:div.cp__settings-inner
 
       [:aside.md:w-64 {:style {:min-width "10rem"}}
+       [:header.cp__settings-header
+        (ui/icon "settings")
+        [:h1.cp__settings-modal-title (t :settings)]]
        [:ul.settings-menu
         (for [[label id text icon]
-              [[:general "general" (t :settings-page/tab-general) (ui/icon "adjustments")]
+              [(when config/ENABLE-SETTINGS-ACCOUNT-TAB
+                [:account "account" (t :settings-page/tab-account) (ui/icon "user-circle")])
+               [:general "general" (t :settings-page/tab-general) (ui/icon "adjustments")]
                [:editor "editor" (t :settings-page/tab-editor) (ui/icon "writing")]
 
                (when (util/electron?)
@@ -871,11 +1068,13 @@
               :on-click #(reset! *active [label (first @*active)])}
 
              [:a.flex.items-center.settings-menu-link
-             {:data-id id}
+              {:data-id id}
               icon
               [:strong text]]]))]]
 
       [:article
+       [:header.cp__settings-header
+        [:h1.cp__settings-category-title (name (first @*active))]]
 
        (case (first @*active)
 
@@ -885,6 +1084,9 @@
            (reset! *active [label label])
            nil)
 
+         :account 
+         (settings-account)
+
          :general
          (settings-general current-repo)
 

+ 62 - 35
src/main/frontend/components/settings.css

@@ -1,9 +1,7 @@
 .cp__settings {
-
   &-main {
     > header {
-      padding: 10px;
-      padding-top: 0;
+      padding: 0 10px 10px;
       border-bottom: 1px solid var(--ls-quaternary-background-color);
 
       h1 {
@@ -11,34 +9,78 @@
         margin: 0;
       }
     }
+
+    aside {
+      @apply bg-gray-400/5 p-4;
+    }
+
+    article {
+      @apply p-4 flex-1 min-h-[12rem] w-auto overflow-y-auto;
+      @apply md:max-h-[70vh] md:w-[40rem];
+      /* margin-right: -17px; */
+      /* margin-bottom: -17px; */
+
+      @screen md {
+        /* max-height: 70vh; */
+        /* width: 680px; */
+      }
+    }
+
+    aside > .cp__settings-header,
+    article > .cp__settings-header {
+      @apply h-10 py-2 flex flex-row items-center justify-start gap-2;
+    }
+
+    aside > .cp__settings-header {
+      @apply px-2;
+    }
+
+    aside > .cp__settings-header > .ui__icon {
+      @apply h-8 w-8 bg-gray-700/10 rounded grid place-items-center;
+    }
+
+    aside > .cp__settings-header > .ui__icon > svg {
+      @apply h-6 w-6;
+    }
+
+    h1.cp__settings-modal-title {
+      @apply text-2xl font-semibold lowercase;
+    }
+
+    h1.cp__settings-category-title {
+      @apply text-xl lowercase;
+    }
+
+    h1.cp__settings-modal-title:first-letter, 
+    h1.cp__settings-category-title:first-letter {
+      @apply uppercase;
+    }
+
+    .settings-menu {
+      @apply p-0 m-0 mt-4 pr-3; 
+    }
+
+    .settings-menu-item {
+      @apply list-none p-0 my-1.5 rounded;
+      @apply hover:bg-black/10;
+    }
+
+    .settings-menu-link {
+      @apply px-2 py-1.5 select-none; 
+      color: var(--ls-primary-text-color);
+    }
   }
 
   &-inner {
     @apply flex flex-col md:flex-row;
 
     > aside {
-      border-right: 0 solid var(--ls-quaternary-background-color);
-      border-bottom: 1px solid var(--ls-quaternary-background-color);
-
-      @screen md {
-        border-right: 1px solid var(--ls-quaternary-background-color);
-        border-bottom: 0 solid var(--ls-quaternary-background-color);
-      }
 
       ul {
-        padding: 12px 12px 12px 5px;
-        margin: 0;
 
         > li {
-          list-style: none;
-          padding: 0;
-          margin: 5px 0;
-          border-radius: 4px;
 
           > a {
-            padding: 10px;
-            user-select: none;
-            color: var(--ls-primary-text-color);
 
             > i {
               overflow: hidden;
@@ -65,21 +107,6 @@
       }
     }
 
-    > article {
-      flex: 1;
-      padding: 0 12px 12px;
-      min-height: 380px;
-      width: auto;
-      overflow: auto;
-      margin-right: -17px;
-      margin-bottom: -17px;
-
-      @screen md {
-        max-height: 70vh;
-        width: 680px;
-      }
-    }
-
     &.no-aside {
       > article {
         padding-left: 0;
@@ -87,7 +114,7 @@
     }
 
     .panel-wrap {
-      padding: 12px;
+      @apply p-1;
 
       @screen sm {
         width: 600px;

+ 90 - 44
src/main/frontend/components/shortcut.cljs

@@ -13,12 +13,15 @@
 
 (rum/defcs customize-shortcut-dialog-inner <
   (rum/local "")
+  (rum/local nil :rum/action)
   (shortcut/record!)
   [state k action-name current-binding]
-  (let [keypress (:rum/local state)
-        keyboard-shortcut (if (= "" @keypress) current-binding @keypress)]
-    [:div
-     [:div
+  (let [*keypress         (:rum/local state)
+        *action           (:rum/action state)
+        keypressed?       (not= "" @*keypress)
+        keyboard-shortcut (if-not keypressed? current-binding @*keypress)]
+    [:<>
+     [:div.sm:w-lsm
       [:p.mb-4 "Press any sequence of keys to set the shortcut for the " [:b action-name] " action."]
       [:p.mb-4.mt-4
        (ui/render-keyboard-shortcut (-> keyboard-shortcut
@@ -26,26 +29,30 @@
                                         (str/lower-case)
                                         (str/split  #" |\+")))
        " "
-       [:a.text-sm
-        {:style {:margin-left "12px"}
-         :on-click (fn []
-                     (dh/remove-shortcut k)
-                     (shortcut/refresh!)
-                     (swap! keypress (fn [] "")) ;; Clear local state
-                     )}
-        "Reset"]]]
+       (when keypressed?
+         [:a.text-sm
+          {:style    {:margin-left "12px"}
+           :on-click (fn []
+                       (dh/remove-shortcut k)
+                       (shortcut/refresh!)
+                       (swap! *keypress (fn [] ""))          ;; Clear local state
+                       )}
+          "Reset"])]]
      [:div.cancel-save-buttons.text-right.mt-4
-      (ui/button "Save" :on-click state/close-modal!)
+      (ui/button "Save" :on-click (fn []
+                                    (reset! *action :save)
+                                    (state/close-modal!)))
       [:a.ml-4
        {:on-click (fn []
-                    (reset! keypress (dh/binding-for-storage current-binding))
+                    (reset! *keypress (dh/binding-for-storage current-binding))
+                    (reset! *action :cancel)
                     (state/close-modal!))} "Cancel"]]]))
 
 (defn customize-shortcut-dialog [k action-name displayed-binding]
   (fn [_]
     (customize-shortcut-dialog-inner k action-name displayed-binding)))
 
-(rum/defc shortcut-col [k binding configurable? action-name]
+(rum/defc shortcut-col [_category k binding configurable? action-name]
   (let [conflict?         (dh/potential-conflict? k)
         displayed-binding (dh/binding-for-display k binding)
         disabled?         (str/includes? displayed-binding "system default")]
@@ -61,28 +68,48 @@
                  (if disabled? "Cannot override system default" "Click to modify"))
         :background (if conflict? "pink" (when disabled? "gray"))
         :on-click (when-not disabled?
-                    #(state/set-modal! (customize-shortcut-dialog k action-name displayed-binding))))])))
-
-(rum/defc shortcut-table < rum/reactive
-  ([name]
-   (shortcut-table name false))
-  ([name configurable?]
-   (let [shortcut-config (rum/cursor-in
-                          state/state
-                          [:config (state/get-current-repo) :shortcuts])
-         _ (rum/react shortcut-config)]
-     [:div
-      [:table
-       [:thead
-        [:tr
-         [:th.text-left [:b (t name)]]
-         [:th.text-right]]]
-       [:tbody
-        (map (fn [[k {:keys [binding]}]]
-               [:tr {:key (str k)}
-                [:td.text-left (t (dh/decorate-namespace k))]
-                (shortcut-col k binding configurable? (t (dh/decorate-namespace k)))])
-          (dh/binding-by-category name))]]])))
+                    #(state/set-sub-modal!
+                       (customize-shortcut-dialog k action-name displayed-binding)
+                       {:center? true})))])))
+
+(rum/defcs shortcut-table
+  < rum/reactive
+    (rum/local true ::folded?)
+    {:will-mount (fn [state]
+                   (let [name (first (:rum/args state))]
+                     (cond-> state
+                             (contains? #{:shortcut.category/basics}
+                                        name)
+                             (-> ::folded? (reset! false) (do state)))))}
+  [state category configurable?]
+  (let [*folded? (::folded? state)
+        plugin?  (= category :shortcut.category/plugins)
+        _        (state/sub [:config (state/get-current-repo) :shortcuts])]
+    [:div.cp__shortcut-table-wrap
+     [:a.fold
+      {:on-click #(reset! *folded? (not @*folded?))}
+      (ui/icon (if @*folded? "chevron-left" "chevron-down"))]
+     [:table
+      [:thead
+       [:tr
+        [:th.text-left [:b (t category)]]
+        [:th.text-right]]]
+      (when-not @*folded?
+        [:tbody
+         (map (fn [[k {:keys [binding]}]]
+                (let [cmd   (dh/shortcut-cmd k)
+                      label (cond
+                              (string? (:desc cmd))
+                              [:<>
+                               [:code.text-xs (namespace k)]
+                               [:small.pl-1 (:desc cmd)]]
+
+                              (not plugin?) (-> k (dh/decorate-namespace) (t))
+                              :else (str k))]
+                  [:tr {:key (str k)}
+                   [:td.text-left.flex.items-center label]
+                   (shortcut-col category k binding configurable? label)]))
+              (dh/binding-by-category category))])]]))
 
 (rum/defc trigger-table []
   [:table
@@ -167,13 +194,9 @@
               [:td.text-right (get rendered name)]])
         list)]]))
 
-(rum/defc shortcut
-  [{:keys [show-title?]
-    :or {show-title? true}}]
-  [:div
-   (when show-title? [:h1.title (t :help/shortcut-page-title)])
-   (trigger-table)
-   (markdown-and-orgmode-syntax)
+(rum/defc keymap-tables
+  []
+  [:div.cp__keymap-tables
    (shortcut-table :shortcut.category/basics true)
    (shortcut-table :shortcut.category/navigating true)
    (shortcut-table :shortcut.category/block-editing true)
@@ -182,4 +205,27 @@
    (shortcut-table :shortcut.category/formatting true)
    (shortcut-table :shortcut.category/toggle true)
    (when (state/enable-whiteboards?) (shortcut-table :shortcut.category/whiteboard true))
+   (shortcut-table :shortcut.category/plugins true)
    (shortcut-table :shortcut.category/others true)])
+
+(rum/defc keymap-pane
+  []
+  (let [[ready?, set-ready!] (rum/use-state false)]
+    (rum/use-effect!
+      (fn [] (js/setTimeout #(set-ready! true) 32))
+      [])
+
+    [:div.cp__keymap-pane
+     [:h1.pb-2.text-3xl.pt-2 "Keymap"]
+     (if ready?
+       (keymap-tables)
+       [:p.flex.justify-center.py-20 (ui/loading "")])]))
+
+(rum/defc shortcut-page
+  [{:keys [show-title?]
+    :or {show-title? true}}]
+  [:div.cp__shortcut-page
+   (when show-title? [:h1.title (t :help/shortcut-page-title)])
+   (trigger-table)
+   (markdown-and-orgmode-syntax)
+   (keymap-tables)])

+ 28 - 0
src/main/frontend/components/shortcut.css

@@ -0,0 +1,28 @@
+.ui__modal {
+  &[label="keymap-manager"] {
+    .panel-content {
+      @apply m-[-16px];
+    }
+
+    @screen lg {
+      .panel-content {
+        width: 980px;
+      }
+    }
+  }
+}
+
+.cp__shortcut {
+  &-table-wrap {
+    @apply relative;
+
+    a.fold {
+      @apply absolute right-0 top-0 w-full pt-3 pr-3
+      pb-3 flex items-center justify-end select-none;
+
+      &:active {
+        @apply bg-white/50 opacity-60;
+      }
+    }
+  }
+}

+ 5 - 1
src/main/frontend/config.cljs

@@ -29,6 +29,10 @@
 
 (goog-define ENABLE-FILE-SYNC-PRODUCTION false)
 
+;; this is a feature flag to enable the account tab 
+;; when it launches (when pro plan launches) it should be removed
+(def ENABLE-SETTINGS-ACCOUNT-TAB false)
+
 (if ENABLE-FILE-SYNC-PRODUCTION
   (do (def FILE-SYNC-PROD? true)
       (def LOGIN-URL
@@ -472,7 +476,7 @@
 (defn get-current-repo-assets-root
   []
   (when-let [repo-dir (and (local-db? (state/get-current-repo))
-                            (get-repo-dir (state/get-current-repo)))]
+                           (get-repo-dir (state/get-current-repo)))]
     (path/path-join repo-dir "assets")))
 
 (defn get-custom-js-path

+ 17 - 5
src/main/frontend/date.cljs

@@ -62,11 +62,13 @@
    (tf/unparse custom-formatter date-time)))
 
 (defn get-locale-string
-  [s]
+  "Accepts a :date-time-no-ms string representation, or a cljs-time date object"
+  [input]
   (try
-    (->> (tf/parse (tf/formatters :date-time-no-ms) s)
-        (t/to-default-time-zone)
-        (tf/unparse (tf/formatter "MMM do, yyyy")))
+    (->> (cond->> input
+          (string? input) (tf/parse (tf/formatters :date-time-no-ms)))
+         (t/to-default-time-zone)
+         (tf/unparse (tf/formatter "MMM do, yyyy")))
     (catch :default _e
       nil)))
 
@@ -209,12 +211,22 @@
    (tf/formatter "yyyy-MM-dd HH:mm")
    (t/to-default-time-zone (tc/from-long n))))
 
+(def iso-parser (tf/formatter "yyyy-MM-dd'T'HH:mm:ss.SSSS'Z'"))
+(defn parse-iso [string]
+  (tf/parse iso-parser string))
+
 (comment
   (def default-formatter (tf/formatter "MMM do, yyyy"))
   (def zh-formatter (tf/formatter "YYYY年MM月dd日"))
 
-  (tf/show-formatters))
+  (tf/show-formatters)
 
   ;; :date 2020-05-31
   ;; :rfc822 Sun, 31 May 2020 03:00:57 Z
 
+  (let [info {:ExpireTime 1680781356,
+              :UserGroups [],
+              :LemonRenewsAt "2024-04-11T07:28:00.000000Z",
+              :LemonEndsAt nil,
+              :LemonStatus "active"}]
+    (->> info :LemonRenewsAt (tf/parse iso-parser) (< (js/Date.))))) 

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

@@ -168,7 +168,6 @@
   ([text]
    (when-let [m (get-selection-and-format)]
      (let [{:keys [selection-start selection-end format selection value edit-id input]} m
-           cur-pos (cursor/pos input)
            empty-selection? (= selection-start selection-end)
            selection-link? (and selection (gp-mldoc/mldoc-link? format selection))
            [content forward-pos] (cond
@@ -190,7 +189,7 @@
                       (subs value 0 selection-start)
                       content
                       (subs value selection-end))
-           cur-pos (or selection-start cur-pos)]
+           cur-pos (or selection-start (cursor/pos input))]
        (state/set-edit-content! edit-id new-value)
        (cursor/move-cursor-to input (+ cur-pos forward-pos))))))
 

+ 6 - 0
src/main/frontend/handler/events.cljs

@@ -23,6 +23,7 @@
             [frontend.components.shell :as shell]
             [frontend.components.whiteboard :as whiteboard]
             [frontend.components.user.login :as login]
+            [frontend.components.shortcut :as shortcut]
             [frontend.config :as config]
             [frontend.context.i18n :refer [t]]
             [frontend.db :as db]
@@ -936,6 +937,11 @@
 (defmethod handle :editor/quick-capture [[_ args]]
   (quick-capture/quick-capture args))
 
+(defmethod handle :modal/keymap-manager [[_]]
+  (state/set-modal!
+    #(shortcut/keymap-pane)
+    {:label "keymap-manager"}))
+
 (defmethod handle :editor/toggle-own-number-list [[_ blocks]]
   (let [batch? (sequential? blocks)
         blocks (cond->> blocks

+ 76 - 69
src/main/frontend/handler/paste.cljs

@@ -114,77 +114,87 @@
     (when (= (set (map :block/uuid blocks)) recent-cut-block-ids)
       (seq revert-tx))))
 
-(defn- paste-copied-blocks-or-text
-  ;; todo: logseq/whiteboard-shapes is now text/html
-  [text e html]
-  (util/stop e)
-  (->
-   (p/let [copied-blocks (get-copied-blocks)]
-     (let [input (state/get-input)
-           input-id (state/get-edit-input-id)
-           text (string/replace text "\r\n" "\n") ;; Fix for Windows platform
-           replace-text-f (fn [text]
-                            (let [input-id (state/get-edit-input-id)]
-                              (commands/delete-selection! input-id)
-                              (commands/simple-insert! input-id text nil)))
-           internal-paste? (seq copied-blocks)]
-       (if internal-paste?
-         (let [revert-cut-tx (get-revert-cut-tx copied-blocks)
-               cut-paste? (boolean (seq revert-cut-tx))
-               keep-uuid? cut-paste?]
-           (editor-handler/paste-blocks copied-blocks {:revert-cut-tx revert-cut-tx
-                                                       :cut-paste? cut-paste?
-                                                       :keep-uuid? keep-uuid?}))
-         (let [shape-refs-text (when (and (not (string/blank? html))
-                                          (get-whiteboard-tldr-from-text html))
-                                 ;; text should always be prepared block-ref generated in tldr
-                                 text)
-               {:keys [value selection] :as selection-and-format} (editor-handler/get-selection-and-format)
-               text-url? (gp-util/url? text)
-               selection-url? (gp-util/url? selection)]
-           (cond
-             (not (string/blank? shape-refs-text))
-             (commands/simple-insert! input-id shape-refs-text nil)
+(defn- paste-copied-text
+  [input *text html]
+  (let [replace-text-f (fn [text]
+                         (let [input-id (state/get-edit-input-id)]
+                           (commands/delete-selection! input-id)
+                           (commands/simple-insert! input-id text nil)))
+        text (string/replace *text "\r\n" "\n") ;; Fix for Windows platform
+        input-id (state/get-edit-input-id)
+        shape-refs-text (when (and (not (string/blank? html))
+                                   (get-whiteboard-tldr-from-text html))
+                          ;; text should always be prepared block-ref generated in tldr
+                          text)
+        {:keys [value selection] :as selection-and-format} (editor-handler/get-selection-and-format)
+        text-url? (gp-util/url? text)
+        selection-url? (gp-util/url? selection)]
+    (cond
+      (not (string/blank? shape-refs-text))
+      (commands/simple-insert! input-id shape-refs-text nil)
+
+      ;; When a url is selected in a formatted link, replaces it with pasted text
+      (or (and (or text-url? selection-url?)
+               (selection-within-link? selection-and-format))
+          (and text-url? selection-url?))
+      (replace-text-f text)
 
-             (or (and (or text-url? selection-url?)
-                      (selection-within-link? selection-and-format))
-                 (and text-url? selection-url?))
-             (replace-text-f text)
+      ;; Pastes a formatted link over selected text
+      (and (or text-url?
+               (and value (gp-util/url? (string/trim value))))
+           (not (string/blank? (util/get-selected-text))))
+      (editor-handler/html-link-format! text)
 
-             (and (or text-url?
-                      (and value (gp-util/url? (string/trim value))))
-                  (not (string/blank? (util/get-selected-text))))
-             (editor-handler/html-link-format! text)
+      ;; Pastes only block id when inside of '(())'
+      (and (block-ref/block-ref? text)
+           (editor-handler/wrapped-by? input block-ref/left-parens block-ref/right-parens))
+      (commands/simple-insert! input-id (block-ref/get-block-ref-id text) nil)
 
-             (and (block-ref/block-ref? text)
-                  (editor-handler/wrapped-by? input block-ref/left-parens block-ref/right-parens))
-             (commands/simple-insert! input-id (block-ref/get-block-ref-id text) nil)
+      :else
+      ;; from external
+      (let [format (or (db/get-page-format (state/get-current-page)) :markdown)
+            html-text (let [result (when-not (string/blank? html)
+                                     (try
+                                       (html-parser/convert format html)
+                                       (catch :default e
+                                         (log/error :exception e)
+                                         nil)))]
+                        (if (string/blank? result) nil result))
+            text-blocks? (if (= format :markdown) markdown-blocks? org-blocks?)
+            blocks? (text-blocks? text)
+            text' (or html-text
+                      (when (gp-util/url? text)
+                        (wrap-macro-url text))
+                      text)]
+        (cond
+          blocks?
+          (paste-text-parseable format text)
 
-             :else
-             ;; from external
-             (let [format (or (db/get-page-format (state/get-current-page)) :markdown)
-                   html-text (let [result (when-not (string/blank? html)
-                                            (try
-                                              (html-parser/convert format html)
-                                              (catch :default e
-                                                (log/error :exception e)
-                                                nil)))]
-                               (if (string/blank? result) nil result))
-                   text-blocks? (if (= format :markdown) markdown-blocks? org-blocks?)
-                   blocks? (text-blocks? text)
-                   text' (or html-text text)]
-               (cond
-                 blocks?
-                 (paste-text-parseable format text)
+          (util/safe-re-find #"(?:\r?\n){2,}" text')
+          (paste-segmented-text format text')
 
-                 (util/safe-re-find #"(?:\r?\n){2,}" text')
-                 (paste-segmented-text format text')
+          :else
+          (replace-text-f text'))))))
 
-                 :else
-                 (replace-text-f text'))))))))
+(defn- paste-copied-blocks-or-text
+  ;; todo: logseq/whiteboard-shapes is now text/html
+  [input text e html]
+  (util/stop e)
+  (->
+   (p/let [copied-blocks (get-copied-blocks)]
+     (if (seq copied-blocks)
+       ;; Handle internal paste
+       (let [revert-cut-tx (get-revert-cut-tx copied-blocks)
+             cut-paste? (boolean (seq revert-cut-tx))
+             keep-uuid? cut-paste?]
+         (editor-handler/paste-blocks copied-blocks {:revert-cut-tx revert-cut-tx
+                                                     :cut-paste? cut-paste?
+                                                     :keep-uuid? keep-uuid?}))
+       (paste-copied-text input text html)))
    (p/catch (fn [error]
-              (prn "Paste failed: ")
-              (log/error :exception error)))))
+              (log/error :msg "Paste failed" :exception error)
+              (state/pub-event! [:capture-error {:error error
+                                                 :payload {:type ::paste-copied-blocks-or-text}}])))))
 
 (defn paste-text-in-one-block-at-point
   []
@@ -206,7 +216,7 @@
     (when-not (mobile-util/native-ios?)
       (util/stop e)
       (paste-text-in-one-block-at-point))
-    (paste-copied-blocks-or-text text e html)))
+    (paste-copied-blocks-or-text input text e html)))
 
 (defn- paste-file-if-exists [id e]
   (when id
@@ -248,10 +258,7 @@
         (paste-file-if-exists id e)
 
         :else
-        (let [text' (or (when (gp-util/url? text)
-                          (wrap-macro-url text))
-                        text)]
-          (paste-text-or-blocks-aux (state/get-input) e text' html))))))
+        (paste-text-or-blocks-aux (state/get-input) e text html)))))
 
 (defn editor-on-paste-raw!
   "Raw pastes without _any_ formatting. Can also replace selected text with a paste"

+ 3 - 2
src/main/frontend/handler/plugin.cljs

@@ -297,9 +297,10 @@
   [pid key keybinding]
   (let [id      (keyword (str "plugin." pid "/" key))
         binding (:binding keybinding)
+        binding (some->> (if (string? binding) [binding] (seq binding))
+                         (map util/normalize-user-keyname))
         binding (if util/mac?
-                  (or (:mac keybinding) binding)
-                  binding)
+                  (or (:mac keybinding) binding) binding)
         mode    (or (:mode keybinding) :global)
         mode    (get keybinding-mode-handler-map (keyword mode))]
     [mode id {:binding binding}]))

+ 10 - 0
src/main/frontend/handler/user.cljs

@@ -199,6 +199,16 @@
   (state/clear-user-info!)
   (state/pub-event! [:user/logout]))
 
+(defn upgrade [] 
+  (let [base-upgrade-url "https://logseqdemo.lemonsqueezy.com/checkout/buy/13e194b5-c927-41a8-af58-ed1a36d6000d"
+        user-uuid (user-uuid)
+        url (cond-> base-upgrade-url
+              user-uuid (str "?checkout[custom][user_uuid]=" (name user-uuid)))]
+    (println " ~~~ LEMON: " url " ~~~ ")
+    (js/window.open url)))
+  ; (js/window.open 
+  ;   "https://logseqdemo.lemonsqueezy.com/checkout/buy/13e194b5-c927-41a8-af58-ed1a36d6000d"))
+
 (defn <ensure-id&access-token
   []
   (go

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

@@ -187,6 +187,7 @@
 (defn get-default-new-whiteboard-tx
   [page-name id]
   [#:block{:name (util/page-name-sanity-lc page-name),
+           :original-name page-name
            :type "whiteboard",
            :properties
            {:ls-type :whiteboard-page,

+ 16 - 9
src/main/frontend/modules/shortcut/config.cljs

@@ -113,19 +113,19 @@
    :whiteboard/zoom-out          {:binding "shift+dash"
                                   :fn      #(.zoomOut (.-api ^js (state/active-tldraw-app)) false)}
 
-   :whiteboard/zoom-in           {:binding "shift+="
+   :whiteboard/zoom-in           {:binding "shift+equals"
                                   :fn      #(.zoomIn (.-api ^js (state/active-tldraw-app)) false)}
 
-   :whiteboard/send-backward     {:binding "["
+   :whiteboard/send-backward     {:binding "open-square-bracket"
                                   :fn      #(.sendBackward ^js (state/active-tldraw-app))}
 
-   :whiteboard/send-to-back      {:binding "shift+["
+   :whiteboard/send-to-back      {:binding "shift+open-square-bracket"
                                   :fn      #(.sendToBack ^js (state/active-tldraw-app))}
 
-   :whiteboard/bring-forward     {:binding "]"
+   :whiteboard/bring-forward     {:binding "close-square-bracket"
                                   :fn      #(.bringForward ^js (state/active-tldraw-app))}
 
-   :whiteboard/bring-to-front    {:binding "shift+]"
+   :whiteboard/bring-to-front    {:binding "shift+close-square-bracket"
                                   :fn      #(.bringToFront ^js (state/active-tldraw-app))}
 
    :whiteboard/lock              {:binding "mod+l"
@@ -439,7 +439,7 @@
                                      :fn      route-handler/redirect-to-whiteboard-dashboard!}
 
    :go/keyboard-shortcuts          {:binding "g s"
-                                    :fn      #(route-handler/redirect! {:to :shortcut-setting})}
+                                    :fn      #(state/pub-event! [:modal/keymap-manager])}
 
    :go/tomorrow                    {:binding "g t"
                                     :fn      journal-handler/go-to-tomorrow!}
@@ -927,7 +927,10 @@
     :dev/show-block-ast
     :dev/show-page-data
     :dev/show-page-ast
-    :ui/clear-all-notifications]})
+    :ui/clear-all-notifications]
+
+   :shortcut.category/plugins
+   []})
 
 (let [category-maps {::category (set (keys category*))
                      ::dicts/category dicts/categories}]
@@ -942,10 +945,14 @@
    (fn [v]
      (vec (remove #(:inactive (get all-default-keyboard-shortcuts %)) v)))))
 
+(def *shortcut-cmds (atom {}))
+
 (defn add-shortcut!
   [handler-id id shortcut-map]
-  (swap! config assoc-in [handler-id id] shortcut-map))
+  (swap! config assoc-in [handler-id id] shortcut-map)
+  (swap! *shortcut-cmds assoc id (:cmd shortcut-map)))
 
 (defn remove-shortcut!
   [handler-id id]
-  (swap! config medley/dissoc-in [handler-id id]))
+  (swap! config medley/dissoc-in [handler-id id])
+  (swap! *shortcut-cmds dissoc id))

+ 20 - 17
src/main/frontend/modules/shortcut/core.cljs

@@ -66,7 +66,7 @@
          (doseq [k (dh/shortcut-binding id)]
            (try
              (log/debug :shortcut/register-shortcut {:id id :binding k})
-             (.registerShortcut handler (util/keyname id) (dh/normalize-user-keyname k))
+             (.registerShortcut handler (util/keyname id) (util/normalize-user-keyname k))
              (catch :default e
                (log/error :shortcut/register-shortcut {:id      id
                                                        :binding k
@@ -81,24 +81,24 @@
   (when-let [handler (get-handler-by-id handler-id)]
     (when-let [ks (dh/shortcut-binding shortcut-id)]
       (doseq [k ks]
-        (.unregisterShortcut ^js handler (dh/normalize-user-keyname k))))
+        (.unregisterShortcut ^js handler (util/normalize-user-keyname k))))
     (shortcut-config/remove-shortcut! handler-id shortcut-id)))
 
-(defn uninstall-shortcut!
+(defn uninstall-shortcut-handler!
   [install-id]
   (when-let [handler (-> (get @*installed install-id)
                          :handler)]
     (.dispose ^js handler)
     (swap! *installed dissoc install-id)))
 
-(defn install-shortcut!
+(defn install-shortcut-handler!
   [handler-id {:keys [set-global-keys?
                       prevent-default?
                       state]
                :or   {set-global-keys? true
                       prevent-default? false}}]
   (when-let [install-id (get-handler-by-id handler-id)]
-    (uninstall-shortcut! install-id))
+    (uninstall-shortcut-handler! install-id))
 
   (let [shortcut-map (dh/shortcut-map handler-id state)
         handler      (new KeyboardShortcutHandler js/window)]
@@ -136,23 +136,23 @@
         :shortcut.handler/editor-global
         :shortcut.handler/global-non-editing-only
         :shortcut.handler/global-prevent-default]
-       (map #(install-shortcut! % {}))
+       (map #(install-shortcut-handler! % {}))
        doall))
 
 (defn mixin [handler-id]
   {:did-mount
    (fn [state]
-     (let [install-id (install-shortcut! handler-id {:state state})]
+     (let [install-id (install-shortcut-handler! handler-id {:state state})]
        (assoc state ::install-id install-id)))
 
    :will-remount (fn [old-state new-state]
-                  (uninstall-shortcut! (::install-id old-state))
-                  (when-let [install-id (install-shortcut! handler-id {:state new-state})]
+                  (uninstall-shortcut-handler! (::install-id old-state))
+                  (when-let [install-id (install-shortcut-handler! handler-id {:state new-state})]
                     (assoc new-state ::install-id install-id)))
    :will-unmount
    (fn [state]
      (when-let [install-id (::install-id state)]
-       (uninstall-shortcut! install-id))
+       (uninstall-shortcut-handler! install-id))
      state)})
 
 (defn unlisten-all []
@@ -183,7 +183,7 @@
     (state/set-state! :ui/shortcut-handler-refreshing? true)
 
     (doseq [id (keys @*installed)]
-      (uninstall-shortcut! id))
+      (uninstall-shortcut-handler! id))
     (install-shortcuts!)
     (state/pub-event! [:shortcut-handler-refreshed])
     (state/set-state! :ui/shortcut-handler-refreshing? false)))
@@ -216,7 +216,7 @@
            keystroke (:rum/local state)]
 
        (doseq [id (keys @*installed)]
-         (uninstall-shortcut! id))
+         (uninstall-shortcut-handler! id))
 
        (events/listen handler "key"
                       (fn [e]
@@ -226,13 +226,16 @@
        (assoc state ::key-record-handler handler)))
 
    :will-unmount
-   (fn [{:rum/keys [args local] :as state}]
+   (fn [{:rum/keys [args local action] :as state}]
      (let [k (first args)
            keystroke (str/trim @local)]
-       (when-not (empty? keystroke)
-         (config-handler/set-config! :shortcuts (merge
-                                                 (:shortcuts (state/get-config))
-                                                 {k keystroke}))))
+       (when (and (= @action :save)
+                  (seq keystroke))
+         (config-handler/set-config!
+           :shortcuts
+           (merge
+             (:shortcuts (state/get-config))
+             {k keystroke}))))
 
      (when-let [^js handler (::key-record-handler state)]
        (.dispose handler))

+ 20 - 25
src/main/frontend/modules/shortcut/data_helper.cljs

@@ -52,27 +52,21 @@
          shortcut)
        (mapv mod-key)))))
 
-(defn normalize-user-keyname
-  [k]
-  (let [keynames {";" "semicolon"
-                  "=" "equals"
-                  "-" "dash"
-                  "[" "open-square-bracket"
-                  "]" "close-square-bracket"
-                  "'" "single-quote"}]
-    (some-> k
-            (util/safe-lower-case)
-            (str/replace #"[;=-\[\]']" (fn [s]
-                                         (get keynames s))))))
+(defn shortcut-cmd
+  [id]
+  (get @shortcut-config/*shortcut-cmds id))
 
 ;; returns a vector to preserve order
 (defn binding-by-category [name]
-  (let [dict (->> (vals @shortcut-config/config)
-                  (apply merge)
-                  (map (fn [[k _]]
-                         {k {:binding (shortcut-binding k)}}))
-                  (into {}))]
-    (->> (shortcut-config/category name)
+  (let [dict    (->> (vals @shortcut-config/config)
+                     (apply merge)
+                     (map (fn [[k _]]
+                            {k {:binding (shortcut-binding k)}}))
+                     (into {}))
+        plugin? (= name :shortcut.category/plugins)]
+    (->> (if plugin?
+           (->> (keys dict) (filter #(str/starts-with? (str %) ":plugin.")))
+           (shortcut-config/category name))
          (mapv (fn [k] [k (k dict)])))))
 
 (defn shortcut-map
@@ -148,13 +142,14 @@
 (defn remove-shortcut [k]
   (let [repo (state/get-current-repo)
         path (config/get-repo-config-path)]
-    (when-let [content (db/get-file path)]
-      (let [result (config-handler/parse-repo-config content)
-            new-result (rewrite/update
-                        result
-                        :shortcuts
-                        #(dissoc (rewrite/sexpr %) k))
-            new-content (str new-result)]
+    (when-let [result (some-> (db/get-file path)
+                              (config-handler/parse-repo-config))]
+      (when-let [new-content (and (:shortcuts result)
+                                  (-> (rewrite/update
+                                        result
+                                        :shortcuts
+                                        #(dissoc (rewrite/sexpr %) k))
+                                      (str)))]
         (repo-config-handler/set-repo-config-state! repo new-content)
         (file/set-file-content! repo path new-content)))))
 

+ 2 - 0
src/main/frontend/quick_capture.cljs

@@ -18,6 +18,8 @@
 
 (defn quick-capture [args]
   (let [{:keys [url title content page append]} (bean/->clj args)
+        title (or title "")
+        url (or url "")
         insert-today? (get-in (state/get-config)
                               [:quick-capture-options :insert-today?]
                               false)

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

@@ -71,7 +71,7 @@
 
    ["/settings/shortcut"
     {:name :shortcut-setting
-     :view shortcut/shortcut}]
+     :view shortcut/shortcut-page}]
 
    ["/settings/zotero"
     {:name :zotero-setting

+ 15 - 0
src/main/frontend/state.cljs

@@ -760,6 +760,18 @@ Similar to re-frame subscriptions"
   (when-let [graphs (seq (get-in @state [:file-sync/remote-graphs :graphs]))]
     (some #(when (= (:GraphUUID %) (str uuid)) %) graphs)))
 
+(defn get-remote-graph-usage 
+  []
+  (when-let [graphs (seq (get-in @state [:file-sync/remote-graphs :graphs]))]
+    (->> graphs
+         (map #(hash-map :uuid (:GraphUUID %)
+                         :name (:GraphName %)
+                         :used-gbs (/ (:GraphStorageUsage %) 1024 1024 1024)
+                         :limit-gbs (/ (:GraphStorageLimit %) 1024 1024 1024)
+                         :used-percent (/ (:GraphStorageUsage %) (:GraphStorageLimit %) 0.01)))
+         (map #(assoc % :free-gbs (- (:limit-gbs %) (:used-gbs %))))
+         (vec))))
+
 (defn delete-remote-graph!
   [repo]
   (swap! state update-in [:file-sync/remote-graphs :graphs]
@@ -2098,6 +2110,9 @@ Similar to re-frame subscriptions"
       (when (seq groups)
         (storage/set :user-groups groups)))))
 
+(defn get-user-info []
+  (sub :user/info))
+
 (defn clear-user-info!
   []
   (storage/remove :user-groups))

+ 4 - 3
src/main/frontend/ui.css

@@ -100,11 +100,12 @@
   }
 
   &-overlay div {
-    background: var(--ls-tertiary-background-color);
+    background-image: linear-gradient(to bottom, var(--ls-primary-background-color), var(--ls-quaternary-background-color));
   }
 
   &-panel {
-    @apply relative rounded-md shadow-xl;
+    @apply relative rounded-md shadow-xl border;
+    border-color: var(--ls-border-color);
 
     overflow: hidden;
     background: var(--ls-secondary-background-color);
@@ -150,7 +151,7 @@
     hover:opacity-100;
 
     &-wrap {
-      @apply absolute top-0 right-0 pt-2 pr-2;
+      @apply z-10 absolute top-0 right-0 pt-2 pr-2;
     }
   }
 

+ 12 - 0
src/main/frontend/util.cljc

@@ -67,6 +67,18 @@
   [parts]
   (string/join "/" parts))
 
+(defn normalize-user-keyname
+  [k]
+  (let [keynames {";" "semicolon"
+                  "=" "equals"
+                  "-" "dash"
+                  "[" "open-square-bracket"
+                  "]" "close-square-bracket"
+                  "'" "single-quote"}]
+    (some-> (str k)
+            (string/lower-case)
+            (string/replace #"[;=-\[\]']" (fn [s]
+                                            (get keynames s))))))
 
 #?(:cljs
    (defn safe-re-find

+ 3 - 3
src/main/logseq/api.cljs

@@ -367,8 +367,8 @@
                                (if palette?
                                  (palette-handler/invoke-command palette-cmd)
                                  (action')))
-                [handler-id id shortcut-map] (update shortcut-args 2 assoc :fn dispatch-cmd)]
-            (js/console.debug :shortcut/register-shortcut [handler-id id shortcut-map])
+                [handler-id id shortcut-map] (update shortcut-args 2 assoc :fn dispatch-cmd :cmd palette-cmd)]
+            (println :shortcut/register-shortcut [handler-id id shortcut-map])
             (st/register-shortcut! handler-id id shortcut-map)))))))
 
 (defn ^:export unregister_plugin_simple_command
@@ -384,7 +384,7 @@
         (palette-handler/unregister (:id cmd))
         ;; remove keybinding commands
         (when (seq (:shortcut cmd))
-          (js/console.debug :shortcut/unregister-shortcut cmd)
+          (println :shortcut/unregister-shortcut cmd)
           (st/unregister-shortcut! (:handler-id cmd) (:id cmd)))))))
 
 (defn ^:export register_search_service

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

@@ -102,6 +102,9 @@
  :block/name "Page name"
  :page/earlier "Earlier"
  :page/copy-page-url "Copy page URL"
+ :page/illegal-page-name "Illegal page name!"
+ :page/page-already-exists "Page “{1}” already exists!"
+ :page/whiteboard-to-journal-error "Whiteboard pages cannot be renamed to journal titles!"
  :file/name "File name"
  :file/last-modified-at "Last modified at"
  :file/no-data "No data"
@@ -223,6 +226,7 @@
  :settings-page/tab-general "General"
  :settings-page/tab-editor "Editor"
  :settings-page/tab-version-control "Version control"
+ :settings-page/tab-account "Account"
  :settings-page/tab-advanced "Advanced"
  :settings-page/tab-assets "Assets"
  :settings-page/tab-features "Features"
@@ -490,6 +494,7 @@
  :shortcut.category/toggle "Toggle"
  :shortcut.category/whiteboard "Whiteboard"
  :shortcut.category/others "Others"
+ :shortcut.category/plugins "Plugins"
  :window/minimize "Minimize"
  :window/maximize "Maximize"
  :window/restore "Restore"
@@ -656,4 +661,4 @@
   :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"}}

+ 630 - 511
src/resources/dicts/ru.edn

@@ -1,520 +1,639 @@
-{:accessibility/skip-to-main-content "Перейти к основному содержимому"
- :tutorial/text #resource "tutorials/tutorial-ru.md"
- :tutorial/dummy-notes #resource "tutorials/dummy-notes-ru.md"
- :on-boarding/demo-graph "Это демонстрационный граф, изменения не будут сохранены, пока вы не откроете локальный файл."
- :on-boarding/add-graph "Добавить новый граф"
- :on-boarding/open-local-dir "Открыть локальный каталог"
- :on-boarding/new-graph-desc-1 "Logseq поддерживает Markdown и Org-mode. Вы можете открыть существующий каталог или создать новый на вашем устройства, каталог также можно назвать просто папкой. Ваши данные будут храниться только на вашем устройстве."
- :on-boarding/new-graph-desc-2 "После того, как вы укажете каталог, в нём будут созданы три папки:"
- :on-boarding/new-graph-desc-3 "/journals - хранит страницы ваших журналов"
- :on-boarding/new-graph-desc-4 "/pages - хранит остальные страницы"
- :on-boarding/new-graph-desc-5 "/logseq - хранит конфигурации, custom.css, и другие метаданные."
- :on-boarding/welcome-whiteboard-modal-title "Новый холст для ваших мыслей."
- :on-boarding/welcome-whiteboard-modal-description "Интерактивные доски - отличный инструмент для мозгового штурма и самоорганизации. Теперь вы можете разместить любые свои мысли из базы знаний или новые мысли рядом друг с другом на пространственном холсте, чтобы соединить, ассоциировать и понять по-новому"
- :on-boarding/welcome-whiteboard-modal-skip "Пропустить"
- :on-boarding/welcome-whiteboard-modal-start "Начать работу с интерактивной доской"
- :on-boarding/tour-whiteboard-home "{1} Дом для ваших досок"
- :on-boarding/tour-whiteboard-home-description "Интерактивные доски имеют свой собственный раздел в приложении, где их сразу можно увидеть, создать новые или удалить."
- :on-boarding/tour-whiteboard-new "{1} Создать новую интерактивную доску"
- :on-boarding/tour-whiteboard-new-description "Существует несколько способов создания новой интерактивной доски. Один из них всегда находится прямо здесь, на панели управления."
- :help/start "Начало работы"
- :help/about "О Logseq"
- :help/roadmap "Дорожная карта"
- :help/bug "Сообщить об ошибке"
- :help/feature "Предложить улучшение"
- :help/changelog "Список изменений"
- :help/blog "Блог Logseq"
- :help/docs "Документация"
- :help/privacy "Политика конфиденциальности"
- :help/terms "Условия"
- :help/forum-community "Форум сообщества"
- :help/awesome-logseq "Потрясающий Logseq"
- :help/shortcuts "Сочетания клавиш"
- :help/shortcuts-triggers "Действия"
- :help/shortcut "Горячие клавиши"
- :help/slash-autocomplete "Слэш - автодополнение"
- :help/block-content-autocomplete "Блок контента - автодополнение"
- :help/reference-autocomplete "Автодополнение ссылок на страницу"
- :help/block-reference "Ссылка на блок"
- :help/open-link-in-sidebar "Открыть ссылку в боковой панели"
- :more "Больше"
- :search/result-for "Искать результат для "
- :search/items "элементы"
- :search/page-names "Искать имена страниц"
- :search-item/whiteboard "Интерактивная доска"
- :search-item/page "Страница"
- :search-item/file "Файл"
- :search-item/block "Блок"
- :help/context-menu "Контекстное меню блока"
- :help/markdown-syntax "Markdown синтаксис"
- :help/org-mode-syntax "Org-mode синтаксис"
- :bold "Жирный"
- :italics "Курсив"
- :highlight "Выделение"
- :strikethrough "Перечёркнутый"
- :code "Код"
- :untitled "Без названия"
- :right-side-bar/help "Справка"
- :right-side-bar/switch-theme "Тема"
- :right-side-bar/contents "Содержание"
- :right-side-bar/page-graph "Граф страницы"
- :right-side-bar/history "(Dev) Отменить/Повторить историю"
- :right-side-bar/block-ref "Ссылка на блок"
- :right-side-bar/graph-view "Визуальный граф"
- :right-side-bar/all-pages "Все страницы"
- :right-side-bar/whiteboards "Интерактивные доски"
- :right-side-bar/flashcards "Карточки"
- :right-side-bar/new-page "Новая страница"
- :right-side-bar/show-journals "Показать журналы"
- :right-side-bar/separator "Изменение размера правой боковой панели"
- :left-side-bar/journals "Журналы"
- :left-side-bar/create "Создать"
- :left-side-bar/new-page "Новая страница"
- :left-side-bar/new-whiteboard "Новая интерактивная доска"
- :left-side-bar/nav-favorites "Избранное"
- :left-side-bar/nav-recent-pages "Недавнее"
- :page/something-went-wrong "Что-то пошло не так"
- :page/logseq-is-having-a-problem "У Logseq возникла проблема. Чтобы попытаться вернуть его в рабочее состояние, пожалуйста, попробуйте выполнить следующие безопасные шаги по порядку:"
- :page/step "Шаг {1}"
- :page/try "Попробовать"
- :page/presentation-mode "Презентация"
- :page/delete-confirmation "Вы уверены, что хотите удалить эту страницу и ее файл?"
- :page/open-in-finder "Открыть в каталоге"
- :page/open-with-default-app "Открыть в приложении по умолчанию"
- :page/make-public "Сделать доступной для публикации"
- :page/version-history "Просмотр истории страницы"
- :page/open-backup-directory "Открыть каталог резервных копий"
- :page/make-private "Сделать приватной"
- :page/delete "Удалить страницу"
- :page/add-to-favorites "Добавить в Избранное"
- :page/unfavorite "Удалить из Избранного"
- :page/show-journals "Показать журналы"
- :page/show-whiteboards "Показать интерактивные доски"
- :block/name "Имя страницы"
- :page/earlier "Ранее"
- :page/copy-page-url "Копировать URL страницы"
- :file/name "Имя файла"
- :file/last-modified-at "Последнее изменение"
- :file/no-data "Нет данных"
- :file/format-not-supported "Расширение .{1} не поддерживается."
- :file/validate-existing-file-error "Страница уже существует с другим файлом: {1}, текущий файл: {2}. Пожалуйста, оставьте только один из них и переиндексируйте ваш граф."
- :file-rn/re-index "Настоятельно рекомендуется повторить индексацию после переименования файлов и синхронизации на других устройствах."
- :file-rn/need-action "Предлагается выполнить действия по переименованию файлов, чтобы они соответствовали новому формату. Переиндексация требуется на всех устройствах после синхронизации переименованных файлов."
- :file-rn/or-select-actions " или переименовать файлы ниже по отдельности, затем "
- :file-rn/or-select-actions-2 ". Эти действия будут недоступны после закрытия этой панели."
- :file-rn/legend "🟢 Необязательные действия по переименованию; 🟡 Действие по переименованию, необходимое для предотвращения изменения заголовка; 🔴 Обязательное изменение."
- :file-rn/close-panel "Закрыть панель"
- :file-rn/select-format "(Опция режима разработчика. Опасно!) Выберите формат имени файла"
- :file-rn/rename "переименовать файл \"{1}\" в \"{2}\""
- :file-rn/apply-rename "Применить операцию переименования файла"
- :file-rn/suggest-rename "Требуется действие: "
- :file-rn/otherwise-breaking "Или заголовок станет"
- :file-rn/no-action "Отлично! Дальнейших действий не требуется."
- :file-rn/confirm-proceed "Обновить формат!"
- :file-rn/select-confirm-proceed "Dev: формат записи"
- :file-rn/unreachable-title "Внимание! Имя страницы станет {1} при текущем формате имени файла, если `title::` не задано вручную"
- :file-rn/optional-rename "Предложение: "
- :file-rn/format-deprecated "В настоящее время вы используете устаревший формат. Настоятельно рекомендуется обновить формат до последней версии. Пожалуйста, создайте резервную копию данных и закройте клиенты Logseq на других устройствах перед началом операции."
- :file-rn/filename-desc-1 "Этот параметр определяет способ сохранения страницы в файл. Logseq сохраняет страницу в файл с таким же именем."
- :file-rn/filename-desc-2 "Некоторые символы, такие как \"/\" или \"?\" недопустимы для имени файла."
- :file-rn/filename-desc-3 "Logseq заменяет недопустимые символы их эквивалентом в кодировке URL, чтобы сделать их допустимыми (например, \"?\" становится \"%3F\")."
- :file-rn/filename-desc-4 "Разделитель пространства имен \"/\" также заменяется на \"___\" (тройное подчеркивание) из эстетических соображений."
- :file-rn/instruct-1 "Этот процесс обновления формата имен файлов состоит из двух этапов:"
- :file-rn/instruct-2 "1. Нажмите "
- :file-rn/instruct-3 "2. Следуйте приведенным ниже инструкциям, чтобы переименовать файлы в новый формат:"
- :page/created-at "Создан в"
- :page/updated-at "Обновлен в"
- :page/backlinks "Обратные ссылки"
- :linked-references/filter-search "Поиск в связанных страницах"
- :editor/block-search "Поиск блока"
- :text/image "Изображение"
- :asset/show-in-folder "Показать изображение в папке"
- :asset/open-in-browser "Открыть изображение в браузере"
- :asset/delete "Удалить изображение"
- :asset/copy "Копировать изображение"
- :asset/maximize "Увеличить изображение"
- :asset/confirm-delete "Вы уверены, что хотите удалить {1}?"
- :asset/physical-delete "Также удалить файл (обратите внимание, что его нельзя будет восстановить)"
- :color/gray "Серый"
- :color/red "Красный"
- :color/yellow "Желтый"
- :color/green "Зеленый"
- :color/blue "Синий"
- :color/purple "Фиолетовый"
- :color/pink "Розовый"
- :editor/copy "Копировать"
- :editor/cut "Вырезать"
- :editor/expand-block-children "Раскрыть всё"
- :editor/collapse-block-children "Свернуть всё"
- :editor/delete-selection "Удалить выбранные блоки"
- :editor/cycle-todo "Изменить статус TODO текущего элемента"
- :dev/show-page-data "(Dev) Показать данные страницы"
- :dev/show-block-data "(Dev) Показать данные блока"
- :dev/show-block-ast "(Dev) Показать AST блока"
- :dev/show-page-ast "(Dev) Показать AST страницы"
- :content/copy-export-as "Копировать / Экспортировать как.."
- :content/copy-block-url "Копировать URL блока"
- :content/copy-block-ref "Копировать ссылку блока"
- :content/copy-block-emebed "Копировать встроенный блок"
- :content/copy-ref "Скопировать эту ссылку"
- :content/delete-ref "Удалить эту ссылку"
- :content/replace-with-text "Заменить на текст"
- :content/replace-with-embed "Заменить на встроенный элемент"
- :content/open-in-sidebar "Открыть в боковой панели"
- :content/click-to-edit "Нажмите для редактирования"
- :context-menu/make-a-flashcard "Создать карточку"
- :context-menu/toggle-number-list "Переключить номерной список"
- :context-menu/preview-flashcard "Предварительный просмотр карточки"
- :context-menu/make-a-template "Создать шаблон"
- :context-menu/input-template-name "Как назовём шаблон?"
- :context-menu/template-include-parent-block "Включить родительский блок в шаблон?"
- :context-menu/template-exists-warning "Шаблон уже существует!"
- :settings-page/git-confirm "Необходимо перезапустить приложение после изменения настроек Git."
- :settings-page/git-switcher-label "Включить автокоммит в Git"
- :settings-page/git-commit-delay "Задержка автокоммита Git в секундах"
- :settings-page/edit-config-edn "Редактировать config.edn"
- :settings-page/edit-global-config-edn "Редактировать глобальный config.edn"
- :settings-page/edit-custom-css "Редактировать custom.css"
- :settings-page/edit-export-css "Редактировать export.css"
- :settings-page/edit-setting "Редактировать"
- :settings-page/custom-configuration "Настройки пользователя"
- :settings-page/custom-global-configuration "Глобальные настройки пользователя"
- :settings-page/custom-theme "Тема пользователя"
- :settings-page/export-theme "Экспорт темы"
- :settings-page/show-brackets "Показывать скобки"
- :settings-page/spell-checker "Проверка орфографии"
- :settings-page/auto-updater "Обновлять автоматически"
- :settings-page/disable-sentry "Отправлять данные использования и диагностики в Logseq"
- :settings-page/disable-sentry-desc "Logseq никогда не будет собирать вашу локальную базу данных графов или продавать ваши данные"
- :settings-page/preferred-outdenting "Логические отступы"
- :settings-page/show-full-blocks "Показать все строки в ссылке на блок"
- :settings-page/auto-expand-block-refs "Автоматически раскрывать ссылки на блок при увеличении масштаба"
- :settings-page/custom-date-format "Формат даты"
- :settings-page/custom-date-format-warning "Требуется переиндексация! Существующие ссылки на журналы будут нарушены!"
- :settings-page/preferred-file-format "Предпочитаемый формат файлов"
- :settings-page/preferred-workflow "Предпочтительный рабочий процесс"
- :settings-page/preferred-pasting-file "Предпочтительнее вставлять файл"
- :settings-page/enable-shortcut-tooltip "Всплывающие подсказки горячих клавиш"
- :settings-page/enable-timetracking "Отслеживание времени"
- :settings-page/enable-tooltip "Всплывающие подсказки"
- :settings-page/enable-journals "Журналы"
- :settings-page/enable-all-pages-public "При публикации все страницы становятся публичными"
- :settings-page/customize-shortcuts "Сочетания клавиш"
- :settings-page/shortcut-settings "Настроить горячие клавиши"
- :settings-page/home-default-page "Установить домашнюю страницу по умолчанию"
- :settings-page/enable-block-time "Временные метки блока"
- :settings-page/clear-cache "Очистить кэш"
- :settings-page/clear "Очистить"
- :settings-page/clear-cache-warning "Очистка кэша приведет к удалению открытых графов. Вы потеряете несохраненные изменения."
- :settings-page/developer-mode "Режим разработчика"
- :settings-page/developer-mode-desc "Режим разработчика помогает людям, участвующим в разработке расширений, более эффективно тестировать свои интеграции с Logseq"
- :settings-page/current-version "Версия"
- :settings-page/tab-general "Общие"
- :settings-page/tab-editor "Редактор"
- :settings-page/tab-version-control "Управление версиями"
- :settings-page/tab-advanced "Продвинутые"
- :settings-page/tab-assets "Объекты"
- :settings-page/tab-features "Особенные"
- :settings-page/plugin-system "Расширения"
- :settings-page/enable-flashcards "Карточки"
- :settings-page/network-proxy "Прокси-сервер"
- :settings-page/filename-format "Формат имени файла"
- :settings-page/alpha-features "Альфа-функции"
- :settings-page/beta-features "Бета-функции"
- :settings-page/login-prompt "Чтобы получить доступ к новым функциям раньше других, вы должны быть открытым коллективным спонсором или сторонником Logseq и, следовательно, войти в систему первым."
- :settings-page/sync "Синхронизация"
- :settings-page/enable-whiteboards "Интерактивные доски"
- :yes "Да"
+{:accessibility/skip-to-main-content                   "Перейти к основному содержимому"
+ :tutorial/text #resource                              "tutorials/tutorial-ru.md"
+ :tutorial/dummy-notes #resource                       "tutorials/dummy-notes-ru.md"
+ :on-boarding/demo-graph                               "Это демонстрационный граф, изменения не будут сохранены, пока вы не откроете локальный файл."
+ :on-boarding/add-graph                                "Добавить новый граф"
+ :on-boarding/open-local-dir                           "Открыть локальный каталог"
+ :on-boarding/new-graph-desc-1                         "Logseq поддерживает Markdown и Org-mode. Вы можете открыть существующий каталог или создать новый на вашем устройства, каталог также можно назвать просто папкой. Ваши данные будут храниться только на вашем устройстве."
+ :on-boarding/new-graph-desc-2                         "После того, как вы укажете каталог, в нём будут созданы три папки:"
+ :on-boarding/new-graph-desc-3                         "/journals - хранит страницы ваших журналов"
+ :on-boarding/new-graph-desc-4                         "/pages - хранит остальные страницы"
+ :on-boarding/new-graph-desc-5                         "/logseq - хранит конфигурации, custom.css, и другие метаданные."
+ :on-boarding/welcome-whiteboard-modal-title           "Новый холст для ваших мыслей."
+ :on-boarding/welcome-whiteboard-modal-description     "Интерактивные доски - отличный инструмент для мозгового штурма и самоорганизации. Теперь вы можете разместить любые свои мысли из базы знаний или новые мысли рядом друг с другом на пространственном холсте, чтобы соединить, ассоциировать и понять по-новому"
+ :on-boarding/welcome-whiteboard-modal-skip            "Пропустить"
+ :on-boarding/welcome-whiteboard-modal-start           "Начать работу с интерактивной доской"
+ :on-boarding/tour-whiteboard-home                     "{1} Дом для ваших интерактивных досок"
+ :on-boarding/tour-whiteboard-home-description         "Интерактивные доски имеют свой собственный раздел в приложении, где их сразу можно увидеть, создать новые или удалить."
+ :on-boarding/tour-whiteboard-new                      "{1} Создать новую интерактивную доску"
+ :on-boarding/tour-whiteboard-new-description          "Существует несколько способов создания новой интерактивной доски. Один из них всегда находится прямо здесь, на панели управления."
+ :help/title-usage                                     "Использование"
+ :help/title-community                                 "Сообщество"
+ :help/title-development                               "Разработка"
+ :help/title-about                                     "О нас"
+ :help/title-terms                                     "Условия и положения"
+ :help/start                                           "Начало работы"
+ :help/about                                           "О Logseq"
+ :help/roadmap                                         "Дорожная карта"
+ :help/bug                                             "Сообщить об ошибке"
+ :help/feature                                         "Предложить улучшение"
+ :help/changelog                                       "Список изменений"
+ :help/blog                                            "Блог Logseq"
+ :help/docs                                            "Документация"
+ :help/privacy                                         "Политика конфиденциальности"
+ :help/terms                                           "Условия"
+ :help/forum-community                                 "Форум сообщества"
+ :help/awesome-logseq                                  "Потрясающий Logseq"
+ :help/shortcuts                                       "Сочетания клавиш"
+ :help/shortcuts-triggers                              "Действия"
+ :help/shortcut                                        "Горячие клавиши"
+ :help/slash-autocomplete                              "Слэш - автодополнение"
+ :help/block-content-autocomplete                      "Блок контента - автодополнение"
+ :help/reference-autocomplete                          "Автодополнение ссылок на страницу"
+ :help/block-reference                                 "Ссылка на блок"
+ :help/open-link-in-sidebar                            "Открыть ссылку на боковой панели"
+ :more                                                 "Больше"
+ :search/result-for                                    "Искать результат для "
+ :search/items                                         "элементы"
+ :search/page-names                                    "Искать имена страниц"
+ :search/recent                                        "История поиска"
+ :search/blocks-in-page                                "Поиск блоков на странице:"
+ :search/cache-outdated                                "Кэш устарел. Пожалуйста, нажмите кнопку 'Переиндексировать' в выпадающем меню графов."
+ :search-item/whiteboard                               "Интерактивная доска"
+ :search-item/page                                     "Страница"
+ :search-item/file                                     "Файл"
+ :search-item/block                                    "Блок"
+ :help/context-menu                                    "Контекстное меню блока"
+ :help/markdown-syntax                                 "Markdown синтаксис"
+ :help/org-mode-syntax                                 "Org-mode синтаксис"
+ :bold                                                 "Жирный"
+ :italics                                              "Курсив"
+ :highlight                                            "Выделение"
+ :strikethrough                                        "Перечёркнутый"
+ :code                                                 "Код"
+ :untitled                                             "Без названия"
+ :right-side-bar/help                                  "Справка"
+ :right-side-bar/switch-theme                          "Тема"
+ :right-side-bar/contents                              "Содержание"
+ :right-side-bar/page-graph                            "Граф страницы"
+ :right-side-bar/history                               "(Dev) Отменить/Повторить историю"
+ :right-side-bar/block-ref                             "Ссылка на блок"
+ :right-side-bar/graph-view                            "Визуальный граф"
+ :right-side-bar/all-pages                             "Все страницы"
+ :right-side-bar/whiteboards                           "Интерактивные доски"
+ :right-side-bar/flashcards                            "Карточки"
+ :right-side-bar/new-page                              "Новая страница"
+ :right-side-bar/show-journals                         "Показать журналы"
+ :right-side-bar/separator                             "Изменение размера правой боковой панели"
+ :right-side-bar/toggle-right-sidebar                  "Переключить правую панель"
+ :left-side-bar/journals                               "Журналы"
+ :left-side-bar/create                                 "Создать"
+ :left-side-bar/new-page                               "Новая страница"
+ :left-side-bar/new-whiteboard                         "Новая интерактивная доска"
+ :left-side-bar/nav-favorites                          "Избранное"
+ :left-side-bar/nav-recent-pages                       "Недавнее"
+ :page/something-went-wrong                            "Что-то пошло не так"
+ :page/logseq-is-having-a-problem                      "У Logseq возникла проблема. Чтобы попытаться вернуть его в рабочее состояние, пожалуйста, попробуйте выполнить следующие безопасные шаги по порядку:"
+ :page/step                                            "Шаг {1}"
+ :page/try                                             "Попробовать"
+ :page/presentation-mode                               "Презентация"
+ :page/delete-confirmation                             "Вы уверены, что хотите удалить эту страницу и её файл(ы)?"
+ :page/open-in-finder                                  "Открыть в каталоге"
+ :page/open-with-default-app                           "Открыть в приложении по умолчанию"
+ :page/make-public                                     "Сделать доступной для публикации"
+ :page/version-history                                 "Просмотр истории страницы"
+ :page/open-backup-directory                           "Открыть каталог резервных копий"
+ :page/make-private                                    "Сделать приватной"
+ :page/delete                                          "Удалить страницу"
+ :page/add-to-favorites                                "Добавить в Избранное"
+ :page/unfavorite                                      "Удалить из Избранного"
+ :page/show-journals                                   "Показать журналы"
+ :page/show-whiteboards                                "Показать интерактивные доски"
+ :block/name                                           "Имя страницы"
+ :page/earlier                                         "Ранее"
+ :page/copy-page-url                                   "Копировать URL страницы"
+ :file/name                                            "Имя файла"
+ :file/last-modified-at                                "Последнее изменение"
+ :file/no-data                                         "Нет данных"
+ :file/format-not-supported                            "Расширение .{1} не поддерживается."
+ :file/validate-existing-file-error                    "Страница уже существует с другим файлом: {1}, текущий файл: {2}. Пожалуйста, оставьте только один из них и переиндексируйте ваш граф."
+ :file-rn/re-index                                     "Настоятельно рекомендуется выполнить переиндексацию после переименования файлов и синхронизации на других устройствах."
+ :file-rn/need-action                                  "Предлагается выполнить действия по переименованию файлов, чтобы они соответствовали новому формату. Переиндексация требуется на всех устройствах после синхронизации переименованных файлов."
+ :file-rn/or-select-actions                            " или переименовать файлы ниже по отдельности, затем "
+ :file-rn/or-select-actions-2                          ". Эти действия будут недоступны после закрытия этой панели."
+ :file-rn/legend                                       "🟢 Необязательные действия по переименованию; 🟡 Действие по переименованию, необходимое для предотвращения изменения заголовка; 🔴 Обязательное изменение."
+ :file-rn/close-panel                                  "Закрыть панель"
+ :file-rn/select-format                                "(Опция режима разработчика. Опасно!) Выберите формат имени файла"
+ :file-rn/rename                                       "Переименовать файл \"{1}\" в \"{2}\""
+ :file-rn/apply-rename                                 "Применить операцию переименования файла"
+ :file-rn/suggest-rename                               "Требуется действие: "
+ :file-rn/otherwise-breaking                           "Или заголовок станет"
+ :file-rn/no-action                                    "Отлично! Дальнейших действий не требуется."
+ :file-rn/confirm-proceed                              "Обновить формат!"
+ :file-rn/select-confirm-proceed                       "Dev: формат записи"
+ :file-rn/unreachable-title                            "Внимание! Имя страницы станет {1} при текущем формате имени файла, если `title::` не задано вручную"
+ :file-rn/optional-rename                              "Предложение: "
+ :file-rn/format-deprecated                            "В настоящее время вы используете устаревший формат. Настоятельно рекомендуется обновить формат до последней версии. Пожалуйста, создайте резервную копию данных и закройте клиенты Logseq на других устройствах перед началом операции."
+ :file-rn/filename-desc-1                              "Этот параметр определяет способ сохранения страницы в файл. Logseq сохраняет страницу в файл с таким же именем."
+ :file-rn/filename-desc-2                              "Некоторые символы, такие как \"/\" или \"?\" недопустимы для имени файла."
+ :file-rn/filename-desc-3                              "Logseq заменяет недопустимые символы их эквивалентом в кодировке URL, чтобы сделать их допустимыми (например, \"?\" становится \"%3F\")."
+ :file-rn/filename-desc-4                              "Разделитель пространства имен \"/\" также заменяется на \"___\" (тройное подчеркивание) из эстетических соображений."
+ :file-rn/instruct-1                                   "Этот процесс обновления формата имен файлов состоит из двух этапов:"
+ :file-rn/instruct-2                                   "1. Нажмите "
+ :file-rn/instruct-3                                   "2. Следуйте приведенным ниже инструкциям, чтобы переименовать файлы в новый формат:"
+ :page/created-at                                      "Создано"
+ :page/updated-at                                      "Обновлено"
+ :page/backlinks                                       "Обратные ссылки"
+ :linked-references/filter-search                      "Поиск в связанных страницах"
+ :editor/block-search                                  "Поиск блока"
+ :text/image                                           "Изображение"
+ :asset/show-in-folder                                 "Показать изображение в папке"
+ :asset/open-in-browser                                "Открыть изображение в браузере"
+ :asset/delete                                         "Удалить изображение"
+ :asset/copy                                           "Копировать изображение"
+ :asset/maximize                                       "Увеличить изображение"
+ :asset/confirm-delete                                 "Вы уверены, что хотите удалить {1}?"
+ :asset/physical-delete                                "Также удалить файл (обратите внимание, что его нельзя будет восстановить)"
+ :color/gray                                           "Серый"
+ :color/red                                            "Красный"
+ :color/yellow                                         "Желтый"
+ :color/green                                          "Зеленый"
+ :color/blue                                           "Синий"
+ :color/purple                                         "Фиолетовый"
+ :color/pink                                           "Розовый"
+ :editor/copy                                          "Копировать"
+ :editor/cut                                           "Вырезать"
+ :editor/expand-block-children                         "Раскрыть всё"
+ :editor/collapse-block-children                       "Свернуть всё"
+ :editor/delete-selection                              "Удалить выбранные блоки"
+ :editor/cycle-todo                                    "Изменить статус TODO текущего элемента"
+ :dev/show-page-data                                   "(Dev) Показать данные страницы"
+ :dev/show-block-data                                  "(Dev) Показать данные блока"
+ :dev/show-block-ast                                   "(Dev) Показать AST блока"
+ :dev/show-page-ast                                    "(Dev) Показать AST страницы"
+ :content/copy-export-as                               "Копировать / Экспортировать как..."
+ :content/copy-block-url                               "Копировать URL блока"
+ :content/copy-block-ref                               "Копировать ссылку блока"
+ :content/copy-block-emebed                            "Копировать встроенный блок"
+ :content/copy-ref                                     "Скопировать эту ссылку"
+ :content/delete-ref                                   "Удалить эту ссылку"
+ :content/replace-with-text                            "Заменить на текст"
+ :content/replace-with-embed                           "Заменить на встроенный элемент"
+ :content/open-in-sidebar                              "Открыть на боковой панели"
+ :content/click-to-edit                                "Нажмите для редактирования"
+ :context-menu/make-a-flashcard                        "Создать карточку"
+ :context-menu/toggle-number-list                      "Переключить формат нумерованного списка"
+ :context-menu/preview-flashcard                       "Предварительный просмотр карточки"
+ :context-menu/make-a-template                         "Создать шаблон"
+ :context-menu/input-template-name                     "Как назовём шаблон?"
+ :context-menu/template-include-parent-block           "Включить родительский блок в шаблон?"
+ :context-menu/template-exists-warning                 "Шаблон уже существует!"
+ :settings-page/git-confirm                            "Необходимо перезапустить приложение после изменения настроек Git."
+ :settings-page/git-switcher-label                     "Включить автокоммит в Git"
+ :settings-page/git-commit-delay                       "Задержка автокоммита Git в секундах"
+ :settings-page/edit-config-edn                        "Редактировать config.edn"
+ :settings-page/edit-global-config-edn                 "Редактировать глобальный config.edn"
+ :settings-page/edit-custom-css                        "Редактировать custom.css"
+ :settings-page/edit-export-css                        "Редактировать export.css"
+ :settings-page/edit-setting                           "Редактировать"
+ :settings-page/custom-configuration                   "Настройки пользователя"
+ :settings-page/custom-global-configuration            "Глобальные настройки пользователя"
+ :settings-page/custom-theme                           "Тема пользователя"
+ :settings-page/export-theme                           "Экспорт темы"
+ :settings-page/show-brackets                          "Показывать скобки"
+ :settings-page/spell-checker                          "Проверка орфографии"
+ :settings-page/auto-updater                           "Обновлять автоматически"
+ :settings-page/disable-sentry                         "Отправлять данные использования и диагностики в Logseq"
+ :settings-page/disable-sentry-desc                    "Logseq никогда не будет собирать вашу локальную базу данных графов или продавать ваши данные"
+ :settings-page/preferred-outdenting                   "Логические отступы"
+ :settings-page/show-full-blocks                       "Показать все строки в ссылке на блок"
+ :settings-page/auto-expand-block-refs                 "Автоматически раскрывать ссылки на блок при увеличении масштаба"
+ :settings-page/custom-date-format                     "Формат даты"
+ :settings-page/custom-date-format-warning             "Требуется переиндексация! Существующие ссылки на журналы будут нарушены!"
+ :settings-page/preferred-pasting-file-hint            "Если функция включена, то при вставке изображения из интернета оно будет загружено и вставлено. При отключении будет вставлена ссылка на изображение."
+ :settings-page/preferred-file-format                  "Предпочитаемый формат файлов"
+ :settings-page/preferred-workflow                     "Предпочтительный рабочий процесс"
+ :settings-page/preferred-pasting-file                 "Предпочтительнее вставлять файл"
+ :settings-page/enable-shortcut-tooltip                "Всплывающие подсказки горячих клавиш"
+ :settings-page/enable-timetracking                    "Отслеживание времени"
+ :settings-page/enable-tooltip                         "Всплывающие подсказки"
+ :settings-page/enable-journals                        "Журналы"
+ :settings-page/enable-all-pages-public                "При публикации все страницы становятся публичными"
+ :settings-page/customize-shortcuts                    "Сочетания клавиш"
+ :settings-page/shortcut-settings                      "Настроить горячие клавиши"
+ :settings-page/home-default-page                      "Установить домашнюю страницу по умолчанию"
+ :settings-page/enable-block-time                      "Временные метки блока"
+ :settings-page/clear-cache                            "Очистить кэш"
+ :settings-page/clear                                  "Очистить"
+ :settings-page/clear-cache-warning                    "Очистка кэша приведет к удалению открытых графов. Вы потеряете несохраненные изменения."
+ :settings-page/developer-mode                         "Режим разработчика"
+ :settings-page/developer-mode-desc                    "Режим разработчика помогает людям, участвующим в разработке расширений, более эффективно тестировать свои интеграции с Logseq"
+ :settings-page/current-version                        "Версия"
+ :settings-page/tab-general                            "Общие"
+ :settings-page/tab-editor                             "Редактор"
+ :settings-page/tab-version-control                    "Управление версиями"
+ :settings-page/tab-advanced                           "Продвинутые"
+ :settings-page/tab-assets                             "Объекты"
+ :settings-page/tab-features                           "Особенные"
+ :settings-page/plugin-system                          "Расширения"
+ :settings-page/enable-flashcards                      "Карточки"
+ :settings-page/network-proxy                          "Прокси-сервер"
+ :settings-page/filename-format                        "Формат имени файла"
+ :settings-page/alpha-features                         "Альфа-функции"
+ :settings-page/beta-features                          "Бета-функции"
+ :settings-page/login-prompt                           "Чтобы получить доступ к новым функциям раньше других, вы должны быть открытым коллективным спонсором или сторонником Logseq и, следовательно, войти в систему первым."
+ :settings-page/sync                                   "Синхронизация"
+ :settings-page/enable-whiteboards                     "Интерактивные доски"
+ :settings-page/native-titlebar                        "Встроенная строка заголовка"
+ :settings-page/native-titlebar-desc                   "Включить встроенную строку заголовка окна в Windows и Linux."
+ :yes                                                  "Да"
 
- :submit "Подтвердить"
- :cancel "Отмена"
- :close "Закрыть"
- :delete "Удалить"
- :save "Сохранить"
- :type "Тип"
- :host "Хост"
- :port "Порт"
- :re-index "Переиндексация"
- :re-index-detail "Переиндексировать граф"
- :re-index-multiple-windows-warning "Перед переиндексацией этого графа необходимо закрыть другие окна."
- :re-index-discard-unsaved-changes-warning "Переиндексация удалит текущий граф, а затем снова обработает все файлы в том виде, в каком они в данный момент хранятся на диске. Вы потеряете несохраненные изменения, и это может занять некоторое время. Продолжить?"
- :open-new-window "Новое окно"
- :sync-from-local-files "Обновить"
- :sync-from-local-files-detail "Импортировать изменения из локальных файлов"
- :sync-from-local-changes-detected "При обновлении будут найдены и обработаны файлы, изменённые на диске и отличающиеся от текущего содержимого страниц Logseq. Продолжить?"
+ :submit                                               "Подтвердить"
+ :cancel                                               "Отмена"
+ :close                                                "Закрыть"
+ :delete                                               "Удалить"
+ :save                                                 "Сохранить"
+ :type                                                 "Тип"
+ :host                                                 "Хост"
+ :port                                                 "Порт"
+ :re-index                                             "Переиндексация"
+ :re-index-detail                                      "Переиндексировать граф"
+ :re-index-multiple-windows-warning                    "Перед переиндексацией этого графа необходимо закрыть другие окна."
+ :re-index-discard-unsaved-changes-warning             "Переиндексация удалит текущий граф, а затем снова обработает все файлы в том виде, в каком они в данный момент хранятся на диске. Вы потеряете несохраненные изменения, и это может занять некоторое время. Продолжить?"
+ :open-new-window                                      "Новое окно"
+ :sync-from-local-files                                "Обновить"
+ :sync-from-local-files-detail                         "Импортировать изменения из локальных файлов"
+ :sync-from-local-changes-detected                     "При обновлении будут найдены и обработаны файлы, изменённые на диске и отличающиеся от текущего содержимого страниц Logseq. Продолжить?"
+ 
+ :search/publishing                                    "Искать"
+ :search                                               "Искать или создать страницу"
+ :whiteboard/link-whiteboard-or-block                  "Ссылка на доску/страницу/блок"
+ :whiteboard/align-left                                "Выровнять по левому краю"
+ :whiteboard/align-center-horizontally                 "Центрировать по горизонтали"
+ :whiteboard/align-right                               "Выровнять по правому краю"
+ :whiteboard/distribute-horizontally                   "Распределить по горизонтали"
+ :whiteboard/align-top                                 "Выровнять по верхнему краю"
+ :whiteboard/align-center-vertically                   "Центрировать по вертикали"
+ :whiteboard/align-bottom                              "Выровнять по нижнему краю"
+ :whiteboard/distribute-vertically                     "Распределить по вертикали"
+ :whiteboard/pack-into-rectangle                       "Упаковать в прямоугольник"
+ :whiteboard/zoom-to-fit                               "Масштабировать, показав все элементы"
+ :whiteboard/ungroup                                   "Разгруппировать"
+ :whiteboard/group                                     "Группировать"
+ :whiteboard/cut                                       "Вырезать"
+ :whiteboard/copy                                      "Копировать"
+ :whiteboard/paste                                     "Вставить"
+ :whiteboard/paste-as-link                             "Вставить как ссылку"
+ :whiteboard/export                                    "Экспорт"
+ :whiteboard/select-all                                "Выбрать всё"
+ :whiteboard/deselect-all                              "Отменить всё"
+ :whiteboard/lock                                      "Заблокировать"
+ :whiteboard/unlock                                    "Разблокировать"
+ :whiteboard/delete                                    "Удалить"
+ :whiteboard/flip-horizontally                         "Отразить по горизонтали"
+ :whiteboard/flip-vertically                           "Отразить по вертикали"
+ :whiteboard/move-to-front                             "Переместить на передний план"
+ :whiteboard/move-to-back                              "Переместить на задний план"
+ :whiteboard/dev-print-shape-props                     "(Dev) Вывести свойства фигуры"
+ :whiteboard/auto-resize                               "Автоматическое изменение размера"
+ :whiteboard/expand                                    "Раскрыть"
+ :whiteboard/collapse                                  "Свернуть"
+ :whiteboard/website-url                               "URL-адрес сайта"
+ :whiteboard/reload                                    "Перезагрузить"
+ :whiteboard/open-website-url                          "Открыть URL-адрес сайта"
+ :whiteboard/youtube-url                               "URL-адрес YouTube"
+ :whiteboard/open-youtube-url                          "Открыть URL-адрес YouTube"
+ :whiteboard/twitter-url                               "URL-адрес Twitter"
+ :whiteboard/open-twitter-url                          "Открыть URL-адрес Twitter"
+ :whiteboard/fill                                      "Заливка"
+ :whiteboard/stroke-type                               "Тип обводки"
+ :whiteboard/arrow-head                                "Тип указателя"
+ :whiteboard/bold                                      "Жирный"
+ :whiteboard/italic                                    "Курсив"
+ :whiteboard/undo                                      "Отменить"
+ :whiteboard/redo                                      "Вернуть"
+ :whiteboard/zoom-in                                   "Увеличить"
+ :whiteboard/zoom-out                                  "Уменьшить"
+ :whiteboard/select                                    "Выделение"
+ :whiteboard/pan                                       "Рука"
+ :whiteboard/add-block-or-page                         "Добавить блок или страницу"
+ :whiteboard/draw                                      "Рисовать"
+ :whiteboard/highlight                                 "Маркер"
+ :whiteboard/eraser                                    "Ластик"
+ :whiteboard/connector                                 "Соединительная стрелка"
+ :whiteboard/text                                      "Текст"
+ :whiteboard/color                                     "Цвет"
+ :whiteboard/select-custom-color                       "Выбрать пользовательский цвет"
+ :whiteboard/opacity                                   "Непрозрачность"
+ :whiteboard/extra-small                               "Очень маленький"
+ :whiteboard/small                                     "Маленький"
+ :whiteboard/medium                                    "Средний"
+ :whiteboard/large                                     "Большой"
+ :whiteboard/extra-large                               "Очень большой"
+ :whiteboard/huge                                      "Огромный"
+ :whiteboard/scale-level                               "Изменение размера"
+ :whiteboard/rectangle                                 "Прямоугольник"
+ :whiteboard/circle                                    "Круг"
+ :whiteboard/triangle                                  "Треугольник"
+ :whiteboard/shape                                     "Фигура"
+ :whiteboard/open-page                                 "Открыть страницу" 
+ :whiteboard/open-page-in-sidebar                      "Открыть страницу на боковой панели"
+ :whiteboard/remove-link                               "Удалить ссылку"
+ :whiteboard/link                                      "Ссылка"
+ :whiteboard/references                                "Связанные страницы"
+ :whiteboard/link-to-any-page-or-block                 "Ссылка на любую страницу или блок"
+ :whiteboard/start-typing-to-search                    "Начните вводить текст для поиска..."
+ :whiteboard/new-block-no-colon                        "Новый блок"
+ :whiteboard/new-block                                 "Новый блок:"
+ :whiteboard/new-page                                  "Новая страница:"
+ :whiteboard/new-whiteboard                            "Новая интерактивная доска"
+ :whiteboard/search-only-blocks                        "Поиск только по блокам"
+ :whiteboard/search-only-pages                         "Поиск только по страницам"
+ :whiteboard/cache-outdated                            "Кэш устарел. Пожалуйста, нажмите кнопку 'Переиндексировать' в выпадающем меню графов."
+ :whiteboard/shape-quick-links                         "Быстрые ссылки фигуры"
+ :page-search                                          "Искать на текущей странице"
+ :graph-search                                         "Искать граф"
+ :home                                                 "Домой"
+ :whiteboard                                           "Интерактивная доска"
+ :whiteboards                                          "Интерактивные доски"
+ :new-graph                                            "Добавить новый граф"
+ :graph                                                "Граф"
+ :graph/persist                                        "Logseq синхронизирует внутреннее состояние, пожалуйста, подождите несколько секунд."
+ :graph/persist-error                                  "Не удалось выполнить внутреннюю синхронизацию состояния."
+ :graph/save                                           "Сохранение..."
+ :graph/save-success                                   "Успешно сохранено"
+ :graph/save-error                                     "Сохранить не удалось"
+ :graph/all-graphs                                     "Все графы"
+ :export                                               "Экспорт"
+ :export-graph                                         "Экспортировать граф"
+ :export-page                                          "Экспортировать страницу"
+ :export-markdown                                      "Экспортировать как стандартный Markdown (без свойств блока)"
+ :export-opml                                          "Экспортировать как OPML"
+ :export-public-pages                                  "Экспорт публичных страниц"
+ :export-json                                          "Экспортировать как JSON"
+ :export-roam-json                                     "Экспортировать как Roam JSON"
+ :export-edn                                           "Экспортировать как EDN"
+ :all-graphs                                           "Все графы"
+ :all-pages                                            "Все страницы"
+ :all-whiteboards                                      "Все интерактивные доски"
+ :all-files                                            "Все файлы"
+ :all-journals                                         "Все журналы"
+ :settings                                             "Настройки"
+ :settings-of-plugins                                  "Расширения"
+ :plugins                                              "Расширения"
+ :themes                                               "Темы"
+ :relaunch-confirm-to-work                             "Необходимо перезапустить приложение, чтобы оно заработало. Перезапустить его сейчас?"
+ :import                                               "Импорт"
+ :importing                                            "Импортирование"
+ :join-community                                       "Присоединиться к сообществу"
+ :discourse-title                                      "Наш форум!"
+ :help-shortcut-title                                  "Нажмите для просмотра горячих клавиш и других советов"
+ :parsing-files                                        "Анализ файлов"
+ :loading-files                                        "Загрузка файлов"
+ :login                                                "Вход"
+ :logout                                               "Выйти"
+ :download                                             "Скачать"
+ :language                                             "Язык"
+ :remove-background                                    "Удалить фон"
+ :remove-heading                                       "Удалить заголовок"
+ :heading                                              "Заголовок {1}"
+ :auto-heading                                         "Автоматический заголовок"
+ :open-a-directory                                     "Открыть локальный каталог"
+ :toggle-theme                                         "Переключить тему"
 
- :search/publishing "Искать"
- :search "Искать или создать страницу"
- :whiteboard/link-whiteboard-or-block "Ссылка на доску/страницу/блок"
- :page-search "Искать на текущей странице"
- :graph-search "Искать граф"
- :new-graph "Добавить новый граф"
- :graph "Граф"
- :graph/persist "Logseq синхронизирует внутреннее состояние, пожалуйста, подождите несколько секунд."
- :graph/persist-error "Не удалось выполнить внутреннюю синхронизацию состояния."
- :graph/save "Сохранение..."
- :graph/save-success "Успешно сохранено"
- :graph/save-error "Сохранить не удалось"
- :graph/all-graphs "Все графы"
- :export "Экспорт"
- :export-graph "Экспортировать граф"
- :export-page "Экспортировать страницу"
- :export-markdown "Экспортировать как стандартный Markdown (без свойств блока)"
- :export-opml "Экспортировать как OPML"
- :export-public-pages "Экспорт публичных страниц"
- :export-json "Экспортировать как JSON"
- :export-roam-json "Экспортировать как Roam JSON"
- :export-edn "Экспортировать как EDN"
- :all-graphs "Все графы"
- :all-pages "Все страницы"
- :all-whiteboards "Все интерактивные доски"
- :all-files "Все файлы"
- :all-journals "Все журналы"
- :settings "Настройки"
- :settings-of-plugins "Расширения"
- :plugins "Расширения"
- :themes "Темы"
- :relaunch-confirm-to-work "Необходимо перезапустить приложение, чтобы оно заработало. Перезапустить его сейчас?"
- :import "Импорт"
- :importing "Импортирование"
- :join-community "Присоединиться к сообществу"
- :discourse-title "Наш форум!"
- :help-shortcut-title "Нажмите для просмотра горячих клавиш и других советов"
- :parsing-files "Анализ файлов"
- :loading-files "Загрузка файлов"
- :login "Вход в систему"
- :logout "Выйти"
- :download "Скачать"
- :language "Язык"
- :remove-background "Удалить фон"
- :remove-heading "Удалить заголовок"
- :heading "Заголовок {1}"
- :auto-heading "Автоматический заголовок"
- :open-a-directory "Открыть локальный каталог"
+ :help/shortcut-page-title                             "Сочетания клавиш"
 
- :help/shortcut-page-title "Сочетания клавиш"
+ :plugin/installed                                     "Установлено"
+ :plugin/not-installed                                 "Не установлено"
+ :plugin/installing                                    "Установка"
+ :plugin/install                                       "Установить"
+ :plugin/reload                                        "Перезагрузить"
+ :plugin/update                                        "Обновить"
+ :plugin/check-update                                  "Проверить обновления"
+ :plugin/check-all-updates                             "Проверить все обновления"
+ :plugin/found-updates                                 "Новые обновления"
+ :plugin/found-n-updates                               "Найдено обновлений: {1}"
+ :plugin/update-all-selected                           "Обновить выбранные"
+ :plugin/all-updated                                   "Все расширения обновлены!"
+ :plugin/updates-downloading                           "Загрузка обновлений"
+ :plugin/refresh-lists                                 "Обновить списки"
+ :plugin/enabled                                       "Включено"
+ :plugin/disabled                                      "Отключено"
+ :plugin/update-available                              "Доступно обновление"
+ :plugin/updating                                      "Обновление"
+ :plugin/uninstall                                     "Удаление"
+ :plugin/marketplace                                   "Каталог расширений"
+ :plugin/downloads                                     "Загрузки"
+ :plugin/stars                                         "Звёзды"
+ :plugin/all                                           "Все"
+ :plugin/unpacked                                      "Распаковано"
+ :plugin/delete-alert                                  "Вы уверены, что хотите удалить расширение [{1}]?"
+ :plugin/open-settings                                 "Открыть настройки"
+ :plugin/open-package                                  "Открыть пакет"
+ :plugin/load-unpacked                                 "Загрузить распакованное расширение"
+ :plugin/restart                                       "Перезапустить приложение"
+ :plugin/unpacked-tips                                 "Выбрать папку для расширений"
+ :plugin/contribute                                    "✨ Написать и отправить новое расширение"
+ :plugin/custom-js-alert                               "Найден файл custom.js, выполнить его? (Если вы не понимаете содержание этого файла, рекомендуется не разрешать его выполнение, поскольку оно сопряжено с определенными рисками для безопасности)."
+ :plugin/security-warning                              "Расширения могут получать доступ к вашему графу и локальным файлам, а также отправлять сетевые запросы. Кроме того, они могут стать причиной повреждения или потери данных. Мы работаем над наилучшими правилами доступа к вашим графам. Тем не менее, убедитесь, что у вас имеются регулярные резервные копии ваших графов. Старайтесь устанавливать расширения только тогда, когда вы можете прочитать и понять исходный код."
+ :plugin/search-plugin                                 "Найти расширения"
+ :plugin/open-preferences                              "Открыть настройки"
+ :plugin/open-logseq-dir                               "Открыть"
+ :plugin/remote-error                                  "Сетевая ошибка: "
+ :plugin/checking-for-updates                          "Проверка обновлений расширения..."
+ :plugin/list-of-updates                               "Обновления расширений: "
+ :plugin.install-from-file/menu-title                  "Установить из plugins.edn"
+ :plugin.install-from-file/title                       "Установить расширения из plugins.edn"
+ :plugin.install-from-file/notice                      "Следующие расширения заменят уже установленные:"
+ :plugin.install-from-file/success                     "Все расширения установлены!"
 
- :plugin/installed "Установлено"
- :plugin/not-installed "Не установлено"
- :plugin/installing "Установка"
- :plugin/install "Установить"
- :plugin/reload "Перезагрузить"
- :plugin/update "Обновить"
- :plugin/check-update "Проверить обновления"
- :plugin/check-all-updates "Проверить все обновления"
- :plugin/found-updates "Новые обновления"
- :plugin/found-n-updates "Найдено обновлений {1}"
- :plugin/update-all-selected "Обновить все выбранные"
- :plugin/updates-downloading "Загрузка обновлений"
- :plugin/refresh-lists "Обновить списки"
- :plugin/enabled "Включено"
- :plugin/disabled "Отключено"
- :plugin/update-available "Доступно обновление"
- :plugin/updating "Обновление"
- :plugin/uninstall "Удаление"
- :plugin/marketplace "Каталог расширений"
- :plugin/downloads "Загрузки"
- :plugin/stars "Звёзды"
- :plugin/all "Все"
- :plugin/unpacked "Распаковано"
- :plugin/delete-alert "Вы уверены, что хотите удалить расширение [{1}]?"
- :plugin/open-settings "Открыть настройки"
- :plugin/open-package "Открыть пакет"
- :plugin/load-unpacked "Загрузить распакованное расширение"
- :plugin/restart "Перезапустить приложение"
- :plugin/unpacked-tips "Выбрать папку для расширений"
- :plugin/contribute "✨ Написать и отправить новое расширение"
- :plugin/custom-js-alert "Найден файл custom.js, выполнить его? (Если вы не понимаете содержание этого файла, рекомендуется не разрешать его выполнение, поскольку оно сопряжено с определенными рисками для безопасности)."
- :plugin.install-from-file/menu-title "Установить из plugins.edn"
- :plugin.install-from-file/title "Установить расширения из plugins.edn"
- :plugin.install-from-file/notice "Следующие плагины заменят ваши плагины:"
- :plugin.install-from-file/success "Все расширения установлены!"
+ :pdf/copy-ref                                         "Копировать ссылку"
+ :pdf/copy-text                                        "Копировать текст"
+ :pdf/linked-ref                                       "Связанные ссылки"
+ :pdf/toggle-dashed                                    "Пунктирный стиль для выделения области"
+ :pdf/hl-block-colored                                 "Цветная метка для выделенного блока"
+ :pdf/doc-metadata                                     "Метаданные документа"
 
- :pdf/copy-ref "Копировать ссылку"
- :pdf/copy-text "Копировать текст"
- :pdf/linked-ref "Связанные ссылки"
- :pdf/toggle-dashed "Пунктирный стиль для выделения области"
- :pdf/hl-block-colored "Цветная метка для выделенного блока"
- :pdf/doc-metadata "Метаданные документа"
+ :updater/new-version-install                          "Была загружена новая версия"
+ :updater/quit-and-install                             "Перезапустить для установки"
 
- :updater/new-version-install "Была загружена новая версия"
- :updater/quit-and-install "Перезапустить для установки"
+ :paginates/pages                                      "Всего {1} страниц(а)"
+ :paginates/prev                                       "Предыдущая"
+ :paginates/next                                       "Следующая"
 
- :paginates/pages "Всего {1} страниц(а)"
- :paginates/prev "Предыдущая"
- :paginates/next "Следующая"
+ :command-palette/prompt                               "Введите команду"
+ :select/default-prompt                                "Выберите"
+ :select/default-select-multiple                       "Выберите один или несколько"
+ :select.graph/prompt                                  "Выберите граф"
+ :select.graph/empty-placeholder-description           "Нет подходящих графов. Хотите добавить другой?"
+ :select.graph/add-graph                               "Да, добавить другой граф"
 
- :command-palette/prompt "Введите команду"
- :select/default-prompt "Выберите"
- :select/default-select-multiple "Выберите один или несколько"
- :select.graph/prompt "Выберите граф"
- :select.graph/empty-placeholder-description "Нет подходящих графов. Хотите добавить другой?"
- :select.graph/add-graph "Да, добавить другой граф"
+ :file-sync/other-user-graph                           "Текущий локальный граф привязан к удаленному графу другого пользователя. Поэтому синхронизацию начать нельзя."
+ :file-sync/graph-deleted                              "Текущий удаленный граф был удален"
+ :file-sync/rsapi-cannot-upload-err                    "Не удалось начать синхронизацию, пожалуйста, проверьте, правильно ли установлено локальное время."
+ 
+ :notification/clear-all                               "Очистить всё"
+ 
+ :shortcut.category/basics                             "Базовые"
+ :shortcut.category/formatting                         "Форматирование"
+ :shortcut.category/navigating                         "Навигация"
+ :shortcut.category/block-editing                      "Общее редактирование блока"
+ :shortcut.category/block-command-editing              "Команды редактирования блока"
+ :shortcut.category/block-selection                    "Выделение блоков (Esc для отмены)"
+ :shortcut.category/toggle                             "Переключатели"
+ :shortcut.category/whiteboard                         "Интерактивная доска"
+ :shortcut.category/others                             "Разное"
+ :shortcut.category/plugins                            "Расширения"
+ :window/minimize                                      "Свернуть"
+ :window/maximize                                      "Развернуть"
+ :window/restore                                       "Восстановить"
+ :window/close                                         "Закрыть"
+ :window/exit-fullscreen                               "Выйти из полноэкранного режима"
 
- :file-sync/other-user-graph "Текущий локальный граф привязан к удаленному графу другого пользователя. Поэтому синхронизацию начать нельзя."
- :file-sync/graph-deleted "Текущий удаленный граф был удален"
- :file-sync/rsapi-cannot-upload-err "Невозможно начать синхронизацию, пожалуйста, проверьте правильное ли установлено локальное время."
-
- :notification/clear-all "Очистить всё"
-
- :shortcut.category/basics                "Базовые"
- :shortcut.category/formatting            "Форматирование"
- :shortcut.category/navigating            "Навигация"
- :shortcut.category/block-editing         "Общее редактирование блока"
- :shortcut.category/block-command-editing "Команды редактирования блока"
- :shortcut.category/block-selection       "Выделение блоков (нажмите Esc для отмены)"
- :shortcut.category/toggle                "Переключатели"
- :shortcut.category/whiteboard            "Интерактивная доска"
- :shortcut.category/others                "Разное"
- :command.date-picker/complete            "Выбор даты: Выбрать указанный день"
- :command.date-picker/prev-day            "Выбор даты: Выбрать предыдущий день"
- :command.date-picker/next-day            "Выбор даты: Выбрать следующий день"
- :command.date-picker/prev-week           "Выбор даты: Выбрать предыдущую неделю"
- :command.date-picker/next-week           "Выбор даты: Выбрать следующую неделю"
- :command.pdf/previous-page               "PDF: Предыдущая страница текущего PDF"
- :command.pdf/next-page                   "PDF: Следующая страница текущего PDF"
- :command.pdf/close                       "PDF: Закрыть текущий просмотр PDF"
- :command.pdf/find                        "PDF: Поиск текста в текущем PDF-документе"
- :command.auto-complete/complete          "Автодополнение: Использовать выбранный элемент"
- :command.auto-complete/prev              "Автодополнение: Выбрать предыдущий"
- :command.auto-complete/next              "Автодополнение: Выбрать следующий"
- :command.auto-complete/shift-complete    "Автодополнение: Открыть выбранный элемент в боковой панели"
- :command.auto-complete/open-link         "Автодополнение: Открыть выбранный элемент в браузере"
- :command.cards/toggle-answers            "Карточки: показать/скрыть ответы/clozes"
- :command.cards/next-card                 "Карточки: следующая карточка"
- :command.cards/forgotten                 "Карточки: забытая"
- :command.cards/remembered                "Карточки: запомненная"
- :command.cards/recall                    "Карточки: нужно время, чтобы вспомнить"
- :command.editor/escape-editing           "Выйти из режима редактирования"
- :command.editor/backspace                "Удалить перед курсором"
- :command.editor/delete                   "Удалить после курсора"
- :command.editor/new-block                "Создать новый блок"
- :command.editor/new-line                 "Новая строка в блоке"
- :command.editor/new-whiteboard           "Новая интерактивная доска"
- :command.editor/follow-link              "Перейти по ссылке под курсором"
- :command.editor/open-link-in-sidebar     "Открыть ссылку в боковой панели"
- :command.editor/bold                     "Жирный"
- :command.editor/italics                  "Курсив"
- :command.editor/highlight                "Выделение"
- :command.editor/strike-through           "Перечёркнутый"
- :command.editor/clear-block              "Удалить содержимое блока"
- :command.editor/kill-line-before         "Удалить строку до курсора"
- :command.editor/copy-embed               "Копировать вставку, ссылающуюся на текущий блок"
- :command.editor/kill-line-after          "Удалить строку после курсора"
- :command.editor/beginning-of-block       "Переместить курсор в начало блока"
- :command.editor/end-of-block             "Переместить курсор в конец блока"
- :command.editor/forward-word             "Переместить курсор на одно слово вперед"
- :command.editor/backward-word            "Переместить курсор на одно слово назад"
- :command.editor/forward-kill-word        "Удалить следующее слово"
- :command.editor/backward-kill-word       "Удалить предыдущее слово"
- :command.editor/replace-block-reference-at-point "Заменить ссылку на блок своим содержимым в указанном месте"
- :command.editor/paste-text-in-one-block-at-point "Вставить текст в один блок в указанном месте"
- :command.editor/insert-youtube-timestamp "Вставить временную метку на Youtube"
- :command.editor/cycle-todo               "Изменить статус TODO текущего элемента"
- :command.editor/up                       "Переместить курсор вверх / Выбрать вверх"
- :command.editor/down                     "Переместить курсор вниз / Выбрать вниз"
- :command.editor/left                     "Переместить курсор влево / Открыть выбранный блок в начале"
- :command.editor/right                    "Переместить курсор вправо / Открыть выбранный блок в конце"
- :command.editor/select-up                "Выбрать контент выше"
- :command.editor/select-down              "Выбрать контент ниже"
- :command.editor/move-block-up            "Передвинуть блок выше"
- :command.editor/move-block-down          "Передвинуть блок ниже"
- :command.editor/open-edit                "Редактировать выбранный блок"
- :command.editor/select-block-up          "Выбрать блок выше"
- :command.editor/select-block-down        "Выбрать блок ниже"
- :command.editor/delete-selection         "Удалить выбранные блоки"
- :command.editor/expand-block-children    "Раскрыть"
- :command.editor/collapse-block-children  "Свернуть"
- :command.editor/indent                   "Увеличить отступ блока"
- :command.editor/outdent                  "Уменьшить отступ блока"
- :command.editor/copy                     "Копировать (выделенное либо ссылку на блок)"
- :command.editor/copy-text                "Копировать как текст"
- :command.editor/cut                      "Вырезать"
- :command.editor/undo                     "Отменить"
- :command.editor/redo                     "Вернуть"
- :command.editor/insert-link              "HTML ссылка"
- :command.editor/select-all-blocks        "Выбрать все блоки"
- :command.editor/select-parent            "Выбрать родительский блок"
- :command.editor/zoom-in                  "Увеличить / Вперед"
- :command.editor/zoom-out                 "Уменьшить / Назад"
- :command.editor/toggle-undo-redo-mode    "Переключить режим отменить/повторить (глобально или только на странице)"
- :command.editor/toggle-number-list       "Переключить режим нумерованный список"
- :command.whiteboard/select               "Выбрать инструмент"
- :command.whiteboard/pan                  "Прокруктка"
- :command.whiteboard/portal               "Добавить блок или страницу"
- :command.whiteboard/pencil               "Карандаш"
- :command.whiteboard/highlighter          "Маркер"
- :command.whiteboard/eraser               "Ластик"
- :command.whiteboard/connector            "Соединитель"
- :command.whiteboard/text                 "Текст"
- :command.whiteboard/rectangle            "Прямоугольник"
- :command.whiteboard/ellipse              "Эллипс"
- :command.whiteboard/reset-zoom           "Сбросить масштаб"
- :command.whiteboard/zoom-to-fit          "Показать все элементы"
- :command.whiteboard/zoom-to-selection    "Показать элемент"
- :command.whiteboard/zoom-out             "Уменьшить"
- :command.whiteboard/zoom-in              "Увеличить"
- :command.whiteboard/send-backward        "Переместить назад"
- :command.whiteboard/send-to-back         "На задний план"
- :command.whiteboard/bring-forward        "Переместить вперёд"
- :command.whiteboard/bring-to-front       "На передний план"
- :command.whiteboard/lock                 "Блокировать"
- :command.whiteboard/unlock               "Разблокировать"
- :command.whiteboard/group                "Группировать"
- :command.whiteboard/ungroup              "Разгруппировать"
- :command.whiteboard/toggle-grid          "Переключить отображение сетки"
- :command.ui/toggle-brackets              "Переключить отображение скобок"
- :command.go/search-in-page               "Поиск блоков на текущей странице"
- :command.go/electron-find-in-page        "Поиск текста на странице"
- :command.go/electron-jump-to-the-next    "Перейти к следующему совпадению в строке поиска"
- :command.go/electron-jump-to-the-previous "Перейти к предыдущему совпадению в строке поиска"
- :command.go/search                       "Поиск страниц и блоков"
- :command.go/journals                     "Перейти в журналы"
- :command.go/backward                     "Назад"
- :command.go/forward                      "Вперёд"
- :command.search/re-index                 "Переиндексация поиска"
- :command.sidebar/open-today-page         "Открыть сегодняшнюю страницу в правой боковой панели"
- :command.sidebar/close-top               "Закрыть верхний элемент в правой боковой панели"
- :command.sidebar/clear                   "Очистить правую панель"
- :command.command-palette/toggle          "Показать палитру команд"
- :command.graph/export-as-html            "Экспорт публичных страниц графов в формате html"
- :command.graph/open                      "Выберите граф для открытия"
- :command.graph/remove                    "Удалить граф"
- :command.graph/add                       "Добавить граф"
- :command.graph/save                      "Сохранить текущий граф на диск"
- :command.graph/re-index                  "Переиндексировать текущий граф"
- :command.command/run                     "Выполнить команду git"
- :command.go/home                         "Домой"
- :command.go/all-graphs                   "Перейти ко всем графам"
- :command.go/whiteboards                  "Перейти к интерактивным доскам"
- :command.go/all-pages                    "Перейти ко всем страницам"
- :command.go/graph-view                   "Перейти к просмотру графа"
- :command.go/keyboard-shortcuts           "Перейти к горячим клавишам"
- :command.go/tomorrow                     "перейти к завтрашнему дню"
- :command.go/next-journal                 "Перейти к следующему журналу"
- :command.go/prev-journal                 "Перейти к предыдущему журналу"
- :command.go/flashcards                   "Переключить карточки"
- :command.ui/toggle-document-mode         "Переключить режим документа"
- :command.ui/toggle-settings              "Переключить параметры"
- :command.ui/toggle-right-sidebar         "Переключить правую панель"
- :command.ui/toggle-left-sidebar          "Переключить левую панель"
- :command.ui/toggle-help                  "Переключить помощь"
- :command.ui/toggle-theme                 "Переключение между темной/светлой темой"
- :command.ui/toggle-contents              "Переключить Контент на боковой панели"
- :command.command/toggle-favorite         "Добавить или удалить из избранного"
- :command.editor/open-file-in-default-app "Открыть файл в программе по умолчанию"
- :command.editor/open-file-in-directory   "Открыть файл в родительском каталоге"
- :command.editor/copy-current-file        "Копировать текущий файл"
- :command.editor/copy-page-url            "Скопировать URL страницы"
- :command.ui/toggle-wide-mode             "Переключить широкоформатный режим"
- :command.ui/select-theme-color           "Выбрать доступные цвета темы"
- :command.ui/goto-plugins                 "Перейдите в панель управления расширениями"
- :command.ui/install-plugins-from-file    "Установить расширения из plugins.edn"
- :command.editor/toggle-open-blocks       "Переключить открытые блоки (свернуть или развернуть все блоки)"
- :command.ui/toggle-cards                 "Переключить карточки"
- :command.ui/clear-all-notifications      "Очистить все уведомления"
- :command.git/commit                      "Создать git-коммит с сообщением"
- :command.dev/show-block-data             "(Dev) Показать данные блока"
- :command.dev/show-block-ast              "(Dev) Показать AST блока"
- :command.dev/show-page-data              "(Dev) Показать данные страницы"
- :command.dev/show-page-ast               "(Dev) Показать AST страницы"}
+ :header/toggle-left-sidebar                           "Переключить левую панель"
+ :header/search                                        "Поиск"
+ :header/more                                          "Больше"
+ :header/go-back                                       "Назад"
+ :header/go-forward                                    "Вперёд"
+ :command.date-picker/complete                         "Выбор даты: Выбрать указанный день"
+ :command.date-picker/prev-day                         "Выбор даты: Выбрать предыдущий день"
+ :command.date-picker/next-day                         "Выбор даты: Выбрать следующий день"
+ :command.date-picker/prev-week                        "Выбор даты: Выбрать предыдущую неделю"
+ :command.date-picker/next-week                        "Выбор даты: Выбрать следующую неделю"
+ :command.pdf/previous-page                            "PDF: Предыдущая страница текущего PDF-документа"
+ :command.pdf/next-page                                "PDF: Следующая страница текущего PDF-документа"
+ :command.pdf/close                                    "PDF: Закрыть просмотр текущего PDF-документа"
+ :command.pdf/find                                     "PDF: Поиск текста в текущем PDF-документе"
+ :command.auto-complete/complete                       "Автодополнение: Использовать выбранный элемент"
+ :command.auto-complete/prev                           "Автодополнение: Выбрать предыдущий"
+ :command.auto-complete/next                           "Автодополнение: Выбрать следующий"
+ :command.auto-complete/shift-complete                 "Автодополнение: Открыть выбранный элемент на боковой панели"
+ :command.auto-complete/open-link                      "Автодополнение: Открыть выбранный элемент в браузере"
+ :command.cards/toggle-answers                         "Карточки: показать/скрыть ответы/clozes"
+ :command.cards/next-card                              "Карточки: следующая карточка"
+ :command.cards/forgotten                              "Карточки: забытая"
+ :command.cards/remembered                             "Карточки: запомненная"
+ :command.cards/recall                                 "Карточки: нужно время, чтобы вспомнить"
+ :command.editor/escape-editing                        "Выйти из режима редактирования"
+ :command.editor/backspace                             "Удалить перед курсором"
+ :command.editor/delete                                "Удалить после курсора"
+ :command.editor/new-block                             "Создать новый блок"
+ :command.editor/new-line                              "Новая строка в блоке"
+ :command.editor/new-whiteboard                        "Новая интерактивная доска"
+ :command.editor/follow-link                           "Перейти по ссылке под курсором"
+ :command.editor/open-link-in-sidebar                  "Открыть ссылку на боковой панели"
+ :command.editor/bold                                  "Жирный"
+ :command.editor/italics                               "Курсив"
+ :command.editor/highlight                             "Выделение"
+ :command.editor/strike-through                        "Перечёркнутый"
+ :command.editor/clear-block                           "Удалить содержимое блока"
+ :command.editor/kill-line-before                      "Удалить строку до курсора"
+ :command.editor/copy-embed                            "Копировать вставку, ссылающуюся на текущий блок"
+ :command.editor/kill-line-after                       "Удалить строку после курсора"
+ :command.editor/beginning-of-block                    "Переместить курсор в начало блока"
+ :command.editor/end-of-block                          "Переместить курсор в конец блока"
+ :command.editor/forward-word                          "Переместить курсор на одно слово вперёд"
+ :command.editor/backward-word                         "Переместить курсор на одно слово назад"
+ :command.editor/forward-kill-word                     "Удалить следующее слово"
+ :command.editor/backward-kill-word                    "Удалить предыдущее слово"
+ :command.editor/replace-block-reference-at-point      "Заменить ссылку на блок своим содержимым в указанном месте"
+ :command.editor/paste-text-in-one-block-at-point      "Вставить текст в один блок в указанном месте"
+ :command.editor/insert-youtube-timestamp              "Вставить временную метку на Youtube"
+ :command.editor/cycle-todo                            "Изменить статус TODO текущего элемента"
+ :command.editor/up                                    "Переместить курсор вверх / Выбрать вверх"
+ :command.editor/down                                  "Переместить курсор вниз / Выбрать вниз"
+ :command.editor/left                                  "Переместить курсор влево / Открыть выбранный блок в начале"
+ :command.editor/right                                 "Переместить курсор вправо / Открыть выбранный блок в конце"
+ :command.editor/select-up                             "Выбрать контент выше"
+ :command.editor/select-down                           "Выбрать контент ниже"
+ :command.editor/move-block-up                         "Передвинуть блок выше"
+ :command.editor/move-block-down                       "Передвинуть блок ниже"
+ :command.editor/open-edit                             "Редактировать выбранный блок"
+ :command.editor/select-block-up                       "Выбрать блок выше"
+ :command.editor/select-block-down                     "Выбрать блок ниже"
+ :command.editor/delete-selection                      "Удалить выбранные блоки"
+ :command.editor/expand-block-children                 "Раскрыть"
+ :command.editor/collapse-block-children               "Свернуть"
+ :command.editor/indent                                "Увеличить отступ блока"
+ :command.editor/outdent                               "Уменьшить отступ блока"
+ :command.editor/copy                                  "Копировать (выделенное либо ссылку на блок)"
+ :command.editor/copy-text                             "Копировать как текст"
+ :command.editor/cut                                   "Вырезать"
+ :command.editor/undo                                  "Отменить"
+ :command.editor/redo                                  "Вернуть"
+ :command.editor/insert-link                           "HTML ссылка"
+ :command.editor/select-all-blocks                     "Выбрать все блоки"
+ :command.editor/select-parent                         "Выбрать родительский блок"
+ :command.editor/zoom-in                               "Увеличить / Вперёд"
+ :command.editor/zoom-out                              "Уменьшить / Назад"
+ :command.editor/toggle-undo-redo-mode                 "Переключить режим Отменить/Повторить (глобально или только на странице)"
+ :command.editor/toggle-number-list                    "Переключить формат нумерованного списка"
+ :command.whiteboard/select                            "Инструмент 'Выделение'"
+ :command.whiteboard/pan                               "Инструмент 'Рука'"
+ :command.whiteboard/portal                            "Инструмент 'портал'"
+ :command.whiteboard/pencil                            "Инструмент 'Карандаш'"
+ :command.whiteboard/highlighter                       "Инструмент 'Маркер'"
+ :command.whiteboard/eraser                            "Инструмент 'Ластик'"
+ :command.whiteboard/connector                         "Инструмент 'Соединительная стрелка'"
+ :command.whiteboard/text                              "Инструмент 'Текст'"
+ :command.whiteboard/rectangle                         "Инструмент 'Прямоугольник'"
+ :command.whiteboard/ellipse                           "Инструмент 'Эллипс'"
+ :command.whiteboard/reset-zoom                        "Сбросить масштаб"
+ :command.whiteboard/zoom-to-fit                       "Масштабировать, показав все элементы"
+ :command.whiteboard/zoom-to-selection                 "Масштабировать, показав выбранные элементы"
+ :command.whiteboard/zoom-out                          "Уменьшить"
+ :command.whiteboard/zoom-in                           "Увеличить"
+ :command.whiteboard/send-backward                     "Переместить назад"
+ :command.whiteboard/send-to-back                      "Переместить на задний план"
+ :command.whiteboard/bring-forward                     "Переместить вперёд"
+ :command.whiteboard/bring-to-front                    "Переместить на передний план"
+ :command.whiteboard/lock                              "Заблокировать выбранное"
+ :command.whiteboard/unlock                            "Разблокировать выбранное"
+ :command.whiteboard/group                             "Группировать выбранное"
+ :command.whiteboard/ungroup                           "Разгруппировать выбранное"
+ :command.whiteboard/toggle-grid                       "Переключить отображение сетки"
+ :command.ui/toggle-brackets                           "Переключить отображение скобок"
+ :command.go/search-in-page                            "Поиск блоков на текущей странице"
+ :command.go/electron-find-in-page                     "Поиск текста на странице"
+ :command.go/electron-jump-to-the-next                 "Перейти к следующему совпадению в строке поиска"
+ :command.go/electron-jump-to-the-previous             "Перейти к предыдущему совпадению в строке поиска"
+ :command.go/search                                    "Поиск страниц и блоков"
+ :command.go/journals                                  "Перейти в журналы"
+ :command.go/backward                                  "Назад"
+ :command.go/forward                                   "Вперёд"
+ :command.search/re-index                              "Переиндексация поиска"
+ :command.sidebar/open-today-page                      "Открыть сегодняшнюю страницу на правой боковой панели"
+ :command.sidebar/close-top                            "Закрыть верхний элемент на правой боковой панели"
+ :command.sidebar/clear                                "Очистить правую панель"
+ :command.command-palette/toggle                       "Показать палитру команд"
+ :command.graph/export-as-html                         "Экспорт публичных страниц графов в формате html"
+ :command.graph/open                                   "Выберите граф для открытия"
+ :command.graph/remove                                 "Удалить граф"
+ :command.graph/add                                    "Добавить граф"
+ :command.graph/save                                   "Сохранить текущий граф на диск"
+ :command.graph/re-index                               "Переиндексировать текущий граф"
+ :command.command/run                                  "Выполнить команду git"
+ :command.go/home                                      "Домой"
+ :command.go/all-graphs                                "Перейти ко всем графам"
+ :command.go/whiteboards                               "Перейти к интерактивным доскам"
+ :command.go/all-pages                                 "Перейти ко всем страницам"
+ :command.go/graph-view                                "Перейти к просмотру графа"
+ :command.go/keyboard-shortcuts                        "Перейти к горячим клавишам"
+ :command.go/tomorrow                                  "перейти к завтрашнему дню"
+ :command.go/next-journal                              "Перейти к следующему журналу"
+ :command.go/prev-journal                              "Перейти к предыдущему журналу"
+ :command.go/flashcards                                "Переключить карточки"
+ :command.ui/toggle-document-mode                      "Переключить режим документа"
+ :command.ui/toggle-settings                           "Переключить параметры"
+ :command.ui/toggle-right-sidebar                      "Переключить правую панель"
+ :command.ui/toggle-left-sidebar                       "Переключить левую панель"
+ :command.ui/toggle-help                               "Переключить помощь"
+ :command.ui/toggle-theme                              "Переключение между тёмной/светлой темой"
+ :command.ui/toggle-contents                           "Переключить Контент на боковой панели"
+ :command.command/toggle-favorite                      "Добавить или удалить из избранного"
+ :command.editor/open-file-in-default-app              "Открыть файл в программе по умолчанию"
+ :command.editor/open-file-in-directory                "Открыть файл в родительском каталоге"
+ :command.editor/copy-current-file                     "Копировать текущий файл"
+ :command.editor/copy-page-url                         "Скопировать URL страницы"
+ :command.ui/toggle-wide-mode                          "Переключить широкоформатный режим"
+ :command.ui/select-theme-color                        "Выбрать доступные цвета темы"
+ :command.ui/goto-plugins                              "Перейдите в панель управления расширениями"
+ :command.ui/install-plugins-from-file                 "Установить расширения из plugins.edn"
+ :command.editor/toggle-open-blocks                    "Переключить открытые блоки (свернуть или развернуть все блоки)"
+ :command.ui/toggle-cards                              "Переключить карточки"
+ :command.ui/clear-all-notifications                   "Очистить все уведомления"
+ :command.git/commit                                   "Создать git-коммит с сообщением"
+ :command.dev/show-block-data                          "(Dev) Показать данные блока"
+ :command.dev/show-block-ast                           "(Dev) Показать AST блока"
+ :command.dev/show-page-data                           "(Dev) Показать данные страницы"
+ :command.dev/show-page-ast                            "(Dev) Показать AST страницы"}

+ 48 - 12
src/test/frontend/handler/paste_test.cljs

@@ -6,6 +6,7 @@
             [frontend.state :as state]
             [frontend.commands :as commands]
             [frontend.util :as util]
+            [frontend.util.cursor :as cursor]
             [promesa.core :as p]
             [frontend.extensions.html-parser :as html-parser]
             [frontend.handler.editor :as editor-handler]
@@ -105,21 +106,58 @@
              (reset)))))
 
 (deftest-async editor-on-paste-with-link
-  (testing "Formatted paste for link should paste macro wrapped link"
+  (testing "Formatted paste for special link should paste macro wrapped link"
     (let [clipboard "https://www.youtube.com/watch?v=xu9p5ynlhZk"
           expected-paste "{{video https://www.youtube.com/watch?v=xu9p5ynlhZk}}"]
       (test-helper/with-reset
         reset
-        [state/get-input (constantly #js {:value "block"})
-         ;; paste-copied-blocks-or-text mocks below
+        [;; paste-copied-blocks-or-text mocks below
          commands/delete-selection! (constantly nil)
          commands/simple-insert! (fn [_input text] (p/resolved text))
          util/stop (constantly nil)
+         util/get-selected-text (constantly "")
          html-parser/convert (constantly nil)]
         (p/let [result ((paste-handler/editor-on-paste! nil)
                         #js {:clipboardData #js {:getData (constantly clipboard)}})]
-               (is (= expected-paste result))
-               (reset))))))
+          (is (= expected-paste result))
+          (reset))))))
+
+(deftest-async editor-on-paste-with-selected-text-and-special-link
+  (testing "Formatted paste with special link on selected text pastes a formatted link"
+    (let [actual-text (atom nil)
+          clipboard "https://www.youtube.com/watch?v=xu9p5ynlhZk"
+          selected-text "great song"
+          block-content (str selected-text " - Obaluaê!")
+          expected-paste "[great song](https://www.youtube.com/watch?v=xu9p5ynlhZk) - Obaluaê!"]
+      (test-helper/with-reset
+        reset
+        [;; paste-copied-blocks-or-text mocks below
+         util/stop (constantly nil)
+         util/get-selected-text (constantly selected-text)
+         editor-handler/get-selection-and-format
+         (constantly {:selection-start 0 :selection-end (count selected-text)
+                      :selection selected-text :format :markdown :value block-content})
+         state/set-edit-content! (fn [_ new-value] (reset! actual-text new-value))
+         cursor/move-cursor-to (constantly nil)]
+        (p/let [_ ((paste-handler/editor-on-paste! nil)
+                   #js {:clipboardData #js {:getData (constantly clipboard)}})]
+          (is (= expected-paste @actual-text))
+          (reset))))))
+
+(deftest-async editor-on-paste-with-block-ref-in-between-parens
+  (let [clipboard "((647f90f4-d733-4ee2-bbf5-907e820a23d3))"
+        expected-paste "647f90f4-d733-4ee2-bbf5-907e820a23d3"]
+    (test-helper/with-reset
+      reset
+      [;; paste-copied-blocks-or-text mocks below
+       util/stop (constantly nil)
+       state/get-input (constantly #js {:value "(())"})
+       cursor/pos (constantly 2)
+       commands/simple-insert! (fn [_input text] (p/resolved text))]
+      (p/let [result ((paste-handler/editor-on-paste! nil)
+                      #js {:clipboardData #js {:getData (constantly clipboard)}})]
+        (is (= expected-paste result))
+        (reset)))))
 
 (deftest-async editor-on-paste-with-copied-blocks
   (let [actual-blocks (atom nil)
@@ -129,15 +167,14 @@
         clipboard "- Test node\n\t- Notes\nid:: 6422ec75-85c7-4e09-9a4d-2a1639a69b2f"]
     (test-helper/with-reset
       reset
-      [state/get-input (constantly #js {:value "block"})
-       ;; paste-copied-blocks-or-text mocks below
+      [;; paste-copied-blocks-or-text mocks below
        util/stop (constantly nil)
        paste-handler/get-copied-blocks (constantly (p/resolved expected-blocks))
        editor-handler/paste-blocks (fn [blocks _] (reset! actual-blocks blocks))]
       (p/let [_ ((paste-handler/editor-on-paste! nil)
-                      #js {:clipboardData #js {:getData (constantly clipboard)}})]
-             (is (= expected-blocks @actual-blocks))
-             (reset)))))
+                 #js {:clipboardData #js {:getData (constantly clipboard)}})]
+        (is (= expected-blocks @actual-blocks))
+        (reset)))))
 
 (deftest-async editor-on-paste-with-selection-in-property
   (let [clipboard "after"
@@ -183,8 +220,7 @@
         text "- Test node\n\t- Notes\nid:: 6422ec75-85c7-4e09-9a4d-2a1639a69b2f"]
     (test-helper/with-reset
       reset
-      [state/get-input (constantly #js {:value "block"})
-       ;; paste-copied-blocks-or-text mocks below
+      [;; paste-copied-blocks-or-text mocks below
        util/stop (constantly nil)
        html-parser/convert (constantly "**bold text**")
        paste-handler/get-copied-blocks (constantly (p/resolved nil))

+ 5 - 0
tailwind.config.js

@@ -213,6 +213,11 @@ module.exports = {
         '250': '2.5',
         '300': '3',
         '400': '4',
+      },
+      width: {
+        'lsm': '600px',
+        'lmd': '728px',
+        'llg': '960px'
       }
     },
     colors: {

+ 1 - 1
tldraw/apps/tldraw-logseq/src/components/QuickSearch/QuickSearch.tsx

@@ -104,7 +104,7 @@ export const LogseqQuickSearch = observer(
     const t = handlers.t
 
     const finishSearching = React.useCallback((id: string) => {
-      onChange(id)
+      setTimeout(() => onChange(id))
       rInput.current?.blur()
       if (id) {
         LogseqPortalShape.defaultSearchQuery = ''