Explorar o código

Merge branch 'master' into fix/search-remove-accents

Andelf %!s(int64=3) %!d(string=hai) anos
pai
achega
271700ec02
Modificáronse 35 ficheiros con 981 adicións e 105 borrados
  1. 2 2
      android/app/build.gradle
  2. 2 3
      deps/graph-parser/src/logseq/graph_parser/block.cljs
  3. 3 1
      deps/graph-parser/src/logseq/graph_parser/property.cljs
  4. 23 1
      deps/graph-parser/test/logseq/graph_parser/mldoc_test.cljs
  5. 27 0
      deps/graph-parser/test/logseq/graph_parser_test.cljs
  6. 1 1
      e2e-tests/basic.spec.ts
  7. 4 3
      e2e-tests/flashcards.spec.ts
  8. 4 4
      ios/App/App.xcodeproj/project.pbxproj
  9. 54 18
      libs/yarn.lock
  10. 1 1
      resources/package.json
  11. 7 3
      src/main/frontend/components/block.cljs
  12. 1 1
      src/main/frontend/components/editor.cljs
  13. 6 9
      src/main/frontend/components/sidebar.cljs
  14. 48 2
      src/main/frontend/db/model.cljs
  15. 487 17
      src/main/frontend/dicts.cljc
  16. 1 1
      src/main/frontend/extensions/pdf/_viewer.css
  17. 1 1
      src/main/frontend/extensions/pdf/highlights.cljs
  18. 10 2
      src/main/frontend/extensions/pdf/utils.cljs
  19. 3 0
      src/main/frontend/fs/capacitor_fs.cljs
  20. 2 1
      src/main/frontend/handler.cljs
  21. 2 2
      src/main/frontend/handler/editor.cljs
  22. 12 9
      src/main/frontend/handler/file.cljs
  23. 7 7
      src/main/frontend/handler/repo.cljs
  24. 118 0
      src/main/frontend/modules/shortcut/dicts.cljc
  25. 4 1
      src/main/frontend/search.cljs
  26. 11 3
      src/main/frontend/state.cljs
  27. 4 3
      src/main/frontend/template.cljs
  28. 1 1
      src/main/frontend/version.cljs
  29. 28 0
      src/test/frontend/db/model_test.cljs
  30. 55 1
      src/test/frontend/handler/editor_test.cljs
  31. 1 1
      src/test/frontend/handler/repo_test.cljs
  32. 1 1
      src/test/frontend/test/helper.cljs
  33. 10 5
      templates/config.edn
  34. 14 0
      templates/dummy-notes-es.md
  35. 26 0
      templates/tutorial-es.md

+ 2 - 2
android/app/build.gradle

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

+ 2 - 3
deps/graph-parser/src/logseq/graph_parser/block.cljs

@@ -198,7 +198,6 @@
   [format properties user-config]
   (when (seq properties)
     (let [properties (seq properties)
-          properties (into {} properties)
           page-refs (get-page-ref-names-from-properties format properties user-config)
           properties (->> properties
                           (map (fn [[k v]]
@@ -512,7 +511,7 @@
                                 :content content
                                 :level 1
                                 :properties properties
-                                :properties-order properties-order
+                                :properties-order (vec properties-order)
                                 :refs property-refs
                                 :pre-block? true
                                 :unordered true
@@ -548,7 +547,7 @@
                 (assoc :properties (:properties properties))
 
                 (seq (:properties-order properties))
-                (assoc :properties-order (:properties-order properties)))
+                (assoc :properties-order (vec (:properties-order properties))))
         block (if (get-in block [:properties :collapsed])
                 (-> (assoc block :collapsed? true)
                     (update :properties (fn [m] (dissoc m :collapsed)))

+ 3 - 1
deps/graph-parser/src/logseq/graph_parser/property.cljs

@@ -33,7 +33,9 @@
 (defn editable-built-in-properties
   "Properties used by logseq that user can edit"
   []
-  (into #{:title :icon :template :template-including-parent :public :filters :exclude-from-graph-view}
+  (into #{:title :icon :template :template-including-parent :public :filters :exclude-from-graph-view
+          ;; org-mode only
+          :macro :filetags}
         editable-linkable-built-in-properties))
 
 (defn hidden-built-in-properties

+ 23 - 1
deps/graph-parser/test/logseq/graph_parser/mldoc_test.cljs

@@ -67,7 +67,7 @@
 " md-config {})))
       "Src example with leading whitespace"))
 
-(deftest properties-test
+(deftest md-properties-test
   (are [x y] (= [["Properties" y] nil]
                 (first (gp-mldoc/->edn x md-config {})))
 
@@ -98,6 +98,28 @@
          (first (gp-mldoc/->edn "term
 : definition" md-config {})))))
 
+(defn- parse-properties
+  [text]
+  (->> (gp-mldoc/->edn text (gp-mldoc/default-config :org) {})
+       (filter #(= "Properties" (ffirst %)))
+       ffirst
+       second))
+
+(deftest org-properties-test
+  []
+  (testing "just title"
+    (let [content "#+TITLE:   some title   "
+          props (parse-properties content)]
+      (is (= "some title   " (:title props)))))
+
+  (testing "filetags"
+    (let [content "#+FILETAGS:   :tag1:tag2:@tag:
+#+TAGS: tag3
+body"
+          props (parse-properties content)]
+      (is ["@tag" "tag1" "tag2"] (sort (:filetags props)))
+      (is ["@tag" "tag1" "tag2" "tag3"] (sort (:tags props))))))
+
 (deftest ^:integration test->edn
   (let [graph-dir "test/docs"
         _ (docs-graph-helper/clone-docs-repo-if-not-exists graph-dir)

+ 27 - 0
deps/graph-parser/test/logseq/graph_parser_test.cljs

@@ -1,5 +1,6 @@
 (ns logseq.graph-parser-test
   (:require [cljs.test :refer [deftest testing is]]
+            [clojure.string :as string]
             [logseq.graph-parser :as graph-parser]
             [logseq.db :as ldb]
             [logseq.graph-parser.block :as gp-block]
@@ -41,3 +42,29 @@
           (catch :default _)))
       (is (= nil @deleted-page)
           "Page should not be deleted when there is unexpected failure"))))
+
+(defn- test-property-order [num-properties]
+  (let [conn (ldb/start-conn)
+        properties (mapv #(keyword (str "p" %)) (range 0 num-properties))
+        text (->> properties
+                  (map #(str (name %) ":: " (name %) "-value"))
+                  (string/join "\n"))
+        ;; Test page properties and block properties
+        body (str text "\n- " text)
+        _ (graph-parser/parse-file conn "foo.md" body {})
+        properties-orders (->> (d/q '[:find (pull ?b [*])
+                                      :in $
+                                      :where [?b :block/content] [(missing? $ ?b :block/name)]]
+                                    @conn)
+                               (map first)
+                               (map :block/properties-order))]
+    (is (every? vector? properties-orders)
+        "Order is persisted as a vec to avoid edn serialization quirks")
+    (is (= [properties properties] properties-orders)
+        "Property order")))
+
+(deftest properties-order
+  (testing "Sort order and persistence of a few properties"
+    (test-property-order 4))
+  (testing "Sort order and persistence of 10 properties"
+    (test-property-order 10)))

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

@@ -15,7 +15,7 @@ test('create page and blocks, save to disk', async ({ page, block, graphDir }) =
   await block.waitForBlocks(2)
 
   await page.keyboard.type('second bullet')
-  await block.clickNext()
+  await block.enterNext()
 
   await page.keyboard.type('third bullet')
   expect(await block.indent()).toBe(true)

+ 4 - 3
e2e-tests/flashcards.spec.ts

@@ -3,7 +3,7 @@ import { test } from './fixtures'
 import { createRandomPage } from './utils'
 
 
-test.skip('flashcard demo', async ({ page, block }) => {
+test('flashcard demo', async ({ page, block }) => {
   await createRandomPage(page)
 
   await block.mustFill('Why do you add cards? #card #logseq')
@@ -46,6 +46,7 @@ test.skip('flashcard demo', async ({ page, block }) => {
   await numberLabel.waitFor({ state: 'visible' })
   expect(await numberLabel.innerText()).toMatch(/\[\[logseq\]\]\s+2\/2/)
 
-  const cardsNum = page.locator('.flashcards-nav span >> nth=1')
-  expect(await cardsNum.innerText()).toBe('2')
+  // DO NOT check number label for now
+  //const cardsNum = page.locator('.flashcards-nav span >> nth=1')
+  //expect(await cardsNum.innerText()).toBe('2')
 })

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

@@ -542,7 +542,7 @@
 				INFOPLIST_FILE = App/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
-				MARKETING_VERSION = 0.7.8;
+				MARKETING_VERSION = 0.7.9;
 				OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -568,7 +568,7 @@
 				INFOPLIST_FILE = App/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
-				MARKETING_VERSION = 0.7.8;
+				MARKETING_VERSION = 0.7.9;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
@@ -593,7 +593,7 @@
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
-				MARKETING_VERSION = 0.7.8;
+				MARKETING_VERSION = 0.7.9;
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq.ShareViewController;
@@ -620,7 +620,7 @@
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
-				MARKETING_VERSION = 0.7.8;
+				MARKETING_VERSION = 0.7.9;
 				MTL_FAST_MATH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq.ShareViewController;
 				PRODUCT_NAME = "$(TARGET_NAME)";

+ 54 - 18
libs/yarn.lock

@@ -7,6 +7,46 @@
   resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz#90420f9f9c6d3987f176a19a7d8e764271a2f55d"
   integrity sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==
 
+"@jridgewell/gen-mapping@^0.3.0":
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
+  integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==
+  dependencies:
+    "@jridgewell/set-array" "^1.0.1"
+    "@jridgewell/sourcemap-codec" "^1.4.10"
+    "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/resolve-uri@^3.0.3":
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
+  integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
+
+"@jridgewell/set-array@^1.0.1":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
+  integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
+
+"@jridgewell/source-map@^0.3.2":
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb"
+  integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==
+  dependencies:
+    "@jridgewell/gen-mapping" "^0.3.0"
+    "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/sourcemap-codec@^1.4.10":
+  version "1.4.14"
+  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
+  integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
+
+"@jridgewell/trace-mapping@^0.3.9":
+  version "0.3.14"
+  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed"
+  integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==
+  dependencies:
+    "@jridgewell/resolve-uri" "^3.0.3"
+    "@jridgewell/sourcemap-codec" "^1.4.10"
+
 "@polka/url@^1.0.0-next.17":
   version "1.0.0-next.17"
   resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.17.tgz#25fdbdfd282c2f86ddf3fcefbd98be99cd2627e2"
@@ -242,10 +282,10 @@ acorn-walk@^8.0.0:
   resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.1.1.tgz#3ddab7f84e4a7e2313f6c414c5b7dac85f4e3ebc"
   integrity sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==
 
-acorn@^8.0.4, acorn@^8.4.1:
-  version "8.4.1"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c"
-  integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==
+acorn@^8.0.4, acorn@^8.4.1, acorn@^8.5.0:
+  version "8.7.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
+  integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
 
 ajv-keywords@^3.5.2:
   version "3.5.2"
@@ -920,10 +960,10 @@ [email protected]:
     dot-case "^3.0.4"
     tslib "^2.0.3"
 
-source-map-support@~0.5.19:
-  version "0.5.19"
-  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
-  integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
+source-map-support@~0.5.20:
+  version "0.5.21"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
+  integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
   dependencies:
     buffer-from "^1.0.0"
     source-map "^0.6.0"
@@ -933,11 +973,6 @@ source-map@^0.6.0, source-map@^0.6.1:
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
   integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
 
-source-map@~0.7.2:
-  version "0.7.3"
-  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
-  integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
-
 strip-final-newline@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
@@ -975,13 +1010,14 @@ terser-webpack-plugin@^5.1.3:
     terser "^5.7.0"
 
 terser@^5.7.0:
-  version "5.7.1"
-  resolved "https://registry.yarnpkg.com/terser/-/terser-5.7.1.tgz#2dc7a61009b66bb638305cb2a824763b116bf784"
-  integrity sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg==
+  version "5.14.2"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10"
+  integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==
   dependencies:
+    "@jridgewell/source-map" "^0.3.2"
+    acorn "^8.5.0"
     commander "^2.20.0"
-    source-map "~0.7.2"
-    source-map-support "~0.5.19"
+    source-map-support "~0.5.20"
 
 to-regex-range@^5.0.1:
   version "5.0.1"

+ 1 - 1
resources/package.json

@@ -1,6 +1,6 @@
 {
   "name": "Logseq",
-  "version": "0.7.8",
+  "version": "0.7.9",
   "main": "electron.js",
   "author": "Logseq",
   "license": "AGPL-3.0",

+ 7 - 3
src/main/frontend/components/block.cljs

@@ -245,7 +245,11 @@
                                       images (if-not (= (count images) 1)
                                                (let [^js _image (.closest (.-target e) ".asset-container")
                                                      image (. _image querySelector "img")]
-                                                 (cons image (remove #(= image %) images)))
+                                                 (->> images
+                                                      (sort-by (juxt #(.-y %) #(.-x %)))
+                                                      (split-with (complement #{image}))
+                                                      reverse
+                                                      (apply concat)))
                                                images)
                                       images (for [^js it images] {:src (.-src it)
                                                                    :w (.-naturalWidth it)
@@ -1841,8 +1845,8 @@
   [config block]
   (let [properties (walk/keywordize-keys (:block/properties block))
         properties-order (:block/properties-order block)
-        properties (apply dissoc properties (property/built-in-properties))
-        properties-order (remove (property/built-in-properties) properties-order)
+        properties (apply dissoc properties (property/hidden-properties))
+        properties-order (remove (property/hidden-properties) properties-order)
         pre-block? (:block/pre-block? block)
         properties (if pre-block?
                      (let [repo (state/get-current-repo)

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

@@ -177,7 +177,7 @@
      result
      {:on-chosen   chosen-handler
       :on-enter    non-exist-block-handler
-      :empty-placeholder   [:div.text-gray-500.pl-4.pr-4 "Search for a block"]
+      :empty-placeholder   [:div.text-gray-500.pl-4.pr-4 (t :editor/block-search)]
       :item-render (fn [{:block/keys [page uuid]}]  ;; content returned from search engine is normalized
                      (let [page (or (:block/original-name page)
                                     (:block/name page))

+ 6 - 9
src/main/frontend/components/sidebar.cljs

@@ -41,18 +41,15 @@
             [reitit.frontend.easy :as rfe]
             [rum.core :as rum]))
 
-(rum/defc nav-content-item
+(rum/defc nav-content-item < rum/reactive
   [name {:keys [class]} child]
 
-  [:div.nav-content-item.is-expand
-   {:class class}
+  [:div.nav-content-item
+   {:class (util/classnames [class {:is-expand (not (state/sub [:ui/navigation-item-collapsed? class]))}])}
    [:div.nav-content-item-inner
     [:div.header.items-center.mb-1
-     {:on-click (fn [^js/MouseEvent e]
-                  (let [^js target (.-target e)
-                        ^js parent (.closest target ".nav-content-item")]
-                    (.toggle (.-classList parent) "is-expand")))}
-
+     {:on-click (fn [^js/MouseEvent _e]
+                  (state/toggle-navigation-item-collapsed! class))}
      [:div.font-medium.fade-link name]
      [:span
       [:a.more svg/arrow-down-v2]]]
@@ -263,7 +260,7 @@
            :active (and (not srs-open?) (= route-name :all-pages))
            :icon   "files"})]]
 
-      (favorites t)
+      (when (and left-sidebar-open? (not config/publishing?)) (favorites t))
 
       (when (and left-sidebar-open? (not config/publishing?)) (recent-pages t))
 

+ 48 - 2
src/main/frontend/db/model.cljs

@@ -15,6 +15,7 @@
             [frontend.state :as state]
             [frontend.util :as util :refer [react]]
             [logseq.graph-parser.util :as gp-util]
+            [logseq.graph-parser.text :as text]
             [logseq.db.rules :refer [rules]]
             [logseq.db.default :as default-db]
             [frontend.util.drawer :as drawer]))
@@ -1094,6 +1095,51 @@
       (util/safe-page-name-sanity-lc page))
      (distinct))))
 
+(def ns-char "/")
+(def ns-re #"/")
+
+(defn- get-parents-namespace-list
+  "Return list of parents namespace"
+  [page-namespace & nested-found]
+  (if (text/namespace-page? page-namespace)
+    (let [pre-nested-vec (drop-last (string/split page-namespace ns-re))
+          my-nested-found (if (nil? nested-found)
+                            []
+                            nested-found)]
+      (if (= (count pre-nested-vec) 1)
+        (conj my-nested-found (nth pre-nested-vec 0))
+        (let [pre-nested-str (string/join ns-char pre-nested-vec)]
+          (recur pre-nested-str (conj my-nested-found pre-nested-str)))))
+    []))
+
+(defn- get-unnecessary-namespaces-name
+  "Return unnecessary namespace from a list of page's name"
+  [pages-list]
+  (->> pages-list
+       (remove nil?)
+       (mapcat get-parents-namespace-list)
+       distinct))
+
+(defn- remove-nested-namespaces-link
+  "Remove relations between pages and their nested namespace"
+  [pages-relations]
+  (let [pages-relations-to-return (distinct (mapcat
+                                             identity
+                                             (for [item (for [a-link-from (mapv (fn [a-rel] (first a-rel)) pages-relations)]
+                                                          [a-link-from (mapv
+                                                                        (fn [a-rel] (second a-rel))
+                                                                        (filterv
+                                                                         (fn [link-target] (=  a-link-from (first link-target)))
+                                                                         pages-relations))])
+                                                   :let [list-to (get item 1)
+                                                         page (get item 0)
+                                                         namespaces-to-remove (get-unnecessary-namespaces-name list-to)
+                                                         list-to-without-nested-ns (filterv (fn [elem] (not (some #{elem} namespaces-to-remove))) list-to)
+                                                         node-links (for [item-ok list-to-without-nested-ns]
+                                                                      [page item-ok])]]
+                                               (seq node-links))))]
+    pages-relations-to-return))
+
 ;; Ignore files with empty blocks for now
 (defn get-pages-relation
   [repo with-journal?]
@@ -1115,7 +1161,8 @@
       (->>
        (d/q q db)
        (map (fn [[page ref-page-name]]
-              [page ref-page-name]))))))
+              [page ref-page-name]))
+       (remove-nested-namespaces-link)))))
 
 ;; get pages who mentioned this page
 ;; TODO: use :block/_refs
@@ -1384,7 +1431,6 @@
     (->> (map keys properties)
          (apply concat)
          distinct
-         (remove #{:id})
          sort)))
 
 (defn get-property-values

+ 487 - 17
src/main/frontend/dicts.cljc

@@ -612,7 +612,356 @@
         :file-sync/other-user-graph "Aktuelle lokale Grafik ist an das Remote-Graph des anderen Benutzers gebunden. Kann also nicht mit der Synchronisierung beginnen."
         :file-sync/graph-deleted "Das aktuelle Ferndiagramm wurde gelöscht"
         }
+   :nl {
+        :all-files "Alle bestanden"
+        :all-graphs "Alle grafieken"
+        :all-journals "Alle Journalen"
+        :all-pages "Alle pagina's"
+        :bold "Vet"
+        :cancel "Annuleren"
+        :cards-view "Kaarten bekijken"
+        :cloning "Klonen"
+        :close "Sluiten"
+        :code "Code"
+        :convert-markdown "Zet Markdown headings om in ongeordende lijsten (# -> -)"
+        :dark "Donker"
+        :delete "Verwijderen"
+        :developer-mode-alert "U moetde app opnieuw opstarten om het plugin systeem in te schakelen. Wil u het nu herstarten?"
+        :discord-title "Onze discord groep!"
+        :discourse-title "Ons forum!"
+        :download "Downloaden"
+        :export "Exporteren"
+        :export-datascript-edn "Datascript EDN exporteren"
+        :export-edn "Exporteren als EDN"
+        :export-graph "Grafiek exporteren"
+        :export-json "Exporteren als JSON"
+        :export-markdown "Exporteren als standaard Markdown (geen blok eigenschappen)"
+        :export-opml "Exporteren als OPML"
+        :export-page "Exporteer pagina"
+        :export-public-pages "Exporteer openbare pagina's"
+        :export-roam-json "Exporteer als Roam JSON"
+        :general "Algemeen"
+        :go-to "Ga naar"
+        :graph "Grafiek"
+        :graph-search "Zoek grafiek"
+        :graph-view "Bekijk grafiek"
+        :help-shortcut-title "Klik om sneltoetsen en andere tips te bekijken"
+        :highlight "Markeer"
+        :host "Host"
+        :html-link "Html-Link"
+        :import "Importeren"
+        :italics "Cursief"
+        :join-community "Sluit u aan bij de gemeenschap"
+        :language "Taal"
+        :loading-files "Bestanden laden"
+        :loading "Laden"
+        :login "Inloggen"
+        :logout "Uitloggen"
+        :logseq "Logseq"
+        :more "Meer"
+        :more-options "Meer opties"
+        :my-publishing "Mijn publicaties"
+        :new-file "Nieuw bestand"
+        :new-graph "Nieuwe grafiek toevoegen"
+        :new-page "Nieuwe pagina"
+        :no "Nee"
+        :on "Aan"
+        :open "Open"
+        :open-a-directory "Open een lokale map"
+        :open-new-window "Nieuwe venster"
+        :or "of"
+        :page-search "Zoek in de huidige pagina"
+        :parsing-files "Bestanden analyseren"
+        :plugins "Plugins"
+        :port "Poort"
+        :publishing "Publiceren"
+        :re-index "Herindexeren"
+        :re-index-detail "Grafiek herbouwen"
+        :re-index-discard-unsaved-changes-warning "Herindexeren zal de huidige grafiek weggooien, en vervolgens alle bestanden opnieuw verwerken zoals ze momenteel op schijf zijn opgeslagen. U verliest niet-opgeslagen wijzigingen en het kan even duren. Verder?"
+        :re-index-multiple-windows-warning "U moet de andere vensters sluiten voordat u deze grafiek opnieuw indexeert."
+        :redo "Herhaal"
+        :relaunch-confirm-to-work "U moet de app opnieuw opstarten om het te laten werken. Wil u het nu opnieuw opstarten?"
+        :remove-background "Verwijder achtergrond"
+        :remove-orphaned-pages "Verwijder gelinkte pagina's"
+        :save "Opslaan"
+        :search "Zoek of maak pagina"
+        :settings "Instellingen"
+        :settings-of-plugins "Plugin instellingen"
+        :sponsor-us "Sponsor ons"
+        :strikethrough "Doorgestreept"
+        :submit "Verzenden"
+        :sync-from-local-changes-detected "ReBijwerkenfresh detecteert en verwerkt bestanden die op uw schijf zijn gewijzigd en die afwijken van de eigenlijke inhoud van de Logseq pagina. Verder?"
+        :sync-from-local-files "Bijwerken"
+        :sync-from-local-files-detail "Importeer veranderingen uit lokale bestanden"
+        :themes "Thema's"
+        :to "naar"
+        :type "Type"
+        :undo "Ongedaan maken"
+        :unlink "ontkoppel"
+        :white "Licht"
+        :yes "Ja"
+
+        :asset/confirm-delete "Weet u zeker dat u dit wilt verwijderen {1}?"
+        :asset/physical-delete "Verwijder het bestand ook (let op dat het niet hersteld kan worden)"
+
+        :block/name "Pagina naam"
+
+        :command-palette/prompt "Type een commando"
+
+        :content/click-to-edit "Klik om te bewerken"
+        :content/copy "Kopieer"
+        :content/copy-as-json "Kopieer als JSON"
+        :content/copy-block-emebed "Kopieer blok embed"
+        :content/copy-block-ref "Kopieer blok ref"
+        :content/cut "Knip"
+        :content/focus-on-block "Concentreer op blok"
+        :content/make-todos "Maak {1}s"
+        :content/open-in-sidebar "Open in zijbalk"
+
+        :draw/back-to-logseq "Terug naar logseq"
+        :draw/delete "Verwijder"
+        :draw/invalid-file "Kon dit ongeldig excalidraw bestand niet laden"
+        :draw/list-files "Bestanden lijst"
+        :draw/more-options "Meer opties"
+        :draw/new-file "Nieuw bestand"
+        :draw/rename-failure "Bestand hernoemen mislukt, reden:"
+        :draw/rename-success "Bestand hernoemen succesvol!"
+        :draw/save "Opslaan"
+        :draw/save-changes "Wijzigingen opslaan"
+        :draw/specify-title "Geef eerst een titel op!"
+        :draw/title-placeholder "Geen titel"
+
+        :editor/block-search "Zoek voor een blok"
+        :editor/image-uploading "Uploaden"
+
+        :file/file "Bestand: "
+        :file/format-not-supported "Formaat .{1} wordt niet ondersteund."
+        :file/last-modified-at "Laatst aangepast op"
+        :file/name "Bestandsnaam"
+        :file/no-data "Geen gegevens"
+        
+        :file-sync/graph-deleted "Het huidige remote grafiek is verwijderd"
+        :file-sync/other-user-graph "De huidige lokale grafiek is gebonden aan de remote grafiek van de andere gebruiker. Dus kan de synchronisatie niet starten."
+        
+        :format/markdown "Markdown"
+        :format/org-mode "Org mode"
+        :format/preferred-mode "Wat is u favoriete mode?"
+        
+        :graph/persist "Logseq is de interne status aan het synchroniseren, wacht alstublieft enkele seconden."
+        :graph/persist-error "Interne status sync mislukt."
+        :graph/save "Opslaan..."
+        :graph/save-error "Opslaan mislukt"
+        :graph/save-success "Opslaan succesvol"
+        
+        :help/about "Over Logseq"
+        :help/awesome-logseq "Awesome Logseq"
+        :help/block-content-autocomplete "Blokinhoud (brontekst, citaten, zoekopdrachten, enz.) autocomplete"
+        :help/block-reference "Blok referentie"
+        :help/blog "Logseq blog"
+        :help/bug "Rapporteer een fout"
+        :help/changelog "Verander logboek"
+        :help/community "Discord gemeenschap"
+        :help/context-menu "Blok context menu"
+        :help/docs "Documentatie"
+        :help/feature "Functie verzoek"
+        :help/fold-unfold "Vouwen/ontvouwen van blokken (indien niet in bewerkingsmodus)"
+        :help/forum-community "Forum gemeenschap"
+        :help/key-commands "Belangrijke commando's"
+        :help/markdown-syntax "Markdown-syntax"
+        :help/org-mode-syntax "Org-mode-syntax"
+        :help/privacy "Privacybeleid"
+        :help/reference-autocomplete "Automatisch aanvullen van paginareferenties"
+        :help/roadmap "Roadmap"
+        :help/select-nfs-browser "Gebruik een andere browser (bijv. de nieuwste Chrome of Firefox) die NFS-functies ondersteunt om lokale mappen te openen."
+        :help/shortcut "Snelkoppeling"
+        :help/shortcut-page-title "Toetsenbord snelkoppelingen"
+        :help/shortcuts "Toetsenbord snelkoppelingen"
+        :help/shortcuts-triggers "Triggers"
+        :help/slash-autocomplete "Slash-Autocomplete"
+        :help/start "Aan de slag"
+        :help/terms "Voorwaarden"
+        :help/working-with-lists "(werken met lijsten)"
+        
+        :journal/go-to "Ga naar bestanden"
+        :journal/multiple-files-with-different-formats "Het lijkt erop dat u meerdere Journaalbestanden (met verschillende formaten) heeft voor dezelfde maand, alstublieft slechts één journaalbestand bewaren voor elke maand."
+        
+        :left-side-bar/journals "Journalen"
+        :left-side-bar/nav-favorites "Favorieten"
+        :left-side-bar/nav-recent-pages "Recent"
+        :left-side-bar/nav-shortcuts "Snelkoppelingen"
+        :left-side-bar/new-page "Nieuwe pagina"
+
+        :linked-references/filter-search "Zoeken in gelinkte pagina's"
+        
+        :on-boarding/add-graph "Grafiek toevoegen"
+        :on-boarding/demo-graph "Dit is een demo grafiek, wijzigingen worden niet opgeslagen totdat u een lokale map opent."
+        :on-boarding/new-graph-desc-1 "Logseq ondersteunt zowel Markdown als Org-mode. U kunt een bestaande map openen of een nieuwe aanmaken op uw apparaat, een map is ook gewoon bekend als een folder. Uw gegevens worden alleen op dit apparaat opgeslagen."
+        :on-boarding/new-graph-desc-2 "Nadat u map hebt geopend, worden er drie mappen in die map aangemaakt:"
+        :on-boarding/new-graph-desc-3 "/journals - bewaart de Journaal pagina's"
+        :on-boarding/new-graph-desc-4 "/pages - bewaart de andere pagina's"
+        :on-boarding/new-graph-desc-5 "/logseq - bewaart configuratie, custom.css, en wat metadata."
+        :on-boarding/open-local-dir "Open een lokale map"
+
+        :page/action-publish "Publiceer"
+        :page/add-to-favorites "Voeg aan favorieten toe"
+        :page/backlinks "Back-Links"
+        :page/copy-page-url "Kopieer pagina URL"
+        :page/copy-to-json "Kopieer de hele pagina als JSON"
+        :page/created-at "Aangemaakt op"
+        :page/delete "Verwijder pagina"
+        :page/delete-confirmation "Weet u zeker dat u deze pagina wilt verwijderen..."
+        :page/delete-success "Pagina {1} is succesvol verwijderd!"
+        :page/earlier "Eerder"
+        :page/edit-properties-placeholder "Eigenschappen"
+        :page/file-sync-versions "Pagina versie"
+        :page/hide-name "Verberg pagina naam"
+        :page/last-modified "Laatst aangepast op"
+        :page/make-private "Maak prive"
+        :page/make-public "Maak publiek"
+        :page/new-title "Wat is de nieuwe pagina titel?"
+        :page/no-more-journals "Geen Journaals meer"
+        :page/open-backup-directory "Open backups map pagina"
+        :page/open-in-finder "Open in map"
+        :page/open-with-default-app "Open met standaard app"
+        :page/presentation-mode "Presentatiemodus"
+        :page/priority "Prioriteit \"{1}\""
+        :page/rename "Hernoem pagina"
+        :page/rename-to "Hernoem \"{1}\" naar:"
+        :page/show-journals "Toon Journaals"
+        :page/show-name "Toon pagina naam"
+        :page/unfavorite "Pagina uit favorieten verwijderen"
+        :page/updated-at "Bijgewerkt op"
+        :page/version-history "Controleer pagina geschiedenis"
+        
+        :paginates/next "Volgende"
+        :paginates/pages "Totaal {1} pagina's"
+        :paginates/prev "Vorige"
+        
+        :pdf/copy-ref "Kopieer ref"
+        :pdf/copy-text "Kopieer tekst"
+        :pdf/linked-ref "Gekoppelde referenties"
+        :pdf/toggle-dashed "Gestippelde stijl voor gebiedsmarkering"
+
+        :plugin/all "Alle"
+        :plugin/check-all-updates "Check alle updates"
+        :plugin/check-update "Check update"
+        :plugin/contribute "✨ Schrijf en dien nieuwe plugin in"
+        :plugin/custom-js-alert "custom.js bestand gevonden, mag deze uitgevoerd worden? (Als je niet begrijpt wat dit bestand inhoud is het aangeraden om het uivoeren hiervan niet toe te staan, dit is in verband met beveiligings risico's.)" ;; TODO
+        :plugin/delete-alert "Weet je zeker dat je plugin [{1}] wilt verwijderen?"
+        :plugin/disabled "Uitgeschakeld"
+        :plugin/downloads "Downloads"
+        :plugin/enabled "Ingeschakeld"
+        :plugin/install "Installeren"
+        :plugin/installed "Geïnstalleerd"
+        :plugin/installing "Installeren"
+        :plugin/load-unpacked "Laad uitgepakte plugin"
+        :plugin/marketplace "Marketplace"
+        :plugin/marketplace-tips "Als de plugin niet correct werkt bij de eerste installatie, probeer dan Logseq te herstarten."
+        :plugin/not-installed "Niet geïnstalleerd"
+        :plugin/open-package "Open pakket"
+        :plugin/open-preferences "Open plugin voorkeuren bestand"
+        :plugin/open-settings "Open instellingen"
+        :plugin/refresh-lists "Vernieuw lijsten"
+        :plugin/reload "Herlaad"
+        :plugin/restart "Herstart app"
+        :plugin/stars "Sterren"
+        :plugin/title "Titel"
+        :plugin/uninstall "Verwijder"
+        :plugin/unpacked "Uitgepakt"
+        :plugin/unpacked-tips "Selecteer de plugin map"
+        :plugin/up-to-date "Het is up to date"
+        :plugin/update "Update"
+        :plugin/update-available "Update beschikbaar"
+        :plugin/updating "Bijwerken"
+        
+        :reference/linked "Gelinkte referentie"
+        :reference/unlinked-ref "Ongelinkte verwijzingen"
+        
+        :repo/download-zip "Download alle bestanden als een zip"
+        
+        :right-side-bar/all-pages "Alle pagina's"
+        :right-side-bar/block-ref "Blok verwijzingen"
+        :right-side-bar/contents "Inhoud"
+        :right-side-bar/favorites "Favorieten"
+        :right-side-bar/flashcards "Flashcards"
+        :right-side-bar/graph-view "Grafiekweergave"
+        :right-side-bar/help "Help"
+        :right-side-bar/new-page "Nieuwe pagina"
+        :right-side-bar/page "Pagina grafiek"
+        :right-side-bar/page-graph "Pagina grafiek"
+        :right-side-bar/recent "Recent"
+        :right-side-bar/show-journals "Toon Journaals"
+        :right-side-bar/switch-theme "Thema modes"
+        :right-side-bar/theme "{1} thema"
 
+        :search/items "items"
+        :search/page-names "Zoek paginanamen"
+        :search/publishing "Zoek"
+        :search/result-for "Zoekresultaat voor "
+        :select/default-prompt "Kies één"
+        :select.graph/add-graph "Ja, voeg nog een grafiek toe"
+        :select.graph/empty-placeholder-description "Geen overeenkomende grafieken. Wilt u er een toevoegen?"
+        :select.graph/prompt "Selecteer een grafiek"
+        
+        :settings-page/auto-updater "Auto updater"
+        :settings-page/clear "Verwijder"
+        :settings-page/clear-cache "Cache wissen"
+        :settings-page/current-graph "Huidige grafiek"
+        :settings-page/current-version "Huidige versie"
+        :settings-page/custom-configuration "Aangepaste configuratie"
+        :settings-page/custom-date-format "Gewenst datumformaat"
+        :settings-page/custom-theme "Aangepast thema"
+        :settings-page/customize-shortcuts "Toetsenbord snelkoppelingen"
+        :settings-page/developer-mode "Ontwikkelaar modus"
+        :settings-page/developer-mode-desc "De ontwikkelaarsmodus helpt bijdragers en extensie-ontwikkelaars om hun integratie met Logseq efficiënter te testen."
+        :settings-page/disable-developer-mode "Ontwikkelaarsmodus uitschakelen"
+        :settings-page/disable-sentry "Gebruiksgegevens en diagnostiek naar Logseq sturen"
+        :settings-page/edit-config-edn "Bewerk config.edn"
+        :settings-page/edit-custom-css "Bewerk custom.css"
+        :settings-page/edit-export-css "Bewerk export.css"
+        :settings-page/enable-all-pages-public "Alle pagina's openbaar bij publiceren"
+        :settings-page/enable-block-time "Tijdstempel voor blokken inschakelen"
+        :settings-page/enable-developer-mode "Ontwikkelaar modus"
+        :settings-page/enable-encryption "Encryptie"
+        :settings-page/enable-flashcards "Flashcards"
+        :settings-page/enable-journals "Journaals"
+        :settings-page/enable-shortcut-tooltip "Snelkoppeling tooltip inschakelen"
+        :settings-page/enable-timetracking "Tijdregistratie inschakelen"
+        :settings-page/enable-tooltip "Tooltips inschakelen"
+        :settings-page/export-theme "Exporteer thema"
+        :settings-page/git-commit-delay "Git auto commit seconden"
+        :settings-page/git-confirm "Je moet de app opnieuw opstarten nadat je de Git instellingen hebt aangepast."
+        :settings-page/git-desc "wordt gebruikt voor versiebeheer van pagina's. U kunt op het menu met de drie verticale stippen klikken om de wijzigingsgeschiedenis van de pagina te zien."
+        :settings-page/git-switcher-label "Git auto commit inschakelen"
+        :settings-page/home-default-page "De standaard startpagina instellen"
+        :settings-page/network-proxy "Netwerk proxy"
+        :settings-page/plugin-system "Plugin systeem"
+        :settings-page/preferred-file-format "Gewenst bestandsformaat"
+        :settings-page/preferred-outdenting "Logische uitdenting"
+        :settings-page/preferred-workflow "Voorkeur voor workflow"
+        :settings-page/shortcut-settings "Snelkoppelingen aanpassen"
+        :settings-page/show-brackets "Toon beugels"
+        :settings-page/spell-checker "Spellingcontrole"
+        :settings-page/tab-advanced "Geavanceerd"
+        :settings-page/tab-editor "Editor"
+        :settings-page/tab-general "Algemeen"
+        :settings-page/tab-shortcuts "Snelkoppelingen"
+        :settings-page/tab-version-control "Versiebeheer"
+
+        :text/image "Afbeelding"
+
+        :tips/all-done "Alles klaar"
+
+        :tutorial/dummy-notes "dummy-notes-en.md"
+        :tutorial/text "tutorial-en.md"
+
+        :updater/new-version-install "Een nieuwe versie is gedownload."
+        :updater/quit-and-install "Herstart om te installeren"
+
+        :user/delete-account "Verwijder account"
+        :user/delete-account-notice "Al uw gepubliceerde pagina's op logseq.com zullen worden verwijderd."
+        :user/delete-your-account "Verwijder uw account"
+        }
    :fr {:help/about "A propos de Logseq"
         :help/bug "Signaler une anomalie"
         :help/feature "Demander une fonctionnalité"
@@ -1366,7 +1715,19 @@
         :file-sync/other-user-graph "Huidige plaaslike grafiek is gebonde aan ander gebruiker se afgeleë grafiek. So kan nie begin om te sinkroniseer nie."
         :file-sync/graph-deleted "Huidige afstandgrafiek is geskrap"}
 
-   :es {:on-boarding/add-graph "Añade un grafo"
+  :es {:tutorial/text #?(:cljs (rc/inline "tutorial-es.md")
+                          :default "tutorial-es.md")
+        :tutorial/dummy-notes #?(:cljs (rc/inline "dummy-notes-es.md")
+                                 :default "dummy-notes-es.md")
+        :on-boarding/demo-graph "This is a demo graph, changes will not be saved until you open a local folder."
+        :on-boarding/add-graph "Add a graph"
+        :on-boarding/open-local-dir "Abrir un directorio local"
+        :on-boarding/new-graph-desc-1 "Logseq soporta tanto Markdown como Org-mode. Puede abrir un directorio existente o crear uno nuevo en su dispositivo, un directorio se conoce simplemente como una carpeta. Sus datos se almacenarán únicamente en este dispositivo."
+        :on-boarding/new-graph-desc-2 "Después que abra un directorio se crearán tres carpetas en ese directorio:"
+        :on-boarding/new-graph-desc-3 "/journals - almacena sus páginas de diarios"
+        :on-boarding/new-graph-desc-4 "/pages - almacena las otras páginas"
+        :on-boarding/new-graph-desc-5 "/logseq - almacena la configuración, custom.css, y algunos metadatos."
+        :help/start "Comenzando"
         :help/about "Acerca de Logseq"
         :help/roadmap "Hoja de ruta"
         :help/bug "Reportar un error"
@@ -1377,6 +1738,8 @@
         :help/privacy "Política de privacidad"
         :help/terms "Términos"
         :help/community "Comunidad de Discord"
+        :help/forum-community "Foro de la comunidad"
+        :help/awesome-logseq "Awesome Logseq"
         :help/shortcuts "Atajos de teclado"
         :help/shortcuts-triggers "Iniciadores"
         :help/shortcut "Atajo"
@@ -1393,6 +1756,7 @@
         :more "Más"
         :search/result-for "Resultado de búsqueda para "
         :search/items "elementos"
+        :search/page-names "Buscar en los nombres de página"
         :help/context-menu "Menú contextual"
         :help/fold-unfold "Ocultar/Expandir bloques (fuera del modo edición)"
         :help/markdown-syntax "Sintaxis de Markdown"
@@ -1405,21 +1769,27 @@
         :code "Código"
         :right-side-bar/help "Ayuda"
         :right-side-bar/switch-theme "Temas"
+        :right-side-bar/theme "Tema {1}"
         :right-side-bar/page "Gráfico de la página"
         :right-side-bar/recent "Reciente"
         :right-side-bar/contents "Contenido"
         :right-side-bar/favorites "Favoritos"
-        :right-side-bar/page-graph "Vista gráfica"
+        :right-side-bar/page-graph "Grafo de página"
         :right-side-bar/block-ref "Referencia de bloque"
-        :right-side-bar/graph-view "Graph view"
+        :right-side-bar/graph-view "Vista de Grafo"
         :right-side-bar/all-pages "Lista de páginas"
         :right-side-bar/flashcards "Flashcards"
-        :right-side-bar/new-page "Nueva pagina"
+        :right-side-bar/new-page "Nueva página"
+        :right-side-bar/show-journals "Mostrar Diarios"
         :left-side-bar/journals "Diarios"
+        :left-side-bar/new-page "Nueva página"
+        :left-side-bar/nav-favorites "Favoritos"
+        :left-side-bar/nav-shortcuts "Atajos de teclado"
+        :left-side-bar/nav-recent-pages "Recientes"
         :format/preferred-mode "¿Cuál es su modo preferido?"
         :format/markdown "Markdown"
         :format/org-mode "Modo Org"
-        :reference/linked "Referencia enlazada"
+        :reference/linked "Referencias enlazadas"
         :reference/unlinked-ref "Referencias sin enlazar"
         :page/presentation-mode "Modo presentación"
         :page/edit-properties-placeholder "Propiedades"
@@ -1433,6 +1803,9 @@
         :page/open-with-default-app "Abrir con la aplicación predeterminada"
         :page/action-publish "Publicar"
         :page/make-public "Hacer pública al publicar"
+        :page/version-history "Revisar el historial de la página"
+        :page/open-backup-directory "Abrir el directorio de copia de seguridad de la página"
+        :page/file-sync-versions "Versiones de la página"
         :page/make-private "Hacer privada"
         :page/delete "Eliminar página"
         :page/add-to-favorites "Añadir a Favoritos"
@@ -1444,13 +1817,18 @@
         :page/new-title "¿Cuál es el título de su nueva página?"
         :page/earlier "Anteriormente"
         :page/no-more-journals "No hay más diarios"
+        :page/copy-page-url "Copiar URL de la página"
         :journal/multiple-files-with-different-formats "Parece que tiene varios archivos de diario (con diferentes formatos) para el mismo mes, por favor guarde solo un archivo de diario para cada mes."
         :journal/go-to "Ir a Archivos"
         :file/name "Nombre del archivo"
-        :file/file "Archivos: "
+        :file/file "Archivo: "
         :file/last-modified-at "Fecha de modificación"
         :file/no-data "No hay datos"
         :file/format-not-supported "Formato .{1} no soportado."
+        :page/created-at "Creada el"
+        :page/updated-at "Actualizada el"
+        :page/backlinks "Back Links"
+        :linked-references/filter-search "Buscar en las páginas vinculadas"
         :editor/block-search "Buscar un bloque"
         :editor/image-uploading "Subiendo"
         :draw/invalid-file "No se pudo cargar este archivo inválido de excalidraw"
@@ -1460,7 +1838,7 @@
         :draw/title-placeholder "Sin título"
         :draw/save "Guardar"
         :draw/save-changes "Guardar cambios"
-        :draw/new-file "Nuevo archivo file"
+        :draw/new-file "Nuevo archivo"
         :draw/list-files "Listado de archivos"
         :draw/delete "Eliminar"
         :draw/more-options "Más opciones"
@@ -1472,21 +1850,37 @@
         :content/cut "Cortar"
         :content/make-todos "Hacer {1}s"
         :content/copy-block-ref "Copiar referencia de bloque"
-        :content/focus-on-block "Enfocar bloque"
+        :content/copy-block-emebed "Copiar bloque a incrustar (embed)"
+        :content/focus-on-block "Enfocar en bloque"
         :content/open-in-sidebar "Abrir en barra lateral"
         :content/copy-as-json "Copiar como JSON"
         :content/click-to-edit "Clic para editar"
+        :settings-page/git-desc "se usa para el control de versiones de páginas, puede presionar el menú vertical de tres puntos para verificar el historial de la página."
+        :settings-page/git-confirm "Debe reiniciar la aplicación después de actualizar las opciones de Git."
+        :settings-page/git-switcher-label "Habilitar Git auto commit"
+        :settings-page/git-commit-delay "Segundos para Git auto commit"
         :settings-page/edit-config-edn "Editar config.edn (para este repositorio)"
+        :settings-page/edit-custom-css "Editar custom.css"
+        :settings-page/edit-export-css "Editar export.css"
+        :settings-page/custom-configuration "Configuración personalizada"
+        :settings-page/custom-theme "Tema personalizado"
+        :settings-page/export-theme "Tema exportación"
         :settings-page/show-brackets "Mostrar corchetes"
+        :settings-page/spell-checker "Corrector ortográfico"
+        :settings-page/auto-updater "Auto actualizador"
         :settings-page/disable-sentry "Enviar datos de uso y diagnósticos a Logseq"
         :settings-page/preferred-outdenting "Disminución lógica de sangría"
-        :settings-page/custom-date-format "Formato de diario preferido"
+        :settings-page/custom-date-format "Formato de fecha preferido"
         :settings-page/preferred-file-format "Formato de archivo preferido"
         :settings-page/preferred-workflow "Flujo de trabajo preferido"
+        :settings-page/enable-shortcut-tooltip "Habilitar descripción emergente de atajos de teclado"
         :settings-page/enable-timetracking "Habilitar rastreo de tiempo"
         :settings-page/enable-tooltip "Habilitar descripción emergente"
         :settings-page/enable-journals "Habilitar diarios"
+        :settings-page/enable-all-pages-public "Hacer todas las páginas públicas al publicar"
         :settings-page/enable-encryption "Habilitar función de cifrado"
+        :settings-page/customize-shortcuts "Atajos de teclado"
+        :settings-page/shortcut-settings "Personalizar atajos"
         :settings-page/home-default-page "Establecer página de inicio"
         :settings-page/enable-block-time "Habilitar marcas temporales de bloque"
         :settings-page/clear-cache "Limpiar caché"
@@ -1494,8 +1888,17 @@
         :settings-page/developer-mode "Modo desarrollador"
         :settings-page/enable-developer-mode "Habilitar modo desarrollador"
         :settings-page/disable-developer-mode "Deshabilitar modo desarrollador"
-        :settings-page/developer-mode-desc "El modo desarrollado permite que los colaboradores y desarrolladore de extensiones probar sus integraciones con Logseq más eficientemente."
+        :settings-page/developer-mode-desc "El modo desarrollador permite a los colaboradores y desarrolladores de extensiones probar sus integraciones con Logseq más eficientemente."
         :settings-page/current-version "Versión actual"
+        :settings-page/current-graph "Grafo actual"
+        :settings-page/tab-general "General"
+        :settings-page/tab-editor "Editor"
+        :settings-page/tab-shortcuts "Atajos de teclado"
+        :settings-page/tab-version-control "Control de versiones"
+        :settings-page/tab-advanced "Avanzado"
+        :settings-page/plugin-system "Habilitar extensiones"
+        :settings-page/enable-flashcards "Flashcards"
+        :settings-page/network-proxy "Proxy de red"
         :logseq "Logseq"
         :on "ON"
         :more-options "Más opciones"
@@ -1505,18 +1908,39 @@
         :submit "Enviar"
         :cancel "Cancelar"
         :close "Cerrar"
+        :delete "Eliminar"
+        :save "Guardar"
+        :type "Tipo"
+        :host "Servidor"
+        :port "Puerto"
         :re-index "Reindexar"
+        :re-index-detail "Reconstruir el grafo"
+        :re-index-multiple-windows-warning "Debe cerrar las otras ventanas antes de reindexar este grafo."
+        :re-index-discard-unsaved-changes-warning "Al reindexar se descartará el grafo actual y se procesarán nuevamente todos los archivos según como están actualmente almacenados en disco. Perderá los cambios no guardados y puede tardar un poco. ¿Continuar?"
+        :open-new-window "Nueva ventana"
+        :sync-from-local-files "Refrescar"
+        :sync-from-local-files-detail "Importar cambios de los archivos locales"
+        :sync-from-local-changes-detected "Refrescar detecta y procesa los archivos modificados en su disco que difieren del contenido actual de la página en Logseq. ¿Continuar?"
         :unlink "desenlazar"
         :search/publishing "Buscar"
         :search "Buscar o Crear Página"
         :page-search "Buscar en la página actual"
+        :graph-search "Buscar grafo"
         :new-page "Nueva página"
         :new-file "Nuevo archivo"
         :new-graph "Añadir nuevo grafo"
         :graph "Grafo"
-        :graph-view "Vista gráfica"
+        :graph-view "Ver grafo"
+        :graph/persist "Logseq está sincronizando su estado interno, por favor espere unos segundos."
+        :graph/persist-error "Falló la sincronización del estado interno."
+        :graph/save "Guardando..."
+        :graph/save-success "Guardado satisfactoriamente"
+        :graph/save-error "Falló el guardado"
+        :cards-view "Ver tarjetas"
         :publishing "Publicar"
         :export "Exportar"
+        :export-graph "Exportar grafo"
+        :export-page "Exportar página"
         :export-markdown "Exportar como Markdown estándar (sin propiedades de bloque)"
         :export-opml "Exportar a OPML"
         :export-public-pages "Exportar páginas públicas"
@@ -1528,15 +1952,19 @@
         :all-graphs "Lista de grafos"
         :all-pages "Lista de páginas"
         :all-files "Lista de archivos"
+        :remove-orphaned-pages "Eliminar páginas huérfanas"
         :all-journals "Lista de diarios"
         :my-publishing "Mi publicación"
         :settings "Opciones"
+        :settings-of-plugins "Opciones de Extensiones"
         :plugins "Extensiones"
         :themes "Temas"
         :developer-mode-alert "Debe reiniciar la aplicación para habilitar las extensiones. ¿Desea reiniciarla ahora?"
+        :relaunch-confirm-to-work "Debe relanzar la aplicación para hacer que funcione. ¿Desea reiniciarla ahora?"
         :import "Importar"
         :join-community "Unirse a la comunidad"
         :sponsor-us "Hazte patrocinador"
+        :discourse-title "Nuestro foro!"
         :discord-title "¡Nuestro grupo de Discord!"
         :help-shortcut-title "Clic para ver atajos y otras sugerencias"
         :loading "Cargando"
@@ -1558,15 +1986,56 @@
         :user/delete-account "Eliminar cuenta"
         :user/delete-your-account "Eliminar su cuenta"
         :user/delete-account-notice "Todas sus páginas publicadas en Logseq serán eliminadas."
-
-        :help/shortcut-page-title "Atajos personalizados"
-
+        :help/shortcut-page-title "Atajos de teclado"
+        :plugin/installed "Instalado"
+        :plugin/not-installed "No instalado"
+        :plugin/installing "Instalando"
+        :plugin/install "Instalar"
+        :plugin/reload "Recargar"
+        :plugin/update "Actualizar"
+        :plugin/check-update "Buscar actualización"
+        :plugin/check-all-updates "Buscar actualizaciones"
+        :plugin/refresh-lists "Actualizar listas"
+        :plugin/enabled "Habilitado"
+        :plugin/disabled "Inactivo"
+        :plugin/update-available "Actualización disponible"
+        :plugin/updating "Actualizando"
+        :plugin/uninstall "Desinstalar"
+        :plugin/marketplace "Marketplace"
+        :plugin/downloads "Descargas"
+        :plugin/stars "Estrellas"
+        :plugin/title "Título"
+        :plugin/all "Todo"
+        :plugin/unpacked "Desempaquetado"
+        :plugin/delete-alert "¿Está seguro de desinstalar la extensión [{1}]?"
+        :plugin/open-settings "Abrir opciones"
+        :plugin/open-package "Abrir paquete"
+        :plugin/load-unpacked "Cargar extensión desempaquetada"
+        :plugin/open-preferences "Abrir el archivo de preferencias de la extensión"
+        :plugin/restart "Reiniciar la aplicación"
+        :plugin/unpacked-tips "Seleccionar el directorio de la extensión"
+        :plugin/contribute "✨ Escribir y publicar nueva extensión"
+        :plugin/marketplace-tips "Si la extensión no funciona correctamente cuando recién se instala, pruebe a reiniciar Logseq."
+        :plugin/up-to-date "Está actualizada"
+        :plugin/custom-js-alert "Se encontró el archivo custom.js, desea permitir que se ejecute? (Si no comprende el contenido de este archivo se recomienda no permitir su ejecución dado que representa ciertos riesgos de seguridad)."
+        :pdf/copy-ref "Copiar referencia"
+        :pdf/copy-text "Copiar texto"
+        :pdf/linked-ref "Referencias vinculadas"
+        :pdf/toggle-dashed "Estilo punteado para las áreas resaltadas"
+        :updater/new-version-install "Se descargó una nueva versión."
+        :updater/quit-and-install "Reiniciar para instalar"
+        :paginates/pages "Total {1} páginas"
+        :paginates/prev "Anterior"
+        :paginates/next "Siguiente"
+        :tips/all-done "Todo Hecho"
+        :command-palette/prompt "Escriba un comando"
         :select/default-prompt "Seleccione uno"
         :select.graph/prompt "Seleccione un grafo"
-        :select.graph/empty-placeholder-description "No encontramos un grafo. Queries añadir otro?"
-        :select.graph/add-graph "Si, añadame otro grafo"
+        :select.graph/empty-placeholder-description "No encontramos un grafo. Querie añadir otro?"
+        :select.graph/add-graph "Sí, añadir otro grafo"
         :file-sync/other-user-graph "El gráfico local actual está unido al gráfico remoto de otro usuario. Así que no se puede empezar a sincronizar"
-        :file-sync/graph-deleted "El gráfico remoto actual se ha eliminado"}
+        :file-sync/graph-deleted "El gráfico remoto actual se ha eliminado"
+     }
 
    :nb-NO {:tutorial/text #?(:cljs (rc/inline "tutorial-no.md")
                              :default "tutorial-no.md")
@@ -4130,6 +4599,7 @@
 (def languages [{:label "English" :value :en}
                 {:label "Français" :value :fr}
                 {:label "Deutsch" :value :de}
+                {:label "Dutch (Nederlands)" :value :nl}
                 {:label "简体中文" :value :zh-CN}
                 {:label "繁體中文" :value :zh-Hant}
                 {:label "Afrikaans" :value :af}

+ 1 - 1
src/main/frontend/extensions/pdf/_viewer.css

@@ -774,8 +774,8 @@
   top: 0;
   right: 0;
   bottom: 0;
-  background: url("images/loading-icon.gif") center no-repeat;
 }
+
 .pdfViewer .page .loadingIcon.notVisible {
   background: none;
 }

+ 1 - 1
src/main/frontend/extensions/pdf/highlights.cljs

@@ -1061,7 +1061,7 @@
         (let [get-doc$ (fn [^js opts] (.-promise (js/pdfjsLib.getDocument opts)))
               opts {:url           url
                     :ownerDocument js/document
-                    :cMapUrl       "./js/pdfjs/cmaps/"
+                    :cMapUrl       "./cmaps/"
                     ;;:cMapUrl       "https://cdn.jsdelivr.net/npm/[email protected]/cmaps/"
                     :cMapPacked    true}]
 

+ 10 - 2
src/main/frontend/extensions/pdf/utils.cljs

@@ -161,11 +161,12 @@
   [text]
 
   (when-not (string/blank? text)
-    (let [sp "@#~#@"]
+    (let [sp "|#|"]
       (-> text
           (string/replace #"[\r\n]+" sp)
           (string/replace (str "-" sp) "")
-          (string/replace sp " ")))))
+          (string/replace #"\|#\|([a-zA-Z_])" " $1")
+          (string/replace sp "")))))
 
 ;; TODO: which viewer instance?
 (defn next-page
@@ -179,3 +180,10 @@
   (try
     (js-invoke js/window.lsPdfViewer "previousPage")
     (catch js/Error _e nil)))
+
+(comment
+ (fix-selection-text-breakline "this is a\ntest paragraph")
+ (fix-selection-text-breakline "he is 1\n8 years old")
+ (fix-selection-text-breakline "这是一个\n\n段落")
+ (fix-selection-text-breakline "これ\n\nは、段落")
+ (fix-selection-text-breakline "this is a te-\nst paragraph"))

+ 3 - 0
src/main/frontend/fs/capacitor_fs.cljs

@@ -259,6 +259,9 @@
     (readdir dir))
   (unlink! [this repo path _opts]
     (p/let [path (get-file-path nil path)
+            path (if (string/starts-with? path "file://")
+                   (string/replace-first path "file://" "")
+                   path)
             repo-dir (config/get-local-dir repo)
             recycle-dir (str repo-dir config/app-name "/.recycle")
             file-name (-> (string/replace path repo-dir "")

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

@@ -56,7 +56,8 @@
               (when (and (not (state/nfs-refreshing?))
                          (not (contains? (:file/unlinked-dirs @state/state)
                                          (config/get-repo-dir repo))))
-                (srs/update-cards-due-count!)
+                (when (state/enable-flashcards?)
+                  (srs/update-cards-due-count!))
                 ;; Don't create the journal file until user writes something
                 (page-handler/create-today-journal!))))]
     (f)

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

@@ -384,7 +384,7 @@
         block (update block :block/refs remove-non-existed-refs!)
         block (attach-page-properties-if-exists! block)
         new-properties (merge
-                        (select-keys properties (property/built-in-properties))
+                        (select-keys properties (property/hidden-properties))
                         (:block/properties block))]
     (-> block
         (dissoc :block/top?
@@ -2758,7 +2758,7 @@
         (do (util/stop e)
             (autopair input-id key format nil))
 
-        hashtag?
+        (and hashtag? (or (zero? pos) (re-matches #"\s" (get value (dec pos)))))
         (do
           (commands/handle-step [:editor/search-page-hashtag])
           (if (= key "#")

+ 12 - 9
src/main/frontend/handler/file.cljs

@@ -106,7 +106,7 @@
 (defn reset-file!
   ([repo-url file content]
    (reset-file! repo-url file content {}))
-  ([repo-url file content options]
+  ([repo-url file content {:keys [verbose] :as options}]
    (let [electron-local-repo? (and (util/electron?)
                                    (config/local-db? repo-url))
          file (cond
@@ -135,18 +135,20 @@
        (db/get-db repo-url false)
        file
        content
-       (merge options
+       (merge (dissoc options :verbose)
               {:new? new?
                :delete-blocks-fn (partial get-delete-blocks repo-url)
-               :extract-options {:user-config (state/get-config)
-                                 :date-formatter (state/get-date-formatter)
-                                 :page-name-order (state/page-name-order)
-                                 :block-pattern (config/get-block-pattern (gp-util/get-format file))
-                                 :supported-formats (gp-config/supported-formats)}}))))))
+               :extract-options (merge
+                                 {:user-config (state/get-config)
+                                  :date-formatter (state/get-date-formatter)
+                                  :page-name-order (state/page-name-order)
+                                  :block-pattern (config/get-block-pattern (gp-util/get-format file))
+                                  :supported-formats (gp-config/supported-formats)}
+                                 (when (some? verbose) {:verbose verbose}))}))))))
 
 ;; TODO: Remove this function in favor of `alter-files`
 (defn alter-file
-  [repo path content {:keys [reset? re-render-root? from-disk? skip-compare? new-graph?]
+  [repo path content {:keys [reset? re-render-root? from-disk? skip-compare? new-graph? verbose]
                       :or {reset? true
                            re-render-root? false
                            from-disk? false
@@ -166,7 +168,8 @@
             [[:db/retract page-id :block/alias]
              [:db/retract page-id :block/tags]]
             opts))
-        (reset-file! repo path content opts))
+        (reset-file! repo path content (merge opts
+                                              (when (some? verbose) {:verbose verbose}))))
       (db/set-file-content! repo path content opts))
     (util/p-handle (write-file!)
                    (fn [_]

+ 7 - 7
src/main/frontend/handler/repo.cljs

@@ -183,14 +183,15 @@
     (load-pages-metadata! repo file-paths files force?)))
 
 (defn- parse-and-load-file!
-  [repo-url file new-graph?]
+  [repo-url file {:keys [new-graph? verbose]}]
   (try
     (file-handler/alter-file repo-url
                              (:file/path file)
                              (:file/content file)
-                             {:new-graph? new-graph?
-                              :re-render-root? false
-                              :from-disk? true})
+                             (merge {:new-graph? new-graph?
+                                     :re-render-root? false
+                                     :from-disk? true}
+                                    (when (some? verbose) {:verbose verbose})))
     (catch :default e
       (state/set-parsing-state! (fn [m]
                                   (update m :failed-parsing-files conj [(:file/path file) e])))))
@@ -215,7 +216,6 @@
 (defn- parse-files-and-create-default-files-inner!
   [repo-url files delete-files delete-blocks file-paths db-encrypted? re-render? re-render-opts opts]
   (let [supported-files (graph-parser/filter-files files)
-        new-graph? (:new-graph? opts)
         delete-data (->> (concat delete-files delete-blocks)
                          (remove nil?))
         chan (async/to-chan! supported-files)
@@ -229,7 +229,7 @@
         (doseq [file supported-files]
           (state/set-parsing-state! (fn [m]
                                       (assoc m :current-parsing-file (:file/path file))))
-          (parse-and-load-file! repo-url file new-graph?))
+          (parse-and-load-file! repo-url file (select-keys opts [:new-graph? :verbose])))
         (after-parse repo-url files file-paths db-encrypted? re-render? re-render-opts opts graph-added-chan))
       (async/go-loop []
         (if-let [file (async/<! chan)]
@@ -237,7 +237,7 @@
             (state/set-parsing-state! (fn [m]
                                         (assoc m :current-parsing-file (:file/path file))))
             (async/<! (async/timeout 10))
-            (parse-and-load-file! repo-url file new-graph?)
+            (parse-and-load-file! repo-url file (select-keys opts [:new-graph? :verbose]))
             (recur))
           (after-parse repo-url files file-paths db-encrypted? re-render? re-render-opts opts graph-added-chan))))
     graph-added-chan))

+ 118 - 0
src/main/frontend/modules/shortcut/dicts.cljc

@@ -251,6 +251,124 @@
              :command.git/commit                     "Git Commit-Nachricht"
              :command.editor/select-block-down       "Block unterhalb auswählen"
              :command.editor/select-all-blocks       "Alle Blöcke auswählen"}
+   :nl      {:command.auto-complete/complete         "Automatisch aanvullen: Kies geselecteerd item"
+             :command.auto-complete/next             "Automatisch aanvullen: Selecteer volgend item"
+             :command.auto-complete/open-link        "Automatisch aanvullen: Open geselecteerd item in browser"
+             :command.auto-complete/prev             "Automatisch aanvullen: Selecteer vorig item"
+             :command.auto-complete/shift-complete   "Automatisch aanvullen: Open geselecteerd item in zijbalk"
+             :command.cards/forgotten                "Kaarten: vergeten"
+             :command.cards/next-card                "Kaarten: volgende kaart"
+             :command.cards/recall                   "Kaarten: het duurt even om terug te roepen"
+             :command.cards/remembered               "Kaarten: herinnerd"
+             :command.cards/toggle-answers           "Kaarten: toon/verberg antwoorden/clozes"
+             :command.command/run                    "Voer git commando uit"
+             :command.command/toggle-favorite        "Toevoegen aan/verwijderen uit favorieten"
+             :command.command-palette/toggle         "Schakel commando lijn"
+             :command.date-picker/complete           "Datumkiezer: Kies de geselecteerde dag"
+             :command.date-picker/next-day           "Datumkiezer: Volgende dag kiezen"
+             :command.date-picker/next-week          "Datumkiezer: Volgende week kiezen"
+             :command.date-picker/prev-day           "Datumkiezer: Vorige dag kiezen"
+             :command.date-picker/prev-week          "Datumkiezer: Vorige week kiezen"
+             :command.editor/backspace               "Backspace / Achteruit wissen"
+             :command.editor/backward-kill-word      "Een woord achterwaarts wissen"
+             :command.editor/backward-word           "Verplaats de cursor een woord achteruit"
+             :command.editor/beginning-of-block      "Verplaats de cursor naar het begin van een blok"
+             :command.editor/bold                    "Vet"
+             :command.editor/clear-block             "Wis volledige blokinhoud"
+             :command.editor/collapse-block-children "Samenvouwen"
+             :command.editor/copy                    "Kopiëren (kopieert een selectie of een blokverwijzing)"
+             :command.editor/copy-current-file       "Kopieer huidig bestand"
+             :command.editor/copy-embed              "Kopieer een blok embed dat wijst naar het huidige blok"
+             :command.editor/copy-text               "Kopieer selecties als tekst"
+             :command.editor/cut                     "Knip"
+             :command.editor/cycle-todo              "Draai de TODO status van het huidige item"
+             :command.editor/delete                  "Verwijderen / verwijderen vooruit"
+             :command.editor/delete-selection        "Verwijder geselecteerde blokken"
+             :command.editor/down                    "Verplaats cursor omlaag / Selecteer omlaag"
+             :command.editor/end-of-block            "Verplaats cursor naar het einde van een blok"
+             :command.editor/escape-editing          "Klaar met bewerken"
+             :command.editor/expand-block-children   "Openvouwen"
+             :command.editor/follow-link             "Volg de link onder de cursor"
+             :command.editor/forward-kill-word       "Een woord vooruit verwijderen"
+             :command.editor/forward-word            "Verplaats de cursor een woord vooruit"
+             :command.editor/highlight               "Markeer"
+             :command.editor/indent                  "Blok inspringen"
+             :command.editor/insert-link             "HTML Link"
+             :command.editor/insert-youtube-timestamp"Voeg youtube tijdstempel toe"
+             :command.editor/italics                 "Cursief"
+             :command.editor/kill-line-after         "Verwijder regel na cursorpositie"
+             :command.editor/kill-line-before        "Verwijder regel voor cursorpositie"
+             :command.editor/left                    "Verplaats cursor naar links / Open geselecteerd blok bij het begin"
+             :command.editor/move-block-down         "Verplaats blok omlaag"
+             :command.editor/move-block-up           "Verplaats blok omhoog"
+             :command.editor/new-block               "Maak nieuw blok"
+             :command.editor/new-line                "Nieuwe regel in huidig blok"
+             :command.editor/open-edit               "Bewerk geselecteerde blok"
+             :command.editor/open-file-in-default-app"Open bestand in standaard app"
+             :command.editor/open-file-in-directory  "Open bestand in bovenliggende map"
+             :command.editor/open-link-in-sidebar    "Open link in zijbalk"
+             :command.editor/outdent                 "Verminder blok inspringing"
+             :command.editor/paste-text-in-one-block-at-point"Plak tekst in een blok op punt"
+             :command.editor/redo                    "Herhaal"
+             :command.editor/replace-block-reference-at-point"Vervang de blokverwijzing door de inhoud op punt"
+             :command.editor/right                   "Beweeg cursor naar rechts / Open geselecteerd blok aan einde"
+             :command.editor/select-all-blocks       "Selecteer alle blokken"
+             :command.editor/select-block-down       "Selecteer blok hieronder"
+             :command.editor/select-block-up         "Selecteer blok hierboven"
+             :command.editor/select-down             "Selecteer inhoud hieronder"
+             :command.editor/select-up               "Selecteer inhoud hierboven"
+             :command.editor/strike-through          "Doorstrepen"
+             :command.editor/toggle-open-blocks      "Toggle open blocks (open of dichtklappen van alle blokken)"
+             :command.editor/undo                    "Ongedaan maken"
+             :command.editor/up                      "Verplaats cursor omhoog / Selecteer omhoog"
+             :command.editor/zoom-in                 "Zoom in bewerkingsblok / Vooruit anders"
+             :command.editor/zoom-out                "Zoom uit bewerkingsblok / Achteruit anders"
+             :command.git/commit                     "Maak git commit met bericht"
+             :command.go/all-pages                   "Ga naar alle pagina's"
+             :command.go/backward                    "Achteruit"
+             :command.go/flashcards                  "Flashcards Toggle"
+             :command.go/forward                     "Vooruit"
+             :command.go/graph-view                  "Ga naar grafiekweergave"
+             :command.go/home                        "Ga naar home"
+             :command.go/journals                    "Ga naar journaals"
+             :command.go/keyboard-shortcuts          "Ga naar sneltoetsen"
+             :command.go/next-journal                "Ga naar volgende journaal"
+             :command.go/prev-journal                "Ga naar vorige journaal"
+             :command.go/search                      "Zoeken in volledige tekst"
+             :command.go/search-in-page              "Zoeken in de huidige pagina"
+             :command.go/tomorrow                    "Ga naar morgen"
+             :command.graph/add                      "Voeg een grafiek toe"
+             :command.graph/open                     "Selecteer grafiek om te openen"
+             :command.graph/re-index                 "Huidige grafiek opnieuw indexeren"
+             :command.graph/remove                   "Verwijder een grafiek"
+             :command.graph/save                     "Huidige grafiek opslaan op schijf"
+             :command.misc/copy                      "Kopieer"
+             :command.pdf/close                      "Huidige pdf-viewer sluiten"
+             :command.pdf/next-page                  "Volgende pagina van huidige pdf doc"
+             :command.pdf/previous-page              "Vorige pagina van huidige pdf doc"
+             :command.search/re-index                "Bouw zoekindex opnieuw op"
+             :command.sidebar/clear                  "Wis alles in de rechter zijbalk"
+             :command.sidebar/open-today-page        "Open de pagina van vandaag in de rechter zijbalk"
+             :command.ui/goto-plugins                "Ga naar plugins dashboard"
+             :command.ui/select-theme-color          "Selecteer beschikbare themakleuren"
+             :command.ui/toggle-brackets             "Toggle tussen het weergeven van beugels"
+             :command.ui/toggle-cards                "Toggle kaarten"
+             :command.ui/toggle-contents             "Toggle inhoud in zijbalk"
+             :command.ui/toggle-document-mode        "Schakel tussen documentmodus"
+             :command.ui/toggle-help                 "Toggle help"
+             :command.ui/toggle-left-sidebar         "Toggle linker zijbalk"
+             :command.ui/toggle-right-sidebar        "Toggle rechter zijbalk"
+             :command.ui/toggle-settings             "Toggle settings"
+             :command.ui/toggle-theme                "Toggle tussen donker/licht thema"
+             :command.ui/toggle-wide-mode            "Toggle brede modus"
+             :shortcut.category/basics               "Basis"
+             :shortcut.category/block-command-editing"Blokcommando's bewerken"
+             :shortcut.category/block-editing        "Blokbewerking algemeen"
+             :shortcut.category/block-selection      "Blokselectie (druk op Esc om selectie te beëindigen)"
+             :shortcut.category/formatting           "Formatteren"
+             :shortcut.category/navigating           "Navigatie"
+             :shortcut.category/others               "Anderen"
+             :shortcut.category/toggle               "Toggle"}
    :fr      {:shortcut.category/formatting           "Formats"
              :command.editor/indent                  "Indenter un Bloc vers la droite"
              :command.editor/outdent                 "Indenter un Bloc vers la gauche"

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

@@ -11,6 +11,7 @@
             [frontend.search.protocol :as protocol]
             [frontend.state :as state]
             [frontend.util :as util]
+            [frontend.util.property :as property]
             [goog.object :as gobj]
             [promesa.core :as p]))
 
@@ -169,7 +170,9 @@
   ([q limit]
    (when q
      (let [q (clean-str q)
-           properties (map name (db-model/get-all-properties))]
+           properties (->> (db-model/get-all-properties)
+                           (remove (property/hidden-properties))
+                           (map name))]
        (when (seq properties)
          (if (string/blank? q)
            properties

+ 11 - 3
src/main/frontend/state.cljs

@@ -60,6 +60,8 @@
      :modal/close-btn?                      nil
      :modal/subsets                         []
 
+     ;; left sidebar
+     :ui/navigation-item-collapsed?         {}
 
      ;; right sidebar
      :ui/fullscreen?                        false
@@ -365,9 +367,11 @@
                 (get (sub-config) repo)))))
 
 (defn enable-flashcards?
-  [repo]
-  (not (false? (:feature/enable-flashcards?
-                (get (sub-config) repo)))))
+  ([]
+   (enable-flashcards? (get-current-repo)))
+  ([repo]
+   (not (false? (:feature/enable-flashcards?
+                 (get (sub-config) repo))))))
 
 (defn export-heading-to-list?
   []
@@ -736,6 +740,10 @@
          :custom-context-menu/show? false
          :custom-context-menu/links nil))
 
+(defn toggle-navigation-item-collapsed!
+  [item]
+  (update-state! [:ui/navigation-item-collapsed? item] not))
+
 (defn toggle-sidebar-open?!
   []
   (swap! state update :ui/sidebar-open? not))

+ 4 - 3
src/main/frontend/template.cljs

@@ -1,6 +1,5 @@
 (ns frontend.template
-  (:require [cljs-time.coerce :as tc]
-            [clojure.string :as string]
+  (:require [clojure.string :as string]
             [frontend.date :as date]
             [frontend.state :as state]
             [frontend.util :as util]))
@@ -29,6 +28,8 @@
                        (get (variable-rules) (string/lower-case match))
                        :else
                        (if-let [nld (date/nld-parse match)]
-                         (let [date (tc/to-local-date-time nld)]
+                         (let [;; NOTE: This following cannot handle timezones
+                               ;; date (tc/to-local-date-time nld)
+                               date (doto (goog.date.DateTime.) (.setTime (.getTime nld)))]
                            (util/format "[[%s]]" (date/journal-name date)))
                          match))))))

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

@@ -1,3 +1,3 @@
 (ns frontend.version)
 
-(defonce version "0.7.8")
+(defonce version "0.7.9")

+ 28 - 0
src/test/frontend/db/model_test.cljs

@@ -111,4 +111,32 @@
          (map first (model/get-pages-that-mentioned-page test-helper/test-db "generic page" false)))
       "Must be empty"))
 
+(deftest remove-links-for-each-level-of-the-namespaces
+  (load-test-files [{:file/path "pages/generic page.md"
+                     :file/content "tags:: [[one/two/tree]], one/two
+- link to ns [[one]]
+- link to page one [[page ONE]]"}])
+
+  (is (= '("one/two/tree" "page one")
+         (map second (model/get-pages-relation test-helper/test-db true)))
+      "(get-pages-relation) Must be only ns one/two/tree")
+
+  (is (= '("one/two/tree" "page one")
+         (map second (#'model/remove-nested-namespaces-link [["generic page" "one/two/tree"]
+                                                           ["generic page" "one/two"]
+                                                           ["generic page" "one"]
+                                                           ["generic page" "page one"]])))
+      "(model/remove-nested-namespaces-link) Must be only ns one/two/tree")
+
+  (is (= '("one/two/tree" "one/two" "one")
+         (#'model/get-parents-namespace-list "one/two/tree/four"))
+      "Must be one/two/tree one/two one")
+
+  (is (= '("one/two" "one")
+         (#'model/get-unnecessary-namespaces-name '("one/two/tree" "one" "one/two" "non nested tag" "non nested link")))
+      "Must be  one/two one"))
+
+
+
+
 #_(cljs.test/test-ns 'frontend.db.model-test)

+ 55 - 1
src/test/frontend/handler/editor_test.cljs

@@ -1,6 +1,9 @@
 (ns frontend.handler.editor-test
   (:require [frontend.handler.editor :as editor]
-            [clojure.test :refer [deftest is testing are]]))
+            [clojure.test :refer [deftest is testing are]]
+            [frontend.util :as util]
+            [frontend.state :as state]
+            [frontend.util.cursor :as cursor]))
 
 (deftest extract-nearest-link-from-text-test
   (testing "Page, block and tag links"
@@ -42,7 +45,58 @@
           "[[https://github.com/logseq/logseq][logseq]] is #awesome :)" 0 editor/url-regex))
       "Finds url in org link correctly"))
 
+(defn- keydown-not-matched-handler
+  "Spied version of editor/keydown-not-matched-handler"
+  [{:keys [value key format cursor-pos] :or {key "#" format "markdown"}}]
+  ;; Reset editor action in order to test result
+  (state/set-editor-action! nil)
+  ;; Default cursor pos to end of line
+  (let [pos (or cursor-pos (count value))]
+    (with-redefs [util/get-selected-text (constantly false)
+                  state/get-input (constantly #js {:value value})
+                  cursor/pos (constantly pos)
+                  cursor/get-caret-pos (constantly {})]
+      ((editor/keydown-not-matched-handler format)
+       #js {:key key} nil))))
+
+(deftest keydown-not-matched-handler-test
+  (testing "Tag autocompletion"
+    (keydown-not-matched-handler {:value "Some words "})
+    (is (= :page-search-hashtag (state/get-editor-action))
+        "Autocomplete tags if starting new word")
+
+    (keydown-not-matched-handler {:value ""})
+    (is (= :page-search-hashtag (state/get-editor-action))
+        "Autocomplete tags if starting a new line")
+
+    (keydown-not-matched-handler {:value "Some words" :cursor-pos 0})
+    (is (= :page-search-hashtag (state/get-editor-action))
+        "Autocomplete tags if there is are existing words and cursor is at start of line")
+
+    (keydown-not-matched-handler {:value "Some words" :cursor-pos 5})
+    (is (= :page-search-hashtag (state/get-editor-action))
+        "Autocomplete tags if there is whitespace before cursor")
+
+    (keydown-not-matched-handler {:value "String"})
+    (is (= nil (state/get-editor-action))
+        "Don't autocomplete tags if at end of word")
+
+    (keydown-not-matched-handler {:value "String" :cursor-pos 3})
+    (is (= nil (state/get-editor-action))
+        "Don't autocomplete tags if in middle of word")
+
+    (keydown-not-matched-handler {:value "`One backtick "})
+    (is (= :page-search-hashtag (state/get-editor-action))
+        "Autocomplete tags if only one backtick")
+
+    (keydown-not-matched-handler {:value "`String#gsub and String`"
+                                  :cursor-pos (dec (count "`String#gsub and String`"))})
+    (is (= nil (state/get-editor-action))
+        "Don't autocomplete tags within backticks")
+    (state/set-editor-action! nil)))
+
 (defn- set-marker
+  "Spied version of editor/set-marker"
   [marker content format]
   (let [actual-content (atom nil)]
     (with-redefs [editor/save-block-if-changed! (fn [_ content]

+ 1 - 1
src/test/frontend/handler/repo_test.cljs

@@ -14,7 +14,7 @@
   (let [graph-dir "src/test/docs"
         _ (docs-graph-helper/clone-docs-repo-if-not-exists graph-dir)
         files (gp-cli/build-graph-files graph-dir)
-        _ (repo-handler/parse-files-and-load-to-db! test-helper/test-db files {:re-render? false})
+        _ (repo-handler/parse-files-and-load-to-db! test-helper/test-db files {:re-render? false :verbose false})
         db (conn/get-db test-helper/test-db)]
 
     (docs-graph-helper/docs-graph-assertions db (map :file/path files))))

+ 1 - 1
src/test/frontend/test/helper.cljs

@@ -22,4 +22,4 @@
     (conn/start! current-repo)))
 
 (defn load-test-files [files]
-  (repo-handler/parse-files-and-load-to-db! test-db files {:re-render? false}))
+  (repo-handler/parse-files-and-load-to-db! test-db files {:re-render? false :verbose false}))

+ 10 - 5
templates/config.edn

@@ -12,7 +12,7 @@
  :preferred-workflow :now
 
  ;; The app will ignore those directories or files.
- ;; E.g. "/archived" "/test.md"
+ ;; E.g. :hidden ["/archived" "/test.md" "../assets/archived"]
  :hidden []
 
  ;; When creating the new journal page, the app will use your template if there is one.
@@ -170,8 +170,13 @@
     :inputs [:today :7d-after]
     :collapsed? false}]}
 
- ;; Add your own commands to speedup.
- ;; E.g. [["js" "Javascript"]]
+ ;; Add your own commands to slash menu to speedup.
+ ;; E.g.
+ ;; :commands
+ ;; [
+ ;; ["js" "Javascript"]
+ ;; ["md" "Markdown"]
+ ;; ]
  :commands
  []
 
@@ -213,14 +218,14 @@
  ;; :srs/initial-interval 4
 
  ;; hide specific properties for blocks
- ;; E.g. #{:created-at :updated-at}
+ ;; E.g. :block-hidden-properties #{:created-at :updated-at}
  ;; :block-hidden-properties #{}
 
  ;; Enable all your properties to have corresponding pages
  :property-pages/enabled? true
 
  ;; Properties to exclude from having property pages
- ;; E.g. #{:duration :author}
+ ;; E.g.:property-pages/excludelist #{:duration :author}
  ;; :property-pages/excludelist
 
  ;; logbook setup

+ 14 - 0
templates/dummy-notes-es.md

@@ -0,0 +1,14 @@
+---
+title: ¿Cómo tomar notas ficticias?
+---
+
+- Hola, ¡yo soy un bloque!
+:PROPERTIES:
+:id: 5f713e91-8a3c-4b04-a33a-c39482428e2d
+:END:
+    - ¡Yo soy un bloque hijo!
+    - ¡Yo soy otro bloque hijo!
+- ¡Oye!, ¡yo soy otro bloque!
+:PROPERTIES:
+:id: 5f713ea8-8cba-403d-ac00-9964b1ec7190
+:END:

+ 26 - 0
templates/tutorial-es.md

@@ -0,0 +1,26 @@
+## Hola, ¡bienvenido a Logseq!
+- Logseq es una plataforma _privacidad-primero_, [open-source](https://github.com/logseq/logseq) para la gestión del _conocimiento_ y colaboración.
+- Este es un tutorial de 3 minutos acerca de cómo usar Logseq. ¡Empecemos!
+- Éstos son algunos consejos que pueden ser útiles.
+#+BEGIN_TIP
+Presione clic para editar un bloque.
+Presione `Enter` para crear un nuevo bloque.
+Presione `Shift+Enter` para crear una nueva línea.
+Escriba `/` para mostrar todos los comandos.
+#+END_TIP
+- 1. Creamos una página llamada [[¿Cómo tomar notas ficticias?]]. Puede presionar clic en el enlace para ir a la página, o puede presionar `Shift+Clic` para abrirla en la barra lateral derecha. Ahora debería ver tanto _Referencias Enlazadas_ como  _Referencias sin enlazar_.
+- 2. Referenciemos algunos bloques de [[¿Cómo tomar notas ficticias?]], puede presionar `Shift+Click` sobre cualquier referencia de bloque para abrirla en la barra lateral derecha. Pruebe a hacer algunos cambios en la barra lateral derecha, ¡los bloques referenciados cambiarán también!
+    - ((5f713e91-8a3c-4b04-a33a-c39482428e2d)) : Ésta es una referencia de bloque.
+    - ((5f713ea8-8cba-403d-ac00-9964b1ec7190)) : Ésta es otra referencia de bloque.
+- 3. Logseq soporta etiquetas?
+    - Por supuesto, esta es una etiqueta #ficticia.
+- 4. Logseq soporta tareas como porhacer/haciendo/terminado y prioridades?
+    - Sí, escriba `/` y seleccione su palabra clave favorita (TODO, DOING, DONE, WAITING, CANCELED, NOW, LATER) o prioridad (A/B/C).
+    - NOW [#A] Un tutorial ficticio acerca de "¿Cómo tomar notas ficticias?"
+    - LATER [#A] Revisar este asombroso video de [:a {:href "https://twitter.com/shuomi3" :target "_blank"} "@shuomi3"] acerca de cómo usar Logseq para tomar notas y organizar su vida!
+    {{youtube https://www.youtube.com/watch?v=BhHfF0P9A80&ab_channel=ShuOmi}}
+
+    - DONE Crear una página
+    - CANCELED [#C] Escribir una página con más de 1000 bloques
+- ¡Eso es todo! ¡Ahora puede crear más viñetas o abrir un directorio locar para importar algunas notas!
+- Puede descargar nuestra aplicación de escritorio de https://github.com/logseq/logseq/releases