Prechádzať zdrojové kódy

Merge branch 'master' into feat/db

Tienson Qin 1 rok pred
rodič
commit
018e1ae8ee
82 zmenil súbory, kde vykonal 1911 pridanie a 886 odobranie
  1. 1 0
      .github/workflows/build-desktop-release.yml
  2. 1 1
      .github/workflows/build-stage.yml
  3. 2 2
      android/app/build.gradle
  4. 1 0
      deps/db/deps.edn
  5. 6 5
      deps/graph-parser/src/logseq/graph_parser/block.cljs
  6. 4 0
      e2e-tests/fixtures.ts
  7. 5 3
      e2e-tests/fs.spec.ts
  8. 1 2
      e2e-tests/headings.spec.ts
  9. 1 1
      e2e-tests/hotkey.spec.ts
  10. 3 2
      e2e-tests/util/search-modal.ts
  11. 4 4
      ios/App/App.xcodeproj/project.pbxproj
  12. 13 1
      libs/CHANGELOG.md
  13. 1 1
      libs/package.json
  14. 38 36
      libs/src/LSPlugin.core.ts
  15. 14 29
      libs/src/LSPlugin.ts
  16. 14 5
      libs/src/LSPlugin.user.ts
  17. 48 32
      libs/src/helpers.ts
  18. 2 2
      package.json
  19. 11 2
      resources/forge.config.js
  20. 0 0
      resources/js/lsplugin.core.js
  21. 0 0
      resources/js/lsplugin.user.js
  22. 1 0
      resources/js/lsplugin.user.js.LICENSE.txt
  23. 4 3
      resources/package.json
  24. 206 206
      resources/whiteboard/onboarding.edn
  25. 1 1
      src/electron/electron/git.cljs
  26. 3 0
      src/electron/electron/handler.cljs
  27. 2 2
      src/main/electron/listener.cljs
  28. 14 9
      src/main/frontend/colors.cljs
  29. 166 84
      src/main/frontend/components/cmdk.cljs
  30. 1 1
      src/main/frontend/components/plugins.cljs
  31. 7 16
      src/main/frontend/components/reference.cljs
  32. 2 3
      src/main/frontend/components/settings.cljs
  33. 1 1
      src/main/frontend/components/settings.css
  34. 9 7
      src/main/frontend/components/shortcut.cljs
  35. 1 1
      src/main/frontend/components/whiteboard.cljs
  36. 1 0
      src/main/frontend/db/model.cljs
  37. 3 15
      src/main/frontend/db/query_dsl.cljs
  38. 2 5
      src/main/frontend/db/utils.cljs
  39. 0 8
      src/main/frontend/db_worker.cljs
  40. 2 2
      src/main/frontend/extensions/excalidraw.cljs
  41. 5 1
      src/main/frontend/extensions/pdf/windows.cljs
  42. 1 1
      src/main/frontend/fs/node.cljs
  43. 54 52
      src/main/frontend/fs/watcher_handler.cljs
  44. 4 1
      src/main/frontend/handler/command_palette.cljs
  45. 20 18
      src/main/frontend/handler/editor.cljs
  46. 1 1
      src/main/frontend/handler/editor/lifecycle.cljs
  47. 3 3
      src/main/frontend/handler/paste.cljs
  48. 10 8
      src/main/frontend/handler/plugin.cljs
  49. 4 12
      src/main/frontend/handler/user.cljs
  50. 28 10
      src/main/frontend/modules/shortcut/config.cljs
  51. 2 1
      src/main/frontend/modules/shortcut/core.cljs
  52. 1 1
      src/main/frontend/search.cljs
  53. 0 1
      src/main/frontend/search/agency.cljs
  54. 4 2
      src/main/frontend/state.cljs
  55. 70 24
      src/main/frontend/util.cljc
  56. 1 1
      src/main/frontend/version.cljs
  57. 16 27
      src/main/logseq/api.cljs
  58. 13 1
      src/main/logseq/sdk/assets.cljs
  59. 32 4
      src/main/logseq/sdk/ui.cljs
  60. 23 0
      src/resources/dicts/en.edn
  61. 6 1
      src/resources/dicts/es.edn
  62. 31 15
      src/resources/dicts/fr.edn
  63. 9 0
      src/resources/dicts/pl.edn
  64. 531 154
      src/resources/dicts/sk.edn
  65. 29 1
      src/resources/dicts/tr.edn
  66. 1 0
      src/resources/dicts/zh-cn.edn
  67. 1 0
      src/resources/dicts/zh-hant.edn
  68. 5 5
      src/resources/tutorials/tutorial-sk.md
  69. 21 6
      src/test/frontend/db/query_dsl_test.cljs
  70. 1 22
      src/test/frontend/handler/paste_test.cljs
  71. 213 9
      static/yarn.lock
  72. 51 0
      tldraw/packages/core/src/lib/TLApi/TLApi.ts
  73. 16 1
      tldraw/packages/core/src/lib/TLApp/TLApp.ts
  74. 7 0
      tldraw/packages/core/src/types/types.ts
  75. 1 0
      tldraw/packages/react/src/components/AppCanvas.tsx
  76. 3 0
      tldraw/packages/react/src/components/Canvas/Canvas.tsx
  77. 26 2
      tldraw/packages/react/src/components/ui/SelectionForeground/SelectionForeground.tsx
  78. 45 0
      tldraw/packages/react/src/components/ui/SelectionForeground/handles/CloneHandle.tsx
  79. 1 0
      tldraw/packages/react/src/components/ui/SelectionForeground/handles/index.ts
  80. 16 1
      tldraw/packages/react/src/hooks/useStylesheet.ts
  81. 1 0
      tldraw/packages/react/src/types/component-props.ts
  82. 8 8
      yarn.lock

+ 1 - 0
.github/workflows/build-desktop-release.yml

@@ -321,6 +321,7 @@ jobs:
           mkdir builds
           mv static\out\make\squirrel.windows\x64\*.exe    builds\Logseq-win-x64-${{ steps.ref.outputs.version }}.exe
           mv static\out\make\squirrel.windows\x64\*.nupkg  builds\Logseq-win-x64-${{ steps.ref.outputs.version }}-full.nupkg
+          mv static\out\make\zip\win32\x64\*.zip           builds\Logseq-win-x64-${{ steps.ref.outputs.version }}.zip
           mv static\out\make\squirrel.windows\x64\RELEASES builds\RELEASES
 
       - name: Upload Artifact

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

@@ -62,7 +62,7 @@ jobs:
         with:
           apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
           accountId: 2553ea8236c11ea0f88de28fce1cbfee
-          projectName: ${{ github.event.inputs.cloudflare-project-name }}
+          projectName: ${{ github.event.inputs.cloudflare-project-name || 'logseq-demo' }}
           directory: 'static'
           gitHubToken: ${{ secrets.GITHUB_TOKEN }}
           branch: 'production'

+ 2 - 2
android/app/build.gradle

@@ -7,8 +7,8 @@ android {
         applicationId "com.logseq.app"
         minSdkVersion rootProject.ext.minSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
-        versionCode 74
-        versionName "0.10.0"
+        versionCode 76
+        versionName "0.10.2"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         aaptOptions {
              // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.

+ 1 - 0
deps/db/deps.edn

@@ -4,6 +4,7 @@
                          :sha     "6d9b5f0db22d960ddf4870503bc181bff9a50c5d"}
   com.cognitect/transit-cljs   {:mvn/version "0.8.280"}
   cljs-bean/cljs-bean          {:mvn/version "1.5.0"}}
+
  :aliases
  {:clj-kondo
   {:replace-deps {clj-kondo/clj-kondo {:mvn/version "2023.05.26"}}

+ 6 - 5
deps/graph-parser/src/logseq/graph_parser/block.cljs

@@ -527,6 +527,7 @@
                                                       (when (coll? refs)
                                                         refs))))
                                             (map :block/original-name))
+                         pre-block? (if (:heading properties) false true)
                          block {:block/uuid id
                                 :block/content content
                                 :block/level 1
@@ -534,16 +535,16 @@
                                 :block/properties-order (vec properties-order)
                                 :block/properties-text-values properties-text-values
                                 :block/invalid-properties invalid-properties
-                                :block/pre-block? true
+                                :block/pre-block? pre-block?
                                 :block/macros (extract-macros-from-ast body)
                                 :block/body body}
                          {:keys [tags refs]}
                          (with-page-block-refs {:body body :refs property-refs} false db date-formatter)]
                      (cond-> block
-                             tags
-                             (assoc :block/tags tags)
-                             true
-                             (assoc :block/refs (concat refs (:block-refs pre-block-properties)))))
+                       tags
+                       (assoc :block/tags tags)
+                       true
+                       (assoc :block/refs (concat refs (:block-refs pre-block-properties)))))
                    (select-keys first-block [:block/format :block/page]))
                   blocks)
                  blocks)]

+ 4 - 0
e2e-tests/fixtures.ts

@@ -126,6 +126,10 @@ base.beforeEach(async () => {
 
     await expect(page.locator('.notification-close-button')).not.toBeVisible()
 
+    if (await page.locator('.notification-clear button').isVisible()) {
+      await page.locator('.notification-clear button').click()
+    }
+
     const rightSidebar = page.locator('.cp__right-sidebar-inner')
     if (await rightSidebar.isVisible()) {
       await page.click('button.toggle-right-sidebar', {delay: 100})

+ 5 - 3
e2e-tests/fs.spec.ts

@@ -2,7 +2,7 @@ import fsp from 'fs/promises';
 import path from 'path';
 import { expect } from '@playwright/test'
 import { test } from './fixtures';
-import { searchPage, captureConsoleWithPrefix, closeSearchBox, createPage, IsWindows } from './utils';
+import { searchPage, captureConsoleWithPrefix, closeSearchBox, createPage, IsWindows, randomString } from './utils';
 
 test('create file on disk then delete', async ({ page, block, graphDir }) => {
   // Since have to wait for file watchers
@@ -122,8 +122,10 @@ test('special page names', async ({ page, block, graphDir }) => {
 
   // Test putting files on disk
   for (const {pageTitle, fileName} of testCases) {
+    const prefix = randomString(10)
+    const fullTitle = `${prefix}${pageTitle}`
     // Create page in Logseq
-    await createPage(page, pageTitle)
+    await createPage(page, fullTitle)
     const text = `content for ${pageTitle}`
     await block.mustFill(text)
     await page.keyboard.press("Enter", { delay: 50 })
@@ -132,7 +134,7 @@ test('special page names', async ({ page, block, graphDir }) => {
     // Wait for the file to be created on disk
     await page.waitForTimeout(2500);
     // Validate that the file is created on disk with the content
-    const filePath = path.join(graphDir, "pages", `${fileName}.md`);
+    const filePath = path.join(graphDir, "pages", `${prefix}${fileName}.md`);
     const fileContent = await fsp.readFile(filePath, "utf8");
     expect(fileContent).toContain(text);
   }

+ 1 - 2
e2e-tests/headings.spec.ts

@@ -33,8 +33,7 @@ test('remove heading', async ({ page }) => {
 
 test('set heading to 2', async ({ page }) => {
   await page.locator('span.bullet-container >> nth=0').click({button: "right"})
-  await page.waitForTimeout(500)
-  await page.locator('#custom-context-menu .to-heading-button[title="Heading 2"]').click()
+  await page.locator('#custom-context-menu .to-heading-button[title="Heading 2"]').click({ delay: 400})
 
   expect(await page.locator('.ls-block .block-content >> nth=0').innerHTML()).toContain('<h2>foo</h2>')
 })

+ 1 - 1
e2e-tests/hotkey.spec.ts

@@ -7,7 +7,7 @@ test('open search dialog', async ({ page }) => {
   await closeSearchBox(page)
   await page.keyboard.press(modKey + '+k')
 
-  await page.waitForSelector('[placeholder="What are you looking for?"]')
+  await page.waitForSelector('[placeholder="What are you looking for?"]', { state: 'visible' })
   await page.keyboard.press('Escape')
   await page.waitForSelector('[placeholder="What are you looking for?"]', { state: 'hidden' })
 })

+ 3 - 2
e2e-tests/util/search-modal.ts

@@ -28,12 +28,13 @@ export async function createPage(page: Page, page_name: string) {// Click #searc
     await page.click('#search-button')
     // Fill [placeholder="What are you looking for?"]
     await page.fill('[placeholder="What are you looking for?"]', page_name)
+    await page.locator('text="Create page"').waitFor({ state: 'visible' })
     await page.keyboard.press('Enter', { delay: 100 })
     // wait for textarea of first block
     await page.waitForSelector('textarea >> nth=0', { state: 'visible' })
 
     return page_name;
-  }
+}
 
 export async function searchAndJumpToPage(page: Page, pageTitle: string) {
     await closeSearchBox(page)
@@ -52,7 +53,7 @@ export async function searchAndJumpToPage(page: Page, pageTitle: string) {
  * @param query the search query to type into the search box
  * @returns the HTML element for the search results ui
  */
-export async function searchPage(page: Page, query: string): Promise<ElementHandle<SVGElement | HTMLElement>[]>{
+export async function searchPage(page: Page, query: string): Promise<ElementHandle<SVGElement | HTMLElement>[]> {
     await closeSearchBox(page)
     await page.click('#search-button')
     await page.waitForSelector('[placeholder="What are you looking for?"]')

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

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

+ 13 - 1
libs/CHANGELOG.md

@@ -4,8 +4,20 @@ All notable changes to this project will be documented in this file.
 
 ## [Unreleased]
 
-## [0.0.15]
+## [0.0.16]
+### Added
+- Support api of `logseq.UI.queryElementRect: (selector: string) => Promise<DOMRectReadOnly | null>`
+- Support api of `logseq.UI.queryElementById: (id: string) => Promise<string | boolean>`
+- Support api of `logseq.UI.checkSlotValid: (slot: UISlotIdentity['slot']) => Promise<boolean>`
+- Support api of `logseq.UI.resolveThemeCssPropsVals: (props: string | Array<string>) => Promise<any>`
+- Support api of `logseq.Assets.builtInOpen(path: string): Promise<boolean | undefined>`
 
+### Fixed
+- fix Plugin can't register command shortcut with editing mode [#10392](https://github.com/logseq/logseq/issues/10392)
+- fix [Plugin API] [Keymap] Command without keybinding can't be present in Keymap [#10466](https://github.com/logseq/logseq/issues/10466)
+- fix [Possible DATA LOSS] [Plugin API] [Keymap] Any plugin could break the global config.edn [#10465](https://github.com/logseq/logseq/issues/10465)
+ 
+## [0.0.15]
 ### Added
 - Support a plug-in flag for the plugin slash commands item
 - Support api of `logseq.App.setCurrentGraphConfigs: (configs: {}) => Promise<void>`

+ 1 - 1
libs/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@logseq/libs",
-  "version": "0.0.15",
+  "version": "0.0.16",
   "description": "Logseq SDK libraries",
   "main": "dist/lsplugin.user.js",
   "typings": "index.d.ts",

+ 38 - 36
libs/src/LSPlugin.core.ts

@@ -54,6 +54,7 @@ const DIR_PLUGINS = 'plugins'
 declare global {
   interface Window {
     LSPluginCore: LSPluginCore
+    DOMPurify: typeof DOMPurify
   }
 }
 
@@ -308,23 +309,25 @@ function initProviderHandlers(pluginLocal: PluginLocal) {
   // provider:ui
   pluginLocal.on(_('ui'), (ui: UIOptions) => {
     pluginLocal._onHostMounted(() => {
-      pluginLocal._dispose(
-        setupInjectedUI.call(
-          pluginLocal,
-          ui,
-          Object.assign(
-            {
-              'data-ref': pluginLocal.id,
-            },
-            ui.attrs || {}
-          ),
-          ({ el, float }) => {
-            if (!float) return
-            const identity = el.dataset.identity
-            pluginLocal.layoutCore.move_container_to_top(identity)
-          }
-        )
+      const ret = setupInjectedUI.call(
+        pluginLocal,
+        ui,
+        Object.assign(
+          {
+            'data-ref': pluginLocal.id,
+          },
+          ui.attrs || {}
+        ),
+        ({ el, float }) => {
+          if (!float) return
+          const identity = el.dataset.identity
+          pluginLocal.layoutCore.move_container_to_top(identity)
+        }
       )
+
+      if (typeof ret === 'function') {
+        pluginLocal._dispose(ret)
+      }
     })
   })
 }
@@ -346,8 +349,6 @@ function initApiProxyHandlers(pluginLocal: PluginLocal) {
       }
     }
 
-    const { _sync } = payload
-
     if (pluginLocal.shadow) {
       if (payload.actor) {
         payload.actor.resolve(ret)
@@ -355,6 +356,8 @@ function initApiProxyHandlers(pluginLocal: PluginLocal) {
       return
     }
 
+    const { _sync } = payload
+
     if (_sync != null) {
       const reply = (result: any) => {
         pluginLocal.caller?.callUserModel(LSPMSG_SYNC, {
@@ -430,7 +433,7 @@ class PluginLocal extends EventEmitter<
 
   async _setupUserSettings(reload?: boolean) {
     const { _options } = this
-    const logger = (this._logger = new PluginLogger('Loader'))
+    const logger = (this._logger = new PluginLogger(`Loader:${this.debugTag}`))
 
     if (_options.settings && !reload) {
       return
@@ -532,7 +535,7 @@ class PluginLocal extends EventEmitter<
     const localRoot = (this._localRoot = safetyPathNormalize(url))
     const logseq: Partial<LSPluginPkgConfig> = pkg.logseq || {}
 
-    // Pick legal attrs
+      // Pick legal attrs
     ;[
       'name',
       'author',
@@ -642,10 +645,10 @@ class PluginLocal extends EventEmitter<
     <meta charset="UTF-8">
     <title>logseq plugin entry</title>
     ${
-      IS_DEV
-        ? `<script src="${sdkPathRoot}/lsplugin.user.js?v=${tag}"></script>`
-        : `<script src="https://cdn.jsdelivr.net/npm/@logseq/libs/dist/lsplugin.user.min.js?v=${tag}"></script>`
-    }
+        IS_DEV
+          ? `<script src="${sdkPathRoot}/lsplugin.user.js?v=${tag}"></script>`
+          : `<script src="https://cdn.jsdelivr.net/npm/@logseq/libs/dist/lsplugin.user.min.js?v=${tag}"></script>`
+      }
     
   </head>
   <body>
@@ -866,7 +869,7 @@ class PluginLocal extends EventEmitter<
 
       this._dispose(cleanInjectedScripts.bind(this))
     } catch (e) {
-      this.logger?.error('[Load Plugin]', e, true)
+      this.logger.error('load', e, true)
 
       this.dispose().catch(null)
       this._status = PluginLocalLoadStatus.ERROR
@@ -924,7 +927,7 @@ class PluginLocal extends EventEmitter<
           )
           this.emit('beforeunload', eventBeforeUnload)
         } catch (e) {
-          this.logger.error('[beforeunload]', e)
+          this.logger.error('beforeunload', e)
         }
 
         await this.dispose()
@@ -932,7 +935,7 @@ class PluginLocal extends EventEmitter<
 
       this.emit('unloaded')
     } catch (e) {
-      this.logger.error('[unload Error]', e)
+      this.logger.error('unload', e)
     } finally {
       this._status = PluginLocalLoadStatus.UNLOADED
     }
@@ -1038,7 +1041,7 @@ class PluginLocal extends EventEmitter<
 
   get debugTag() {
     const name = this._options?.name
-    return `#${this._id} ${name ?? ''}`
+    return `#${this._id} - ${name ?? ''}`
   }
 
   get localRoot(): string {
@@ -1103,8 +1106,7 @@ class LSPluginCore
     | 'beforereload'
     | 'reloaded'
   >
-  implements ILSPluginThemeManager
-{
+  implements ILSPluginThemeManager {
   private _isRegistering = false
   private _readyIndicator?: DeferredActor
   private readonly _hostMountedActor: DeferredActor = deferred()
@@ -1566,12 +1568,12 @@ class LSPluginCore
       await this.saveUserPreferences(
         theme.mode
           ? {
-              themes: {
-                ...this._userPreferences.themes,
-                mode: theme.mode,
-                [theme.mode]: theme,
-              },
-            }
+            themes: {
+              ...this._userPreferences.themes,
+              mode: theme.mode,
+              [theme.mode]: theme,
+            },
+          }
           : { theme: theme }
       )
     }

+ 14 - 29
libs/src/LSPlugin.ts

@@ -192,7 +192,7 @@ export interface BlockEntity {
   level?: number
   meta?: { timestamps: any; properties: any; startPos: number; endPos: number }
   title?: Array<any>
-        marker?: string
+  marker?: string
 }
 
 /**
@@ -235,9 +235,10 @@ export type BlockCursorPosition = {
   rect: DOMRect
 }
 
+export type Keybinding = string | Array<string>
 export type SimpleCommandKeybinding = {
   mode?: 'global' | 'non-editing' | 'editing'
-  binding: string
+  binding: Keybinding
   mac?: string // special for Mac OS
 }
 
@@ -469,25 +470,6 @@ export interface IAppProxy {
   removeTemplate: (name: string) => Promise<any>
   insertTemplate: (target: BlockUUID, name: string) => Promise<any>
 
-  // ui
-  queryElementById: (id: string) => Promise<string | boolean>
-
-  /**
-   * @added 0.0.5
-   * @param selector
-   */
-  queryElementRect: (selector: string) => Promise<DOMRectReadOnly | null>
-
-  /**
-   * @deprecated Use `logseq.UI.showMsg` instead
-   * @param content
-   * @param status
-   */
-  showMsg: (
-    content: string,
-    status?: 'success' | 'warning' | 'error' | string
-  ) => void
-
   setZoomFactor: (factor: number) => void
   setFullScreen: (flag: boolean | 'toggle') => void
   setLeftSidebarVisible: (flag: boolean | 'toggle') => void
@@ -891,20 +873,16 @@ export type UIMsgOptions = {
 export type UIMsgKey = UIMsgOptions['key']
 
 export interface IUIProxy {
-  /**
-   * @added 0.0.2
-   *
-   * @param content
-   * @param status
-   * @param opts
-   */
   showMsg: (
     content: string,
     status?: 'success' | 'warning' | 'error' | string,
     opts?: Partial<UIMsgOptions>
   ) => Promise<UIMsgKey>
-
   closeMsg: (key: UIMsgKey) => void
+  queryElementRect: (selector: string) => Promise<DOMRectReadOnly | null>
+  queryElementById: (id: string) => Promise<string | boolean>
+  checkSlotValid: (slot: UISlotIdentity['slot']) => Promise<boolean>
+  resolveThemeCssPropsVals: (props: string | Array<string>) => Promise<Record<string, string | undefined> | null>
 }
 
 /**
@@ -938,6 +916,13 @@ export interface IAssetsProxy {
    * @param path
    */
   makeUrl(path: string): Promise<string>
+
+  /**
+   * try to open asset type file in Logseq app
+   * @added 0.0.16
+   * @param path
+   */
+  builtInOpen(path: string): Promise<boolean | undefined>
 }
 
 export interface ILSPluginThemeManager {

+ 14 - 5
libs/src/LSPlugin.user.ts

@@ -4,7 +4,7 @@ import {
   mergeSettingsWithSchema,
   PluginLogger,
   safeSnakeCase,
-  safetyPathJoin,
+  safetyPathJoin, normalizeKeyStr,
 } from './helpers'
 import { LSPluginCaller } from './LSPlugin.caller'
 import * as callableAPIs from './callable.apis'
@@ -77,12 +77,21 @@ function registerSimpleCommand (
   },
   action: SimpleCommandCallback
 ) {
+  const { key, label, desc, palette, keybinding, extras } = opts
+
   if (typeof action !== 'function') {
+    this.logger.error(`${key || label}: command action should be function.`)
     return false
   }
 
-  const { key, label, desc, palette, keybinding, extras } = opts
-  const eventKey = `SimpleCommandHook${key}${++registeredCmdUid}`
+  const normalizedKey = normalizeKeyStr(key)
+
+  if (!normalizedKey) {
+    this.logger.error(`${label}: command key is required.`)
+    return false
+  }
+
+  const eventKey = `SimpleCommandHook${normalizedKey}${++registeredCmdUid}`
 
   this.Editor['on' + eventKey](action)
 
@@ -92,7 +101,7 @@ function registerSimpleCommand (
       this.baseInfo.id,
       // [cmd, action]
       [
-        { key, label, type, desc, keybinding, extras },
+        { key: normalizedKey, label, type, desc, keybinding, extras },
         ['editor/hook', eventKey],
       ],
       palette,
@@ -171,7 +180,7 @@ const app: Partial<IAppProxy> = {
 
     const { binding } = keybinding
     const group = '$shortcut$'
-    const key = group + safeSnakeCase(binding)
+    const key = opts.key || (group + safeSnakeCase(binding?.toString()))
 
     return registerSimpleCommand.call(
       this,

+ 48 - 32
libs/src/helpers.ts

@@ -2,7 +2,7 @@ import { SettingSchemaDesc, StyleString, UIOptions } from './LSPlugin'
 import { PluginLocal } from './LSPlugin.core'
 import * as nodePath from 'path'
 import DOMPurify from 'dompurify'
-import merge from 'deepmerge';
+import merge from 'deepmerge'
 import { snakeCase } from 'snake-case'
 import * as callables from './callable.apis'
 import EventEmitter from 'eventemitter3'
@@ -211,12 +211,23 @@ export function deferred<T = any>(timeout?: number, tag?: string) {
 
 export function invokeHostExportedApi(method: string, ...args: Array<any>) {
   method = method?.startsWith('_call') ? method : method?.replace(/^[_$]+/, '')
-  const method1 = safeSnakeCase(method)
+  let method1 = safeSnakeCase(method)
+
+  // @ts-ignore
+  const nsSDK = window.logseq?.sdk
+  const supportedNS = nsSDK && Object.keys(nsSDK)
+  let nsTarget = {}
+  const ns0 = method1?.split('_')?.[0]
+
+  if (ns0 && supportedNS.includes(ns0)) {
+    method1 = method1.replace(new RegExp(`^${ns0}_`), '')
+    nsTarget = nsSDK?.[ns0]
+  }
 
   const logseqHostExportedApi = Object.assign(
     // @ts-ignore
-    window.logseq?.api || {},
-    callables
+    {}, window.logseq?.api,
+    nsTarget, callables
   )
 
   const fn =
@@ -266,9 +277,9 @@ export function setupInjectedStyle(
   el.textContent = style
 
   attrs &&
-    Object.entries(attrs).forEach(([k, v]) => {
-      el.setAttribute(k, v)
-    })
+  Object.entries(attrs).forEach(([k, v]) => {
+    el.setAttribute(k, v)
+  })
 
   document.head.append(el)
 
@@ -313,7 +324,7 @@ export function setupInjectedUI(
     console.error(
       `${this.debugTag} can not resolve selector target ${selector}`
     )
-    return
+    return false
   }
 
   if (ui.template) {
@@ -344,22 +355,22 @@ export function setupInjectedUI(
 
     // update attributes
     attrs &&
-      Object.entries(attrs).forEach(([k, v]) => {
-        el.setAttribute(k, v)
-      })
+    Object.entries(attrs).forEach(([k, v]) => {
+      el.setAttribute(k, v)
+    })
 
     let positionDirty = el.dataset.dx != null
     ui.style &&
-      Object.entries(ui.style).forEach(([k, v]) => {
-        if (
-          positionDirty &&
-          ['left', 'top', 'bottom', 'right', 'width', 'height'].includes(k)
-        ) {
-          return
-        }
-
-        el.style[k] = v
-      })
+    Object.entries(ui.style).forEach(([k, v]) => {
+      if (
+        positionDirty &&
+        ['left', 'top', 'bottom', 'right', 'width', 'height'].includes(k)
+      ) {
+        return
+      }
+
+      el.style[k] = v
+    })
     return
   }
 
@@ -379,14 +390,14 @@ export function setupInjectedUI(
   content.innerHTML = ui.template
 
   attrs &&
-    Object.entries(attrs).forEach(([k, v]) => {
-      el.setAttribute(k, v)
-    })
+  Object.entries(attrs).forEach(([k, v]) => {
+    el.setAttribute(k, v)
+  })
 
   ui.style &&
-    Object.entries(ui.style).forEach(([k, v]) => {
-      el.style[k] = v
-    })
+  Object.entries(ui.style).forEach(([k, v]) => {
+    el.style[k] = v
+  })
 
   let teardownUI: () => void
   let disposeFloat: () => void
@@ -399,11 +410,11 @@ export function setupInjectedUI(
     el.classList.add('lsp-ui-float-container', 'visible')
     disposeFloat =
       (pl._setupResizableContainer(el, key),
-      pl._setupDraggableContainer(el, {
-        key,
-        close: () => teardownUI(),
-        title: attrs?.title,
-      }))
+        pl._setupDraggableContainer(el, {
+          key,
+          close: () => teardownUI(),
+          title: attrs?.title,
+        }))
   }
 
   if (!!slot && ui.reset) {
@@ -542,3 +553,8 @@ export function mergeSettingsWithSchema(
   // shadow copy
   return Object.assign(defaults, settings)
 }
+
+export function normalizeKeyStr(s: string) {
+  if (typeof s !== 'string') return
+  return s.trim().replace(/\s/g, '_').toLowerCase()
+}

+ 2 - 2
package.json

@@ -95,7 +95,7 @@
         "@dnd-kit/sortable": "^7.0.2",
         "@emoji-mart/data": "^1.1.2",
         "@emoji-mart/react": "^1.1.1",
-        "@excalidraw/excalidraw": "0.15.3",
+        "@excalidraw/excalidraw": "0.16.1",
         "@highlightjs/cdn-assets": "10.4.1",
         "@isomorphic-git/lightning-fs": "^4.6.0",
         "@logseq/capacitor-file-sync": "5.0.1",
@@ -117,7 +117,7 @@
         "d3-force": "3.0.0",
         "diff": "5.0.0",
         "dompurify": "2.4.0",
-        "electron": "25.9.3",
+        "electron": "27.1.3",
         "electron-dl": "3.3.0",
         "emoji-mart": "^5.5.2",
         "fs": "0.0.1-security",

+ 11 - 2
resources/forge.config.js

@@ -4,7 +4,8 @@ module.exports = {
   packagerConfig: {
     name: 'Logseq',
     icon: './icons/logseq_big_sur.icns',
-    buildVersion: 74,
+    buildVersion: 76,
+    asar: true,
     protocols: [
       {
         "protocol": "logseq",
@@ -47,8 +48,9 @@ module.exports = {
     },
     {
       name: '@electron-forge/maker-zip',
-      platforms: ['darwin', 'linux']
+      platforms: ['darwin', 'linux', 'win32'],
     },
+
     {
       name: 'electron-forge-maker-appimage',
       platforms: ['linux'],
@@ -69,5 +71,12 @@ module.exports = {
         prerelease: true
       }
     }
+  ],
+
+  plugins: [
+    {
+      name: '@electron-forge/plugin-auto-unpack-natives',
+      config: {}
+    }
   ]
 }

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
resources/js/lsplugin.core.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
resources/js/lsplugin.user.js


+ 1 - 0
resources/js/lsplugin.user.js.LICENSE.txt

@@ -0,0 +1 @@
+/*! @license DOMPurify 2.3.8 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.8/LICENSE */

+ 4 - 3
resources/package.json

@@ -1,7 +1,7 @@
 {
   "name": "Logseq",
   "productName": "Logseq",
-  "version": "0.10.0",
+  "version": "0.10.2",
   "main": "electron.js",
   "author": "Logseq",
   "license": "AGPL-3.0",
@@ -50,13 +50,14 @@
     "@electron-forge/maker-rpm": "^6.0.4",
     "@electron-forge/maker-squirrel": "^6.0.4",
     "@electron-forge/maker-zip": "^6.0.4",
+    "@electron-forge/plugin-auto-unpack-natives": "^6.0.4",
     "@electron/rebuild": "3.2.10",
-    "electron": "25.9.3",
+    "electron": "27.1.3",
     "electron-builder": "^22.11.7",
     "electron-forge-maker-appimage": "https://github.com/logseq/electron-forge-maker-appimage.git"
   },
   "resolutions": {
-    "**/electron": "25.9.3",
+    "**/electron": "27.1.3",
     "**/node-abi": "3.51.0",
     "**/node-gyp": "9.0.0"
   }

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 206 - 206
resources/whiteboard/onboarding.edn


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

@@ -54,7 +54,7 @@
   (try
     (let [graph-path (state/get-graph-path)
           _ (when (string/blank? graph-path)
-              (utils/send-to-renderer "getCurrentGraph" {})
+              (utils/send-to-renderer :setCurrentGraph {})
               (throw (js/Error. "Empty graph path")))
           p (.join node-path graph-path ".git")]
       (when (and (fs/existsSync p)

+ 3 - 0
src/electron/electron/handler.cljs

@@ -671,6 +671,9 @@
 (defmethod handle :system/info [^js _win _]
   {:home-dir (.homedir os)})
 
+(defmethod handle :window/open-blank-callback [^js win [_ _type]]
+  (win/setup-window-listeners! win) nil)
+
 (defn set-ipc-handler! [window]
   (let [main-channel "main"]
     (.handle ipcMain main-channel

+ 2 - 2
src/main/electron/listener.cljs

@@ -62,10 +62,10 @@
                  (fn []
                    (state/pub-event! [:modal/set-git-username-and-email])))
 
-  (safe-api-call "getCurrentGraph"
+  (safe-api-call "setCurrentGraph"
                  (fn []
                    (when-let [graph (state/get-current-repo)]
-                     (ipc/ipc "setCurrentGraph" graph))))
+                     (ipc/ipc :setCurrentGraph graph))))
 
   (safe-api-call "redirect"
                  (fn [data]

+ 14 - 9
src/main/frontend/colors.cljs

@@ -30,6 +30,7 @@
                           "--ls-quaternary-background-color: var(--rx-" (name gray) "-04); "
                           "--ls-link-text-color: var(--rx-" (name color) "-11); "
                           "--ls-link-text-hover-color: var(--rx-" (name color) "-12); "
+                          "--ls-block-ref-link-text-color: var(--rx-" (name color) "-09);"
                           "--ls-secondary-text-color: var(--rx-" (name gray) "-12); "
                           "--ls-primary-text-color: var(--rx-" (name gray) "-11); "
                           "--ls-border-color: var(--rx-" (name gray) "-05); "
@@ -79,12 +80,16 @@
 
 (defn get-accent-color
   []
-  (when-let [hsl-color (some-> js/document.documentElement
-                       (js/getComputedStyle)
-                       (.getPropertyValue "--lx-accent-09")
-                       (str/replace "hsl(" "")
-                       (str/replace ")" "")
-                       (str/split ","))]
-    (when-let [hsl-color (and (not (str/blank? (first hsl-color)))
-                           (map js/parseFloat hsl-color))]
-      (apply util/hsl2hex hsl-color))))
+  (when-let [color (some-> js/document.documentElement
+                     (js/getComputedStyle)
+                     (.getPropertyValue "--lx-accent-09"))]
+    (when-not (str/blank? color)
+      (if (str/starts-with? color "#")
+        color
+        (let [hsl-color (some-> color
+                          (str/replace "hsl(" "")
+                          (str/replace ")" "")
+                          (str/split ","))]
+          (when-let [hsl-color (and (not (str/blank? (first hsl-color)))
+                                 (map js/parseFloat hsl-color))]
+            (apply util/hsl2hex hsl-color)))))))

+ 166 - 84
src/main/frontend/components/cmdk.cljs

@@ -1,33 +1,35 @@
 (ns frontend.components.cmdk
-  (:require
-    [clojure.string :as string]
-    [frontend.components.block :as block]
-    [frontend.context.i18n :refer [t]]
-    [frontend.db :as db]
-    [frontend.db.model :as model]
-    [frontend.handler.command-palette :as cp-handler]
-    [frontend.handler.editor :as editor-handler]
-    [frontend.handler.page :as page-handler]
-    [frontend.handler.route :as route-handler]
-    [frontend.handler.whiteboard :as whiteboard-handler]
-    [frontend.modules.shortcut.core :as shortcut]
-    [frontend.search :as search]
-    [frontend.shui :refer [make-shui-context]]
-    [frontend.state :as state]
-    [frontend.ui :as ui]
-    [frontend.util :as util]
-    [frontend.util.page :as page-util]
-    [goog.functions :as gfun]
-    [goog.object :as gobj]
-    [logseq.shui.core :as shui]
-    [promesa.core :as p]
-    [rum.core :as rum]
-    [frontend.mixins :as mixins]
-    [logseq.graph-parser.util.block-ref :as block-ref]
-    [logseq.graph-parser.util :as gp-util]
-    [logseq.shui.button.v2 :as button]
-    [frontend.modules.shortcut.utils :as shortcut-utils]
-    [frontend.util.text :as text-util]))
+  (:require [clojure.string :as string]
+   [frontend.components.block :as block]
+   [frontend.context.i18n :refer [t]]
+   [frontend.db :as db]
+   [frontend.db.model :as model]
+   [frontend.handler.command-palette :as cp-handler]
+   [frontend.handler.editor :as editor-handler]
+   [frontend.handler.page :as page-handler]
+   [frontend.handler.route :as route-handler]
+   [frontend.handler.whiteboard :as whiteboard-handler]
+   [frontend.modules.shortcut.core :as shortcut]
+   [frontend.search :as search]
+   [frontend.shui :refer [make-shui-context]]
+   [frontend.state :as state]
+   [frontend.ui :as ui]
+   [frontend.util :as util]
+   [frontend.util.page :as page-util]
+   [goog.functions :as gfun]
+   [goog.object :as gobj]
+   [logseq.shui.core :as shui]
+   [promesa.core :as p]
+   [rum.core :as rum]
+   [frontend.mixins :as mixins]
+   [logseq.graph-parser.util.block-ref :as block-ref]
+   [logseq.graph-parser.util :as gp-util]
+   [logseq.shui.button.v2 :as button]
+   [frontend.modules.shortcut.utils :as shortcut-utils]
+   [frontend.config :as config]
+   [logseq.common.path :as path]
+   [electron.ipc :as ipc]
+   [frontend.util.text :as text-util]))
 
 (defn translate [t {:keys [id desc]}]
   (when id
@@ -47,6 +49,8 @@
                                                                                                            :group :blocks}}
    {:text "Search only commands"     :info "Add filter to search" :icon-theme :gray :icon "command" :filter {:mode "search"
                                                                                                              :group :commands}}
+   {:text "Search only whiteboards"  :info "Add filter to search" :icon-theme :gray :icon "whiteboard" :filter {:mode "search"
+                                                                                                                :group :whiteboards}}
    {:text "Search only files"        :info "Add filter to search" :icon-theme :gray :icon "file" :filter {:mode "search"
                                                                                                           :group :files}}])
 
@@ -225,6 +229,26 @@
                                       :source-page page)))))]
       (swap! !results update group        merge {:status :success :items items}))))
 
+(defmethod load-results :whiteboards [group state]
+  (let [!input (::input state)
+        !results (::results state)]
+    (swap! !results assoc-in [group :status] :loading)
+    (p/let [whiteboards (->> (model/get-all-whiteboards (state/get-current-repo))
+                             (map :block/original-name))
+            pages (search/fuzzy-search whiteboards @!input {:limit 100})
+            items (->> pages
+                       (remove nil?)
+                       (keep
+                        (fn [page]
+                          (let [entity (db/entity [:block/name (util/page-name-sanity-lc page)])
+                                whiteboard? (= (:block/type entity) "whiteboard")]
+                            (when whiteboard?
+                              (hash-map :icon "whiteboard"
+                                        :icon-theme :gray
+                                        :text page
+                                        :source-page page))))))]
+      (swap! !results update group        merge {:status :success :items items}))))
+
 (defn highlight-content-query
   "Return hiccup of highlighted content FTS result"
   [content q]
@@ -272,7 +296,17 @@
   (let [!input (::input state)
         !results (::results state)]
     (swap! !results assoc-in [group :status] :loading)
-    (p/let [files (search/file-search @!input 99)
+    (p/let [files* (search/file-search @!input 99)
+            files (remove
+                   (fn [f]
+                     (and
+                      f
+                      (string/ends-with? f ".edn")
+                      (or (string/starts-with? f "whiteboards/")
+                          (string/starts-with? f "assets/")
+                          (string/starts-with? f "logseq/version-files")
+                          (contains? #{"logseq/metadata.edn" "logseq/pages-metadata.edn" "logseq/graphs-txid.edn"} f))))
+                   files*)
             items (map
                    (fn [file]
                      (hash-map :icon "file"
@@ -319,17 +353,44 @@
                         (search/fuzzy-search filters q {:extract-fn :text}))]
     (swap! !results update group merge {:status :success :items matched-items})))
 
+(defmethod load-results :current-page [group state]
+  (if-let [current-page (page-util/get-current-page-id)]
+    (let [!results (::results state)
+          !input (::input state)
+          repo (state/get-current-repo)
+          opts {:limit 100 :page current-page}]
+      (swap! !results assoc-in [group :status] :loading)
+      (swap! !results assoc-in [:current-page :status] :loading)
+      (p/let [blocks (search/block-search repo @!input opts)
+              blocks (remove nil? blocks)
+              items (map (fn [block]
+                           (let [id (if (uuid? (:block/uuid block))
+                                      (:block/uuid block)
+                                      (uuid (:block/uuid block)))]
+                             {:icon "block"
+                              :icon-theme :gray
+                              :text (:block/content block)
+                              :header (block/breadcrumb {:search? true} repo id {})
+                              :current-page? true
+                              :source-block block})) blocks)]
+        (swap! !results update :current-page merge {:status :success :items items})))
+    (reset! (::filter state) nil)))
+
 ;; The default load-results function triggers all the other load-results function
 (defmethod load-results :default [_ state]
-  ;; (js/console.log "load-results/default" @(::input state))
   (if-not (some-> state ::input deref seq)
     (load-results :initial state)
-    (do
-      (load-results :commands state)
-      (load-results :blocks state)
-      (load-results :pages state)
-      (load-results :filters state)
-      (load-results :files state))))
+    (let [filter-group (:group @(::filter state))]
+      (if filter-group
+        (load-results filter-group state)
+        (do
+          (load-results :commands state)
+          (load-results :blocks state)
+          (load-results :pages state)
+          (load-results :filters state)
+          (load-results :files state)
+          (load-results :recents state)
+          (load-results :whiteboards state))))))
 
 (defn- copy-block-ref [state]
   (when-let [block-uuid (some-> state state->highlighted-item :source-block :block/uuid)]
@@ -356,14 +417,20 @@
     (state/close-modal!)))
 
 (defmethod handle-action :open-block [_ state _event]
-  (let [block-id (some-> state state->highlighted-item :source-block :block/uuid)
-        get-block-page (partial model/get-block-page (state/get-current-repo))]
-    (when-let [page (some-> block-id get-block-page)]
-      (let [page-name (:block/name page)]
-        (if (= (:block/type page) "whiteboard")
-          (route-handler/redirect-to-whiteboard! page-name {:block-id block-id})
-          (route-handler/redirect-to-page! page-name {:anchor (str "ls-block-" block-id)})))
-      (state/close-modal!))))
+  (let [block-id (some-> state state->highlighted-item :source-block :block/uuid uuid)
+        get-block-page (partial model/get-block-page (state/get-current-repo))
+        block (db/entity [:block/uuid block-id])]
+    (when block
+      (when-let [page (some-> block-id get-block-page)]
+        (let [page-name (:block/name page)]
+          (cond
+            (= (:block/type page) "whiteboard")
+            (route-handler/redirect-to-whiteboard! page-name {:block-id block-id})
+            (model/parents-collapsed? (state/get-current-repo) block-id)
+            (route-handler/redirect-to-page! (:block/uuid block))
+            :else
+            (route-handler/redirect-to-page! page-name {:anchor (str "ls-block-" block-id)})))
+        (state/close-modal!)))))
 
 (defmethod handle-action :open-page-right [_ state _event]
   (when-let [page-name (get-highlighted-page-name state)]
@@ -378,6 +445,18 @@
     (editor-handler/open-block-in-sidebar! block-uuid)
     (state/close-modal!)))
 
+(defn- open-file
+  [file-path]
+  (if (or (string/ends-with? file-path ".edn")
+          (string/ends-with? file-path ".js")
+          (string/ends-with? file-path ".css"))
+    (route-handler/redirect! {:to :file
+                              :path-params {:path file-path}})
+    ;; open this file in directory
+    (when (util/electron?)
+      (let [file-fpath (path/path-join (config/get-repo-dir (state/get-current-repo)) file-path)]
+        (ipc/ipc "openFileInFolder" file-fpath)))))
+
 (defmethod handle-action :open [_ state event]
   (when-let [item (some-> state state->highlighted-item)]
     (let [block-uuid (:block/uuid (:source-block item))
@@ -390,8 +469,7 @@
           graph-view? (= search-mode :graph)]
       (cond
         (:file-path item) (do
-                            (route-handler/redirect! {:to :file
-                                                      :path-params {:path (:file-path item)}})
+                            (open-file (:file-path item))
                             (state/close-modal!))
         (and graph-view? page? (not shift?)) (do
                                                (state/add-graph-search-filter! @(::input state))
@@ -471,8 +549,8 @@
 (rum/defc mouse-active-effect!
   [*mouse-active? deps]
   (rum/use-effect!
-    #(reset! *mouse-active? false)
-    deps)
+   #(reset! *mouse-active? false)
+   deps)
   nil)
 
 (rum/defcs result-group
@@ -510,9 +588,9 @@
        [:div {:class "flex-1"}]
 
        (when (and (= group highlighted-group)
-               (or can-show-more? can-show-less?)
-               (empty? filter)
-               (not sidebar?))
+                  (or can-show-more? can-show-less?)
+                  (empty? filter)
+                  (not sidebar?))
          [:a.text-link.select-node.opacity-50.hover:opacity-90
           {:on-click (if (= show :more) show-less show-more)}
           (if (= show :more)
@@ -527,28 +605,28 @@
        (for [item visible-items
              :let [highlighted? (= item highlighted-item)]]
          (let [item (shui/list-item (assoc item
-                                      :query (when-not (= group :create) @(::input state))
-                                      :compact true
-                                      :rounded false
-                                      :hoverable @*mouse-active?
-                                      :highlighted highlighted?
+                                           :query (when-not (= group :create) @(::input state))
+                                           :compact true
+                                           :rounded false
+                                           :hoverable @*mouse-active?
+                                           :highlighted highlighted?
                                       ;; for some reason, the highlight effect does not always trigger on a
                                       ;; boolean value change so manually pass in the dep
-                                      :on-highlight-dep highlighted-item
-                                      :on-click (fn [e]
-                                                  (reset! (::highlighted-item state) item)
-                                                  (handle-action :default state item)
-                                                  (when-let [on-click (:on-click item)]
-                                                    (on-click e)))
+                                           :on-highlight-dep highlighted-item
+                                           :on-click (fn [e]
+                                                       (reset! (::highlighted-item state) item)
+                                                       (handle-action :default state item)
+                                                       (when-let [on-click (:on-click item)]
+                                                         (on-click e)))
                                       ;; :on-mouse-enter (fn [e]
                                       ;;                   (when (not highlighted?)
                                       ;;                     (reset! (::highlighted-item state) (assoc item :mouse-enter-triggered-highlight true))))
-                                      :on-highlight (fn [ref]
-                                                      (reset! (::highlighted-group state) group)
-                                                      (when (and ref (.-current ref)
-                                                              (not (:mouse-enter-triggered-highlight @(::highlighted-item state))))
-                                                        (scroll-into-view-when-invisible state (.-current ref)))))
-                      context)]
+                                           :on-highlight (fn [ref]
+                                                           (reset! (::highlighted-group state) group)
+                                                           (when (and ref (.-current ref)
+                                                                      (not (:mouse-enter-triggered-highlight @(::highlighted-item state))))
+                                                             (scroll-into-view-when-invisible state (.-current ref)))))
+                                    context)]
            (if (= group :blocks)
              (ui/lazy-visible (fn [] item) {:trigger-once? true})
              item)))]]]))
@@ -565,7 +643,7 @@
 (defn handle-input-change
   ([state e] (handle-input-change state e (.. e -target -value)))
   ([state e input]
-   (let [composing? (util/onchange-event-is-composing? e)
+   (let [composing? (util/native-event-is-composing? e)
          e-type (gobj/getValueByKeys e "type")
          !input (::input state)
          !load-results-throttled (::load-results-throttled state)]
@@ -591,7 +669,7 @@
         keyname (.-key e)
         enter? (= keyname "Enter")
         esc? (= keyname "Escape")
-        composing? (util/event-is-composing? e)
+        composing? (util/goog-event-is-composing? e)
         highlighted-group @(::highlighted-group state)
         show-less (fn [] (swap! (::results state) assoc-in [highlighted-group :show] :less))
         show-more (fn [] (swap! (::results state) assoc-in [highlighted-group :show] :more))
@@ -601,14 +679,13 @@
     (reset! (::shift? state) shift?)
     (reset! (::meta? state) meta?)
     (when (or as-keydown? as-keyup?)
-      (.preventDefault e))
+      (util/stop e))
 
     (cond
       (and meta? enter?)
       (let [repo (state/get-current-repo)]
         (state/close-modal!)
         (state/sidebar-add-block! repo input :search))
-
       as-keydown? (if meta?
                     (show-more)
                     (move-highlight state 1))
@@ -619,11 +696,12 @@
                                       (handle-action :default state e)
                                       (util/stop-propagation e))
       esc? (let [filter @(::filter state)]
-             (when (or (and filter @(::input-changed? state))
-                       (not (string/blank? input)))
+             (when-not (string/blank? input)
+               (util/stop e)
+               (handle-input-change state nil ""))
+             (when (and filter (string/blank? input))
                (util/stop e)
-               (reset! (::filter state) nil)
-               (when-not filter (handle-input-change state nil ""))))
+               (reset! (::filter state) nil)))
       (and meta? (= keyname "c")) (do
                                     (copy-block-ref state)
                                     (util/stop-propagation e))
@@ -678,9 +756,13 @@
        :on-key-down (fn [e]
                       (let [value (.-value @input-ref)
                             last-char (last value)
-                            backspace? (= (util/ekey e) "Backspace")]
-                        (when (and (some? @(::filter state))
-                                   (or (= (util/ekey e) "/")
+                            backspace? (= (util/ekey e) "Backspace")
+                            filter-group (:group @(::filter state))
+                            slash? (= (util/ekey e) "/")
+                            namespace-page-matched? (when (and slash? (contains? #{:pages :whiteboards} filter-group))
+                                                      (some #(string/includes? % "/") (search/page-search (str value "/"))))]
+                        (when (and filter-group
+                                   (or (and slash? (not namespace-page-matched?))
                                        (and backspace? (= last-char "/"))
                                        (and backspace? (= input ""))))
                           (reset! (::filter state) nil))))
@@ -695,7 +777,7 @@
      [:div "to filter search results"]]
     [:div.flex.flex-row.gap-1.items-center.opacity-50.hover:opacity-100
      (shui/shortcut ["mod" "enter"] context)
-     [:div "to open search in the sidebar"]]])  )
+     [:div "to open search in the sidebar"]]]))
 
 (rum/defcs tip <
   {:init (fn [state]
@@ -723,7 +805,7 @@
                                   :on-click #(handle-action action (assoc state :opts opts) %)
                                   :shortcut shortcut
                                   :muted true}
-                      context))]
+                                 context))]
     (when action
       [:div {:class "flex w-full px-3 py-2 gap-2 justify-between"
              :style {:background "var(--lx-gray-03)"
@@ -837,7 +919,7 @@
 
       (when group-filter
         [:div.flex.flex-col.px-3.py-1.opacity-70.text-sm
-         (search-only state (name group-filter))])
+         (search-only state (string/capitalize (name group-filter)))])
 
       (let [items (filter
                    (fn [[_group-name group-key group-count _group-items]]
@@ -851,7 +933,7 @@
                    results-ordered)]
         (if (seq items)
           (for [[group-name group-key _group-count group-items] items]
-            (let [title group-name]
+            (let [title (string/capitalize group-name)]
               (result-group state title group-key group-items first-item sidebar?)))
           [:div.flex.flex-col.p-4.opacity-50
            (when-not (string/blank? (rum/react *input))

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

@@ -373,7 +373,7 @@
                         (some-> (js/document.querySelector ".cp__plugins-page") (.focus))
                         (reset! *search-key nil))))
      :on-change   #(let [^js target (.-target %)]
-                     (reset! *search-key (util/trim-safe (.-value target))))
+                     (reset! *search-key (some-> (.-value target) (string/triml))))
      :value       (or search-key "")}]])
 
 (rum/defc panel-tab-developer

+ 7 - 16
src/main/frontend/components/reference.cljs

@@ -63,18 +63,18 @@
       [:div.mx-auto.flex-shrink-0.flex.items-center.justify-center.h-12.w-12.rounded-full.bg-gray-200.text-gray-500.sm:mx-0.sm:h-10.sm:w-10
        (ui/icon "filter" {:size 20})]
       [:div.mt-3.text-center.sm:mt-0.sm:ml-4.sm:text-left.pb-2
-       [:h3#modal-headline.text-lg.leading-6.font-medium "Filter"]
+       [:h3#modal-headline.text-lg.leading-6.font-medium (t :linked-references/filter-heading)]
        [:span.text-xs
-        "Click to include and shift-click to exclude. Click again to remove."]]]
+        (t :linked-references/filter-directions)]]]
      (when (seq filters)
        [:div.cp__filters.mb-4.ml-2
         (when (seq includes)
           [:div.flex.flex-row.flex-wrap.center-items
-           [:div.mr-1.font-medium.py-1 "Includes: "]
+           [:div.mr-1.font-medium.py-1 (t :linked-references/filter-includes)]
            (filtered-refs page-name filters filters-atom includes)])
         (when (seq excludes)
           [:div.flex.flex-row.flex-wrap
-           [:div.mr-1.font-medium.py-1 "Excludes: " ]
+           [:div.mr-1.font-medium.py-1 (t :linked-references/filter-excludes)]
            (filtered-refs page-name filters filters-atom excludes)])])
      [:div.cp__filters-input-panel.flex
       (ui/icon "search")
@@ -134,14 +134,9 @@
         *collapsed? (atom nil)]
     (ui/foldable
      [:div.flex.flex-row.flex-1.justify-between.items-center
-      [:h2.font-medium (str
-                        (when (seq filters)
-                          (str filter-n " of "))
-                        total
-                        " Linked Reference"
-                        (when (> total 1) "s"))]
+      [:h2.font-medium (t :linked-references/reference-count (if (seq filters) filter-n nil) total)]
       [:a.filter.fade-link
-       {:title "Filter"
+       {:title (t :linked-references/filter-heading)
         :on-mouse-over (fn [_e]
                          (when @*collapsed? ; collapsed
                            ;; expand
@@ -294,11 +289,7 @@
         [:div.references.page-unlinked.mt-6.flex-1.flex-row
          [:div.content.flex-1
           (ui/foldable
-           [:h2.font-medium
-            (if @n-ref
-              (str @n-ref " Unlinked Reference" (when (> @n-ref 1)
-                                                  "s"))
-              "Unlinked References")]
+           [:h2.font-medium (t :unlinked-references/reference-count @n-ref)]
            (fn [] (unlinked-references-aux page-name n-ref))
            {:default-collapsed? true
             :title-trigger? true})]]))))

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

@@ -58,8 +58,7 @@
 
      [:div.ctls.flex.items-center
 
-      [:div.mt-1.sm:mt-0.sm:col-span-2
-       {:style {:display "flex" :gap "0.5rem" :align-items "center"}}
+      [:div.mt-1.sm:mt-0.sm:col-span-2.flex.gap-4.items-center.flex-wrap
        [:div (cond
                (mobile-util/native-android?)
                (ui/button
@@ -1136,7 +1135,7 @@
 
     [:div#settings.cp__settings-main
      (settings-effect @*active)
-     [:div.cp__settings-inner {:class "min-h-[65dvh] max-h-[65dvh]"}
+     [:div.cp__settings-inner {:class "min-h-[65dvh] max-h-[70dvh]"}
       [:aside.md:w-64 {:style {:min-width "10rem"}}
        [:header.cp__settings-header
         [:h1.cp__settings-modal-title (t :settings)]]

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

@@ -481,7 +481,7 @@ svg.cmd {
 body[data-settings-tab=keymap] {
   .cp__settings-inner {
     > article {
-      @apply p-0;
+      @apply p-0 lg:w-[680px];
 
       > header {
         @apply p-4 pb-2 h-auto;

+ 9 - 7
src/main/frontend/components/shortcut.cljs

@@ -15,8 +15,7 @@
             [frontend.util :as util]
             [frontend.modules.shortcut.utils :as shortcut-utils]
             [frontend.modules.shortcut.config :as shortcut-config]
-            [logseq.shui.core :as shui]
-            [frontend.shui :refer [make-shui-context]])
+            [logseq.shui.core :as shui])
   (:import [goog.events KeyHandler]))
 
 (defonce categories
@@ -152,9 +151,13 @@
 
 (rum/defc shortcut-desc-label
   [id binding-map]
-  (when-let [id' (and id binding-map (some-> (str id) (string/replace "plugin." "")))]
-    [:span {:title (str id' "#" (some-> (:handler-id binding-map) (name)))}
-     [:span.pl-1 (dh/get-shortcut-desc (assoc binding-map :id id))]]))
+  (when-let [id' (and id binding-map (str id))]
+    (let [plugin? (string/starts-with? id' ":plugin.")
+          id' (if plugin? (some-> id' (string/replace "plugin." "")) id')
+          plugin-id (when plugin? (namespace id))]
+      [:span {:title (str id' "#" (some-> (:handler-id binding-map) (name)))}
+       [:span.px-1 (dh/get-shortcut-desc (assoc binding-map :id id))]
+       (when plugin? [:code plugin-id])])))
 
 (defn- open-customize-shortcut-dialog!
   [id]
@@ -473,5 +476,4 @@
                         (not unset?)
                         [:code.flex.items-center.bg-transparent
                          (shui/shortcut-v1 (string/join " | " (map #(dh/binding-for-display id %) binding))
-                                           (make-shui-context)
-                                           {:size :md})])]]))))])])]]))
+                           nil {:size :md})])]]))))])])]]))

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

@@ -276,7 +276,7 @@
                          "text-md px-3 py-2 cursor-default whiteboard-page-refs-count"
                          {:hover? true
                           :render-fn (fn [open? refs-count] [:span.whiteboard-page-refs-count-label
-                                                             (if (> refs-count 1) "References" "Reference")
+                                                             (t :whiteboard/reference-count refs-count)
                                                              (ui/icon (if open? "references-hide" "references-show")
                                                                       {:extension? true})])})]]
      (tldraw-app page-name block-id)]))

+ 1 - 0
src/main/frontend/db/model.cljs

@@ -1565,6 +1565,7 @@ independent of format as format specific heading characters are stripped"
   [repo]
   (d/q
     '[:find [(pull ?page [:block/name
+                          :block/original-name
                           :block/created-at
                           :block/updated-at]) ...]
       :where

+ 3 - 15
src/main/frontend/db/query_dsl.cljs

@@ -480,12 +480,11 @@ Some bindings in this fn:
               (string/replace tag-placeholder "#")))))
 
 (defn- add-bindings!
-  [form q]
+  [q]
   (let [forms (set (flatten q))
         syms ['?b '?p 'not]
         [b? p? not?] (-> (set/intersection (set syms) forms)
-                         (map syms))
-        or? (contains? (set (flatten form)) 'or)]
+                         (map syms))]
     (cond
       not?
       (cond
@@ -501,16 +500,6 @@ Some bindings in this fn:
         :else
         q)
 
-      or?
-      (cond
-        (->> (flatten form)
-             (remove (every-pred string? page-ref/page-ref?))
-             (some string?))            ; block full-text search
-        (concat [['?b :block/content '?content]] [q])
-
-        :else
-        q)
-
       (and b? p?)
       (concat [['?b :block/page '?p]] q)
 
@@ -554,8 +543,7 @@ Some bindings in this fn:
                                 ;; [(not (page-ref ?b "page 2"))]
                                 (keyword (ffirst result))
                                 (keyword (first result)))]
-                      (add-bindings! form
-                                     (if (= key :and) (rest result) result))))]
+                      (add-bindings! (if (= key :and) (rest result) result))))]
       {:query result'
        :rules (if db-graph?
                 (rules/extract-rules rules/db-query-dsl-rules rules)

+ 2 - 5
src/main/frontend/db/utils.cljs

@@ -45,7 +45,7 @@
 
 (defn entity
   "This function will return nil if passed `id-or-lookup-ref` is an integer and
-  the entity doesn't exist in db (Datascript will return {:id id}).
+  the entity doesn't exist in db.
   `repo-or-db`: a repo string or a db,
   `id-or-lookup-ref`: same as d/entity."
   ([id-or-lookup-ref]
@@ -57,10 +57,7 @@
                      (conn/get-db repo))
                    ;; db
                    repo-or-db)]
-     (if (integer? id-or-lookup-ref)
-       (when (d/datoms db :eavt id-or-lookup-ref)
-         (d/entity db id-or-lookup-ref))
-       (d/entity db id-or-lookup-ref)))))
+     (d/entity db id-or-lookup-ref))))
 
 (defn special-id->page
   "Convert special id backs to page name."

+ 0 - 8
src/main/frontend/db_worker.cljs

@@ -202,14 +202,6 @@
   [repo]
   (get-sqlite-conn repo {:search? true}))
 
-(defn <remove-all-files!
-  "!! Dangerous: use it only for development."
-  []
-  (p/let [all-files (<list-all-files)
-          files (filter #(= (.-kind %) "file") all-files)
-          dirs (filter #(= (.-kind %) "directory") all-files)
-          _ (p/all (map (fn [file] (.remove file)) files))]
-    (p/all (map (fn [dir] (.remove dir)) dirs))))
 
 #_:clj-kondo/ignore
 (defclass SQLiteDB

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

@@ -106,7 +106,7 @@
                  :height (if wide-mode? 650 500)}}
         (excalidraw
          (merge
-          {:on-change (fn [elements app-state]
+          {:on-change (fn [elements app-state files]
                         (when-not (or (= "down" (gobj/get app-state "cursorButton"))
                                       (gobj/get app-state "draggingElement")
                                       (gobj/get app-state "editingElement")
@@ -118,7 +118,7 @@
                               (reset! *elements elements->clj)
                               (draw/save-excalidraw!
                                file
-                               (serializeAsJSON elements app-state))))))
+                               (serializeAsJSON elements app-state files "local"))))))
 
            :zen-mode-enabled @*zen-mode?
            :view-mode-enabled @*view-mode?

+ 5 - 1
src/main/frontend/extensions/pdf/windows.cljs

@@ -1,6 +1,7 @@
 (ns frontend.extensions.pdf.windows
   (:require [frontend.state :as state]
             [rum.core :as rum]
+            [cljs-bean.core :as bean]
             [frontend.storage :as storage]))
 
 (def *active-win (atom nil))
@@ -103,7 +104,10 @@
                                                                  :y      (.-screenY win)})))
 
                 (reset! *active-win win)
-                (state/set-state! :pdf/system-win? true))))]
+                (state/set-state! :pdf/system-win? true)
+                ;; NOTE: must do ipc in new window
+                (some-> (.-apis win)
+                  (.doAction (bean/->js [:window/open-blank-callback :pdf]))))))]
 
       (js/setTimeout
        (fn []

+ 1 - 1
src/main/frontend/fs/node.cljs

@@ -83,7 +83,7 @@
     (-> (ipc/ipc "mkdir" dir)
         (p/then (fn [_] (js/console.log (str "Directory created: " dir))))
         (p/catch (fn [error]
-                   (when (not= (.-code error) "EEXIST")
+                   (when-not (string/includes? (str error) "EEXIST")
                      (js/console.error (str "Error creating directory: " dir) error))))))
 
   (mkdir-recur! [_this dir]

+ 54 - 52
src/main/frontend/fs/watcher_handler.cljs

@@ -67,69 +67,71 @@
                  :else (config/get-local-repo dir))
           repo-dir (config/get-local-dir repo)
           {:keys [mtime]} stat
-          db-content (db/get-file repo path)
-          exists-in-db? (not (nil? db-content))
-          db-content (or db-content "")]
-      (when (or content (contains? #{"unlink" "unlinkDir" "addDir"} type))
-        (cond
-          (and (= "unlinkDir" type) dir)
-          (state/pub-event! [:graph/dir-gone dir])
+          ext (keyword (path/file-ext path))]
+      (when (contains? #{:org :md :markdown :css :js :edn :excalidraw :tldr} ext)
+        (let [db-content (db/get-file repo path)
+              exists-in-db? (not (nil? db-content))
+              db-content (or db-content "")]
+          (when (or content (contains? #{"unlink" "unlinkDir" "addDir"} type))
+            (cond
+              (and (= "unlinkDir" type) dir)
+              (state/pub-event! [:graph/dir-gone dir])
 
-          (and (= "addDir" type) dir)
-          (state/pub-event! [:graph/dir-back repo dir])
+              (and (= "addDir" type) dir)
+              (state/pub-event! [:graph/dir-back repo dir])
 
-          (contains? (:file/unlinked-dirs @state/state) dir)
-          nil
+              (contains? (:file/unlinked-dirs @state/state) dir)
+              nil
 
-          (and (= "add" type)
-               (not= (string/trim content) (string/trim db-content)))
-          (let [backup? (not (string/blank? db-content))]
-            (handle-add-and-change! repo path content db-content mtime backup?))
+              (and (= "add" type)
+                   (not= (string/trim content) (string/trim db-content)))
+              (let [backup? (not (string/blank? db-content))]
+                (handle-add-and-change! repo path content db-content mtime backup?))
 
-          (and (= "change" type)
-               (= dir repo-dir)
-               (not= (string/trim content) (string/trim db-content))
-               (not (gp-config/local-asset? path)))
-          (when-not (and
-                     (string/includes? path (str "/" (config/get-journals-directory) "/"))
-                     (or
-                      (= (string/trim content)
-                         (string/trim (or (state/get-default-journal-template) "")))
-                      (= (string/trim content) "-")
-                      (= (string/trim content) "*")))
-            (handle-add-and-change! repo path content db-content mtime (not global-dir))) ;; no backup for global dir
+              (and (= "change" type)
+                   (= dir repo-dir)
+                   (not= (string/trim content) (string/trim db-content))
+                   (not (gp-config/local-asset? path)))
+              (when-not (and
+                         (string/includes? path (str "/" (config/get-journals-directory) "/"))
+                         (or
+                          (= (string/trim content)
+                             (string/trim (or (state/get-default-journal-template) "")))
+                          (= (string/trim content) "-")
+                          (= (string/trim content) "*")))
+                (handle-add-and-change! repo path content db-content mtime (not global-dir))) ;; no backup for global dir
 
-          (and (= "unlink" type)
-               exists-in-db?)
-          (p/let [dir-exists? (fs/file-exists? dir "")]
-            (when dir-exists?
-              (when-let [page-name (db/get-file-page path)]
-                (println "Delete page: " page-name ", file path: " path ".")
-                (page-handler/delete! page-name #() :delete-file? false))))
+              (and (= "unlink" type)
+                   exists-in-db?)
+              (p/let [dir-exists? (fs/file-exists? dir "")]
+                (when dir-exists?
+                  (when-let [page-name (db/get-file-page path)]
+                    (println "Delete page: " page-name ", file path: " path ".")
+                    (page-handler/delete! page-name #() :delete-file? false))))
 
           ;; global config handling
-          (and (= "change" type)
-               (= dir (global-config-handler/global-config-dir)))
-          (when (= path "config.edn")
-            (file-handler/alter-global-file
-             (global-config-handler/global-config-path) content {:from-disk? true}))
+              (and (= "change" type)
+                   (= dir (global-config-handler/global-config-dir)))
+              (when (= path "config.edn")
+                (file-handler/alter-global-file
+                 (global-config-handler/global-config-path) content {:from-disk? true}))
 
-          (and (= "change" type)
-               (not exists-in-db?))
-          (js/console.error "Can't get file in the db: " path)
+              (and (= "change" type)
+                   (not exists-in-db?))
+              (js/console.error "Can't get file in the db: " path)
 
-          (and (contains? #{"add" "change" "unlink"} type)
-               (string/ends-with? path "logseq/custom.css"))
-          (do
-            (println "reloading custom.css")
-            (ui-handler/add-style-if-exists!))
+              (and (contains? #{"add" "change" "unlink"} type)
+                   (string/ends-with? path "logseq/custom.css"))
+              (do
+                (println "reloading custom.css")
+                (ui-handler/add-style-if-exists!))
 
-          (contains? #{"add" "change" "unlink"} type)
-          nil
+              (contains? #{"add" "change" "unlink"} type)
+              nil
 
-          :else
-          (log/error :fs/watcher-no-handler {:type type
-                                             :payload payload})))
+              :else
+              (log/error :fs/watcher-no-handler {:type type
+                                                 :payload payload})))))
 
       ;; return nil, otherwise the entire db will be transferred by ipc
       nil)))

+ 4 - 1
src/main/frontend/handler/command_palette.cljs

@@ -35,7 +35,10 @@
           @(get @state/state :command-palette/commands)))
 
 (defn history
-  ([] (or (storage/get "commands-history") []))
+  ([] (or (try (storage/get "commands-history")
+               (catch js/Error e
+                 (log/error :commands-history e)))
+          []))
   ([vals] (storage/set "commands-history" vals)))
 
 (defn- assoc-invokes [cmds]

+ 20 - 18
src/main/frontend/handler/editor.cljs

@@ -2040,8 +2040,8 @@
                   keep-uuid?
                   revert-cut-txs]
            :or {exclude-properties []}}]
-  (let [repo (state/get-current-repo)
-        editing-block (when-let [editing-block (state/get-edit-block)]
+  (state/set-editor-op! :paste-blocks)
+  (let [editing-block (when-let [editing-block (state/get-edit-block)]
                         (some-> (db/pull [:block/uuid (:block/uuid editing-block)])
                                 (assoc :block/content (state/get-edit-content))))
         has-unsaved-edits (and editing-block
@@ -2080,19 +2080,21 @@
        (outliner-core/save-block! editing-block)))
 
     (outliner-tx/transact!
-     {:outliner-op :insert-blocks
-      :additional-tx revert-cut-txs}
-     (when target-block'
-       (let [format (or (:block/format target-block') (state/get-preferred-format))
-             blocks' (map (fn [block]
-                            (paste-block-cleanup repo block page exclude-properties format content-update-fn keep-uuid?))
-                       blocks)
-             result (outliner-core/insert-blocks! blocks' target-block' {:sibling? sibling?
-                                                                         :outliner-op :paste
-                                                                         :replace-empty-target? replace-empty-target?
-                                                                         :keep-uuid? keep-uuid?})]
-         (state/set-block-op-type! nil)
-         (edit-last-block-after-inserted! result))))))
+      {:outliner-op :insert-blocks
+       :additional-tx revert-cut-txs}
+      (when target-block'
+        (let [format (or (:block/format target-block') (state/get-preferred-format))
+              repo (state/get-current-repo)
+              blocks' (map (fn [block]
+                             (paste-block-cleanup repo block page exclude-properties format content-update-fn keep-uuid?))
+                        blocks)
+              result (outliner-core/insert-blocks! blocks' target-block' {:sibling? sibling?
+                                                                          :outliner-op :paste
+                                                                          :replace-empty-target? replace-empty-target?
+                                                                          :keep-uuid? keep-uuid?})]
+          (state/set-block-op-type! nil)
+          (edit-last-block-after-inserted! result)))))
+  (state/set-editor-op! nil))
 
 (defn- block-tree->blocks
   "keep-uuid? - maintain the existing :uuid in tree vec"
@@ -2897,7 +2899,7 @@
              (contains? #{:property-search :property-value-search} (state/get-editor-action)))
         (state/clear-editor-action!)
 
-        (and (util/event-is-composing? e true) ;; #3218
+        (and (util/goog-event-is-composing? e true) ;; #3218
              (not hashtag?) ;; #3283 @Rime
              (not (state/get-editor-show-page-search-hashtag?))) ;; #3283 @MacOS pinyin
         nil
@@ -3056,7 +3058,7 @@
 (defn keyup-handler
   [_state input input-id]
   (fn [e key-code]
-    (when-not (util/event-is-composing? e)
+    (when-not (util/goog-event-is-composing? e)
       (let [current-pos (cursor/pos input)
             value (gobj/get input "value")
             c (util/nth-safe value (dec current-pos))
@@ -3083,7 +3085,7 @@
                  (gobj/get e "key")
                  (gobj/getValueByKeys e "event_" "code"))
                 ;; #3440
-               (util/event-is-composing? e true)])]
+               (util/goog-event-is-composing? e true)])]
         (cond
           ;; When you type something after /
           (and (= :commands (state/get-editor-action)) (not= k commands/command-trigger))

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

@@ -42,7 +42,7 @@
     (when (and
            (not (contains? #{:insert :indent-outdent :auto-save :undo :redo :delete} (state/get-editor-op)))
            ;; Don't trigger auto-save if the latest op is undo or redo
-           (not (contains? #{:undo :redo} (state/get-editor-latest-op))))
+           (not (contains? #{:undo :redo :paste-blocks} (state/get-editor-latest-op))))
       (editor-handler/save-block! (get-state) value)))
   state)
 

+ 3 - 3
src/main/frontend/handler/paste.cljs

@@ -159,14 +159,14 @@
                                          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)]
+                      text)
+            blocks? (text-blocks? text')]
         (cond
           blocks?
-          (paste-text-parseable format text)
+          (paste-text-parseable format text')
 
           (util/safe-re-find #"(?:\r?\n){2,}" text')
           (paste-segmented-text format text')

+ 10 - 8
src/main/frontend/handler/plugin.cljs

@@ -25,12 +25,13 @@
 (defn- normalize-keyword-for-json
   [input]
   (when input
-    (walk/postwalk
-      (fn [a]
-        (cond
-          (keyword? a) (csk/->camelCase (name a))
-          (uuid? a) (str a)
-          :else a)) input)))
+    (let [f (fn [[k v]] (if (keyword? k) [(csk/->camelCase (name k)) v] [k v]))]
+      (walk/postwalk
+        (fn [x]
+          (cond
+            (map? x) (into {} (map f x))
+            (uuid? x) (str x)
+            :else x)) input))))
 
 (defn invoke-exported-api
   [type & args]
@@ -297,7 +298,8 @@
   [pid key keybinding]
   (let [id      (keyword (str "plugin." pid "/" key))
         binding (:binding keybinding)
-        binding (some->> (if (string? binding) [binding] (seq binding))
+        binding (some->> (if (string? binding) [binding] (vec binding))
+                         (remove string/blank?)
                          (map shortcut-utils/undecorate-binding))
         binding (if util/mac?
                   (or (:mac keybinding) binding) binding)
@@ -495,7 +497,7 @@
                    payload)
                  (if (keyword? plugin-id) (name plugin-id) plugin-id))
       (catch :default e
-        (js/console.error "[Hook Plugin Err]" e)))))
+        (log/error :invoke-hook-exception e)))))
 
 (defn hook-plugin-app
   ([type payload] (hook-plugin-app type payload nil))

+ 4 - 12
src/main/frontend/handler/user.cljs

@@ -134,19 +134,11 @@
           (and (<= 400 (:status resp))
                (> 500 (:status resp)))
           ;; invalid refresh-token
-          (do
+          (let [invalid-grant? (and (= 400 (:status resp))
+                                    (= (:error (:body resp)) "invalid_grant"))]
             (prn :debug :refresh-token-failed
-                 :status (:status resp)
-                 :user-id (user-uuid)
-                 :refresh-token refresh-token
-                 :resp resp)
-            (state/pub-event! [:instrument {:type :refresh-token-failed
-                                            :payload {:status (:status resp)
-                                                      :user-id (user-uuid)
-                                                      :refresh-token refresh-token
-                                                      :resp resp}}])
-            (when (and (= 400 (:status resp))
-                       (= (:error (:body resp)) "invalid_grant"))
+                 :status (:status resp))
+            (when invalid-grant?
               (clear-tokens)))
 
           ;; e.g. api return 500, server internal error

+ 28 - 10
src/main/frontend/modules/shortcut/config.cljs

@@ -153,6 +153,18 @@
    :whiteboard/toggle-grid                  {:binding "t g"
                                              :fn      #(.toggleGrid (.-api ^js (state/active-tldraw-app)))}
 
+   :whiteboard/clone-right                  {:binding (if mac? "ctrl+shift+right" "alt+right")
+                                             :fn      #(.clone (.-api ^js (state/active-tldraw-app)) "right")}
+
+   :whiteboard/clone-left                   {:binding (if mac? "ctrl+shift+left" "alt+left")
+                                             :fn      #(.clone (.-api ^js (state/active-tldraw-app)) "left")}
+
+   :whiteboard/clone-up                     {:binding (if mac? "ctrl+shift+up" "alt+up")
+                                             :fn      #(.clone (.-api ^js (state/active-tldraw-app)) "up")}
+
+   :whiteboard/clone-down                   {:binding (if mac? "ctrl+shift+down" "alt+down")
+                                             :fn      #(.clone (.-api ^js (state/active-tldraw-app)) "down")}
+
    :auto-complete/complete                  {:binding "enter"
                                              :fn      ui-handler/auto-complete-complete}
 
@@ -907,7 +919,11 @@
      :whiteboard/unlock
      :whiteboard/group
      :whiteboard/ungroup
-     :whiteboard/toggle-grid]
+     :whiteboard/toggle-grid
+     :whiteboard/clone-left
+     :whiteboard/clone-right
+     :whiteboard/clone-top
+     :whiteboard/clone-bottom]
 
     :shortcut.category/others
     [:pdf/previous-page
@@ -966,15 +982,17 @@
 (def *shortcut-cmds (atom {}))
 
 (defn add-shortcut!
-  [handler-id id shortcut-map]
-  (swap! *config assoc-in [handler-id id] shortcut-map)
-  (swap! *shortcut-cmds assoc id (:cmd shortcut-map))
-  (let [plugin? (str/starts-with? (str id) ":plugin.")
-        category (or (:category shortcut-map)
-                     (if plugin?
-                       :shortcut.category/plugins
-                       :shortcut.category/others))]
-    (swap! *category update category #(conj % id))))
+  ([handler-id id shortcut-map] (add-shortcut! handler-id id shortcut-map false))
+  ([handler-id id shortcut-map config-only?]
+   (swap! *config assoc-in [handler-id id] shortcut-map)
+   (when-not config-only?
+     (swap! *shortcut-cmds assoc id (:cmd shortcut-map))
+     (let [plugin? (str/starts-with? (str id) ":plugin.")
+           category (or (:category shortcut-map)
+                        (if plugin?
+                          :shortcut.category/plugins
+                          :shortcut.category/others))]
+       (swap! *category update category #(conj % id))))))
 
 (defn remove-shortcut!
   [handler-id id]

+ 2 - 1
src/main/frontend/modules/shortcut/core.cljs

@@ -92,7 +92,8 @@
   (when-let [handler (get-handler-by-id handler-id)]
     (when-let [ks (dh/shortcut-binding shortcut-id)]
       (doseq [k ks]
-        (.unregisterShortcut ^js handler (shortcut-utils/undecorate-binding k))))
+        (.unregisterShortcut ^js handler (shortcut-utils/undecorate-binding k)))))
+  (when shortcut-id
     (shortcut-config/remove-shortcut! handler-id shortcut-id)))
 
 (defn uninstall-shortcut-handler!

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

@@ -116,7 +116,7 @@
 (defn page-search
   "Return a list of page names that match the query"
   ([q]
-   (page-search q 500))
+   (page-search q 100))
   ([q limit]
    (when-let [repo (state/get-current-repo)]
      (let [q (util/search-normalize q (state/enable-search-remove-accents?))

+ 0 - 1
src/main/frontend/search/agency.cljs

@@ -19,7 +19,6 @@
 
 (deftype Agency [repo]
   protocol/Engine
-
   (query [_this q opts]
     (let [[e1 e2] (get-registered-engines repo)]
       (doseq [e e2]

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

@@ -2313,12 +2313,14 @@ Similar to re-frame subscriptions"
 (defn set-color-accent! [color]
   (swap! state assoc :ui/radix-color color)
   (storage/set :ui/radix-color color)
-  (colors/set-radix color))
+  (colors/set-radix color)
+  (util/set-android-theme))
 
 (defn unset-color-accent! []
   (swap! state assoc :ui/radix-color nil)
   (storage/remove :ui/radix-color)
-  (colors/unset-radix))
+  (colors/unset-radix)
+  (util/set-android-theme))
 
 (defn cycle-color! []
   (let [current-color (get-color-accent)

+ 70 - 24
src/main/frontend/util.cljc

@@ -211,23 +211,53 @@
      (gobj/get js/window "innerWidth")))
 
 ;; Keep the following colors in sync with common.css
+#?(:cljs
+   (defn- get-computed-bg-color
+     []
+     ;; window.getComputedStyle(document.body, null).getPropertyValue('background-color');
+     (let [styles (js/window.getComputedStyle (js/document.querySelector "#app-container"))
+           bg-color (gobj/get styles "background-color")
+           ;; convert rgb(r,g,b) to #rrggbb
+           rgb2hex (fn [rgb]
+                     (->> rgb
+                          (map (comp #(.toString % 16) parse-long string/trim))
+                          (map #(if (< (count %) 2)
+                                  (str "0" %)
+                                  %))
+                          (string/join)
+                          (str "#")))]
+       (when (string/starts-with? bg-color "rgb(")
+         (let [rgb (-> bg-color
+                       (string/replace #"^rgb\(" "")
+                       (string/replace #"\)$" "")
+                       (string/split #","))
+               rgb (take 3 rgb)]
+           (rgb2hex rgb)))))
+)
+
+#?(:cljs
+   (defn set-android-theme
+     []
+     (when (mobile-util/native-android?)
+       (when-let [bg-color (try (get-computed-bg-color)
+                                (catch :default _
+                                  nil))]
+         (.setNavigationBarColor NavigationBar (clj->js {:color bg-color}))
+         (.setBackgroundColor StatusBar (clj->js {:color bg-color}))))))
+
 #?(:cljs
    (defn set-theme-light
      []
      (p/do!
       (.setStyle StatusBar (clj->js {:style (.-Light Style)}))
-      (when (mobile-util/native-android?)
-        (.setNavigationBarColor NavigationBar (clj->js {:color "#ffffff"}))
-        (.setBackgroundColor StatusBar (clj->js {:color "#ffffff"}))))))
+      (set-android-theme))))
 
 #?(:cljs
    (defn set-theme-dark
      []
      (p/do!
       (.setStyle StatusBar (clj->js {:style (.-Dark Style)}))
-      (when (mobile-util/native-android?)
-        (.setNavigationBarColor NavigationBar (clj->js {:color "#002b36"}))
-        (.setBackgroundColor StatusBar (clj->js {:color "#002b36"}))))))
+      (set-android-theme))))
 
 (defn find-first
   [pred coll]
@@ -1048,7 +1078,8 @@
                      (d/set-attr! :type "text/css")
                      (d/set-attr! :href style)
                      (d/set-attr! :media "all"))]
-           (d/append! parent-node link))))))
+           (d/append! parent-node link))
+         (set-android-theme)))))
 
 (defn remove-common-preceding
   [col1 col2]
@@ -1370,25 +1401,40 @@
        [] (breakpoint? 640))))
 
 #?(:cljs
-   (defn event-is-composing?
-     "Check if keydown event is a composing (IME) event.
-      Ignore the IME process by default."
-     ([e]
-      (event-is-composing? e false))
-     ([e include-process?]
-      (let [event-composing? (gobj/getValueByKeys e "event_" "isComposing")]
-        (if include-process?
-          (or event-composing?
-              (= (gobj/get e "keyCode") 229)
-              (= (gobj/get e "key") "Process"))
-          event-composing?)))))
-
-#?(:cljs
-   (defn onchange-event-is-composing?
+   (do
+     (defn goog-event?
+       [^js e]
+       (and e (fn? (gobj/get e "getBrowserEvent"))))
+
+     (defn goog-event-is-composing?
+       "Check if keydown event is a composing (IME) event.
+        Ignore the IME process by default."
+       ([^js e]
+        (goog-event-is-composing? e false))
+       ([^js e include-process?]
+        (when (goog-event? e)
+          (let [event-composing? (some-> (.getBrowserEvent e) (.-isComposing))]
+            (if include-process?
+              (or event-composing?
+                (= (gobj/get e "keyCode") 229)
+                (= (gobj/get e "key") "Process"))
+              event-composing?)))))))
+
+#?(:cljs
+   (defn native-event-is-composing?
      "Check if onchange event of Input is a composing (IME) event.
        Always ignore the IME process."
-     [e]
-     (gobj/getValueByKeys e "nativeEvent" "isComposing"))) ;; No keycode available
+     [^js e]
+     (when-let [^js native-event
+                (and e (cond
+                         (goog-event? e)
+                         (.getBrowserEvent e)
+
+                         (js-in "_reactName" e)
+                         (.-nativeEvent e)
+
+                         :else e))]
+       (.-isComposing native-event))))
 
 #?(:cljs
    (defn open-url

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

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

+ 16 - 27
src/main/logseq/api.cljs

@@ -4,7 +4,6 @@
             [logseq.sdk.core]
             [logseq.sdk.utils :as sdk-utils]
             [logseq.sdk.ui :as sdk-ui]
-            [logseq.sdk.git :as sdk-git]
             [logseq.sdk.assets :as sdk-assets]
             [clojure.string :as string]
             [datascript.core :as d]
@@ -34,6 +33,7 @@
             [frontend.modules.outliner.tree :as outliner-tree]
             [frontend.handler.command-palette :as palette-handler]
             [frontend.modules.shortcut.core :as st]
+            [frontend.modules.shortcut.config :as shortcut-config]
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.util.cursor :as cursor]
@@ -352,7 +352,7 @@
             cmd         (assoc cmd :key (string/replace (:key cmd) ":" "-"))
             key         (:key cmd)
             keybinding  (:keybinding cmd)
-            palette-cmd (and palette? (plugin-handler/simple-cmd->palette-cmd pid cmd action))
+            palette-cmd (plugin-handler/simple-cmd->palette-cmd pid cmd action)
             action'     #(state/pub-event! [:exec-plugin-cmd {:type type :key key :pid pid :cmd cmd :action action}])]
 
         ;; handle simple commands
@@ -369,8 +369,16 @@
                                  (palette-handler/invoke-command palette-cmd)
                                  (action')))
                 [mode-id id shortcut-map] (update shortcut-args 2 merge cmd {:fn dispatch-cmd :cmd palette-cmd})]
-            (println :shortcut/register-shortcut [mode-id id shortcut-map])
-            (st/register-shortcut! mode-id id shortcut-map)))))))
+
+            (cond
+              ;; FIX ME: move to register logic
+              (= mode-id :shortcut.handler/block-editing-only)
+              (shortcut-config/add-shortcut! mode-id id shortcut-map)
+
+              :else
+              (do
+                (println :shortcut/register-shortcut [mode-id id shortcut-map])
+                (st/register-shortcut! mode-id id shortcut-map)))))))))
 
 (defn ^:export unregister_plugin_simple_command
   [pid]
@@ -378,10 +386,10 @@
   (plugin-handler/unregister-plugin-simple-command pid)
 
   ;; remove palette commands
-  (let [palette-matched (->> (palette-handler/get-commands)
-                             (filter #(string/includes? (str (:id %)) (str "plugin." pid))))]
-    (when (seq palette-matched)
-      (doseq [cmd palette-matched]
+  (let [cmds-matched (->> (vals @shortcut-config/*shortcut-cmds)
+                          (filter #(string/includes? (str (:id %)) (str "plugin." pid))))]
+    (when (seq cmds-matched)
+      (doseq [cmd cmds-matched]
         (palette-handler/unregister (:id cmd))
         ;; remove keybinding commands
         (when (seq (:shortcut cmd))
@@ -898,19 +906,10 @@
   (when-let [args (and args (seq (bean/->clj args)))]
     (shell/run-git-command! args)))
 
-;; git
-(def ^:export git_exec_command sdk-git/exec_command)
-(def ^:export git_load_ignore_file sdk-git/load_ignore_file)
-(def ^:export git_save_ignore_file sdk-git/save_ignore_file)
-
 ;; ui
 (def ^:export show_msg sdk-ui/-show_msg)
-(def ^:export ui_show_msg sdk-ui/show_msg)
-(def ^:export ui_close_msg sdk-ui/close_msg)
 
 ;; assets
-(def ^:export assets_list_files_of_current_graph sdk-assets/list_files_of_current_graph)
-(def ^:export assets_make_url sdk-assets/make_url)
 (def ^:export make_asset_url sdk-assets/make_url)
 
 ;; experiments
@@ -1002,16 +1001,6 @@
       (p/then #(bean/->js %))))
 
 ;; helpers
-(defn ^:export query_element_by_id
-  [id]
-  (when-let [^js el (gdom/getElement id)]
-    (if el (str (.-tagName el) "#" id) false)))
-
-(defn ^:export query_element_rect
-  [selector]
-  (when-let [^js el (js/document.querySelector selector)]
-    (bean/->js (.toJSON (.getBoundingClientRect el)))))
-
 (defn ^:export set_focused_settings
   [pid]
   (when-let [plugin (state/get-plugin-by-id pid)]

+ 13 - 1
src/main/logseq/sdk/assets.cljs

@@ -2,7 +2,10 @@
   (:require [electron.ipc :as ipc]
             [cljs-bean.core :as bean]
             [promesa.core :as p]
-            [frontend.handler.editor :as editor-handler]))
+            [frontend.handler.editor :as editor-handler]
+            [frontend.extensions.pdf.assets :as pdf-assets]
+            [frontend.state :as state]
+            [frontend.util :as util]))
 
 (def ^:export make_url editor-handler/make-asset-url)
 
@@ -10,3 +13,12 @@
   [^js exts]
   (p/let [files (ipc/ipc :getAssetsFiles {:exts exts})]
     (bean/->js files)))
+
+(defn ^:export built_in_open
+  [asset-file]
+  (when-let [ext (util/trim-safe (util/get-file-ext asset-file))]
+    (cond
+      (contains? #{"pdf"} ext)
+      (state/set-current-pdf! (pdf-assets/inflate-asset asset-file))
+
+      :else false)))

+ 32 - 4
src/main/logseq/sdk/ui.cljs

@@ -1,7 +1,9 @@
 (ns logseq.sdk.ui
   (:require [frontend.handler.notification :as notification]
             [cljs-bean.core :as bean]
+            [goog.dom :as gdom]
             [sci.core :as sci]
+            [frontend.util :as util]
             [clojure.string :as string]))
 
 (defn- parse-hiccup-ui
@@ -19,9 +21,9 @@
    (let [{:keys [key timeout]} (bean/->clj opts)
          hiccup? (and (string? content) (string/starts-with? (string/triml content) "[:"))
          content (if hiccup? (parse-hiccup-ui content) content)
-         uid     (when (string? key) (keyword key))
-         clear?  (not= timeout 0)
-         key'    (notification/show! content (keyword status) clear? uid timeout nil)]
+         uid (when (string? key) (keyword key))
+         clear? (not= timeout 0)
+         key' (notification/show! content (keyword status) clear? uid timeout nil)]
      (name key'))))
 
 (defn ^:export show_msg
@@ -31,4 +33,30 @@
 (defn ^:export close_msg
   [key]
   (when (string? key)
-    (notification/clear! (keyword key)) nil))
+    (notification/clear! (keyword key)) nil))
+
+(defn ^:export query_element_rect
+  [selector]
+  (when-let [^js el (js/document.querySelector selector)]
+    (bean/->js (.toJSON (.getBoundingClientRect el)))))
+
+(defn ^:export query_element_by_id
+  [id]
+  (when-let [^js el (gdom/getElement id)]
+    (if el (str (.-tagName el) "#" id) false)))
+
+(defn ^:export check_slot_valid
+  [slot]
+  (when (string? slot)
+    (boolean (query_element_by_id slot))))
+
+(defn ^:export resolve_theme_css_props_vals
+  [props]
+  (when-let [props (if (string? props) [props] (bean/->clj props))]
+    (let [^js s (js/window.getComputedStyle js/document.body)]
+      (some->> (for [prop props]
+                 (when (string? prop)
+                   [prop (util/trim-safe (.getPropertyValue s prop))]))
+               (remove empty?)
+               (into {})
+               (bean/->js)))))

+ 23 - 0
src/resources/dicts/en.edn

@@ -222,6 +222,24 @@
  :page/updated-at "Updated At"
  :page/backlinks "Backlinks"
  :linked-references/filter-search "Search in linked pages"
+ :linked-references/filter-heading "Filter"
+ :linked-references/filter-directions "Click to include and shift-click to exclude. Click again to remove."
+ :linked-references/filter-includes "Includes: "
+ :linked-references/filter-excludes "Excludes: "
+ :linked-references/reference-count (fn [filtered-count total]
+                                      ;; 1 Linked Reference
+                                      ;; 1 of 1 Linked Reference
+                                      ;; 2 of 5 Linked References
+                                      (str
+                                       (when filtered-count
+                                         (str filtered-count " of "))
+                                       total
+                                       (if (= total 1) " Linked Reference" " Linked References")))
+ :unlinked-references/reference-count (fn [total]
+                                        ;; 1 Unlinked Reference
+                                        ;; 5 Unlinked References
+                                        (str total
+                                         (if (= total 1) " Unlinked Reference" " Unlinked References")))
  :editor/block-search "Search for a block"
  :text/image "Image"
  :asset/show-in-folder "Show image in folder"
@@ -459,6 +477,7 @@
  :whiteboard/toggle-grid "Toggle grid"
  :whiteboard/snap-to-grid "Snap to grid"
  :whiteboard/toggle-pen-mode "Toggle pen mode"
+ :whiteboard/reference-count (fn [refs-count] (if (= refs-count 1) "Reference" "References"))
  :flashcards/modal-welcome-title "Time to create a card!"
  :flashcards/modal-welcome-desc-1 "You can add \"#card\" to any block to turn it into a card or trigger \"/cloze\" to add some clozes."
  :flashcards/modal-welcome-desc-2 "You can "
@@ -760,6 +779,10 @@
   :whiteboard/group               "Group selection"
   :whiteboard/ungroup             "Ungroup selection"
   :whiteboard/toggle-grid         "Toggle the canvas grid"
+  :whiteboard/clone-right         "Clone right"
+  :whiteboard/clone-left          "Clone left"
+  :whiteboard/clone-up            "Clone up"
+  :whiteboard/clone-down          "Clone down"
   :ui/toggle-brackets             "Toggle whether to display brackets"
   :go/electron-find-in-page       "Find text in page"
   :go/electron-jump-to-the-next   "Jump to the next match to your Find bar search"

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

@@ -119,6 +119,7 @@
  :command.cards/recall                              "Tarjetas: tomar un momento para recordar"
  :command.cards/remembered                          "Tarjetas: recordadas"
  :command.cards/toggle-answers                      "Tarjetas: mostrar/ocultar respuestas/prueba de completar oraciones "
+ :command.command-palette/toggle                    "Buscar comandos"
  :command.command/run                               "Ejecutar comando git"
  :command.command/toggle-favorite                   "Agregar a/eliminar de favoritos"
  :command.date-picker/complete                      "Selector de fechas: Escoger día seleccionado"
@@ -205,6 +206,7 @@
  :command.go/next-journal                           "Ir al siguiente diario"
  :command.go/prev-journal                           "Ir al diario anterior"
  :command.go/search                                 "Buscar en el grafo"
+ :command.go/search-in-page                         "Buscar bloques en página"
  :command.go/tomorrow                               "Ir a mañana"
  :command.go/whiteboards                            "Ir a pizarras"
  :command.graph/add                                 "Agregar un grafo"
@@ -222,6 +224,8 @@
  :command.sidebar/close-top                         "Cierra el elemento superior en la barra lateral derecha"
  :command.sidebar/open-today-page                   "Abrir la página de hoy en barra lateral derecha"
  :command.ui/clear-all-notifications                "Borrar todas las notificaciones"
+ :command.ui/cycle-color                            "Ciclo de color"
+ :command.ui/cycle-color-off                        "Ciclo de color desactivado"
  :command.ui/goto-plugins                           "Ir al panel de extensiones"
  :command.ui/install-plugins-from-file              "Instalar extensiones de plugins.edn"
  :command.ui/select-theme-color                     "Seleccionar temas de colores disponibles"
@@ -338,7 +342,7 @@
  :flashcards/modal-toggle-preview-mode              "Alternar modo de vista previa"
  :flashcards/modal-toggle-random-mode               "Alternar modo aleatorio"
  :flashcards/modal-welcome-desc-1                   "Puedes agregar \"#card\" a cualquier bloque para convertirlo en una tarjeta o ejecutar \"/cloze\" para agregar algunos clozes."
- :flashcards/modal-welcome-desc-2                   "Puedes"
+ :flashcards/modal-welcome-desc-2                   "Puedes "
  :flashcards/modal-welcome-desc-3                   "dar clic en este enlace"
  :flashcards/modal-welcome-desc-4                   " para revisar la documentación."
  :flashcards/modal-welcome-title                    "¡Hora de crear una tarjeta!"
@@ -374,6 +378,7 @@
  :help/privacy                                      "Política de privacidad"
  :help/reference-autocomplete                       "Referencia de página"
  :help/roadmap                                      "Hoja de ruta"
+ :help/search                                       "Buscar páginas/bloques/comandos"
  :help/shortcut                                     "Atajo"
  :help/shortcut-page-title                          "Atajos de teclado"
  :help/shortcuts                                    "Atajos de teclado"

+ 31 - 15
src/resources/dicts/fr.edn

@@ -9,6 +9,7 @@
     :help/docs "Documentation"
     :help/privacy "Politique de confidentialité"
     :help/terms "Conditions d'utilisation"
+    :help/search "Recherches des pages, blocs ou commandes"
     :help/shortcuts "Raccourcis clavier"
     :help/shortcuts-triggers "Actions déclenchées"
     :help/shortcut "Raccourcis"
@@ -132,7 +133,7 @@
     :file-rn/close-panel "Fermer le panneau"
     :file-rn/confirm-proceed "Mettre à jour le format !"
     :file-rn/filename-desc-1 "Ce réglage configure la manière dont une page est enregistrée dans un fichier. Logseq enregistre une page dans un fichier portant le même nom."
-    :file-rn/filename-desc-2 "Certains caractères spéciaux comme \"/\" or \"?\" sont invalides pour un nom de fichier."
+    :file-rn/filename-desc-2 "Certains caractères spéciaux comme \"/\" ou \"?\" sont invalides pour un nom de fichier."
     :file-rn/filename-desc-3 "Logseq remplace les caractères invalides par leur équivalent encodé façon URL pour les rendre valides (ex. : \"?\" devient \"%3F\")"
     :file-rn/filename-desc-4 "Le séparateur d'espace de nommage \"/\" est également remplacé par \"___\" (triple barre de soulignement) pour des raisons esthétiques."
     :file-rn/format-deprecated "Vous utilisez un format obsolète. Mettre à jour le format est fortement recommandé. Veuillez sauvegarder vos données, fermez Logseq sur vos autres postes avant de lancer l'opération."
@@ -149,7 +150,7 @@
     :file-rn/re-index "La réindexation est fortement recommandée après que les fichiers aient été renommés, puis sur les autres postes après synchronisation."
     :file-rn/rename "renommer le fichier \"{1}\" en \"{2}\""
     :file-rn/select-confirm-proceed "Dev: format d'écriture"
-    :file-rn/select-format "(Option du Mode Developpeur, Danger !) Sélectionnez le format de nom de fichier"
+    :file-rn/select-format "(Option du Mode veloppeur, Danger !) Sélectionnez le format de nom de fichier"
     :file-rn/suggest-rename "Action requise : "
     :file-rn/unreachable-title "Attention ! La page deviendra {1} sous le format actuel, à moins que vous n'ayez modifié la propriété `title::`"
     :graph/all-graphs "Tous les graphes"
@@ -161,8 +162,13 @@
     :left-side-bar/nav-recent-pages "Récentes"
     :left-side-bar/new-page "Nouvelle page"
     :left-side-bar/new-whiteboard "Nouveau tableau blanc"
+    :linked-references/filter-directions "Cliquer pour inclure et `Maj+clic` pour exclure. Cliquez à nouveau pour enlever."
+    :linked-references/filter-excludes "Exclut : "
+    :linked-references/filter-heading "Filtrer"
+    :linked-references/filter-includes "Inclut : "
+    :linked-references/reference-count (fn [filtered-count total] (str filtered-count (when filtered-count (if (= filtered-count 1) " référence liée" " références liées")) " parmi " total))
     :linked-references/filter-search "Rechercher dans les pages liées"
-    :on-boarding/add-graph "Ajouter un graphe"
+ :on-boarding/add-graph "Ajouter un graphe"
     :on-boarding/demo-graph "Il s'agit d'un graphe de démo, les changements ne seront pas enregistrés à moins que vous n'ouvriez un dossier local."
     :on-boarding/new-graph-desc-1 "Logseq supporte à la fois le Markdown et l'Org-mode. Vous pouvez ouvrir un dossier existant ou en créer un nouveau sur cet appareil. Vos données seront enregistrées uniquement sur cet appareil."
     :on-boarding/new-graph-desc-2 "Après avoir ouvert votre dossier, cela créera 3 sous-dossiers :"
@@ -303,6 +309,7 @@
     :command.ui/toggle-help                 "Afficher l'aide"
     :command.git/commit                     "Message de validation Git"
     :command.go/search                      "Recherche globale dans le texte"
+    :command.go/search-in-page "Rechercher des blocs dans la page"
     :command.ui/toggle-document-mode        "Intervertir le mode document"
     :command.ui/toggle-theme                "Intervertir le thème foncé/clair"
     :command.ui/toggle-right-sidebar        "Afficher/cacher la barre latérale"
@@ -317,6 +324,7 @@
     :command.cards/recall  "Cartes : ayant pris du temps à se souvenir"
     :command.cards/remembered  "Cartes : souvenues"
     :command.cards/toggle-answers  "Cartes : montrer/masquer les réponses/clozes"
+    :command.command-palette/toggle "Rechercher des commandes"
     :command.command/run  "Lancer une commande Git"
     :command.command/toggle-favorite  "Ajouter/retirer des favoris"
     :command.date-picker/complete  "Sélecteur de date : choisir le jour sélectionné"
@@ -391,6 +399,8 @@
     :command.search/re-index  "Reconstruire l'index de recherche"
     :command.sidebar/clear  "Vider intégralement la barre latérale droite"
     :command.sidebar/open-today-page  "Ouvrir la page du jour dans la barre latérale droite"
+    :command.ui/cycle-color "Bascule de la couleur"
+    :command.ui/cycle-color-off "Bascule de la couleur désactivée"
     :command.ui/goto-plugins  "Aller vers le panneau des extensions"
     :command.ui/install-plugins-from-file  "Installer les extensions depuis plugins.edn"
     :command.ui/select-theme-color  "Sélectionner les couleurs disponibles du thème"
@@ -399,6 +409,10 @@
     :command.ui/toggle-left-sidebar  "Basculer la barre latérale gauche"
     :command.ui/toggle-settings  "Basculer l'affichage des réglages"
     :command.ui/toggle-wide-mode  "Basculer la vue en pleine largeur"
+    :command.whiteboard/clone-down "Cloner au-dessous"
+    :command.whiteboard/clone-left "Cloner à gauche"
+    :command.whiteboard/clone-right "Cloner à droite"
+    :command.whiteboard/clone-up "Cloner au-dessus"
     :shortcut.category/basics  "Bases"
     :shortcut.category/block-command-editing  "Édition de commande de bloc"
     :shortcut.category/block-editing  "Édition de bloc en général"
@@ -525,7 +539,9 @@
     :whiteboard/align-center-horizontally "Aligner horizontalement au centre"
     :whiteboard/align-bottom "Aligner en bas"
     :whiteboard/add-block-or-page "Ajouter un bloc ou une page"
-    :tips/all-done "Tout est complété!"
+    :whiteboard/reference-count (fn [refs-count] (if (= refs-count 1) "Référence" "Références"))
+    :whiteboard/toggle-pen-mode "Activer/désactiver le mode stylet"
+    :tips/all-done "Tout est complété !"
     :shortcut.category/whiteboard "Tableau blanc"
     :shortcut.category/plugins "Plugins"
     :settings-permission/start-granting "Permettre"
@@ -554,13 +570,13 @@
     :page/slide-view-tip-go-fullscreen "(fn [] [[:span.opacity-70 \"Conseil: appuyer \"] [:code \"f\"] [:span.opacity-70 \" pour entrer en mode plein écran\"]])"
     :bug-report/inspector-page-placeholder "Appuyer longuement ici pour coller si vous êtes sur mobile"
     :bug-report/inspector-page-tip "Quelque chose ne va pas ? Pas de problème, cliquez pour retourner à…"
-    :bug-report/main-desc "Pouvez-vous nous aider en signalant un bogue? Nous…"
+    :bug-report/main-desc "Pouvez-vous nous aider en signalant un bogue ? Nous…"
     :bug-report/main-title "Signalement de bogue"
     :bug-report/section-clipboard-btn-desc "Inspecter et récupérer les données du presse-papier"
     :bug-report/section-clipboard-btn-title "Assistant Presse-papier"
     :bug-report/section-clipboard-desc "Vous pouvez utiliser ces outils pratiques pour nous donner plus…"
     :bug-report/section-clipboard-title "Est-ce que le bogue rencontré est relié à ces fonctionn…"
-    :bug-report/section-issues-btn-desc "Aidez à rendre Logseq meilleur!"
+    :bug-report/section-issues-btn-desc "Aidez à rendre Logseq meilleur !"
     :bug-report/section-issues-btn-title "Soumettre un signalement de bogue"
     :bug-report/section-issues-desc "S'il n'y a pas d'outils accessibles pour vous permettre de…"
     :bug-report/section-issues-title "Ou…"
@@ -607,14 +623,14 @@
     :context-menu/make-a-flashcard "Créer une carte mémoire"
     :context-menu/make-a-template "Créer un gabarit"
     :context-menu/preview-flashcard "Prévisualiser la carte mémoire"
-    :context-menu/template-exists-warning "Le gabarit existe déjà!"
+    :context-menu/template-exists-warning "Le gabarit existe déjà !"
     :context-menu/toggle-number-list "Activer la liste numérotée"
     :dev/show-block-ast "(Dev) Montrer le bloc AST"
     :dev/show-block-data "(Dev) Montrer les données du bloc"
     :dev/show-page-ast "(Dev) Montrer la page AST"
     :dev/show-page-data "(Dev) Montrer les données de la page"
     :editor/collapse-block-children "Réduire tout"
-    :editor/cycle-todo "Tourner l'état TODO de l'élément courant"
+    :editor/cycle-todo "Basculer l'état TODO de l'élément courant"
     :editor/delete-selection "Supprimer les blocs sélectionnés"
     :editor/expand-block-children "Étendre tout"
     :file-rn/all-action "Appliquer toutes les actions ({1})"
@@ -635,9 +651,9 @@
     :flashcards/modal-toggle-preview-mode "Activer la prévisualisation"
     :flashcards/modal-toggle-random-mode "Activer le mode aléatoire"
     :flashcards/modal-welcome-desc-1 "Vous pouvez ajouter \"#card\" à un bloc pour le transformer en…"
-    :flashcards/modal-welcome-desc-2 "Vous pouvez"
+    :flashcards/modal-welcome-desc-2 "Vous pouvez "
     :flashcards/modal-welcome-desc-3 "cliquer ce lien"
-    :flashcards/modal-welcome-desc-4 "pour consulter la documentation."
+    :flashcards/modal-welcome-desc-4 " pour consulter la documentation."
     :flashcards/modal-welcome-title "C'est le temps de créer une carte mémoire !"
     :flashcards/modal-btn-remembered "Souvenue"
     :graph/local-graphs "Graphes locaux :"
@@ -664,7 +680,7 @@
     :on-boarding/importing-roam-desc "Importer un export JSON de votre graphe Roam"
     :on-boarding/importing-title "Avez-vous déjà des notes que vous désirez importer ?…"
     :on-boarding/main-desc "Premièrement, vous devez choisir un dossier où Logseq…"
-    :on-boarding/main-title "(fn [] [\"Bienvenue sur \" [:strong \"Logseq!\"]])"
+    :on-boarding/main-title "(fn [] [\"Bienvenue sur \" [:strong \"Logseq !\"]])"
     :on-boarding/quick-tour-btn-back "Retour"
     :on-boarding/quick-tour-btn-finish "Terminer"
     :on-boarding/quick-tour-btn-next "Suivant"
@@ -691,9 +707,9 @@
     :on-boarding/tour-whiteboard-btn-back "Retour"
     :on-boarding/tour-whiteboard-btn-finish "Terminer"
     :on-boarding/tour-whiteboard-btn-next "Suivant"
-    :page/illegal-page-name "Nom de page interdit!"
+    :page/illegal-page-name "Nom de page interdit !"
     :page/logseq-is-having-a-problem "Logseq a un problème. Pour essayer de le ravoir…"
-    :page/page-already-exists "Page “{1}” existe déjà!"
+    :page/page-already-exists "Page “{1}” existe déjà !"
     :page/slide-view "Visualiser en diapositives"
     :page/something-went-wrong "Quelque chose s'est mal passée"
     :page/step "Étape {1}"
@@ -729,7 +745,7 @@
     :settings-page/app-updated "Votre appli est à jour 🎉"
     :settings-page/auto-expand-block-refs "Étendre les références de bloc automatiquement lors du zoom…"
     :settings-page/auto-expand-block-refs-tip "Cette option contrôle s'il faut étendre le bloc…"
-    :settings-page/changelog "Quoi de neuf?"
+    :settings-page/changelog "Quoi de neuf ?"
     :settings-page/check-for-updates "Vérifier les mises à jour"
     :settings-page/checking "Vérification en cours…"
     :settings-page/enable-journals "Journaux"
@@ -780,5 +796,5 @@
     :settings-page/auto-chmod "Automatiquement changer les permissions du fichier"
     :settings-page/auto-chmod-desc "Désactiver pour permettre l'édition par plusieurs utilisateurs avec les permissions données par l'appartenance au groupe."
     :settings-page/tab-keymap "Raccourcis"
-    :whiteboard/toggle-pen-mode "Activer/désactiver le mode stylet"
+    :unlinked-references/reference-count (fn [total] (str total (if (= total 1) " référence non liée" " références liées")))
 }

+ 9 - 0
src/resources/dicts/pl.edn

@@ -46,6 +46,7 @@
  :right-side-bar/new-page "Nowa strona"
  :right-side-bar/show-journals "Pokaż dzienniki"
  :left-side-bar/journals "Dzienniki"
+ :left-side-bar/create "Utwórz"
  :left-side-bar/new-page "Nowa strona"
  :left-side-bar/nav-favorites "Ulubione"
  :left-side-bar/nav-recent-pages "Ostatnio odwiedzane"
@@ -96,6 +97,7 @@
  :settings-page/disable-sentry "Wyślij statystyki użycia aplikacji do Logseq"
  :settings-page/preferred-outdenting "Logiczne zmniejszanie wcięć"
  :settings-page/custom-date-format "Preferowany format daty"
+ :settings-page/custom-date-format-warning "Wymagane ponowne indeksowanie! Istniejące odniesienia mogą zostać uszkodzone!"
  :settings-page/preferred-file-format "Preferowany format pliku"
  :settings-page/preferred-workflow "Preferowany flow TODOsów"
  :settings-page/enable-shortcut-tooltip "Włącz podpowiedzi skrótu klawiszowego"
@@ -116,6 +118,13 @@
  :settings-page/plugin-system "System pluginów"
  :settings-page/enable-flashcards "Fiszki"
  :settings-page/network-proxy "Ustawienia proxy"
+ :settings-page/changelog "Co nowego?"
+ :settings-page/theme-light "jasny"
+ :settings-page/theme-dark "ciemny"
+ :settings-page/theme-system "systemowy"
+ :settings-page/tab-keymap "Skróty klawiaturowe"
+ :settings-page/disable-sentry-desc "Logseq nigdy nie zbiera danych Twoich lokalnych grafów i nie sprzedaje twoich danych."
+ :settings-page/clear-cache-warning "Wyczyszczenie pamięci podręcznej spowoduje usunięcie otwartych grafów. Twoje niezapisane zmiany zostaną utracone."
  :yes "Tak"
  :submit "Wyślij"
  :cancel "Anuluj"

+ 531 - 154
src/resources/dicts/sk.edn

@@ -17,6 +17,84 @@
  :on-boarding/tour-whiteboard-home-description     "Tabule majú v aplikácii svoju vlastnú sekciu, kde si ich môžete jednoducho prehliadať, vytvárať nové alebo ich ľahko vymazať."
  :on-boarding/tour-whiteboard-new                  "{1} Vytvoriť novú tabuľu"
  :on-boarding/tour-whiteboard-new-description      "Existuje niekoľko spôsobov, ako vytvoriť novú tabuľu. Jeden z nich je vždy priamo tu na ovládacom paneli."
+ :handbook/title                                   "Pomoc"
+ :handbook/topics                                  "Témy"
+ :handbook/popular-topics                          "Populárne témy"
+ :handbook/help-categories                         "Kategórie pomoci"
+ :handbook/search                                  "Hľadať"
+ :handbook/home                                    "Domov"
+ :handbook/settings                                "Nastavenia"
+ :handbook/close                                   "Zavrieť"
+ :on-boarding/tour-whiteboard-btn-next             "Ďalej"
+ :on-boarding/tour-whiteboard-btn-back             "Späť"
+ :on-boarding/tour-whiteboard-btn-finish           "Dokončiť"
+ :on-boarding/quick-tour-btn-next                  "Ďalej"
+ :on-boarding/quick-tour-btn-back                  "Späť"
+ :on-boarding/quick-tour-btn-finish                "Dokončiť"
+ :on-boarding/quick-tour-btn-skip                  "Preskočiť Rýchlu prehliadku"
+ :on-boarding/quick-tour-steps                     "KROK "
+ :on-boarding/quick-tour-help-title                "❓ Pomoc"
+ :on-boarding/quick-tour-help-desc                 "Kedykoľvek môžete kliknúť sem pre pomoc a ďalšie informácie o Logseq."
+ :on-boarding/quick-tour-journal-page-title        "📆 Denná stránka"
+ :on-boarding/quick-tour-journal-page-desc-1       "Toto je dnešná denná stránka. Môžete sem vyprázdniť svoje myšlienky, učenia a nápady. Nemusíte sa starať o organizáciu. Stačí písať a"
+ :on-boarding/quick-tour-journal-page-desc-2       "[[odkaz]]"
+ :on-boarding/quick-tour-journal-page-desc-3       "svoje myšlienky."
+ :on-boarding/quick-tour-left-sidebar-title        "👀 Ľavý bočný panel"
+ :on-boarding/quick-tour-left-sidebar-desc         "Otvorte ľavý bočný panel a preskúmajte dôležité položky v menu Logseq."
+ :on-boarding/quick-tour-favorites-title           "⭐️ Obľúbené"
+ :on-boarding/quick-tour-favorites-desc-1          "Pripnite si obľúbené stránky cez menu `... ` na akejkoľvek stránke."
+ :on-boarding/quick-tour-favorites-desc-2          "Tu sme tiež pridali niektoré vzorové stránky, aby sme vám pomohli začať. Môžete ich odstrániť, keď začnete písať vlastné poznámky."
+ :on-boarding/command-palette-quick-tour           "Rýchla prehliadka pre onboarding"
+ :on-boarding/importing-main-title                 "Import existujúcich poznámok"
+ :on-boarding/importing-main-desc                  "Môžete to urobiť aj neskôr v aplikácii."
+ :on-boarding/importing-title                      "Už máte poznámky, ktoré chcete importovať?"
+ :on-boarding/importing-desc                       "Ak sú vo formáte JSON, EDN alebo Markdown, Logseq s nimi môže pracovať."
+ :on-boarding/importing-roam-desc                  "Importujte JSON export vášho Roam grafu"
+ :on-boarding/importing-lsq-desc                   "Importujte EDN alebo JSON export vášho Logseq grafu"
+ :on-boarding/importing-opml-desc                  " Importujte súbory OPML"
+ :on-boarding/main-title                           (fn [] ["Vitajte v " [:strong "Logseq!"]])
+ :on-boarding/main-desc                            "Najprv musíte vybrať adresár, kde Logseq bude ukladať vaše myšlienky, nápady, poznámky."
+ :on-boarding/section-btn-title                    "Vyberte adresár"
+ :on-boarding/section-btn-desc                     "Otvorte existujúci adresár alebo vytvorte nový"
+ :on-boarding/section-title                        "Ako Logseq ukladá vašu prácu"
+ :on-boarding/section-desc                         "V adresári, ktorý si vyberiete, Logseq vytvorí 4 podadresáre."
+ :on-boarding/section-tip-1                        "Každá stránka je súbor uložený len na vašom {1}."
+ :on-boarding/section-tip-2                        "Môžete si vybrať, či ju chcete neskôr synchronizovať."
+ :on-boarding/section-assets                       "Grafika a dokumenty"
+ :on-boarding/section-computer                     "počítač"
+ :on-boarding/section-journals                     "Denné poznámky"
+ :on-boarding/section-pages                        "STRÁNKY"
+ :on-boarding/section-phone                        "telefón"
+ :on-boarding/section-app                          "Interná aplikácia"
+ :on-boarding/section-config                       "Konfiguračný súbor"
+ :query/config-property-settings                   "Nastavenia vlastností pre túto otázku:"
+ :bug-report/main-title                            "Hlásenie o chybe"
+ :bug-report/clipboard-inspector-title             "Inšpektor údajov zo schránky"
+ :bug-report/main-desc                             "Môžete nám pomôcť tým, že podáte správu o chybe? Čo najskôr to vyriešime."
+ :bug-report/section-clipboard-title               "Súvisí chyba, na ktorú ste narazili, s týmito funkciami?"
+ :bug-report/section-clipboard-desc                "Pomocou týchto praktických nástrojov nám môžete poskytnúť ďalšie informácie."
+ :bug-report/section-clipboard-btn-title           "Pomocník schránky"
+ :bug-report/section-clipboard-btn-desc            "Skotrolujte a zbierajte údaje zo schránky"
+ :bug-report/section-issues-title                  "Alebo ..."
+ :bug-report/section-issues-desc                   "Ak nie sú k dispozícii žiadne nástroje na zhromaždenie ďalších informácií, prosím, nahláste chybu priamo."
+ :bug-report/section-issues-btn-title              "Odoslať hlásenie o chybe"
+ :bug-report/section-issues-btn-desc               "Pomôžte vylepšiť Logseq!"
+ :bug-report/inspector-page-desc-1                 "Stlačením klávesovej skratky Ctrl+V / ⌘+V vložíte dáta zo schránky pre kontrolu"
+ :bug-report/inspector-page-desc-2                 "alebo sem kliknite pre vloženie, ak používate mobilnú verziu"
+ :bug-report/inspector-page-placeholder            "Ak používate mobilné zariadenie, obsah schránky vložíte dlhým stlačením tu"
+ :bug-report/inspector-page-tip                    "Niečo nie je v poriadku? Žiadny problém, kliknite na tlačidlo Späť pre návrat na predchádzajúci krok."
+ :bug-report/inspector-page-btn-back               "Späť"
+ :bug-report/inspector-page-btn-copy               "Kopírovať výsledok"
+ :bug-report/inspector-page-copy-notif             "Skopírované do schránky!"
+ :bug-report/inspector-page-btn-create-issue       "Vytvoriť problém"
+ :bug-report/inspector-page-desc-clipboard         "Toto sú údaje prečítané zo schránky."
+ :bug-report/inspector-page-desc-copy              "Ak je ich zdieľanie v poriadku, kliknite na tlačidlo Kopírovať."
+ :bug-report/inspector-page-desc-create-issue      "Teraz môžete nahlásiť výsledok vložený do vašej schránky. Prosím, vložte výsledek do sekcie 'Dodatočný kontext' a uveďte, odkiaľ ste skopírovali pôvodný obsah. Ďakujeme!"
+ :help/title-usage                                 "Použitie"
+ :help/title-community                             "Komunita"
+ :help/title-development                           "Vývoj"
+ :help/title-about                                 "O nás"
+ :help/title-terms                                 "Podmienky"
  :help/start                                       "Začíname"
  :help/about                                       "O Logseq"
  :help/roadmap                                     "Plán vývoja"
@@ -25,18 +103,20 @@
  :help/changelog                                   "Prehľad zmien"
  :help/docs                                        "Dokumentácia"
  :help/privacy                                     "Zásady ochrany osobných údajov"
- :help/terms                                       "Podmineky používania"
+ :help/terms                                       "Podmienky používania"
  :help/forum-community                             "Komunitné fórum"
  :help/shortcuts                                   "Klávesové skratky"
  :help/shortcuts-triggers                          "Akcie"
- :help/shortcut                                    "Skratky"
+ :help/shortcut                                    "Skratka"
+ :help/search                                      "Vyhľadať stránky/bloky/príkazy"
  :help/slash-autocomplete                          "Automatické dopĺňanie s lomítkom"
  :help/reference-autocomplete                      "Automatické dopĺňanie referencie na stránku"
  :help/block-reference                             "Referencia na blok"
  :help/open-link-in-sidebar                        "Otvoriť odkaz v bočnom paneli"
- :search/page-names                                "Hľadať názvy stránok"
+ :search/page-names                                "Vyhľadávať názvy stránok"
  :search-item/whiteboard                           "Tabuľa"
  :search-item/page                                 "Stránka"
+ :search-item/no-result                            "Žiadne zhody"
  :help/context-menu                                "Kontextové menu bloku"
  :bold                                             "Tučné"
  :italics                                          "Kurzíva"
@@ -48,6 +128,11 @@
  :right-side-bar/switch-theme                      "Motívy"
  :right-side-bar/contents                          "Obsah"
  :right-side-bar/page-graph                        "Graf stránok"
+ :right-side-bar/history                           "(Dev) Historia Späť/Znova"
+ :right-side-bar/history-undos                     "Späť"
+ :right-side-bar/history-redos                     "Znova"
+ :right-side-bar/history-global                    "globálny"
+ :right-side-bar/history-pageonly                  "len stránka"
  :right-side-bar/block-ref                         "Referencie bloku"
  :right-side-bar/graph-view                        "Zobrazenie grafu"
  :right-side-bar/all-pages                         "Všetky stránky"
@@ -56,26 +141,47 @@
  :right-side-bar/new-page                          "Nová stránka"
  :right-side-bar/show-journals                     "Zobraziť denníky"
  :right-side-bar/separator                         "Nástroj na zmenu veľkosti pravého bočného panelu"
+ :right-side-bar/toggle-right-sidebar              "Prepnúť pravý bočný panel"
+ :right-side-bar/pane-close                        "Zavrieť"
+ :right-side-bar/pane-close-others                 "Zavrieť ostatné"
+ :right-side-bar/pane-close-all                    "Zavrieť všetko"
+ :right-side-bar/pane-collapse                     "Zbaliť"
+ :right-side-bar/pane-collapse-others              "Zbaliť ostatné"
+ :right-side-bar/pane-collapse-all                 "Zbaliť všetko"
+ :right-side-bar/pane-expand                       "Rozbaliť"
+ :right-side-bar/pane-expand-all                   "Rozbaliť všetko"
+ :right-side-bar/pane-open-as-page                 "Otvoriť ako stránku"
+ :right-side-bar/pane-more                         "Viac"
+ :left-side-bar/switch                             "Prepnúť na:"
  :left-side-bar/journals                           "Denníky"
  :left-side-bar/create                             "Vytvoriť"
  :left-side-bar/new-page                           "Nová stránka"
  :left-side-bar/new-whiteboard                     "Nová tabuľa"
  :left-side-bar/nav-favorites                      "Obľúbené"
- :left-side-bar/nav-recent-pages                   "Posledné"
+ :left-side-bar/nav-recent-pages                   "Nedávne"
+ :page/something-went-wrong                        "Niečo sa pokazilo"
+ :page/logseq-is-having-a-problem                  "Logseq má problém. Skúste tieto bezpečné kroky, aby ste ho dostali do funkčného stavu:"
+ :page/step                                        "Krok {1}"
+ :page/try                                         "Vyskúšať"
+ :page/slide-view                                  "Zobraziť ako prezentáciu"
+ :page/slide-view-tip-go-fullscreen                (fn [] [[:span.opacity-70 "Tip: stlačte "] [:code "f"] [:span.opacity-70 " pre celú obrazovku"]])
  :page/delete-confirmation                         "Naozaj chcete odstrániť túto stránku a jej súbor?"
  :page/open-in-finder                              "Otvoriť v adresári"
- :page/open-with-default-app                       "Otvoriť pomocou predvolenej aplikácie"
+ :page/open-with-default-app                       "Otvoriť v predvolenej aplikácii"
  :page/make-public                                 "Označiť stránku ako verejnú"
  :page/version-history                             "Zobraziť históriu stránky"
  :page/open-backup-directory                       "Otvoriť adresár so zálohami stránky"
  :page/make-private                                "Označiť stránku ako súkromnú"
  :page/delete                                      "Odstrániť stránku"
- :page/add-to-favorites                            "Pridať medzi obľúbené"
- :page/unfavorite                                  "Odobrať z obľubených"
+ :page/add-to-favorites                            "Pridať do obľúbených"
+ :page/unfavorite                                  "Odstrániť z obľúbených"
  :page/show-journals                               "Zobraziť denníky"
  :block/name                                       "Názav stránky"
  :page/earlier                                     "Skôr"
- :page/copy-page-url                               "Kopírovať URL stránky"
+ :page/copy-page-url                               "Kopírovať adresu stránky"
+ :page/illegal-page-name                           "Neplatný názov stránky!"
+ :page/page-already-exists                         "Stránka “{1}” už existuje!"
+ :page/whiteboard-to-journal-error                 "Stránky tabule nemôžu byť premenované na názvy denníkov!"
  :file/name                                        "Názov súboru"
  :file/last-modified-at                            "Naposledy upravený"
  :file/no-data                                     "Žiadne dáta"
@@ -87,11 +193,12 @@
  :file-rn/or-select-actions-2                      ". Po zatvorení tohto panela nebudú tieto akcie dostupné."
  :file-rn/legend                                   "🟢 Voliteľné akcie premenovania; 🟡 Vyžaduje sa akcia premenovania, aby sa zabránilo zmene názvu; 🔴 Nekompatibilná zmena."
  :file-rn/close-panel                              "Zatvoriť panel"
- :file-rn/select-format                            "(Režimu vývojára, Nebezpečné!) Vybrať formát súboru"
+ :file-rn/all-action                               "Použiť všetky akcie! ({1})"
+ :file-rn/select-format                            "(Režim vývojára, nebezpečné!) Vybrať formát súboru"
  :file-rn/rename                                   "premenovať súbor \"{1}\" na \"{2}\""
  :file-rn/apply-rename                             "Použiť operáciu premenovania súboru"
- :file-rn/suggest-rename                           "Vyžaduje sa akcia:"
- :file-rn/otherwise-breaking                       "Alebo sa názov stane"
+ :file-rn/suggest-rename                           "Vyžaduje sa akcia: "
+ :file-rn/otherwise-breaking                       "Inak názov bude"
  :file-rn/no-action                                "Výborne! Nevyžaduje sa žiadna ďalšia akcia."
  :file-rn/confirm-proceed                          "Aktualizujte formát!"
  :file-rn/select-confirm-proceed                   "Dev: formát zápisu"
@@ -125,15 +232,40 @@
  :color/blue                                       "Modrá"
  :color/purple                                     "Fialová"
  :color/pink                                       "Ružová"
- :editor/copy                                     "Kopírovať"
- :editor/cut                                      "Vystrihnúť"
+ :editor/copy                                      "Kopírovať"
+ :editor/cut                                       "Vystrihnúť"
+ :editor/expand-block-children                     "Rozbaliť všetko"
+ :editor/collapse-block-children                   "Zbaliť všetko"
+ :editor/delete-selection                          "Zmazať vybrané bloky"
+ :editor/cycle-todo                                "Prepnúť stav TODO aktuálneho položky"
+ :dev/show-page-data                               "(Dev) Zobraziť údaje o stránke"
+ :dev/show-block-data                              "(Dev) Zobraziť údaje o bloku"
+ :dev/show-block-ast                               "(Dev) Zobraziť AST bloku"
+ :dev/show-page-ast                                "(Dev) Zobraziť AST stránky"
+ :content/copy-export-as                           "Kopírovať / Exportovať ako..."
+ :content/copy-block-url                           "Kopírovať adresu bloku"
  :content/copy-block-ref                           "Kopírovať referenciu bloku"
  :content/copy-block-emebed                        "Kopírovať blok ako vložený"
- :content/open-in-sidebar                          "Otvoriť na bočnom paneli"
- :content/click-to-edit                            "Kliknutím upravíte"
- :settings-page/git-confirm                        "Po aktualizácii Git nastavení musíte reštartovať aplikáciu."
+ :content/copy-ref                                 "Kopírovať túto referenciu"
+ :content/delete-ref                               "Zmazať túto referenciu"
+ :content/replace-with-text                        "Nahradit textom"
+ :content/replace-with-embed                       "Nahradit vložením"
+ :content/open-in-sidebar                          "Otvoriť v bočnom paneli"
+ :content/click-to-edit                            "Kliknutím upraviť"
+ :context-menu/make-a-flashcard                    "Vytvoriť kartičku"
+ :context-menu/toggle-number-list                  "Prepnúť číslovaný zoznam"
+ :context-menu/preview-flashcard                   "Náhľad kartičky"
+ :context-menu/make-a-template                     "Vytvoriť šablónu"
+ :context-menu/input-template-name                 "Ako sa bude šablóna volať?"
+ :context-menu/template-include-parent-block       "Vložiť aj nadradený blok do šablóny?"
+ :context-menu/template-exists-warning             "Šablóna už existuje!"
+ :settings-page/git-tip                            "Ak máte povolenú synchronizáciu Logseq, môžete zobraziť históriu úprav stránky priamo. Táto časť je určená len pre pokročilých používateľov."
+ :settings-page/git-desc-1                         "Pre zobrazenie histórie úprav stránky kliknite na tri horizontálne body v pravom hornom rohu a vyberte „Zobraziť históriu stránky“."
+ :settings-page/git-desc-2                         "Pre profesionálnych používateľov Logseq podporuje aj používanie "
+ :settings-page/git-desc-3                         " pre správu verzií. Používajte Git na vlastné riziko, pretože všeobecné problémy s Git nie sú podporované tímom Logseq."
  :settings-page/git-switcher-label                 "Povoliť automatický zápis do Git"
  :settings-page/git-commit-delay                   "Automatický zápis do Git po sekundách"
+ :settings-page/git-confirm                        "Po aktualizácii nastavení Git je potrebné reštartovať aplikáciu."
  :settings-page/edit-config-edn                    "Upraviť config.edn"
  :settings-page/edit-global-config-edn             "Upraviť globálny config.edn"
  :settings-page/edit-custom-css                    "Upraviť custom.css"
@@ -141,6 +273,9 @@
  :settings-page/edit-setting                       "Upraviť"
  :settings-page/custom-configuration               "Vlastná konfigurácia"
  :settings-page/custom-global-configuration        "Vlastná globálna konfigurácia"
+ :settings-page/theme-light                        "Svetlý"
+ :settings-page/theme-dark                         "Tmavý"
+ :settings-page/theme-system                       "Systémový"
  :settings-page/custom-theme                       "Vlastný motív"
  :settings-page/export-theme                       "Exportovať motív"
  :settings-page/show-brackets                      "Zobraziť zátvorky"
@@ -149,12 +284,18 @@
  :settings-page/disable-sentry                     "Odoslať údaje o používaní a diagnostiku do Logseq"
  :settings-page/disable-sentry-desc                "Logseq nebude nikdy zhromažďovať vašu lokálnu databázu grafov ani predávať vaše údaje."
  :settings-page/preferred-outdenting               "Logické odsadenie"
+ :settings-page/preferred-outdenting-tip           "Ľavá strana ukazuje odsadenie s predvoleným nastavením, a pravá ukazuje odsadenie s logickým odsadením "
+ :settings-page/preferred-outdenting-tip-more      "→ Ďalšie informácie"
  :settings-page/show-full-blocks                   "Zobraziť všetky riadky referencie bloku"
+ :settings-page/auto-expand-block-refs             "Automaticky rozbaliť referencie bloku pri priblížení"
+ :settings-page/auto-expand-block-refs-tip         "Táto možnosť ovplyvňuje, či sa referencie bloku majú automaticky rozbaliť pri priblížení."
  :settings-page/custom-date-format                 "Preferovaný formát dátumu"
  :settings-page/custom-date-format-warning         "Vyžaduje sa preindexovanie! Existujúce odkazy na denníky by boli poškodené!"
+ :settings-page/custom-date-format-notification    "Aby sa táto zmena prejavila, musíte graf znovu preindexovať."
+ :settings-page/preferred-pasting-file-hint        "Ak je táto možnosť povolená, vložením obrázka z internetu sa obrázok stiahne a vloží. Ak je vypnutá, vloží sa odkaz na obrázok."
  :settings-page/preferred-file-format              "Preferovaný formát súboru"
  :settings-page/preferred-workflow                 "Preferovaný pracovný postup"
- :settings-page/preferred-pasting-file             "Preferovaný súbor na prilepenie"
+ :settings-page/preferred-pasting-file             "Uprednostniť vloženie súboru"
  :settings-page/enable-shortcut-tooltip            "Povoliť nápovedy ku klávesovým skratkám"
  :settings-page/enable-timetracking                "Meranie času"
  :settings-page/enable-tooltip                     "Povoliť okno s nápovedou"
@@ -168,8 +309,11 @@
  :settings-page/developer-mode-desc                "Režim pre vývojárov pomáha prispievateľom a vývojárom doplnkov efektívnejšie testovať integráciu s Logseq."
  :settings-page/current-version                    "Aktuálna verzia"
  :settings-page/tab-general                        "Všeobecné"
+ :settings-page/tab-keymap                         "Klávesové skratky"
  :settings-page/tab-version-control                "Verzovanie"
+ :settings-page/tab-account                        "Účet"
  :settings-page/tab-advanced                       "Pokročilé"
+ :settings-page/tab-assets                         "Zdroje"
  :settings-page/tab-features                       "Funkcie"
  :settings-page/plugin-system                      "Doplnky"
  :settings-page/enable-flashcards                  "Kartičky"
@@ -179,8 +323,29 @@
  :settings-page/beta-features                      "Beta funkcie"
  :settings-page/login-prompt                       "Ak chcete získať prístup k novým funkciám skôr ako ktokoľvek iný, musíte byť sponzorom nadácie Open Collective alebo podporovateľom Logseq, následne sa musíte prihlásiť."
  :settings-page/sync                               "Synchronizovať"
+ :settings-page/sync-desc-1                        "Kliknite"
+ :settings-page/sync-desc-2                        "sem"
+ :settings-page/sync-desc-3                        "pre inštrukcie, ako nastaviť a používať synchronizáciu."
+ :settings-page/sync-diff-merge                    "Povoliť inteligentné slučovanie pri synchronizácii"
+ :settings-page/sync-diff-merge-desc               "Automaticky slučuje lokálne aktualizácie s vzdialenými súbormi pri konflikte, namiesto prepísania vzdialených súborov"
+ :settings-page/sync-diff-merge-warn               "Schopnosť inteligentného slučovania sa aktivuje iba na klientovi po prvom úspešnom zosynchronizovaní so vzdialeným serverom na grafe v novej verzii Logseq. Aktivujte toto na všetkých zariadeniach pre dosiahnutie najlepšej skúsenosti."
  :settings-page/enable-whiteboards                 "Tabule"
+ :settings-page/native-titlebar                    "Natívne záhlavie okna"
+ :settings-page/native-titlebar-desc               "Aktivuje natívne záhlavie okna na systémoch Windows a Linux."
+ :settings-page/check-for-updates                  "Skontrolovať aktualizácie"
+ :settings-page/checking                           "Kontrola ..."
+ :settings-page/revision                           "Revízia: "
+ :settings-page/changelog                          "Čo je nové?"
+ :settings-page/app-updated                        "Vaša aplikácia je aktuálna 🎉"
+ :settings-page/update-available                   "Nájdená nová verzia "
+ :settings-page/update-error-1                     "⚠️ Ups, niečo sa pokazilo!"
+ :settings-page/update-error-2                     " Prosím, pozrite si "
+ :settings-permission/start-granting               "Udeliť"
+
+ :settings-page/auto-chmod                         "Automaticky meniť oprávnenia súborov"
+ :settings-page/auto-chmod-desc                    "Zakázať umožňuje editáciu viacerými používateľmi s oprávneniami udelenými členstvom v skupine."
  :yes                                              "Áno"
+
  :submit                                           "Odoslať"
  :cancel                                           "Zrušiť"
  :close                                            "Zavrieť"
@@ -188,16 +353,134 @@
  :save                                             "Uložiť"
  :type                                             "Typ"
  :re-index                                         "Preindexovať"
- :re-index-detail                                  "Obnovte graf"
+ :re-index-detail                                  "Obnov graf"
  :re-index-multiple-windows-warning                "Pred preindexovaním grafu musíte zavrieť ostatné okná."
  :re-index-discard-unsaved-changes-warning         "Preindexovanie vymaže aktuálny graf a potom znova spracuje všetky súbory tak, ako sú aktuálne uložené na disku. Prídete o neuložené zmeny a môže to chvíľu trvať. Chcete pokračovať?"
  :sync-from-local-files                            "Obnoviť"
  :sync-from-local-files-detail                     "Importovať zmeny z lokálnych súborov"
  :sync-from-local-changes-detected                 "Obnovenie detekuje a spracuje upravené súbory na vašom disku, ktoré sa líšia od aktuálneho obsahu Logseq stránky. Chcete pokračovať?"
 
+ :whiteboard/align-left                            "Zarovnať vľavo"
+ :whiteboard/align-center-horizontally             "Zarovnať vodorovne na stred"
+ :whiteboard/align-right                           "Zarovnať vpravo"
+ :whiteboard/distribute-horizontally               "Rozložiť vodorovne"
+ :whiteboard/align-top                             "Zarovnať hore"
+ :whiteboard/align-center-vertically               "Zarovnať zvisle na stred"
+ :whiteboard/align-bottom                          "Zarovnať dole"
+ :whiteboard/distribute-vertically                 "Rozložiť zvisle"
+ :whiteboard/pack-into-rectangle                   "Zabaliť do obdĺžnika"
+ :whiteboard/zoom-to-fit                           "Priblížiť, aby sa zmestilo"
+ :whiteboard/ungroup                               "Zrušiť zoskupenie"
+ :whiteboard/group                                 "Zoskupiť"
+ :whiteboard/cut                                   "Vystrihnúť"
+ :whiteboard/copy                                  "Kopírovať"
+ :whiteboard/paste                                 "Vložiť"
+ :whiteboard/paste-as-link                         "Vložiť ako odkaz"
+ :whiteboard/export                                "Exportovať"
+ :whiteboard/select-all                            "Vybrať všetko"
+ :whiteboard/deselect-all                          "Odznačiť všetko"
+ :whiteboard/lock                                  "Zamknúť"
+ :whiteboard/unlock                                "Odomknúť"
+ :whiteboard/delete                                "Odstrániť"
+ :whiteboard/flip-horizontally                     "Otočiť vodorovne"
+ :whiteboard/flip-vertically                       "Otočiť zvisle"
+ :whiteboard/move-to-front                         "Presunúť na popredie"
+ :whiteboard/move-to-back                          "Presunúť dozadu"
+ :whiteboard/dev-print-shape-props                 "(Dev) Zobraziť vlastnosti tvaru"
+ :whiteboard/auto-resize                           "Automatická veľkosť"
+ :whiteboard/expand                                "Rozbaliť"
+ :whiteboard/collapse                              "Zbaliť"
+ :whiteboard/website-url                           "Webová adresa"
+ :whiteboard/reload                                "Znovu načítať"
+ :whiteboard/open-website-url                      "Otvoriť webovú adresu"
+ :whiteboard/youtube-url                           "YouTube adresa"
+ :whiteboard/open-youtube-url                      "Otvoriť YouTube adresu"
+ :whiteboard/twitter-url                           "Twitter adresa"
+ :whiteboard/open-twitter-url                      "Otvoriť Twitter adresu"
+ :whiteboard/fill                                  "Vyplniť"
+ :whiteboard/stroke-type                           "Typ čiary"
+ :whiteboard/arrow-head                            "Šípka"
+ :whiteboard/bold                                  "Tučné"
+ :whiteboard/italic                                "Kurzíva"
+ :whiteboard/undo                                  "Späť"
+ :whiteboard/redo                                  "Znova"
+ :whiteboard/zoom-in                               "Priblížiť"
+ :whiteboard/zoom-out                              "Oddialiť"
+ :whiteboard/select                                "Vybrať"
+ :whiteboard/pan                                   "Posúvať"
+ :whiteboard/add-block-or-page                     "Pridať blok alebo stránku"
+ :whiteboard/draw                                  "Kresliť"
+ :whiteboard/highlight                             "Zvýrazniť"
+ :whiteboard/eraser                                "Guma"
+ :whiteboard/connector                             "Konektor"
+ :whiteboard/color                                 "Farba"
+ :whiteboard/select-custom-color                   "Vybrať vlastnú farbu"
+ :whiteboard/opacity                               "Priehľadnosť"
+ :whiteboard/extra-small                           "Extra malé"
+ :whiteboard/small                                 "Malé"
+ :whiteboard/medium                                "Stredné"
+ :whiteboard/large                                 "Veľké"
+ :whiteboard/extra-large                           "Extra veľké"
+ :whiteboard/huge                                  "Obrovské"
+ :whiteboard/scale-level                           "Úroveň merania"
+ :whiteboard/rectangle                             "Obdĺžnik"
+ :whiteboard/circle                                "Kruh"
+ :whiteboard/triangle                              "Trojuholník"
+ :whiteboard/shape                                 "Tvar"
+ :whiteboard/open-page                             "Otvoriť stránku"
+ :whiteboard/open-page-in-sidebar                  "Otvoriť stránku v bočnom paneli"
+ :whiteboard/remove-link                           "Odstrániť odkaz"
+ :whiteboard/link                                  "Odkaz"
+ :whiteboard/references                            "Referencie"
+ :whiteboard/link-to-any-page-or-block             "Odkaz na akúkoľvek stránku alebo blok"
+ :whiteboard/start-typing-to-search                "Začnite písať na vyhľadávanie ..."
+ :whiteboard/new-block-no-colon                    "Nový blok"
+ :whiteboard/new-block                             "Nový blok:"
+ :whiteboard/new-page                              "Nová stránka:"
+ :whiteboard/new-whiteboard                        "Nová tabuľa"
+ :whiteboard/search-only-blocks                    "Vyhľadávať iba bloky"
+ :whiteboard/search-only-pages                     "Vyhľadávať iba stránky"
+ :whiteboard/cache-outdated                        "Cache je zastaraná. Kliknite na tlačidlo 'Preindexovať' v rozbaľovacom menu grafu."
+ :whiteboard/shape-quick-links                     "Rýchle odkazy na tvary"
+ :whiteboard/edit-pdf                              "Upraviť PDF"
+ :whiteboard/dashboard-card-new-whiteboard         "Nová tabuľa"
+ :whiteboard/dashboard-card-created                "Vytvorené "
+ :whiteboard/dashboard-card-edited                 "Upravené "
+ :whiteboard/toggle-grid                           "Prepnúť mriežku"
+ :whiteboard/snap-to-grid                          "Zarovnať do mriežky"
+ :whiteboard/toggle-pen-mode                       "Prepnúť na režim pera"
+ :flashcards/modal-welcome-title                   "Čas vytvoriť kartu!"
+ :flashcards/modal-welcome-desc-1                  "Môžete pridať \"#card\" k akémukoľvek bloku, aby ste ho zmenili na kartu alebo spustili \"/cloze\" pre pridanie niektorých clozes."
+ :flashcards/modal-welcome-desc-2                  "Môžete "
+ :flashcards/modal-welcome-desc-3                  "kliknúť na tento odkaz"
+ :flashcards/modal-welcome-desc-4                  " na prečítanie dokumentácie."
+ :flashcards/modal-btn-show-answers                "Zobraziť odpovede"
+ :flashcards/modal-btn-hide-answers                "Skryť odpovede"
+ :flashcards/modal-btn-show-clozes                 "Zobraziť clozes"
+ :flashcards/modal-btn-next-card                   "Ďalej"
+ :flashcards/modal-btn-reset                       "Resetovať"
+ :flashcards/modal-btn-reset-tip                   "Resetovať túto kartu, aby ste ju mohli okamžite zrevidovať."
+ :flashcards/modal-btn-forgotten                   "Zabudnuté"
+ :flashcards/modal-btn-remembered                  "Zapamätané"
+ :flashcards/modal-btn-recall                      "Chvíľu trvalo, kým som si spomenul"
+ :flashcards/modal-finished                        "Gratulujeme, prešli ste všetky karty pre túto otázku, uvidíme sa nabudúce! 💯"
+ :flashcards/modal-select-all                      "Všetko"
+ :flashcards/modal-select-switch                   "Prepnúť na"
+ :flashcards/modal-current-total                   "Aktuálne/Celkom"
+ :flashcards/modal-overdue-total                   "Omeškané/Celkom"
+ :flashcards/modal-toggle-preview-mode             "Prepnúť do režimu náhľadu"
+ :flashcards/modal-toggle-random-mode              "Prepnúť do náhodného režimu"
+
+ :home                                             "Domov"
+ :new-page                                         "Nová stránka:"
+ :whiteboard                                       "Tabuľa"
+ :whiteboards                                      "Tabule"
  :new-graph                                        "Pridať nový graf"
  :graph                                            "Graf"
  :graph/all-graphs                                 "Všetky grafy"
+ :graph/local-graphs                               "Lokálne grafy:"
+ :graph/remote-graphs                              "Vzdialené grafy:"
+ :export                                           "Exportovať"
  :export-graph                                     "Exportovať graf"
  :export-page                                      "Exportovať stránku"
  :export-markdown                                  "Exportovať ako štandardný Markdown (žiadne vlastnosti bloku)"
@@ -206,25 +489,32 @@
  :export-json                                      "Exportovať ako JSON"
  :export-roam-json                                 "Exportovať ako Roam JSON"
  :export-edn                                       "Exportovať ako EDN"
+ :export-transparent-background                    "Transparentné pozadie"
+ :export-copy-to-clipboard                         "Kopírovať do schránky"
+ :export-copied-to-clipboard                       "Skopírované do schránky!"
+ :export-save-to-file                              "Uložiť do súboru"
  :all-graphs                                       "Všetky grafy"
  :all-pages                                        "Všetky stránky"
  :all-whiteboards                                  "Všetky tabule"
  :all-files                                        "Všetky súbory"
+ :remove-orphaned-pages                            "Odstrániť stránky bez rodičovskej stránky?"
  :all-journals                                     "Všetky denníky"
  :settings                                         "Nastavenia"
  :settings-of-plugins                              "Doplnky"
  :plugins                                          "Doplnky"
  :themes                                           "Motívy"
- :relaunch-confirm-to-work                         "Aby aplikácia fungovala, mala byť reštartovaná. Chcete ju reštartovať teraz?"
+ :relaunch-confirm-to-work                         "Aby aplikácia fungovala správne, je potrebné ju reštartovať. Chcete ju reštartovať teraz?"
  :import                                           "Importovať"
  :importing                                        "Importuje sa"
  :join-community                                   "Pridať sa ku komunite"
  :discourse-title                                  "Naše fórum!"
  :help-shortcut-title                              "Kliknutím zobrazíte skratky a ďalšie tipy"
+ :loading                                          "Načítava sa ..."
  :parsing-files                                    "Parsovanie súborov"
  :loading-files                                    "Načítavajú sa súbory"
  :login                                            "Prihlásiť sa"
  :logout                                           "Odhlásiť sa"
+ :logout-user                                      "Odhlásiť ({1})"
  :download                                         "Stiahnuť"
  :language                                         "Jazyk"
  :remove-background                                "Odstrániť pozadie"
@@ -232,18 +522,26 @@
  :heading                                          "Nadpis {1}"
  :auto-heading                                     "Automatický nadpis"
  :open-a-directory                                 "Otvoriť lokálny adresár"
+ :toggle-theme                                     "Prepnúť motív"
 
  :help/shortcut-page-title                         "Klávesové skratky"
 
  :plugin/installed                                 "Nainštalované"
+ :plugin/installed-plugin                          "Inštalovaný doplnok: {1}"
  :plugin/not-installed                             "Nenainštalované"
  :plugin/installing                                "Inštaluje sa"
  :plugin/install                                   "Inštalovať"
  :plugin/reload                                    "Znova načítať"
  :plugin/update                                    "Aktualizovať"
- :plugin/check-update                              "Skontrolovať aktualizáciu"
+ :plugin/update-plugin                             "Aktualizovať doplnok: {1} - {2}"
+ :plugin/check-update                              "Skontrolovať aktualizácie"
  :plugin/check-all-updates                         "Skontrolovať všetky aktualizácie"
- :plugin/refresh-lists                             "Obnoviť zoznam"
+ :plugin/found-updates                             "Nové aktualizácie"
+ :plugin/found-n-updates                           "Nájdené {1} aktualizácií"
+ :plugin/update-all-selected                       "Aktualizovať všetko zvolené"
+ :plugin/all-updated                               "Všetko aktualizované!"
+ :plugin/updates-downloading                       "Sťahujú sa aktualizácie"
+ :plugin/refresh-lists                             "Obnoviť zoznamy"
  :plugin/enabled                                   "Povolené"
  :plugin/disabled                                  "Zakázané"
  :plugin/update-available                          "S dostupnou aktualizáciou"
@@ -251,7 +549,8 @@
  :plugin/uninstall                                 "Odinštalovať"
  :plugin/marketplace                               "Obchod"
  :plugin/downloads                                 "Počet stiahnutí"
- :plugin/stars                                     "Počet hviezd"
+ :plugin/stars                                     "Počet hviezdičiek"
+ :plugin/title                                     "Názov ({1})"
  :plugin/all                                       "Všetky"
  :plugin/unpacked                                  "Rozbalené"
  :plugin/delete-alert                              "Naozaj chcete odinštalovať doplnok [{1}]?"
@@ -259,19 +558,28 @@
  :plugin/open-package                              "Otvoriť balík"
  :plugin/load-unpacked                             "Načítať rozbalený doplnok"
  :plugin/restart                                   "Reštartovať aplikáciu"
- :plugin/unpacked-tips                             "Vyberte adresár doplnku"
+ :plugin/unpacked-tips                             "Vyberte adresár rozbaleného doplnku"
  :plugin/contribute                                "✨ Vytvorte a odošlite nový doplnok"
- :plugin/custom-js-alert                           "Našiel sa súbor custom.js, je povolené jeho spustenie? (Ak nerozumiete obsahu tohto súboru, odporúčame nepovoliť jeho spustenie, ktoré má určité bezpečnostné riziká.)"
+ :plugin/up-to-date                                "Je aktuálne {1}"
+ :plugin/custom-js-alert                           "Našiel sa súbor custom.js. Chcete ho spustiť? (Ak nerozumiete obsahu tohto súboru, odporúčame jeho spustenie zakázať, pretože môže predstavovať určité bezpečnostné riziká.)"
+ :plugin/security-warning                          "Doplňky majú prístup k vášmu grafu a lokálnym súborom, môžu posielať sieťové požiadavky. Tiež môžu spôsobiť poškodenie alebo stratu dát. Pracujeme na správnych pravidlách prístupu k vašim grafom. Zatiaľ sa uistite, že máte pravidelné zálohy svojich grafov a doplnky inštalujte len v prípade, že rozumiete ich zdrojovému kódu."
+ :plugin/search-plugin                             "Vyhľadávanie doplnkov"
+ :plugin/open-preferences                          "Otvoriť predvoľby"
+ :plugin/open-logseq-dir                           "Otvoriť Logseq adresár"
+ :plugin/remote-error                              "Vzdialená chyba: "
+ :plugin/checking-for-updates                      "Kontrola aktualizácií doplnkov ..."
+ :plugin/list-of-updates                           "Aktualizácie doplnkov: "
+ :plugin/auto-check-for-updates                    "Automatická kontrola aktualizácií"
  :plugin.install-from-file/menu-title              "Inštalovať z plugins.edn"
  :plugin.install-from-file/title                   "Inštalovať doplnky z plugins.edn"
- :plugin.install-from-file/notice                  "Nasledujúce doplnky nahradia vaše doplnky:"
- :plugin.install-from-file/success                 "Všetky doplnky nainštalované!"
-
+ :plugin.install-from-file/notice                  "Nasledujúce doplnky nahradia vaše aktuálne doplnky:"
+ :plugin.install-from-file/success                 "Všetky doplnky boli úspešne nainštalované!"
  :pdf/copy-ref                                     "Kopírovať referenciu"
  :pdf/copy-text                                    "Kopírovať text"
  :pdf/linked-ref                                   "Prepojené referencie"
- :pdf/toggle-dashed                                "Prerušovaný štýl pre zvýraznenie oblasti"
+ :pdf/toggle-dashed                                "Prepnúť prerušovaný štýl pre zvýraznenie oblasti"
  :pdf/hl-block-colored                             "Farebný štítok pre zvýraznený blok"
+ :pdf/auto-open-context-menu                       "Automaticky otvárať kontextové menu pre výbery"
  :pdf/doc-metadata                                 "Metadáta dokumentu"
 
  :updater/new-version-install                      "Bola stiahnutá nová verzia."
@@ -281,139 +589,208 @@
  :paginates/prev                                   "Predchádzajúca"
  :paginates/next                                   "Ďalšia"
 
+ :tips/all-done                                    "Hotovo!"
+
  :select/default-prompt                            "Vybrať jeden"
+ :select/default-select-multiple                   "Vybrať jeden alebo viacero"
  :select.graph/prompt                              "Vybrať graf"
  :select.graph/empty-placeholder-description       "Žiadne zodpovedajúce grafy. Chcete pridať ďalší?"
  :select.graph/add-graph                           "Áno, pridať ďalší graf"
 
- :file-sync/other-user-graph                       "Aktuálny lokálny graf je prepojený so vzdialeným grafom iného používateľa. Nie je možné spustiť synchronizáciu.."
+ :file-sync/other-user-graph                       "Aktuálny lokálny graf je prepojený so vzdialeným grafom iného používateľa. Nie je možné spustiť synchronizáciu."
  :file-sync/graph-deleted                          "Aktuálny vzdialený graf bol odstránený."
+ :file-sync/rsapi-cannot-upload-err                "Nie je možné spustiť synchronizáciu. Skontrolujte, či máte na lokálnom zariadení nastavený správny čas."
+ :file-sync/connectivity-testing-failed            "Testovanie sieťovej konektivity zlyhalo. Skontrolujte svoje nastavenia siete. Testovacie adresy: "
 
  :notification/clear-all                           "Zmazať všetko"
 
- :shortcut.category/formatting                             "Formátovanie"
- :shortcut.category/basics                                 "Základy"
- :shortcut.category/navigating                             "Navigácia"
- :shortcut.category/block-editing                          "Úprava bloku (všeobecné)"
- :shortcut.category/block-command-editing                  "Príkazy na úpravu bloku"
- :shortcut.category/block-selection                        "Výber bloku (výber ukončíte stlačením klávesy Esc)"
- :shortcut.category/toggle                                 "Prepínače"
- :shortcut.category/others                                 "Ostatné"
+ :shortcut.category/basics                         "Základy"
+ :shortcut.category/formatting                     "Formátovanie"
+ :shortcut.category/navigating                     "Navigácia"
+ :shortcut.category/block-editing                  "Úprava bloku (všeobecné)"
+ :shortcut.category/block-command-editing          "Príkazy na úpravu bloku"
+ :shortcut.category/block-selection                "Výber bloku (výber ukončíte stlačením klávesy Esc)"
+ :shortcut.category/toggle                         "Prepínače"
+ :shortcut.category/others                         "Ostatné"
+ :shortcut.category/plugins                        "Doplnky"
+ :shortcut.category/whiteboard                     "Tabuľa"
+
+ :keymap/all                                       "Všetky"
+ :keymap/disabled                                  "Zakázané"
+ :keymap/unset                                     "Nenastavené"
+ :keymap/custom                                    "Vlastné"
+ :keymap/search                                    "Hľadať"
+ :keymap/total                                     "Celkový počet skratiek"
+ :keymap/keystroke-filter                          "Filtrovať klávesové skratky"
+ :keymap/keystroke-record-desc                     "Pre filtrovanie skratiek stlačte ľubovoľnú sekvenciu kláves"
+ :keymap/keystroke-record-setup-label              "Skratku nastavíte stlačením ľubovoľnej sekvencie kláves"
+ :keymap/restore-to-default                        "Obnoviť na predvolené nastavenia systému"
+ :keymap/customize-for-label                       "Prispôsobiť skratky"
+ :keymap/conflicts-for-label                       "Konflikty klávesových skratiek pre"
+
+ :window/minimize                                  "Minimalizovať"
+ :window/maximize                                  "Maximalizovať"
+ :window/restore                                   "Obnoviť"
+ :window/close                                     "Zatvoriť"
+ :window/exit-fullscreen                           "Ukončiť režim celého obrazovky"
+
+ :header/toggle-left-sidebar                       "Zobraziť/Skryť ľavý bočný panel"
+ :header/search                                    "Hľadať"
+ :header/more                                      "Viac"
+ :header/go-back                                   "Späť"
+ :header/go-forward                                "Vpred"
 
- :command.date-picker/complete                             "Výber dátumu: podvrďte vybraný deň"
- :command.date-picker/next-day                             "Výber dátumu: vyberte nasledujúci deň"
- :command.date-picker/next-week                            "Výber dátumu: vyberte nasledujúci týždeň"
- :command.date-picker/prev-day                             "Výber dátumu: vyberte predchádzajúci deň"
- :command.date-picker/prev-week                            "Výber dátumu: vyberte predchádzajúci týždeň"
- :command.pdf/previous-page                                "PDF: Predchádzajúca strana aktuálneho PDF dokumentu"
- :command.pdf/next-page                                    "PDF: Nasledujúca strana aktuálneho PDF dokumentu"
- :command.pdf/close                                        "PDF: Zatvoriť aktuálny PDF dokument"
- :command.pdf/find                                         "PDF: Vyhľadať text v aktuálnom pdf dokumente"
- :command.auto-complete/complete                           "Automatické dokončovanie: Potvrdiť vybranú položku"
- :command.auto-complete/prev                               "Automatické dokončovanie: Vybrať predchádzajúcu položku"
- :command.auto-complete/next                               "Automatické dokončovanie: Vybrať ďalšiu položku"
- :command.auto-complete/shift-complete                     "Automatické dokončovanie: Otvoriť vybranú položku na bočnom paneli"
- :command.auto-complete/open-link                          "Automatické dokončovanie: Otvoriť vybranú položku v prehliadači"
- :command.cards/toggle-answers                             "Karty: Zobraziť/Skryť odpovede"
- :command.cards/next-card                                  "Karty: Ďalšia karta"
- :command.cards/forgotten                                  "Karty: Zabudnuté"
- :command.cards/remembered                                 "Karty: Zapamätané"
- :command.cards/recall                                     "Karty: take a while to recall"
- :command.editor/escape-editing                            "Zrušiť editovanie"
- :command.editor/backspace                                 "Zmazať dozadu"
- :command.editor/delete                                    "Zmazať dopredu"
- :command.editor/new-block                                 "Vytvoriť nový blok"
- :command.editor/new-line                                  "Nový riadok v aktuálnom bloku"
- :command.editor/new-whiteboard                            "Nová tabuľa"
- :command.editor/follow-link                               "Prejsť na odkaz pod kurzorom"
- :command.editor/open-link-in-sidebar                      "Otvoriť odkaz v bočnom paneli"
- :command.editor/bold                                      "Tučné"
- :command.editor/italics                                   "Kurzíva"
- :command.editor/highlight                                 "Zvýrazniť"
- :command.editor/strike-through                            "Prečiarknuté"
- :command.editor/clear-block                               "Odstrániť celý obsah bloku"
- :command.editor/kill-line-before                          "Odstrániť riadok pred pozíciou kurzora"
- :command.editor/copy-embed                                "Kopírovať vložený blok ukazujúci na aktuálny blok"
- :command.editor/kill-line-after                           "Odstrániť riadok za pozíciou kurzora"
- :command.editor/beginning-of-block                        "Presunúť kurzor na začiatok bloku"
- :command.editor/end-of-block                              "Presunúť kurzor na koniec bloku"
- :command.editor/forward-word                              "Posunúť kurzor o slovo dopredu"
- :command.editor/backward-word                             "Presunúť kurzor o slovo dozadu"
- :command.editor/forward-kill-word                         "Vymazať slovo dopredu"
- :command.editor/backward-kill-word                        "Vymazať slovo dozadu"
- :command.editor/replace-block-reference-at-point          "Nahradiť referenciu bloku jeho obsahom v bode"
- :command.editor/paste-text-in-one-block-at-point          "Prilepiť text do jedného bloku v bode"
- :command.editor/insert-youtube-timestamp                  "Vložiť YouTube časovú značku"
- :command.editor/cycle-todo                                "Zmeniť stav TODO aktuálnej položky"
- :command.editor/up                                        "Posunúť kurzor nahor/Vybrať nahor"
- :command.editor/down                                      "Posunúť kurzor nadol/Vybrať nadol"
- :command.editor/left                                      "Posunúť kurzor doľava/Otvoriť vybraný blok na začiatku"
- :command.editor/right                                     "Posunúť kurzor doprava/Otvoriť vybraný blok na konci"
- :command.editor/select-up                                 "Vybrať obsah vyššie"
- :command.editor/select-down                               "Vybrať obsah nižšie"
- :command.editor/move-block-up                             "Posunúť blok nahor"
- :command.editor/move-block-down                           "Posunúť blok nadol"
- :command.editor/open-edit                                 "Upraviť vybraný blok"
- :command.editor/select-block-up                           "Vybrať blok vyššie"
- :command.editor/select-block-down                         "Vybrať blok nižšie"
- :command.editor/delete-selection                          "Odstrániť vybrané bloky"
- :command.editor/expand-block-children                     "Rozbaliť"
- :command.editor/collapse-block-children                   "Zbaliť"
- :command.editor/indent                                    "Odsadiť blok"
- :command.editor/outdent                                   "Zrusiť odsadenie bloku"
- :command.editor/copy                                      "Kopírovať (skopíruje buď výber alebo referenciu na blok)"
- :command.editor/copy-text                                 "Kopírovať výber ako text"
- :command.editor/cut                                       "Vystrihnúť"
- :command.editor/undo                                      "Zrusiť zmenu"
- :command.editor/redo                                      "Znova"
- :command.editor/insert-link                               "HTML odkaz"
- :command.editor/select-all-blocks                         "Vybrať všetky bloky"
- :command.editor/select-parent                             "Vybrať rodičovský blok"
- :command.editor/zoom-in                                   "Priblížiť upravovaný blok/Inak vpred"
- :command.editor/zoom-out                                  "Oddialiť upravaný blok/Inak dozadu"
- :command.ui/toggle-brackets                               "Prepnúť, či sa majú zobraziť hranaté zátvorky"
- :command.go/electron-find-in-page                         "Nájsť text na stránke"
- :command.go/electron-jump-to-the-next                     "Preskočiť na ďalšiu zhodu s vyhľadávaním v Paneli hľadania"
- :command.go/electron-jump-to-the-previous                 "Preskočiť na predchádzajúcu zhodu s vyhľadávaním v Paneli hľadania"
- :command.go/search                                        "Hľadať stránky a bloky"
- :command.go/journals                                      "Choď do denníkov"
- :command.go/backward                                      "Späť"
- :command.go/forward                                       "Vpred"
- :command.search/re-index                                  "Obnoviť index vyhľadávania"
- :command.sidebar/open-today-page                          "Otvoriť dnešnú stránku v pravom bočnom paneli"
- :command.sidebar/close-top                                "Zavri hornú položku na pravom bočnom paneli"
- :command.sidebar/clear                                    "Vymazať všetko na pravom bočnom paneli"
- :command.misc/copy                                        "Kopírovať"
- :command.graph/export-as-html                             "Exportovať verejné stránky grafu ako HTML"
- :command.graph/open                                       "Vybrať graf na otvorenie"
- :command.graph/remove                                     "Odstrániť graf"
- :command.graph/add                                        "Pridať graf"
- :command.graph/re-index                                   "Preindexovať aktuálny graf"
- :command.command/run                                      "Spustiť príkaz GIT"
- :command.go/home                                          "Prejsť na hlavnú stránku"
- :command.go/all-graphs                                    "Prejsť na všetky grafy"
- :command.go/whiteboards                                   "Prejsť na tabule"
- :command.go/all-pages                                     "Prejsť na všetky stránky"
- :command.go/graph-view                                    "Prejsť na zobrazenie grafu"
- :command.go/keyboard-shortcuts                            "Prejsť na klávesové skratky"
- :command.go/tomorrow                                      "Prejsť na zajtrajší denník"
- :command.go/next-journal                                  "Prejsť na ďalší denník"
- :command.go/prev-journal                                  "Prejsť na predchádzajúci denník"
- :command.go/flashcards                                    "Zobraziť/Skryť kartičky"
- :command.ui/toggle-document-mode                          "Zobraziť/Skryť režim dokumentu"
- :command.ui/toggle-settings                               "Prepnúť nastavenia"
- :command.ui/toggle-right-sidebar                          "Zobraziť/Skryť pravý bočný panel"
- :command.ui/toggle-left-sidebar                           "Zobraziť/Skryť ľavý bočný panel"
- :command.ui/toggle-help                                   "Zobraziť/Skryť pomocníka"
- :command.ui/toggle-theme                                  "Prepínať medzi tmavým/svetlým motívom"
- :command.ui/toggle-contents                               "Zobraziť/Skryť obsah na bočnom paneli"
- :command.command/toggle-favorite                          "Pridať/Odstrániť z obľúbených"
- :command.editor/open-file-in-default-app                  "Otvoriť súbor v predvolenej aplikácii"
- :command.editor/open-file-in-directory                    "Otvoriť súbor v nadradenom adresári"
- :command.editor/copy-current-file                         "Skopírovať aktuálny súbor"
- :command.ui/toggle-wide-mode                              "Prepnúť širokouhlý režim"
- :command.ui/select-theme-color                            "Vyberte dostupné farby motívu"
- :command.ui/goto-plugins                                  "Prejsť na zoznam doplnkov"
- :command.ui/install-plugins-from-file                     "Inštalovať doplnky z plugins.edn"
- :command.editor/toggle-open-blocks                        "Prepnúť otvorené bloky (zbaliť alebo rozbaliť všetky bloky)"
- :command.git/commit                                       "Spusiť príkaz git commit so správou"}
+ :command.date-picker/complete                     "Výber dátumu: Potvrdiť vybraný deň"
+ :command.date-picker/prev-day                     "Výber dátumu: Vybrať predchádzajúci deň"
+ :command.date-picker/next-day                     "Výber dátumu: Vybrať nasledujúci deň"
+ :command.date-picker/prev-week                    "Výber dátumu: Vybrať predchádzajúci týždeň"
+ :command.date-picker/next-week                    "Výber dátumu: Vybrať nasledujúci týždeň"
+ :command.pdf/previous-page                        "PDF: Predchádzajúca strana aktuálneho PDF dokumentu"
+ :command.pdf/next-page                            "PDF: Nasledujúca strana aktuálneho PDF dokumentu"
+ :command.pdf/close                                "PDF: Zatvoriť aktuálny PDF dokument"
+ :command.pdf/find                                 "PDF: Vyhľadať text v aktuálnom PDF dokumente"
+ :command.auto-complete/complete                   "Automatické dokončovanie: Potvrdiť vybranú položku"
+ :command.auto-complete/prev                       "Automatické dokončovanie: Vybrať predchádzajúcu položku"
+ :command.auto-complete/next                       "Automatické dokončovanie: Vybrať ďalšiu položku"
+ :command.auto-complete/shift-complete             "Automatické dokončovanie: Otvoriť vybranú položku na bočnom paneli"
+ :command.auto-complete/open-link                  "Automatické dokončovanie: Otvoriť vybranú položku v prehliadači"
+ :command.cards/toggle-answers                     "Karty: Zobraziť/Skryť odpovede"
+ :command.cards/next-card                          "Karty: Ďalšia karta"
+ :command.cards/forgotten                          "Karty: Zabudnuté"
+ :command.cards/remembered                         "Karty: Zapamätané"
+ :command.cards/recall                             "Karty: Chvíľu trvalo, kým som si spomenul"
+ :command.editor/escape-editing                    "Zrušiť úpravu"
+ :command.editor/backspace                         "Zmazať dozadu"
+ :command.editor/delete                            "Zmazať dopredu"
+ :command.editor/new-block                         "Vytvoriť nový blok"
+ :command.editor/new-line                          "Nový riadok v aktuálnom bloku"
+ :command.editor/new-whiteboard                    "Nová tabuľa"
+ :command.editor/follow-link                       "Prejsť na odkaz pod kurzorom"
+ :command.editor/open-link-in-sidebar              "Otvoriť odkaz v bočnom paneli"
+ :command.editor/bold                              "Tučné"
+ :command.editor/italics                           "Kurzíva"
+ :command.editor/highlight                         "Zvýrazniť"
+ :command.editor/strike-through                    "Prečiarknuť"
+ :command.editor/clear-block                       "Odstrániť celý obsah bloku"
+ :command.editor/kill-line-before                  "Odstrániť riadok pred pozíciou kurzora"
+ :command.editor/copy-embed                        "Kopírovať vložený blok ukazujúci na aktuálny blok"
+ :command.editor/kill-line-after                   "Odstrániť riadok za pozíciou kurzora"
+ :command.editor/beginning-of-block                "Presunúť kurzor na začiatok bloku"
+ :command.editor/end-of-block                      "Presunúť kurzor na koniec bloku"
+ :command.editor/forward-word                      "Posunúť kurzor o slovo dopredu"
+ :command.editor/backward-word                     "Presunúť kurzor o slovo dozadu"
+ :command.editor/forward-kill-word                 "Vymazať slovo dopredu"
+ :command.editor/backward-kill-word                "Vymazať slovo dozadu"
+ :command.editor/replace-block-reference-at-point  "Nahradiť referenciu bloku jeho obsahom na danom mieste"
+ :command.editor/paste-text-in-one-block-at-point  "Prilepiť text do jedného bloku na danom mieste"
+ :command.editor/insert-youtube-timestamp          "Vložiť časovú značku z YouTube"
+ :command.editor/cycle-todo                        "Zmeniť stav TODO aktuálnej položky"
+ :command.editor/up                                "Posunúť kurzor hore/Vybrať hore"
+ :command.editor/down                              "Posunúť kurzor dole/Vybrať dole"
+ :command.editor/left                              "Posunúť kurzor doľava/Otvoriť vybraný blok na začiatku"
+ :command.editor/right                             "Posunúť kurzor doprava/Otvoriť vybraný blok na konci"
+ :command.editor/select-up                         "Vybrať obsah vyššie"
+ :command.editor/select-down                       "Vybrať obsah nižšie"
+ :command.editor/move-block-up                     "Posunúť blok nahor"
+ :command.editor/move-block-down                   "Posunúť blok nadol"
+ :command.editor/open-edit                         "Upraviť vybraný blok"
+ :command.editor/select-block-up                   "Vybrať blok vyššie"
+ :command.editor/select-block-down                 "Vybrať blok nižšie"
+ :command.editor/delete-selection                  "Odstrániť vybrané bloky"
+ :command.editor/expand-block-children             "Rozbaliť"
+ :command.editor/collapse-block-children           "Zbaliť"
+ :command.editor/indent                            "Odsadiť blok"
+ :command.editor/outdent                           "Zrusiť odsadenie bloku"
+ :command.editor/copy                              "Kopírovať (skopíruje buď výber alebo referenciu na blok)"
+ :command.editor/copy-text                         "Kopírovať výber ako text"
+ :command.editor/cut                               "Vystrihnúť"
+ :command.editor/undo                              "Späť"
+ :command.editor/redo                              "Znova"
+ :command.editor/insert-link                       "HTML odkaz"
+ :command.editor/select-all-blocks                 "Vybrať všetky bloky"
+ :command.editor/select-parent                     "Vybrať rodičovský blok"
+ :command.editor/zoom-in                           "Priblížiť upravovaný blok/Inak vpred"
+ :command.editor/zoom-out                          "Oddialiť upravaný blok/Inak dozadu"
+ :command.editor/toggle-undo-redo-mode             "Prepnúť režim undo/redo (globálny alebo len na stránke)"
+ :command.editor/toggle-number-list                "Prepnúť číslovaný zoznam"
+ :command.whiteboard/select                        "Nástroj výberu"
+ :command.whiteboard/pan                           "Nástroj posunu"
+ :command.whiteboard/portal                        "Nástroj portálu"
+ :command.whiteboard/pencil                        "Nástroj ceruzky"
+ :command.whiteboard/highlighter                   "Nástroj zvýrazňovača"
+ :command.whiteboard/eraser                        "Nástroj gumy"
+ :command.whiteboard/connector                     "Nástroj konektora"
+ :command.whiteboard/text                          "Nástroj textu"
+ :command.whiteboard/rectangle                     "Nástroj obdĺžnika"
+ :command.whiteboard/ellipse                       "Nástroj elipsy"
+ :command.whiteboard/reset-zoom                    "Obnoviť priblíženie"
+ :command.whiteboard/zoom-to-fit                   "Priblížiť na výkres"
+ :command.whiteboard/zoom-to-selection             "Priblížiť na výber"
+ :command.whiteboard/zoom-out                      "Oddialiť"
+ :command.whiteboard/zoom-in                       "Priblížiť"
+ :command.whiteboard/send-backward                 "Posunúť dozadu"
+ :command.whiteboard/send-to-back                  "Posunúť na pozadie"
+ :command.whiteboard/bring-forward                 "Posunúť dopredu"
+ :command.whiteboard/bring-to-front                "Posunúť na popredie"
+ :command.whiteboard/lock                          "Zamknúť výber"
+ :command.whiteboard/unlock                        "Odomknúť výber"
+ :command.whiteboard/group                         "Zoskupiť výber"
+ :command.whiteboard/ungroup                       "Rozoskupiť výber"
+ :command.whiteboard/toggle-grid                   "Prepnúť mriežku plátna"
+ :command.ui/toggle-brackets                       "Prepnúť zobrazenie zátvoriek"
+ :command.go/electron-find-in-page                 "Nájsť text na stránke"
+ :command.go/electron-jump-to-the-next             "Preskočiť na ďalšiu zhodu v Paneli hľadania"
+ :command.go/electron-jump-to-the-previous         "Preskočiť na predchádzajúcu zhodu v Paneli hľadania"
+ :command.go/search                                "Hľadať stránky a bloky"
+ :command.go/search-in-page                        "Hľadať bloky na stránke"
+ :command.command-palette/toggle                   "Hľadať príkazy"
+ :command.go/journals                              "Prejsť do denníkov"
+ :command.go/backward                              "Späť"
+ :command.go/forward                               "Vpred"
+ :command.search/re-index                          "Obnoviť index vyhľadávania"
+ :command.sidebar/open-today-page                  "Otvoriť dnešnú stránku v pravom bočnom paneli"
+ :command.sidebar/close-top                        "Zavrieť hornú položku na pravom bočnom paneli"
+ :command.sidebar/clear                            "Vymazať všetko na pravom bočnom paneli"
+ :command.misc/copy                                "Kopírovať"
+ :command.graph/export-as-html                     "Exportovať verejné stránky grafu ako HTML"
+ :command.graph/open                               "Vybrať graf na otvorenie"
+ :command.graph/remove                             "Odstrániť graf"
+ :command.graph/add                                "Pridať graf"
+ :command.graph/re-index                           "Preindexovať aktuálny graf"
+ :command.command/run                              "Spustiť príkaz GIT"
+ :command.go/home                                  "Prejsť na hlavnú stránku"
+ :command.go/all-graphs                            "Prejsť na všetky grafy"
+ :command.go/whiteboards                           "Prejsť na tabule"
+ :command.go/all-pages                             "Prejsť na všetky stránky"
+ :command.go/graph-view                            "Prejsť na zobrazenie grafu"
+ :command.go/keyboard-shortcuts                    "Prejsť na klávesové skratky"
+ :command.go/tomorrow                              "Prejsť na zajtrajší denník"
+ :command.go/next-journal                          "Prejsť na ďalší denník"
+ :command.go/prev-journal                          "Prejsť na predchádzajúci denník"
+ :command.go/flashcards                            "Zobraziť/Skryť kartičky"
+ :command.ui/toggle-document-mode                  "Zobraziť/Skryť režim dokumentu"
+ :command.ui/toggle-settings                       "Prepnúť nastavenia"
+ :command.ui/toggle-right-sidebar                  "Zobraziť/Skryť pravý bočný panel"
+ :command.ui/toggle-left-sidebar                   "Zobraziť/Skryť ľavý bočný panel"
+ :command.ui/toggle-help                           "Zobraziť/Skryť pomocníka"
+ :command.ui/toggle-theme                          "Prepínať medzi tmavým/svetlým motívom"
+ :command.ui/toggle-contents                       "Zobraziť/Skryť obsah na bočnom paneli"
+ :command.ui/cycle-color-off                       "Vypnúť zmenu farieb"
+ :command.ui/cycle-color                           "Zmeniť farbu"
+ :command.command/toggle-favorite                  "Pridať/Odstrániť z obľúbených"
+ :command.editor/open-file-in-default-app          "Otvoriť súbor v predvolenej aplikácii"
+ :command.editor/open-file-in-directory            "Otvoriť súbor v nadradenom adresári"
+ :command.editor/copy-current-file                 "Kopírovať aktuálny súbor"
+ :command.editor/copy-page-url                     "Kopírovať URL adresu stránky"
+ :command.ui/toggle-wide-mode                      "Prepnúť širokouhlý režim"
+ :command.ui/select-theme-color                    "Vybrať dostupné farby motívu"
+ :command.ui/goto-plugins                          "Prejsť na zoznam doplnkov"
+ :command.ui/install-plugins-from-file             "Inštalovať doplnky z plugins.edn"
+ :command.editor/toggle-open-blocks                "Prepnúť otvorené bloky (zbaliť alebo rozbaliť všetky bloky)"
+ :command.ui/clear-all-notifications               "Zmazať všetky oznámenia"
+ :command.git/commit                               "Spustiť príkaz git commit so správou"
+ :command.dev/show-block-data                      "(Dev) Zobraziť údaje bloku"
+ :command.dev/show-block-ast                       "(Dev) Zobraziť AST bloku"
+ :command.dev/show-page-data                       "(Dev) Zobraziť údaje stránky"
+ :command.dev/show-page-ast                        "(Dev) Zobraziť AST stránky"
+ :command.window/close                             "Zavrieť okno"}

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

@@ -110,6 +110,7 @@
  :help/shortcuts "Klavye kısayolları"
  :help/shortcuts-triggers "Tetikleyiciler"
  :help/shortcut "Kısayol"
+ :help/search "Sayfalarda/bloklarda/komutlarda ara"
  :help/slash-autocomplete "Eğik çizgi otomatik tamamlama"
  :help/reference-autocomplete "Sayfa referansı otomatik tamamlama"
  :help/block-reference "Blok referansı"
@@ -219,6 +220,24 @@
  :page/updated-at "Güncellenme Zamanı"
  :page/backlinks "Geri Bağlantılar"
  :linked-references/filter-search "Bağlantılı sayfalarda ara"
+ :linked-references/filter-heading "Filtre"
+ :linked-references/filter-directions "Dahil etmek için tıklayın, hariç tutmak için Shift tuşunu basılı tutarak tıklayın. Kaldırmak için tekrar tıklayın."
+ :linked-references/filter-includes "Dahil et: "
+ :linked-references/filter-excludes "Hariç tut: "
+ :linked-references/reference-count (fn [filtered-count total]
+                                      ;; 1 Linked Reference
+                                      ;; 1 of 1 Linked Reference
+                                      ;; 2 of 5 Linked References
+                                      (str
+                                       (when filtered-count
+                                         (str filtered-count " / "))
+                                       total
+                                       (if (= total 1) " Bağlantılı Referans" " Bağlantılı Referans")))
+ :unlinked-references/reference-count (fn [total]
+                                        ;; 1 Unlinked Reference
+                                        ;; 5 Unlinked References
+                                        (str total
+                                         (if (= total 1) " Bağlantısız Referans" " Bağlantısız Referans")))
  :editor/block-search "Blok ara"
  :text/image "Resim"
  :asset/show-in-folder "Resmi klasörde göster"
@@ -456,6 +475,7 @@
  :whiteboard/toggle-grid "Kılavuzu aç/kapat"
  :whiteboard/snap-to-grid "Kılavuzlara daya"
  :whiteboard/toggle-pen-mode "Kalem modunu aç/kapat"
+ :whiteboard/reference-count (fn [refs-count] (if (= refs-count 1) "Referans" "Referans"))
  :flashcards/modal-welcome-title "Bir kart oluşturma zamanı!"
  :flashcards/modal-welcome-desc-1 "Herhangi bir bloğu karta dönüştürmek için \"#card\" etiketini ekleyebilir veya cümle tamamlama eklemek için \"/cloze\" komutunu kullanabilirsiniz."
  :flashcards/modal-welcome-desc-2 "Belgeleri kontrol etmek için "
@@ -746,11 +766,17 @@
  :command.whiteboard/group               "Seçimi gruplandır"
  :command.whiteboard/ungroup             "Seçimi gruptan çıkar"
  :command.whiteboard/toggle-grid         "Tuval ızgarasını değiştir"
+ :command.whiteboard/clone-right         "Sağa kopyala"
+ :command.whiteboard/clone-left          "Sola kopyala"
+ :command.whiteboard/clone-up            "Yukarı kopyala"
+ :command.whiteboard/clone-down          "Aşağı kopyala"
  :command.ui/toggle-brackets             "Köşeli ayraçların görüntülenip görüntülenmeyeceğini değiştir"
  :command.go/electron-find-in-page       "Sayfada bul"
  :command.go/electron-jump-to-the-next   "Aramanız için bir sonraki eşleşmeye atlayın"
  :command.go/electron-jump-to-the-previous "Aramanız için bir önceki eşleşmeye atlayın"
- :command.go/search                      "Tam metin araması"
+ :command.go/search                      "Sayfalarda ve bloklarda ara"
+ :command.go/search-in-page              "Sayfa içindeki blokları ara"
+ :command.command-palette/toggle         "Komutları ara"
  :command.go/journals                    "Günlüğe git"
  :command.go/backward                    "Geriye git"
  :command.go/forward                     "İleriye git"
@@ -782,6 +808,8 @@
  :command.ui/toggle-help                 "Yardımı aç/kapat"
  :command.ui/toggle-theme                "Koyu ve açık tema arasında geçiş yap"
  :command.ui/toggle-contents             "Kenar çubuğundaki içeriği aç/kapat"
+ :command.ui/cycle-color-off             "Renk değiştirmeyi kapat"
+ :command.ui/cycle-color                 "Renk değiştir"
  :command.command/toggle-favorite        "Sık kullanılanlara ekle/çıkar"
  :command.editor/open-file-in-default-app "Dosyayı varsayılan uygulamada aç"
  :command.editor/open-file-in-directory   "Dosyayı üst dizinde aç"

+ 1 - 0
src/resources/dicts/zh-cn.edn

@@ -262,6 +262,7 @@
  :all-graphs "所有图谱"
  :all-pages "所有页面"
  :all-files "所有文件"
+ :remove-orphaned-pages "删除孤立页面?"
  :settings "设置"
  :settings-of-plugins "插件设置"
  :plugins "插件"

+ 1 - 0
src/resources/dicts/zh-hant.edn

@@ -220,6 +220,7 @@
  :all-pages "所有分頁"
  :all-whiteboards "所有白板"
  :all-files "所有資料"
+ :remove-orphaned-pages "移除孤立頁面?"
  :all-journals "所有日記"
  :settings "設定"
  :settings-of-plugins "外掛設定"

+ 5 - 5
src/resources/tutorials/tutorial-sk.md

@@ -1,12 +1,12 @@
 ## Ahoj, vitaj v Logseq!
-- Logseq je [open-source](https://github.com/logseq/logseq) platforma určená na zdielanie _znalostí_ a spoluprácu s dôrazom na _súkromie_.
+- Logseq je [open-source](https://github.com/logseq/logseq) platforma určená na zdielanie _znalostí_ a spoluprácu s dôrazom na _ochranu súkromia_.
 - Toto je trojminutový návod ako používať Logseq. Poďme na to!
 - Tu je niekoľko tipov, ktoré môžu byť užitočné.
   #+BEGIN_TIP
   Kliknutím upravíte ľubovoľný blok.
-  Stlačním klávesi `Enter` vytvoríš nový blok.
-  Stlačním klávesovej kombinácie `Shift+Enter` vytvoríš nový riadok.
-  Stlačením `/` zobrazíš všetky príkazy.
+  Stlačním klávesi `Enter` vytvoríte nový blok.
+  Stlačním klávesovej kombinácie `Shift+Enter` vytvoríte nový riadok.
+  Stlačením `/` zobrazíte všetky príkazy.
   #+END_TIP
 - 1. Vytvorme stránku [[Ako vytvoriť poznámky?]]. Môžete na ňu kliknúť a prejsť na danú stránku, alebo ju môžete otvoriť pomocou klávesovej skratky `Shift+Click` na pravom bočnom paneli! Teraz by ste mali vidieť _Prepojené referencie_ aj _Neprepojené referencie_.
 - 2. Vytvorme referenciu na nejaké bloky na stránke [[Ako vytvoriť poznámky?]]. Pomocou klávesovej skratky `Shift+Click` môžete otvoriť referenciu na ľubovoľný blok na pravom bočnom paneli. Skúste čokoľvek zmeniť v pravom paneli a tieto zmeny sa premietnu do referencovaného bloku!
@@ -23,4 +23,4 @@
     - DONE Vytvoriť stránku
     - CANCELED [#C] Napísať stránku s viac ako tisíc blokmi
 - A to je všetko! Teraz môžete vytvoriť viac odrážok alebo otvoriť lokálny adresár a importovať nejaké poznámky!
-- Takisto môžeš stiahnúť našu desktopovú aplikáciu z https://github.com/logseq/logseq/releases
+- Môžete si tiež stiahnúť našu desktopovú aplikáciu z https://github.com/logseq/logseq/releases

+ 21 - 6
src/test/frontend/db/query_dsl_test.cljs

@@ -198,6 +198,10 @@ prop-d:: nada"}])
           (dsl-query "(or (page-property parent [[child page 1]]) (page-property parent [[child page 2]]))")))
       "Page property queries ORed")
 
+  (is (= ["page1" "page3"]
+         (map :block/name
+              (dsl-query "(and (page-property parent [[child page 1]]) (or (page-property interesting true) (page-property parent [[child page 2]])))"))))
+
   (is (= ["page4"]
          (map
           :block/name
@@ -230,31 +234,36 @@ prop-d:: nada"}])
 - NOW b1
 - TODO b2
 - LATER b3
-- LATER [#A] b4"}])
+- LATER [#A] b4
+- LATER [#B] b5"}])
 
   (testing "Lowercase query"
     (is (= ["NOW b1"]
            (map :block/content (dsl-query "(task now)"))))
 
-    (is (= ["LATER b3" "LATER [#A] b4"]
+    (is (= ["LATER b3" "LATER [#A] b4" "LATER [#B] b5"]
            (map :block/content (dsl-query "(task later)")))))
 
-  (is (= ["LATER b3" "LATER [#A] b4"]
+  (is (= ["LATER b3" "LATER [#A] b4" "LATER [#B] b5"]
          (map :block/content (dsl-query "(task LATER)")))
       "Uppercase query")
 
   (testing "Multiple specified tasks results in ORed results"
-    (is (= ["NOW b1" "LATER b3" "LATER [#A] b4"]
+    (is (= ["NOW b1" "LATER b3" "LATER [#A] b4" "LATER [#B] b5"]
            (map :block/content (dsl-query "(task now later)"))))
 
-    (is (= ["NOW b1" "LATER b3" "LATER [#A] b4"]
+    (is (= ["NOW b1" "LATER b3" "LATER [#A] b4" "LATER [#B] b5"]
            (map :block/content (dsl-query "(task [now later])")))
         "Multiple arguments specified with vector notation"))
 
   (is (= ["NOW b1" "LATER [#A] b4"]
          (map :block/content
               (dsl-query "(or (todo now) (and (todo later) (priority a)))")))
-      "Multiple boolean operators with todo and priority operators"))
+      "Multiple boolean operators with todo and priority operators")
+
+  (is (= ["LATER [#A] b4" "LATER [#B] b5"]
+         (map :block/content
+              (dsl-query "(and (todo later) (or (priority a) (priority b)))")))))
 
 (deftest sample-queries
   (load-test-files [{:file/path "pages/page1.md"
@@ -330,6 +339,12 @@ prop-d:: nada"}])
               (map str/trimr)
               set)))
 
+  (is (= #{"DONE b2 [[page 1]]" "LATER b4 [[page 2]]"}
+         (->> (dsl-query "(and \"b\" (or \"2\" \"4\"))")
+              (keep :block/content)
+              set))
+      "AND-OR with full text search")
+
   ;; FIXME: not working
   ;; Requires or-join and not-join which aren't supported yet
   ; (is (= []

+ 1 - 22
src/test/frontend/handler/paste_test.cljs

@@ -231,25 +231,4 @@
                       #js {:clipboardData #js {:getData (constantly clipboard)
                                                :files files}})]
              (is (= files (js->clj @pasted-file)))
-             (reset)))))
-
-(deftest-async editor-on-paste-prefer-text-blocks-to-html
-  (let [actual-blocks (atom nil)
-        ;; Simplified version of block attributes that are copied
-        expected-blocks [{:block/content "Test node"}
-                         {:block/content "Notes\nid:: 6422ec75-85c7-4e09-9a4d-2a1639a69b2f"}]
-        html "<b>bold text</b>"
-        text "- Test node\n\t- Notes\nid:: 6422ec75-85c7-4e09-9a4d-2a1639a69b2f"]
-    (test-helper/with-reset
-      reset
-      [;; 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))
-       state/get-edit-block (constantly {})
-       editor-handler/paste-blocks (fn [blocks _] (reset! actual-blocks blocks))]
-      (p/let [_ ((paste-handler/editor-on-paste! nil)
-                 #js {:clipboardData #js {:getData (fn [kind]
-                                                     (if (= kind "text/html") html text))}})]
-        (is (= expected-blocks (map #(select-keys % [:block/content]) @actual-blocks)))
-        (reset)))))
+             (reset)))))

+ 213 - 9
static/yarn.lock

@@ -178,6 +178,21 @@
     cross-zip "^4.0.0"
     fs-extra "^10.0.0"
 
+"@electron-forge/plugin-auto-unpack-natives@^6.0.4":
+  version "6.4.2"
+  resolved "https://registry.yarnpkg.com/@electron-forge/plugin-auto-unpack-natives/-/plugin-auto-unpack-natives-6.4.2.tgz#8c8b0066d352b2b5983c4ca3b47818b7b8f9bc1c"
+  integrity sha512-AXmPQc2nUFuh/xFC+Qsebg/eg3M+5GQV6MOzMvTBZ9N1w49XtZbtvEGAdISnZWERExNRcwH+j+zuSAGtm6Y2Yw==
+  dependencies:
+    "@electron-forge/plugin-base" "6.4.2"
+    "@electron-forge/shared-types" "6.4.2"
+
+"@electron-forge/[email protected]":
+  version "6.4.2"
+  resolved "https://registry.yarnpkg.com/@electron-forge/plugin-base/-/plugin-base-6.4.2.tgz#65054f841728688cc31edeb94479e6bd4bc07782"
+  integrity sha512-g6AAtQ7fZ94djBmwcnWasQ8xgaNVNjgaQ00GLK0NkmQ7n0PNbsnlMDuw9vdfTiL6WaLg5nxNSYc9bFJP/rtyeA==
+  dependencies:
+    "@electron-forge/shared-types" "6.4.2"
+
 "@electron-forge/plugin-base@^6.0.4":
   version "6.0.4"
   resolved "https://registry.yarnpkg.com/@electron-forge/plugin-base/-/plugin-base-6.0.4.tgz#a3b62e006632dab66d35f845ca61e09745ccbb82"
@@ -202,6 +217,15 @@
     electron-rebuild "^3.2.6"
     ora "^5.0.0"
 
+"@electron-forge/[email protected]":
+  version "6.4.2"
+  resolved "https://registry.yarnpkg.com/@electron-forge/shared-types/-/shared-types-6.4.2.tgz#ffc25a21d1bc7e6cb8fcf8dc1e1767c6a8c52d02"
+  integrity sha512-DKOUMsdTXZIq8XiqY0Hi3C+dam/JKUnvfBjwcUeyZqPdgEE1qry8xZmmjorXuLrRf1Jq8rhxYGQInSK4af0QYw==
+  dependencies:
+    "@electron/rebuild" "^3.2.10"
+    electron-packager "^17.1.2"
+    listr2 "^5.0.3"
+
 "@electron-forge/shared-types@^6.0.4":
   version "6.0.4"
   resolved "https://registry.yarnpkg.com/@electron-forge/shared-types/-/shared-types-6.0.4.tgz#f8cdcf453459caa156d513582054b8e81057584f"
@@ -304,6 +328,18 @@
     minimist "^1.2.6"
     plist "^3.0.5"
 
+"@electron/osx-sign@^1.0.5":
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/@electron/osx-sign/-/osx-sign-1.0.5.tgz#0af7149f2fce44d1a8215660fd25a9fb610454d8"
+  integrity sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==
+  dependencies:
+    compare-version "^0.1.2"
+    debug "^4.3.4"
+    fs-extra "^10.0.0"
+    isbinaryfile "^4.0.8"
+    minimist "^1.2.6"
+    plist "^3.0.5"
+
 "@electron/[email protected]", "@electron/rebuild@^3.2.10":
   version "3.2.10"
   resolved "https://registry.yarnpkg.com/@electron/rebuild/-/rebuild-3.2.10.tgz#adc9443179709d4e4b93a68fac6a08b9a3b9e5e6"
@@ -1068,6 +1104,14 @@ base64-js@^1.3.1, base64-js@^1.5.1:
   resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
   integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
 
[email protected]:
+  version "8.0.1"
+  resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-8.0.1.tgz#3a596d21fbcefadf36f94e126c5cf24d5697d0b8"
+  integrity sha512-JhTZjpyapA1icCEjIZB4TSSgkGdFgpWZA2Wszg7Cf4JwJwKQmbvuNnJBeR+EYG/Z29OXvR4G//Rbg31BW/Z7Yg==
+  dependencies:
+    bindings "^1.5.0"
+    prebuild-install "^7.1.0"
+
 binary-extensions@^2.0.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
@@ -1080,7 +1124,7 @@ bindings@^1.5.0:
   dependencies:
     file-uri-to-path "1.0.0"
 
-bl@^4.1.0:
+bl@^4.0.3, bl@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
   integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
@@ -1358,6 +1402,11 @@ chokidar@^3.5.1:
   optionalDependencies:
     fsevents "~2.3.2"
 
+chownr@^1.1.1:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
+  integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
+
 chownr@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
@@ -1707,6 +1756,11 @@ depd@^1.1.2:
   resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
   integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
 
+detect-libc@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d"
+  integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==
+
 detect-libc@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd"
@@ -2008,6 +2062,31 @@ electron-packager@^17.1.1:
     semver "^7.1.3"
     yargs-parser "^21.1.1"
 
+electron-packager@^17.1.2:
+  version "17.1.2"
+  resolved "https://registry.yarnpkg.com/electron-packager/-/electron-packager-17.1.2.tgz#18030b28024d242b706d0a8a67ed4cd1a57311aa"
+  integrity sha512-XofXdikjYI7MVBcnXeoOvRR+yFFFHOLs3J7PF5KYQweigtgLshcH4W660PsvHr4lYZ03JBpLyEcUB8DzHZ+BNw==
+  dependencies:
+    "@electron/asar" "^3.2.1"
+    "@electron/get" "^2.0.0"
+    "@electron/notarize" "^1.2.3"
+    "@electron/osx-sign" "^1.0.5"
+    "@electron/universal" "^1.3.2"
+    cross-spawn-windows-exe "^1.2.0"
+    debug "^4.0.1"
+    extract-zip "^2.0.0"
+    filenamify "^4.1.0"
+    fs-extra "^11.1.0"
+    galactus "^1.0.0"
+    get-package-info "^1.0.0"
+    junk "^3.1.0"
+    parse-author "^2.0.0"
+    plist "^3.0.0"
+    rcedit "^3.0.1"
+    resolve "^1.1.6"
+    semver "^7.1.3"
+    yargs-parser "^21.1.1"
+
 [email protected]:
   version "21.2.0"
   resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-21.2.0.tgz#cc225cb46aa62e74b899f2f7299b396c9802387d"
@@ -2080,10 +2159,10 @@ electron-winstaller@^5.0.0:
     lodash.template "^4.2.2"
     temp "^0.9.0"
 
-electron@*, electron@25.9.3:
-  version "25.9.3"
-  resolved "https://registry.yarnpkg.com/electron/-/electron-25.9.3.tgz#cdd53a30fb914adadcfbd34124237fb38b1c07d0"
-  integrity sha512-dacaHg/PuwVcFRgPDCM5j7UDzqGJWOsbBRdS5wPKLNS/ejPeccIjuNUT1cqcrpvCJKAFW8swHWg9kdizNSEDHQ==
+electron@*, electron@27.1.3:
+  version "27.1.3"
+  resolved "https://registry.yarnpkg.com/electron/-/electron-27.1.3.tgz#3fd6decda95c1dd0a7e51a9ac77ee0ba37b7c5c6"
+  integrity sha512-7eD8VMhhlL5J531OOawn00eMthUkX1e3qN5Nqd7eMK8bg5HxQBrn8bdPlvUEnCano9KhrVwaDnGeuzWoDOGpjQ==
   dependencies:
     "@electron/get" "^2.0.0"
     "@types/node" "^18.11.18"
@@ -2111,7 +2190,7 @@ encoding@^0.1.13:
   dependencies:
     iconv-lite "^0.6.2"
 
-end-of-stream@^1.1.0:
+end-of-stream@^1.1.0, end-of-stream@^1.4.1:
   version "1.4.4"
   resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
   integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
@@ -2188,6 +2267,11 @@ execa@^1.0.0:
     signal-exit "^3.0.0"
     strip-eof "^1.0.0"
 
+expand-template@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
+  integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
+
 expand-tilde@^2.0.0, expand-tilde@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502"
@@ -2406,6 +2490,14 @@ flora-colossus@^1.0.0:
     debug "^4.1.1"
     fs-extra "^7.0.0"
 
+flora-colossus@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/flora-colossus/-/flora-colossus-2.0.0.tgz#af1e85db0a8256ef05f3fb531c1235236c97220a"
+  integrity sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA==
+  dependencies:
+    debug "^4.3.4"
+    fs-extra "^10.1.0"
+
 fmix@^0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/fmix/-/fmix-0.1.0.tgz#c7bbf124dec42c9d191cfb947d0a9778dd986c0c"
@@ -2427,6 +2519,11 @@ [email protected]:
   resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
   integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
 
+fs-constants@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
+  integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
+
 [email protected], fs-extra@^9.0.0, fs-extra@^9.0.1:
   version "9.1.0"
   resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
@@ -2446,6 +2543,15 @@ fs-extra@^10.0.0, fs-extra@^10.1.0:
     jsonfile "^6.0.1"
     universalify "^2.0.0"
 
+fs-extra@^11.1.0:
+  version "11.2.0"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b"
+  integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==
+  dependencies:
+    graceful-fs "^4.2.0"
+    jsonfile "^6.0.1"
+    universalify "^2.0.0"
+
 fs-extra@^4.0.0:
   version "4.0.3"
   resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
@@ -2516,6 +2622,15 @@ galactus@^0.2.1:
     flora-colossus "^1.0.0"
     fs-extra "^4.0.0"
 
+galactus@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/galactus/-/galactus-1.0.0.tgz#c2615182afa0c6d0859b92e56ae36d052827db7e"
+  integrity sha512-R1fam6D4CyKQGNlvJne4dkNF+PvUUl7TAJInvTGa9fti9qAv95quQz29GXapA4d8Ec266mJJxFVh82M4GIIGDQ==
+  dependencies:
+    debug "^4.3.4"
+    flora-colossus "^2.0.0"
+    fs-extra "^10.1.0"
+
 gar@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/gar/-/gar-1.0.4.tgz#f777bc7db425c0572fdeb52676172ca1ae9888b8"
@@ -2602,6 +2717,11 @@ get-stream@^5.1.0:
   dependencies:
     pump "^3.0.0"
 
[email protected]:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
+  integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==
+
 github-url-to-object@^4.0.4:
   version "4.0.6"
   resolved "https://registry.yarnpkg.com/github-url-to-object/-/github-url-to-object-4.0.6.tgz#5ea8701dc8c336b8d582dc3fa5bf964165c3b365"
@@ -3479,6 +3599,11 @@ minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.6:
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
   integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
 
+minimist@^1.2.3:
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
+  integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
+
 minipass-collect@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
@@ -3533,6 +3658,11 @@ minizlib@^2.1.1, minizlib@^2.1.2:
     minipass "^3.0.0"
     yallist "^4.0.0"
 
+mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
+  version "0.5.3"
+  resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
+  integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
+
 mkdirp@^0.5.1:
   version "0.5.6"
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
@@ -3586,6 +3716,11 @@ nan@^2.4.0:
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb"
   integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==
 
+napi-build-utils@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806"
+  integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==
+
 negotiator@^0.6.3:
   version "0.6.3"
   resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
@@ -3596,7 +3731,7 @@ nice-try@^1.0.4:
   resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
   integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
 
[email protected], node-abi@^3.0.0:
[email protected], node-abi@^3.0.0, node-abi@^3.3.0:
   version "3.51.0"
   resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.51.0.tgz#970bf595ef5a26a271307f8a4befa02823d4e87d"
   integrity sha512-SQkEP4hmNWjlniS5zdnfIXTk1x7Ome85RDzHlTbBtzE97Gfwz/Ipw4v/Ryk20DWIy3yCNVLVlGKApCnmvYoJbA==
@@ -3996,6 +4131,24 @@ [email protected]:
   dependencies:
     fflate "^0.4.1"
 
+prebuild-install@^7.1.0:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45"
+  integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==
+  dependencies:
+    detect-libc "^2.0.0"
+    expand-template "^2.0.3"
+    github-from-package "0.0.0"
+    minimist "^1.2.3"
+    mkdirp-classic "^0.5.3"
+    napi-build-utils "^1.0.1"
+    node-abi "^3.3.0"
+    pump "^3.0.0"
+    rc "^1.2.7"
+    simple-get "^4.0.0"
+    tar-fs "^2.0.0"
+    tunnel-agent "^0.6.0"
+
 prepend-http@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
@@ -4092,7 +4245,7 @@ random-path@^0.1.0:
     base32-encode "^0.1.0 || ^1.0.0"
     murmur-32 "^0.1.0 || ^0.2.0"
 
[email protected], rc@^1.2.8:
[email protected], rc@^1.2.7, rc@^1.2.8:
   version "1.2.8"
   resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
   integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
@@ -4149,6 +4302,15 @@ read-pkg@^2.0.0:
     normalize-package-data "^2.3.2"
     path-type "^2.0.0"
 
+readable-stream@^3.1.1:
+  version "3.6.2"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
+  integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
+  dependencies:
+    inherits "^2.0.3"
+    string_decoder "^1.1.1"
+    util-deprecate "^1.0.1"
+
 readable-stream@^3.4.0, readable-stream@^3.6.0:
   version "3.6.0"
   resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
@@ -4339,7 +4501,7 @@ rxjs@^7.5.7:
   dependencies:
     tslib "^2.1.0"
 
-safe-buffer@~5.2.0:
+safe-buffer@^5.0.1, safe-buffer@~5.2.0:
   version "5.2.1"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
   integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@@ -4467,6 +4629,20 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.7:
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
   integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
 
+simple-concat@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
+  integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
+
+simple-get@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543"
+  integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==
+  dependencies:
+    decompress-response "^6.0.0"
+    once "^1.3.1"
+    simple-concat "^1.0.0"
+
 slice-ansi@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787"
@@ -4697,6 +4873,27 @@ supports-preserve-symlinks-flag@^1.0.0:
   resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
   integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
 
+tar-fs@^2.0.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784"
+  integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==
+  dependencies:
+    chownr "^1.1.1"
+    mkdirp-classic "^0.5.2"
+    pump "^3.0.0"
+    tar-stream "^2.1.4"
+
+tar-stream@^2.1.4:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
+  integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
+  dependencies:
+    bl "^4.0.3"
+    end-of-stream "^1.4.1"
+    fs-constants "^1.0.0"
+    inherits "^2.0.3"
+    readable-stream "^3.1.1"
+
 tar@^6.0.5, tar@^6.1.11, tar@^6.1.2:
   version "6.1.11"
   resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
@@ -4829,6 +5026,13 @@ tslib@^2.1.0, tslib@^2.2.0:
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
   integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
 
+tunnel-agent@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+  integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==
+  dependencies:
+    safe-buffer "^5.0.1"
+
 tunnel@^0.0.6:
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"

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

@@ -1,5 +1,6 @@
 import Vec from '@tldraw/vec'
 import type { TLAsset, TLBinding, TLEventMap } from '../../types'
+import { TLCloneDirection, Geometry } from '../../types'
 import { BoundsUtils, isNonNullable, uniqueId } from '../../utils'
 import type { TLShape, TLShapeModel } from '../shapes'
 import type { TLApp } from '../TLApp'
@@ -230,6 +231,56 @@ export class TLApi<S extends TLShape = TLShape, K extends TLEventMap = TLEventMa
     return this.app.createNewLineBinding(source, target)
   }
 
+  clone = (direction: TLCloneDirection) => {
+    if (
+      this.app.readOnly ||
+      this.app.selectedShapesArray.length !== 1 ||
+      !Object.values(Geometry).some((geometry: string) => geometry === this.app.selectedShapesArray[0].type)
+    ) return;
+
+    const shape = this.app.allSelectedShapesArray[0]
+    const ShapeClass = this.app.getShapeClass(shape.type)
+
+    const {minX, minY, maxX, maxY, width, height} = shape.bounds
+    const spacing = 100
+    let point = [0, 0]
+
+    switch(direction) {
+      case TLCloneDirection.Down: {
+        point = [minX, maxY + spacing]
+        break
+      }
+      case TLCloneDirection.Up: {
+        point = [minX, minY - spacing  - height]
+        break
+      }
+      case TLCloneDirection.Left: {
+        point = [minX - spacing - width, minY]
+        break
+      }
+      case TLCloneDirection.Right: {
+        point = [maxX + spacing, minY]
+        break
+      }
+    }
+
+    const clone = new ShapeClass({
+      ...shape.serialized,
+      id: uniqueId(),
+      nonce: Date.now(),
+      refs: [],
+      label: '',
+      point: point,
+    })
+
+    this.app.history.pause()
+    this.app.currentPage.addShapes(clone)
+    this.app.createNewLineBinding(shape, clone)
+    this.app.history.resume()
+    this.app.persist();
+    setTimeout(() => this.editShape(clone)) 
+  }
+  
   /** Clone shapes with given context */
   cloneShapes = ({
     shapes,

+ 16 - 1
tldraw/packages/core/src/lib/TLApp/TLApp.ts

@@ -15,7 +15,7 @@ import type {
   TLSubscriptionEventInfo,
   TLSubscriptionEventName,
 } from '../../types'
-import { AlignType, DistributeType } from '../../types'
+import { AlignType, DistributeType, Geometry } from '../../types'
 import { BoundsUtils, createNewLineBinding, dedupe, isNonNullable, uniqueId } from '../../utils'
 import type { TLShape, TLShapeConstructor, TLShapeModel } from '../shapes'
 import { TLApi } from '../TLApi'
@@ -837,6 +837,21 @@ export class TLApp<
     )
   }
 
+  @computed get showCloneHandles() {
+    const { selectedShapesArray } = this
+    return (
+      this.isInAny(
+        'select.idle',
+        'select.hoveringSelectionHandle',
+        'select.pointingShape',
+        'select.pointingSelectedShape',
+      ) &&
+      selectedShapesArray.length === 1 &&
+      Object.values(Geometry).some((geometry: string) => geometry === this.selectedShapesArray[0].type) &&
+      !this.readOnly
+    )
+  }
+
   /* ------------------ Shape Classes ----------------- */
 
   Shapes = new Map<string, TLShapeConstructor<S>>()

+ 7 - 0
tldraw/packages/core/src/types/types.ts

@@ -41,6 +41,13 @@ export enum TLResizeEdge {
   Left = 'left_edge',
 }
 
+export enum TLCloneDirection {
+  Up = 'up',
+  Right = 'right',
+  Down = 'down',
+  Left = 'left',
+}
+
 export enum TLResizeCorner {
   TopLeft = 'top_left_corner',
   TopRight = 'top_right_corner',

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

@@ -32,6 +32,7 @@ export const AppCanvas = observer(function InnerApp<S extends TLReactShape>(
       showSelectionRotation={app.showSelectionRotation}
       showResizeHandles={app.showResizeHandles}
       showRotateHandles={app.showRotateHandles}
+      showCloneHandles={app.showCloneHandles}
       showSelectionDetail={app.showSelectionDetail}
       showContextBar={app.showContextBar}
       cursor={app.cursors.cursor}

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

@@ -64,6 +64,7 @@ export interface TLCanvasProps<S extends TLReactShape> {
   showHandles: boolean
   showResizeHandles: boolean
   showRotateHandles: boolean
+  showCloneHandles: boolean
   showContextBar: boolean
   showSelectionDetail: boolean
   showSelectionRotation: boolean
@@ -92,6 +93,7 @@ export const Canvas = observer(function Renderer<S extends TLReactShape>({
   showSelectionRotation = false,
   showResizeHandles = true,
   showRotateHandles = true,
+  showCloneHandles = true,
   showSelectionDetail = true,
   showContextBar = true,
   showGrid = true,
@@ -200,6 +202,7 @@ export const Canvas = observer(function Renderer<S extends TLReactShape>({
                     bounds={selectionBounds}
                     showResizeHandles={showResizeHandles}
                     showRotateHandles={showRotateHandles}
+                    showCloneHandles={showCloneHandles}
                   />
                 </Container>
               )}

+ 26 - 2
tldraw/packages/react/src/components/ui/SelectionForeground/SelectionForeground.tsx

@@ -1,16 +1,17 @@
-import { TLResizeCorner, TLResizeEdge, TLRotateCorner } from '@tldraw/core'
+import { TLCloneDirection, TLResizeCorner, TLResizeEdge, TLRotateCorner } from '@tldraw/core'
 import { observer } from 'mobx-react-lite'
 import { useApp } from '../../../hooks'
 import type { TLReactShape } from '../../../lib'
 import type { TLSelectionComponentProps } from '../../../types'
 import { SVGContainer } from '../../SVGContainer'
-import { CornerHandle, EdgeHandle } from './handles'
+import { CornerHandle, EdgeHandle, CloneHandle } from './handles'
 import { RotateCornerHandle } from './handles/RotateCornerHandle'
 
 export const SelectionForeground = observer(function SelectionForeground<S extends TLReactShape>({
   bounds,
   showResizeHandles,
   showRotateHandles,
+  showCloneHandles,
   shapes,
 }: TLSelectionComponentProps<S>) {
   const app = useApp()
@@ -19,6 +20,8 @@ export const SelectionForeground = observer(function SelectionForeground<S exten
 
   const size = 8 / zoom
   const targetSize = 6 / zoom
+  const clonePadding = 30 / zoom
+  const cloneHandleSize = size * 2
 
   const canResize = shapes.length === 1 ? shapes[0].canResize : [true, true]
 
@@ -107,6 +110,27 @@ export const SelectionForeground = observer(function SelectionForeground<S exten
             corner={TLRotateCorner.BottomLeft}
             isHidden={!showRotateHandles}
           />
+          <CloneHandle
+            cx={- clonePadding}
+            cy={height / 2}
+            size={cloneHandleSize}
+            direction={TLCloneDirection.Left}
+            isHidden={!showCloneHandles}
+          />
+          <CloneHandle
+            cx={width + clonePadding}
+            cy={height / 2}
+            size={cloneHandleSize}
+            direction={TLCloneDirection.Right}
+            isHidden={!showCloneHandles}
+          />
+          <CloneHandle
+            cx={width / 2}
+            cy={height + clonePadding}
+            size={cloneHandleSize}
+            direction={TLCloneDirection.Down}
+            isHidden={!showCloneHandles}
+          />
           {canResize?.every(r => r) && (
             <>
               <CornerHandle

+ 45 - 0
tldraw/packages/react/src/components/ui/SelectionForeground/handles/CloneHandle.tsx

@@ -0,0 +1,45 @@
+import { observer } from 'mobx-react-lite'
+import type { TLCloneDirection, } from '@tldraw/core'
+import { useApp } from '../../../../hooks'
+interface CloneHandleProps {
+    cx: number
+    cy: number
+    size: number
+    direction: TLCloneDirection
+    isHidden?: boolean
+}
+
+export const CloneHandle = observer(function CloneHandle({
+    cx,
+    cy,
+    size,
+    direction,
+    isHidden,
+}: CloneHandleProps): JSX.Element {
+    const app = useApp()
+
+    return (
+        <g className="tl-clone-handle" opacity={isHidden ? 0 : 1}>
+            <circle
+                aria-label={`${direction} handle`}
+                pointerEvents="all"
+                onPointerDown={(e) => app.api.clone(direction)}
+                cx={cx}
+                cy={cy}
+                r={size}
+            />
+            <line 
+                x1={cx - size / 2}
+                y1={cy}
+                x2={cx + size / 2}
+                y2={cy} 
+            />
+            <line 
+                x1={cx}
+                y1={cy - size / 2}
+                x2={cx}
+                y2={cy + size / 2} 
+            />
+        </g>
+    )
+})

+ 1 - 0
tldraw/packages/react/src/components/ui/SelectionForeground/handles/index.ts

@@ -1,3 +1,4 @@
 export * from './CornerHandle'
+export * from './CloneHandle'
 export * from './EdgeHandle'
 export * from './RotateHandle'

+ 16 - 1
tldraw/packages/react/src/hooks/useStylesheet.ts

@@ -81,7 +81,7 @@ const tlcss = css`
   .tl-container {
     --tl-zoom: 1;
     --tl-scale: calc(1 / var(--tl-zoom));
-    --tl-padding: 64px;
+    --tl-padding: calc(64px / var(--tl-zoom));;
     --tl-shadow-color: 0deg 0% 0%;
     --tl-binding-distance: ${BINDING_DISTANCE}px;
     --tl-shadow-elevation-low: 0px 0.4px 0.5px hsl(var(--tl-shadow-color) / 0.04),
@@ -226,6 +226,21 @@ const tlcss = css`
     stroke-width: calc(1.5px * var(--tl-scale));
   }
 
+  .tl-clone-handle {
+    stroke: var(--tl-selectStroke);
+    fill: var(--tl-background);
+    stroke-width: calc(1.5px * var(--tl-scale));
+  }
+
+  .tl-clone-handle:hover {
+    fill: var(--tl-selectStroke);
+    cursor: pointer; 
+  }
+
+  .tl-clone-handle:hover line {
+    stroke: var(--tl-background);
+  }
+
   .tl-user {
     left: -4px;
     top: -4px;

+ 1 - 0
tldraw/packages/react/src/types/component-props.ts

@@ -10,6 +10,7 @@ export type TLSelectionComponentProps<S extends TLReactShape = TLReactShape> = {
   bounds: TLBounds
   showResizeHandles?: boolean
   showRotateHandles?: boolean
+  showCloneHandles?: boolean
 }
 
 export type TLBoundsComponent<S extends TLReactShape = TLReactShape> = (

+ 8 - 8
yarn.lock

@@ -391,10 +391,10 @@
   resolved "https://registry.yarnpkg.com/@emoji-mart/react/-/react-1.1.1.tgz#ddad52f93a25baf31c5383c3e7e4c6e05554312a"
   integrity sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g==
 
-"@excalidraw/[email protected]5.3":
-  version "0.15.3"
-  resolved "https://registry.yarnpkg.com/@excalidraw/excalidraw/-/excalidraw-0.15.3.tgz#5dea570f76451adf68bc24d4bfdd67a375cfeab1"
-  integrity sha512-/gpY7fgMO/AEaFLWnPqzbY8H7ly+/zocFf7D0Is5sWNMD2mhult5tana12lXKLSJ6EAz7ubo1A7LajXzvJXJDA==
+"@excalidraw/[email protected]6.1":
+  version "0.16.1"
+  resolved "https://registry.yarnpkg.com/@excalidraw/excalidraw/-/excalidraw-0.16.1.tgz#a928945d567a1f5c0aa75bd1b1c05b50329eb27a"
+  integrity sha512-4zirHk7dNx6SVq2jQmYOLliqAa1h3WPVqHM5qtJyhD769VsOqwlkopAcnZMb3G1PeIMm6cf2F31quS5MVqvoOQ==
 
 "@highlightjs/[email protected]":
   version "10.4.1"
@@ -2700,10 +2700,10 @@ electron-to-chromium@^1.4.526:
   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.528.tgz#7c900fd73d9d2e8bb0dab0e301f25f0f4776ef2c"
   integrity sha512-UdREXMXzLkREF4jA8t89FQjA8WHI6ssP38PMY4/4KhXFQbtImnghh4GkCgrtiZwLKUKVD2iTVXvDVQjfomEQuA==
 
-electron@25.9.3:
-  version "25.9.3"
-  resolved "https://registry.yarnpkg.com/electron/-/electron-25.9.3.tgz#cdd53a30fb914adadcfbd34124237fb38b1c07d0"
-  integrity sha512-dacaHg/PuwVcFRgPDCM5j7UDzqGJWOsbBRdS5wPKLNS/ejPeccIjuNUT1cqcrpvCJKAFW8swHWg9kdizNSEDHQ==
+electron@27.1.3:
+  version "27.1.3"
+  resolved "https://registry.yarnpkg.com/electron/-/electron-27.1.3.tgz#3fd6decda95c1dd0a7e51a9ac77ee0ba37b7c5c6"
+  integrity sha512-7eD8VMhhlL5J531OOawn00eMthUkX1e3qN5Nqd7eMK8bg5HxQBrn8bdPlvUEnCano9KhrVwaDnGeuzWoDOGpjQ==
   dependencies:
     "@electron/get" "^2.0.0"
     "@types/node" "^18.11.18"

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov