Przeglądaj źródła

Merge branch 'master' into refactor/apis-types

charlie 1 miesiąc temu
rodzic
commit
0c9345ec1c

+ 1 - 18
AGENTS.md

@@ -1,5 +1,4 @@
 ## Repository Layout
-- Use clojure-mcp `clojure_inspect_project` to get project structure.
 - `src/`: Core source code
   - `src/main/`: The core logic of the application
     - `src/main/mobile/`: Mobile app code
@@ -21,10 +20,7 @@
 - Run single focused unit-test:
   - Add the `:focus` keyword to the test case: `(deftest ^:focus test-name ...)`
   - `bb dev:test -i focus`
-- Run e2e basic tests:
-  - `bb dev:e2e-basic-test`
-- Run e2e rtc extra tests:
-  - `bb dev:e2e-rtc-extra-test`
+- E2E tests files are located in `/clj-e2e`
 
 ## Common used cljs keywords
 - All commonly used ClojureScript keywords are defined using `logseq.common.defkeywords/defkeyword`.
@@ -37,16 +33,3 @@
 - Linters and unit-tests must pass
 - Check the review notes listed in `prompts/review.md`.
 
-# *IMPORTANT RULES*
-
-WARNING: The following are non-negotiable, highest-priority instructions. They *MUST* be followed unconditionally in all cases. Failure to comply with these rules will result in task failure.
-
-1. Clojure Code Editing Rules
-    * Instruction: When editing any .clj, .cljs, or .cljc file, you MUST and ONLY use the clojure-mcp toolkit.
-    * Prohibition: Absolutely do NOT use any general file writing tools (such as file_edit, file_write) to modify Clojure source files.
-    * Reason: This is to ensure the integrity of the code structure, avoid syntax errors, and maintain the project's code style.
-
-2. Code Review/Modification Prerequisites
-    * Instruction: Before EACH “review” or “modification” of the code, you MUST first execute the `clojure_inspect_project` tool.
-    * Prohibition: Do NOT begin analyzing or modifying code directly without obtaining project-wide information.
-    * Reason: This is to obtain complete, up-to-date project context, which is the foundation for making correct judgments and modifications.

+ 6 - 1
bb.edn

@@ -166,7 +166,12 @@
   logseq.tasks.dev/e2e-basic-test
 
   dev:e2e-rtc-extra-test
-  logseq.tasks.dev/e2e-rtc-extra-test
+  {:doc "Run e2e rtc extra tests. HTTP server should be available at localhost:3001"
+   :task (clojure {:dir "clj-e2e"} "-X:dev-run-rtc-extra-test")}
+
+  dev:e2e-rtc-extra-part2-test
+  {:doc "Run e2e rtc extra part2 tests. HTTP server should be available at localhost:3001"
+   :task (clojure {:dir "clj-e2e"} "-X:dev-run-rtc-extra-part2-test")}
 
   dev:gen-malli-kondo-config
   logseq.tasks.dev/gen-malli-kondo-config

+ 23 - 0
clj-e2e/AGENTS.md

@@ -0,0 +1,23 @@
+# E2E test Guide
+
+- Source Layout (`src/logseq/e2e/`)
+  - `assert.clj`: Custom assertion functions
+  - `block.clj`: Block manipulation helpers
+  - `config.clj`: Configuration settings
+  - `const.clj`: Shared constants
+  - `graph.clj`: Graph management helpers
+  - `keyboard.clj`: Keyboard shortcut helpers
+  - `locator.clj`: Playwright locator utilities
+  - `page.clj`: Page operations helpers
+  - `playwright_page.clj`: Playwright page initialization
+  - `rtc.clj`: RTC testing helpers
+  - `settings.clj`: Logseq settings manipulation
+  - `util.clj`: General utilities
+- Tests (`test/logseq/e2e/`)
+  - `fixtures.clj`: Test fixtures
+  - Basic Tests: `*_basic_test.clj`
+  - RTC Tests: `rtc_*_test.clj`
+- Run tests commands (in `clj-e2e` directory)
+  - `clojure -X:dev-run-rtc-extra-test`
+  - `clojure -X:dev-run-rtc-extra-part2-test`
+  - `clojure -X:dev-run-all-basic-test`

+ 2 - 0
clj-e2e/deps.edn

@@ -21,5 +21,7 @@
   :dev {:extra-paths ["dev" "test"]}
   :dev-run-rtc-extra-test {:extra-paths ["dev" "test"]
                            :exec-fn user/run-rtc-extra-test2}
+  :dev-run-rtc-extra-part2-test {:extra-paths ["dev" "test"]
+                                 :exec-fn user/run-rtc-extra-part2-test2}
   :dev-run-all-basic-test {:extra-paths ["dev" "test"]
                            :exec-fn user/run-all-basic-test}}}

+ 6 - 0
clj-e2e/dev/user.clj

@@ -15,6 +15,7 @@
             [logseq.e2e.property-basic-test]
             [logseq.e2e.reference-basic-test]
             [logseq.e2e.rtc-basic-test]
+            [logseq.e2e.rtc-extra-part2-test]
             [logseq.e2e.rtc-extra-test]
             [logseq.e2e.tag-basic-test]
             [logseq.e2e.util :as util]
@@ -79,6 +80,11 @@
   (run-tests 'logseq.e2e.rtc-extra-test)
   (System/exit 0))
 
+(defn run-rtc-extra-part2-test2
+  [& _args]
+  (run-tests 'logseq.e2e.rtc-extra-part2-test)
+  (System/exit 0))
+
 (defn run-editor-basic-test
   []
   (->> (future (run-tests 'logseq.e2e.editor-basic-test))

+ 2 - 0
clj-e2e/src/logseq/e2e/const.clj

@@ -7,3 +7,5 @@
 (def *page2
   "this 'page' means playwright-page, not logseq-page. it points to the client2 when testing rtc"
   (atom nil))
+
+(def ^:dynamic *graph-name* nil)

+ 11 - 2
clj-e2e/src/logseq/e2e/graph.clj

@@ -59,6 +59,15 @@
                              (format "div[data-testid='logseq_db_%s']" graph-name)
                              refresh-all-remote-graphs))
 
+(defn remove-local-graph
+  [graph-name]
+  (wait-for-remote-graph graph-name)
+  (let [action-btn
+        (.first (w/-query (format "div[data-testid='logseq_db_%s'] .graph-action-btn" graph-name)))]
+    (w/click action-btn)
+    (w/click ".delete-local-graph-menu-item")
+    (w/click "div[role='alertdialog'] button:text('ok')")))
+
 (defn remove-remote-graph
   [graph-name]
   (wait-for-remote-graph graph-name)
@@ -69,11 +78,11 @@
     (w/click "div[role='alertdialog'] button:text('ok')")))
 
 (defn switch-graph
-  [to-graph-name wait-sync?]
+  [to-graph-name wait-sync? need-input-password?]
   (goto-all-graphs)
   (w/click (.last (w/-query (format "div[data-testid='logseq_db_%1$s'] span:has-text('%1$s')" to-graph-name))))
   (when wait-sync?
-    (input-e2ee-password)
+    (when need-input-password? (input-e2ee-password))
     (w/wait-for "button.cloud.on.idle" {:timeout 20000}))
   (assert/assert-graph-loaded?))
 

+ 16 - 11
clj-e2e/src/logseq/e2e/page.clj

@@ -18,6 +18,10 @@
       (k/esc)
       (util/search-and-click page-name))))
 
+(defn get-page-name
+  []
+  (util/get-text "div[data-testid='page title'] .block-title-wrap"))
+
 (defn new-page
   [title]
   ;; Question: what's the best way to close all the popups?
@@ -42,17 +46,18 @@
   (k/esc))
 
 (defn- set-tag-extends
-  [extends & [in-retry?]]
-  (util/wait-timeout 500)
-  (w/click (loc/filter ".property-value" :has-text "root tag"))
-  (let [extends-visible? (mapv #(w/visible? (format "div:has(> button):has(div:text('%s'))" %)) extends)]
-    (if (every? identity extends-visible?)
-      (doseq [extend extends]
-        (w/click (format "div:has(> button):has(div:text('%s'))" extend)))
-      (if in-retry?
-        (throw (ex-info "parent-tag not found" {:extends extends :visible? extends-visible?}))
-        (do (k/esc)
-            (set-tag-extends extends true))))))
+  [extends & [retry-count]]
+  (let [retry-count (or retry-count 5)]
+    (util/wait-timeout 500)
+    (w/click (loc/filter ".property-value" :has-text "root tag"))
+    (let [extends-visible? (mapv #(w/visible? (format "div:has(> button):has(div:text('%s'))" %)) extends)]
+      (if (every? identity extends-visible?)
+        (doseq [extend extends]
+          (w/click (format "div:has(> button):has(div:text('%s'))" extend)))
+        (if (zero? retry-count)
+          (throw (ex-info "parent-tag not found" {:extends extends :visible? extends-visible?}))
+          (do (k/esc)
+              (set-tag-extends extends (dec retry-count))))))))
 
 (defn convert-to-tag
   [page-name & {:keys [extends]}]

+ 6 - 3
clj-e2e/test/logseq/e2e/fixtures.clj

@@ -2,7 +2,7 @@
   (:require [com.climate.claypoole :as cp]
             [logseq.e2e.assert :as assert]
             [logseq.e2e.config :as config]
-            [logseq.e2e.const :refer [*page1 *page2]]
+            [logseq.e2e.const :refer [*page1 *page2 *graph-name*]]
             [logseq.e2e.custom-report :as custom-report]
             [logseq.e2e.graph :as graph]
             [logseq.e2e.page :as page]
@@ -23,6 +23,7 @@
     (w/grant-permissions :clipboard-write :clipboard-read)
     (binding [custom-report/*pw-contexts* #{(.context (w/get-page))}
               custom-report/*pw-page->console-logs* (atom {})]
+      (w/grant-permissions :clipboard-write :clipboard-read)
       (w/navigate (pw-page/get-test-url port))
       (settings/developer-mode)
       (w/refresh)
@@ -50,6 +51,7 @@
               w/*page* (delay (throw (ex-info "Don't use *page*, use *page1* and *page2* instead" {})))]
       (run!
        #(w/with-page %
+          (w/grant-permissions :clipboard-write :clipboard-read)
           (w/navigate (pw-page/get-test-url port))
           (settings/developer-mode)
           (w/refresh)
@@ -146,9 +148,10 @@
       (graph/new-graph graph-name true))
     (w/with-page @*page2
       (graph/wait-for-remote-graph graph-name)
-      (graph/switch-graph graph-name true))
+      (graph/switch-graph graph-name true true))
 
-    (binding [custom-report/*preserve-graph* false]
+    (binding [custom-report/*preserve-graph* false
+              *graph-name* graph-name]
       (f)
       ;; cleanup
       (if custom-report/*preserve-graph*

+ 3 - 3
clj-e2e/test/logseq/e2e/multi_tabs_basic_test.clj

@@ -34,15 +34,15 @@
                 (w/with-page p2
                   (util/goto-journals)
                   (assert/assert-in-normal-mode?)
-                  (graph/switch-graph graph-name false))
+                  (graph/switch-graph graph-name false false))
                 (w/with-page p3
                   (util/goto-journals)
                   (assert/assert-in-normal-mode?)
-                  (graph/switch-graph graph-name false))
+                  (graph/switch-graph graph-name false false))
                 (w/with-page p1
                   (util/goto-journals)
                   (assert/assert-in-normal-mode?)
-                  (graph/switch-graph graph-name false))
+                  (graph/switch-graph graph-name false false))
                 (let [graph-new-blocks (map #(str graph-name "-b1-" %) (range 5))]
                   (add-blocks-and-check-on-other-tabs graph-new-blocks p1 [p2 p3])))]
         (w/with-page p1

+ 1 - 1
clj-e2e/test/logseq/e2e/rtc_basic_test.clj

@@ -30,7 +30,7 @@
         (graph/new-graph graph-name true))
       (w/with-page @*page2
         (graph/wait-for-remote-graph graph-name)
-        (graph/switch-graph graph-name true)))
+        (graph/switch-graph graph-name true true)))
     (testing "logseq pages add/delete"
       (doseq [page-name page-names]
         (let [{:keys [_local-tx remote-tx]}

+ 85 - 3
clj-e2e/test/logseq/e2e/rtc_extra_part2_test.clj

@@ -1,9 +1,9 @@
 (ns logseq.e2e.rtc-extra-part2-test
-  (:require [clojure.string :as string]
-            [clojure.test :refer [deftest testing is use-fixtures run-test]]
+  (:require [clojure.test :refer [deftest testing is use-fixtures run-test]]
             [logseq.e2e.block :as b]
-            [logseq.e2e.const :refer [*page1 *page2]]
+            [logseq.e2e.const :refer [*page1 *page2 *graph-name*]]
             [logseq.e2e.fixtures :as fixtures]
+            [logseq.e2e.graph :as graph]
             [logseq.e2e.page :as page]
             [logseq.e2e.rtc :as rtc]
             [logseq.e2e.util :as util]
@@ -53,3 +53,85 @@ wait for 5-10 seconds, will found that \"aaa/bbb\" became \"aaa/<encrypted-strin
       (page/goto-page "aaa/bbb"))
 
     (rtc/validate-graphs-in-2-pw-pages)))
+
+(deftest paste-multiple-blocks-test
+  (testing "
+1. create 3 blocks
+  - block1
+  - block2
+  - block3
+2. copy these 3 blocks
+3. when cursor at block3, press <enter> to create a new block
+4. paste them at current position 5 times
+5. validate blocks are same on both clients"
+    (w/with-page @*page1
+      (b/new-blocks ["block1" "block2" "block3"])
+      (util/exit-edit)
+      (b/select-blocks 2)
+      (b/copy)
+      (b/jump-to-block "block3")
+      (util/repeat-keyboard 1 "Enter"))
+
+    (dotimes [_ 5]
+      (let [{:keys [remote-tx]}
+            (w/with-page @*page1
+              (rtc/with-wait-tx-updated
+                (b/paste)))]
+        (w/with-page @*page2
+          (rtc/wait-tx-update-to remote-tx))))
+
+    (let [{:keys [remote-tx]}
+          (w/with-page @*page1
+            (rtc/with-wait-tx-updated
+              (b/new-block "sync-trigger")))]
+      (w/with-page @*page2
+        (rtc/wait-tx-update-to remote-tx)))
+
+    (let [expected (vec (concat ["block1" "block2" "block3"]
+                                (take (* 3 5) (cycle ["block1" "block2" "block3"]))
+                                ["sync-trigger"]))]
+      (w/with-page @*page1
+        (util/exit-edit)
+        (is (= expected
+               (util/get-page-blocks-contents))))
+
+      (w/with-page @*page2
+        (util/exit-edit)
+        (is (= expected
+               (util/get-page-blocks-contents)))))
+
+    (rtc/validate-graphs-in-2-pw-pages)))
+
+(deftest asset-blocks-validate-after-init-downloaded-test
+  (testing "
+- add some assets in client1
+- remove local graph in client2
+- re-download the remote graph in client2
+- compare asset-blocks data in both clients"
+    (let [asset-file "../assets/icon.png"
+          page-title (w/with-page @*page1 (page/get-page-name))]
+      (w/with-page @*page1
+        (let [p (w/get-page)]
+          (.onFileChooser p (reify java.util.function.Consumer
+                              (accept [_ fc]
+                                (.setFiles fc (into-array java.nio.file.Path [(java.nio.file.Paths/get asset-file (into-array String []))])))))
+          (b/new-block "asset block")
+          (util/input-command "Upload an asset")
+          (w/wait-for ".ls-block img")))
+
+      (let [{:keys [remote-tx]}
+            (w/with-page @*page1
+              (rtc/with-wait-tx-updated
+                (b/new-block "sync done")))]
+        (w/with-page @*page2
+          (rtc/wait-tx-update-to remote-tx)))
+
+      (w/with-page @*page2
+        (graph/remove-local-graph *graph-name*)
+        (graph/wait-for-remote-graph *graph-name*)
+        (graph/switch-graph *graph-name* true false)
+        (page/goto-page page-title)
+        (w/wait-for ".ls-block img")
+        (is (some? (.getAttribute (w/-query ".ls-block img") "src"))))
+
+      (rtc/validate-graphs-in-2-pw-pages))))

+ 6 - 0
deps/cli/CHANGELOG.md

@@ -1,3 +1,9 @@
+## 0.4.2
+* Add `--help` to all commands as an alternative to `help [command]`
+* Add `--validate` option to `export-edn` command
+* Fix cli can't run in CI environments
+* Fix `import-edn` and `mcp-server` commands not building refs for new or edited nodes
+
 ## 0.4.1
 * Add `--open-schema` option to `validate` command
 * Fix `append` command fails

+ 1 - 1
deps/cli/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@logseq/cli",
-  "version": "0.4.1",
+  "version": "0.4.2",
   "description": "Logseq CLI",
   "bin": {
     "logseq": "cli.mjs"

+ 43 - 28
deps/cli/src/logseq/cli.cljs

@@ -24,7 +24,7 @@
              :desc "Print version"}})
 
 (declare table)
-(defn- help [_m]
+(defn- print-general-help [_m]
   (println (str "Usage: logseq [command] [options]\n\nOptions:\n"
                 (cli/format-opts {:spec default-spec})))
   (println (str "\nCommands:\n" (format-commands {:table table}))))
@@ -37,7 +37,7 @@
         (println (-> (fs/readFileSync package-json)
                      js/JSON.parse
                      (aget "version")))))
-    (help m)))
+    (print-general-help m)))
 
 (defn- print-command-help [command cmd-map]
   (println (str "Usage: logseq " command
@@ -50,24 +50,30 @@
                 (when (:description cmd-map)
                   (str "\n\nDescription:\n" (cli-text-util/wrap-text (:description cmd-map) 80))))))
 
-(defn- help-command [{{:keys [command]} :opts}]
+(defn- help-command [{{:keys [command help]} :opts}]
   (if-let [cmd-map (and command (some #(when (= command (first (:cmds %))) %) table))]
     (print-command-help command cmd-map)
-    (println "Command" (pr-str command) "does not exist")))
+    ;; handle help --help
+    (if-let [cmd-map (and help (some #(when (= "help" (first (:cmds %))) %) table))]
+      (print-command-help "help" cmd-map)
+      (println "Command" (pr-str command) "does not exist"))))
 
 (defn- lazy-load-fn
-  "Lazy load fn to speed up start time. After nbb requires ~30 namespaces, start time gets close to 1s"
+  "Lazy load fn to speed up start time. After nbb requires ~30 namespaces, start time gets close to 1s.
+   Also handles --help on all commands"
   [fn-sym]
   (fn [& args]
-    (-> (p/let [_ (require (symbol (namespace fn-sym)))]
-          (apply (resolve fn-sym) args))
-        (p/catch (fn [err]
-                   (if (= :sci/error (:type (ex-data err)))
-                     (nbb.error/print-error-report err)
-                     (js/console.error "Error:" err))
-                   (js/process.exit 1))))))
+    (if (get-in (first args) [:opts :help])
+      (help-command {:opts {:command (-> args first :dispatch first)}})
+      (-> (p/let [_ (require (symbol (namespace fn-sym)))]
+            (apply (resolve fn-sym) args))
+          (p/catch (fn [err]
+                     (if (= :sci/error (:type (ex-data err)))
+                       (nbb.error/print-error-report err)
+                       (js/console.error "Error:" err))
+                     (js/process.exit 1)))))))
 
-(def ^:private table
+(def ^:private table*
   [{:cmds ["list"] :desc "List local graphs"
     :fn (lazy-load-fn 'logseq.cli.commands.graph/list-graphs)}
    {:cmds ["show"] :desc "Show DB graph(s) info"
@@ -116,30 +122,39 @@
     :spec default-spec
     :fn default-command}])
 
+;; Spec shared with all commands
+(def ^:private shared-spec
+  {:help {:alias :h
+          :desc "Print help"}})
+
+(def ^:private table
+  (mapv (fn [m] (update m :spec #(merge % shared-spec))) table*))
+
 (defn- warn-if-db-version-not-installed
   []
   (when-not (fs/existsSync (cli-common-graph/get-db-graphs-dir))
     (println "[WARN] The database version's desktop app is not installed. Please install per https://github.com/logseq/logseq/#-database-version.")))
 
 (defn ^:api -main [& args]
-  (when-not (contains? #{nil "-h" "--help"} (first args))
-    (warn-if-db-version-not-installed))
+  (warn-if-db-version-not-installed)
   (try
     (cli/dispatch table
                   args
-                  {:error-fn (fn [{:keys [cause msg option] type' :type :as data}]
-                               (if (and (= :org.babashka/cli type')
-                                        (= :require cause))
-                                 (do
-                                   (println "Error: Command missing required"
-                                            (if (get-in data [:spec option]) "option" "argument")
-                                            (pr-str (name option)))
-                                   (when-let [cmd-m (some #(when (= {:spec (:spec %)
-                                                                     :require (:require %)}
-                                                                    (select-keys data [:spec :require])) %) table)]
-                                     (print-command-help (-> cmd-m :cmds first) cmd-m)))
-                                 (throw (ex-info msg data)))
-                               (js/process.exit 1))})
+                  {:error-fn (fn [{:keys [cause msg option opts] type' :type :as data}]
+                               ;; Options aren't required when printing help
+                               (when-not (:help opts)
+                                 (if (and (= :org.babashka/cli type')
+                                          (= :require cause))
+                                   (do
+                                     (println "Error: Command missing required"
+                                              (if (get-in data [:spec option]) "option" "argument")
+                                              (pr-str (name option)))
+                                     (when-let [cmd-m (some #(when (= {:spec (:spec %)
+                                                                       :require (:require %)}
+                                                                      (select-keys data [:spec :require])) %) table)]
+                                       (print-command-help (-> cmd-m :cmds first) cmd-m)))
+                                   (throw (ex-info msg data)))
+                                 (js/process.exit 1)))})
     (catch ^:sci/error js/Error e
       (nbb.error/print-error-report e)
       (js/process.exit 1))))

+ 5 - 5
deps/cli/src/logseq/cli/spec.cljs

@@ -17,10 +17,6 @@
                          :desc "Include timestamps in export"}
    :file {:alias :f
           :desc "File to save export"}
-   :validate {:alias :v
-              :desc "Validates export by importing into a temp graph and validating it"}
-   :catch-validation-errors? {:alias :c
-                              :desc "Catch validation errors for dev"}
    :exclude-namespaces {:alias :e
                         :coerce #{}
                         :desc "Namespaces to exclude from properties and classes"}
@@ -31,7 +27,11 @@
    :export-type {:alias :t
                  :coerce :keyword
                  :desc "Export type"
-                 :default :graph}})
+                 :default :graph}
+   :validate {:alias :v
+              :desc "(Dev) Validate export with a temp graph built on exported EDN"}
+   :catch-validation-errors? {:alias :c
+                              :desc "(Dev) Catch validation errors and still write invalid EDN"}})
 
 (def import-edn
   {:api-server-token {:alias :a

+ 15 - 16
deps/db/src/logseq/db/common/initial_data.cljs

@@ -86,10 +86,10 @@
       block)))
 
 (defn get-block-children-ids
-  "Returns children UUIDs, notice the result doesn't include property value children ids."
-  [db block-uuid & {:keys [include-collapsed-children?]
-                    :or {include-collapsed-children? true}}]
-  (when-let [eid (:db/id (d/entity db [:block/uuid block-uuid]))]
+  "Returns children ids, notice the result doesn't include property value children ids."
+  [db block-eid & {:keys [include-collapsed-children?]
+                   :or {include-collapsed-children? true}}]
+  (when-let [eid (:db/id (d/entity db block-eid))]
     (let [seen (volatile! #{})]
       (loop [eids-to-expand [eid]]
         (when (seq eids-to-expand)
@@ -100,31 +100,30 @@
                                       (not (:block/collapsed? e))
                                       (common-entity-util/page? e))
                               (:block/_parent e)))) eids-to-expand)
-                uuids-to-add (keep :block/uuid children)]
-            (vswap! seen (partial apply conj) uuids-to-add)
+                ids-to-add (keep :db/id children)]
+            (vswap! seen (partial apply conj) ids-to-add)
             (recur (keep :db/id children)))))
       @seen)))
 
 (defn get-block-children
   "Including nested children, notice the result doesn't include property values."
-  {:arglists '([db block-uuid & {:keys [include-collapsed-children?]}])}
-  [db block-uuid & {:as opts}]
-  (let [ids (get-block-children-ids db block-uuid opts)]
+  {:arglists '([db eid & {:keys [include-collapsed-children?]}])}
+  [db eid & {:as opts}]
+  (let [ids (get-block-children-ids db eid opts)]
     (when (seq ids)
-      (map (fn [id] (d/entity db [:block/uuid id])) ids))))
+      (map (fn [id] (d/entity db id)) ids))))
 
 (defn get-block-full-children-ids
   "Including nested, collapsed and property value children."
-  {:arglists '([db block-uuid])}
-  [db block-uuid]
+  {:arglists '([db block-eid])}
+  [db block-eid]
   (d/q
    '[:find [?c ...]
      :in $ ?id %
      :where
-     [?p :block/uuid ?id]
-     (parent ?p ?c)]
+     (parent ?id ?c)]
    db
-   block-uuid
+   block-eid
    (:parent rules/rules)))
 
 (defn- with-raw-title
@@ -218,7 +217,7 @@
       ;; (prn :debug :get-block (:db/id block) (:block/title block) :children? children?
       ;;      :include-collapsed-children? include-collapsed-children?)
       (let [children (when children?
-                       (let [children-blocks (get-block-children db (:block/uuid block) {:include-collapsed-children? include-collapsed-children?})
+                       (let [children-blocks (get-block-children db (:db/id block) {:include-collapsed-children? include-collapsed-children?})
                              large-page? (>= (count children-blocks) 100)
                              children (let [children' (if large-page?
                                                         (:block/_parent block)

+ 274 - 87
deps/db/src/logseq/db/common/reference.cljs

@@ -9,52 +9,38 @@
             [logseq.db.common.entity-plus :as entity-plus]
             [logseq.db.common.initial-data :as common-initial-data]
             [logseq.db.frontend.class :as db-class]
-            [logseq.db.frontend.rules :as rules]))
+            [logseq.db.frontend.entity-util :as entity-util]))
 
-(defn get-filters
-  [db page]
-  (let [db-based? (entity-plus/db-based-graph? db)]
-    (if db-based?
-      (let [included-pages (:logseq.property.linked-references/includes page)
-            excluded-pages (:logseq.property.linked-references/excludes page)]
-        (when (or (seq included-pages) (seq excluded-pages))
-          {:included included-pages
-           :excluded excluded-pages}))
-      (let [k :filters
-            properties (:block/properties page)
-            properties-str (or (get properties k) "{}")]
-        (try (let [result (reader/read-string properties-str)]
-               (when (seq result)
-                 (let [excluded-pages (->> (filter #(false? (second %)) result)
-                                           (keep first)
-                                           (keep #(ldb/get-page db %)))
-                       included-pages (->> (filter #(true? (second %)) result)
-                                           (keep first)
-                                           (keep #(ldb/get-page db %)))]
-                   {:included included-pages
-                    :excluded excluded-pages})))
-             (catch :default e
-               (log/error :syntax/filters e)))))))
+;; -----------------------------------------------------------------------------
+;; Helpers (fast datoms access)
+;; -----------------------------------------------------------------------------
 
-(defn- build-include-exclude-query
-  [includes excludes]
-  (concat
-   (for [include includes]
-     (list 'has-ref '?b include))
-   (for [exclude excludes]
-     (list 'not (list 'has-ref '?b exclude)))))
-
-(defn- filter-refs-query
-  [includes excludes class-ids]
-  (let [clauses (concat
-                 (build-include-exclude-query includes excludes)
-                 (for [class-id class-ids]
-                   (list 'not ['?b :block/tags class-id])))]
-    (into [:find '[?b ...]
-           :in '$ '% '[?id ...]
-           :where
-           (list 'has-ref '?b '?id)]
-          clauses)))
+(defn- has-datom? [db e a v]
+  (boolean (seq (d/datoms db :eavt e a v))))
+
+(defn- entid
+  "Normalize a datom value into an entity id.
+   In DataScript, ref values are often raw entids (numbers), not maps."
+  [v]
+  (cond
+    (nil? v) nil
+    (number? v) v
+    (and (map? v) (contains? v :db/id)) (:db/id v)
+    :else v))
+
+(defn- datom-v
+  "Return the first value for attr a on entity e, normalized to entid."
+  [db e a]
+  (some-> (first (d/datoms db :eavt e a))
+          :v
+          entid))
+
+(defn- datom-vs
+  "Return all values for attr a on entity e, normalized to entids, as a set."
+  [db e a]
+  (into #{}
+        (comp (map :v) (map entid))
+        (d/datoms db :eavt e a)))
 
 (defn- get-path-refs
   [db entity]
@@ -79,51 +65,251 @@
                      [(:block/title ref) size])))
            (sort-by second #(> %1 %2))))))
 
-(defn- get-block-parents-until-top-ref
-  [db id ref-id ref-block-ids *result]
-  (loop [eid ref-id
-         parents' []]
-    (when eid
-      (cond
-        (contains? @*result eid)
-        (swap! *result into parents')
-
-        (contains? ref-block-ids eid)
-        (when-not (common-initial-data/hidden-ref? db (d/entity db eid) id)
-          (swap! *result into (conj parents' eid)))
-        :else
-        (let [e (d/entity db eid)]
-          (recur (:db/id (:block/parent e)) (conj parents' eid)))))))
-
-(defn get-linked-references
-  [db id]
-  (let [entity (d/entity db id)
-        ids (set (cons id (ldb/get-block-alias db id)))
+(defn- child-ids
+  "Direct children via AVET on :block/parent."
+  [db parent-eid]
+  (into #{} (map :e) (d/datoms db :avet :block/parent parent-eid)))
+
+(defn- own-refs
+  "Refs contributed by this block itself:
+   - direct :block/refs
+   - implicit :block/page as a ref"
+  [db eid]
+  (let [direct (datom-vs db eid :block/refs)
+        page   (datom-v  db eid :block/page)]
+    (cond-> direct
+      page (conj page))))
+
+(defn- effective-refs-fn
+  "effective-refs(eid) = own-refs(eid) ∪ effective-refs(parent(eid))"
+  [db]
+  (let [memo (volatile! {})]
+    (letfn [(eff [eid]
+              (if (contains? @memo eid)
+                (get @memo eid)
+                (let [own (own-refs db eid)
+                      res (if-let [p (datom-v db eid :block/parent)]
+                            (into own (eff p))
+                            own)]
+                  (vswap! memo assoc eid res)
+                  res)))]
+      eff)))
+
+(defn- allowed-subtree-refs-fn
+  "Like subtree-refs-fn but PRUNES branches that are under an excluded ref.
+  This implements:
+    - parent should not be excluded just because a child has an excluded ref
+    - BUT refs (e.g. [[bar]]) that appear under an excluded block (e.g. [[baz]])
+      should NOT satisfy includes for ancestors.
+
+  Concretely: if (effective-refs node) contains any excludes, this node and its
+  descendants contribute NOTHING to the include reachability set."
+  [db eff excludes]
+  (let [memo (volatile! {})]
+    (letfn [(blocked? [eid]
+              (and (seq excludes)
+                   (some #(contains? (eff eid) %) excludes)))
+            (sub [eid]
+              (if (contains? @memo eid)
+                (get @memo eid)
+                (let [res (if (blocked? eid)
+                            #{}
+                            (reduce (fn [acc c] (into acc (sub c)))
+                                    (own-refs db eid)
+                                    (child-ids db eid)))]
+                  (vswap! memo assoc eid res)
+                  res)))]
+      sub)))
+
+(defn- matches-filters?
+  "Include semantics: AND (must all be present) against include-set.
+   Exclude semantics: NONE (must not contain any) against exclude-set."
+  [include-set exclude-set includes excludes]
+  (and
+   (or (empty? includes)
+       (every? #(contains? include-set %) includes))
+   (or (empty? excludes)
+       (not (some #(contains? exclude-set %) excludes)))))
+
+(defn- filter-matched-ref-blocks
+  [db top-ref-block-ids includes excludes {:keys [eff class-ok? can-satisfy-includes? allowed-subrefs]}]
+  (loop [stack   (vec top-ref-block-ids)
+         visited #{}
+         out     #{}]
+    (if (empty? stack)
+      out
+      (let [eid   (peek stack)
+            stack (pop stack)]
+        (if (contains? visited eid)
+          (recur stack visited out)
+          (let [visited (conj visited eid)
+                eff-refs (eff eid)]
+            (cond
+              ;; don't match this node, but still traverse
+              (not (class-ok? eid))
+              (recur (into stack (child-ids db eid)) visited out)
+
+                ;; prune: AND includes cannot be satisfied anywhere below
+              (not (can-satisfy-includes? eff-refs eid))
+              (recur stack visited out)
+
+              :else
+              (let [include-set (into eff-refs (allowed-subrefs eid))
+                    exclude-set eff-refs
+                    out' (if (matches-filters? include-set exclude-set includes excludes)
+                           (conj out eid)
+                           out)]
+                (recur (into stack (child-ids db eid)) visited out')))))))))
+
+(defn- matched-ref-block-ids-under-top
+  "Return the set of block ids (top or child) that match filters.
+
+  Semantics (matches your example):
+  - Includes (AND) may be satisfied by descendants, BUT only from descendants
+    that are NOT under an excluded ref.
+    Example:
+      - [[foo]]
+        - [[baz]]
+          - [[bar]]
+    With include=bar, exclude=baz => foo should NOT match because bar is under baz.
+
+  - Excludes are checked ONLY against effective-refs(node) (self + parents),
+    so a parent isn't excluded just because a child mentions an excluded page.
+
+  Pruning:
+  - If includes cannot be satisfied anywhere below (effective + allowed-subtree),
+    prune subtree."
+  [db top-ref-block-ids includes excludes class-ids]
+  (let [eff        (effective-refs-fn db)
+        ;; allowed subtree refs for includes (prunes excluded branches)
+        allowed-subrefs (allowed-subtree-refs-fn db eff (->> excludes (remove nil?) vec))
+        includes   (->> includes (remove nil?) vec)
+        excludes   (->> excludes (remove nil?) vec)
+        class-ids  (when (seq class-ids) (->> class-ids (remove nil?) vec))
+
+        can-satisfy-includes? (fn [eff-refs node]
+                                (or (empty? includes)
+                                    (let [possible (into eff-refs (allowed-subrefs node))]
+                                      (every? #(contains? possible %) includes))))
+
+        class-ok? (fn [eid]
+                    (or (empty? class-ids)
+                        (not (some #(has-datom? db eid :block/tags %) class-ids))))]
+
+    (filter-matched-ref-blocks db top-ref-block-ids includes excludes
+                               {:eff eff
+                                :class-ok? class-ok?
+                                :can-satisfy-includes? can-satisfy-includes?
+                                :allowed-subrefs allowed-subrefs})))
+
+;; -----------------------------------------------------------------------------
+;; Expand matched refs up to top refs
+;; -----------------------------------------------------------------------------
+
+(defn- expand-to-top-refs
+  "Given matched refs, add ancestors until reaching a top ref in `top-ref-ids`.
+   Uses cached parent lookups (datoms only)."
+  [db top-ref-ids matched-ref-ids]
+  (let [parent-cache (volatile! {}) ;; eid -> parent-eid|nil
+        result       (volatile! #{})
+        top?         (fn [eid] (contains? top-ref-ids eid))
+        parent-of    (fn [eid]
+                       (if (contains? @parent-cache eid)
+                         (get @parent-cache eid)
+                         (let [p (some-> (first (d/datoms db :eavt eid :block/parent))
+                                         :v
+                                         entid)]
+                           (vswap! parent-cache assoc eid p)
+                           p)))]
+    (doseq [start matched-ref-ids]
+      (loop [eid start]
+        (when eid
+          (cond
+            (contains? @result eid)
+            nil
+            (top? eid)
+            (vswap! result conj eid)
+            :else
+            (do
+              (vswap! result conj eid)
+              (recur (parent-of eid)))))))
+    @result))
+
+;; -----------------------------------------------------------------------------
+;; Public API
+;; -----------------------------------------------------------------------------
+
+(defn get-filters
+  [db page]
+  (let [db-based? (entity-plus/db-based-graph? db)]
+    (if db-based?
+      (let [included-pages (:logseq.property.linked-references/includes page)
+            excluded-pages (:logseq.property.linked-references/excludes page)]
+        (when (or (seq included-pages) (seq excluded-pages))
+          {:included included-pages
+           :excluded excluded-pages}))
+      (let [k :filters
+            properties (:block/properties page)
+            properties-str (or (get properties k) "{}")]
+        (try (let [result (reader/read-string properties-str)]
+               (when (seq result)
+                 (let [excluded-pages (->> (filter #(false? (second %)) result)
+                                           (keep first)
+                                           (keep #(ldb/get-page db %)))
+                       included-pages (->> (filter #(true? (second %)) result)
+                                           (keep first)
+                                           (keep #(ldb/get-page db %)))]
+                   {:included included-pages
+                    :excluded excluded-pages})))
+             (catch :default e
+               (log/error :syntax/filters e)))))))
+
+(defn get-linked-references [db id]
+  (let [entity       (d/entity db id)
+        ids          (set (cons id (ldb/get-block-alias db id)))
         page-filters (get-filters db entity)
-        excludes (map :db/id (:excluded page-filters))
-        includes (map :db/id (:included page-filters))
-        class-ids (when (ldb/class? entity)
-                    (let [class-children (db-class/get-structured-children db id)]
-                      (set (conj class-children id))))
-        full-ref-block-ids (->> (mapcat (fn [id] (map :db/id (:block/_refs (d/entity db id)))) ids)
-                                set)
-        matched-ref-block-ids (set (d/q (filter-refs-query includes excludes class-ids)
-                                        db
-                                        (rules/extract-rules rules/db-query-dsl-rules
-                                                             [:has-ref]
-                                                             {:deps rules/rules-dependencies})
-                                        ids))
-        matched-refs-with-children-ids (let [*result (atom #{})]
-                                         (doseq [ref-id matched-ref-block-ids]
-                                           (get-block-parents-until-top-ref db id ref-id full-ref-block-ids *result))
-                                         @*result)
-        ref-blocks (->> (set/intersection full-ref-block-ids matched-refs-with-children-ids)
-                        (map (fn [id] (d/entity db id))))
-        filter-exists? (or (seq excludes) (seq includes))
-        children-ids (set (remove full-ref-block-ids matched-refs-with-children-ids))]
+        excludes     (map :db/id (:excluded page-filters))
+        includes     (map :db/id (:included page-filters))
+        has-filters? (or (seq excludes) (seq includes))
+
+        class-ids    (when (ldb/class? entity)
+                       (let [class-children (db-class/get-structured-children db id)]
+                         (set (conj class-children id))))
+        ;; Collect all top ref blocks that directly reference the page (or any alias).
+        full-ref-block-ids
+        (->> ids
+             (mapcat (fn [pid] (:block/_refs (d/entity db pid))))
+             (remove (fn [ref]
+                       (or
+                        (when class-ids
+                          (some class-ids (map :db/id (:block/tags ref))))
+                        (entity-util/hidden? ref)
+                        (entity-util/hidden? (:block/page ref)))))
+             (map :db/id)
+             set)
+        ;; matched can be top or child ids
+        matched-ref-block-ids
+        (when has-filters?
+          (matched-ref-block-ids-under-top db full-ref-block-ids includes excludes class-ids))
+        ;; Expand matches up to top refs so we can show parent chains and matched children.
+        matched-refs-with-children-ids
+        (when has-filters?
+          (expand-to-top-refs db full-ref-block-ids matched-ref-block-ids))
+        final-ref-ids
+        (if has-filters?
+          (set/intersection full-ref-block-ids matched-refs-with-children-ids)
+          full-ref-block-ids)
+        ;; Materialize only at the end.
+        ref-blocks (map #(d/entity db %) final-ref-ids)
+        children-ids
+        (if has-filters?
+          (set (remove full-ref-block-ids matched-refs-with-children-ids))
+          (->> ref-blocks
+               (mapcat (fn [ref] (ldb/get-block-children-ids db (:db/id ref))))
+               set))]
     {:ref-blocks ref-blocks
      :ref-pages-count (get-ref-pages-count db id ref-blocks children-ids)
-     :ref-matched-children-ids (when filter-exists? children-ids)}))
+     :ref-matched-children-ids (when has-filters? children-ids)}))
 
 (defn get-unlinked-references
   [db id]
@@ -132,7 +318,8 @@
     (when-not (string/blank? title)
       (let [ids (->> (d/datoms db :avet :block/title)
                      (keep (fn [d]
-                             (when (and (not= id (:e d)) (string/includes? (string/lower-case (:v d)) title))
+                             (when (and (not= id (:e d))
+                                        (string/includes? (string/lower-case (:v d)) title))
                                (:e d)))))]
         (keep
          (fn [eid]

+ 5 - 1
deps/db/src/logseq/db/frontend/property.cljs

@@ -15,6 +15,10 @@
    :rtc/ignore-attr-when-init-download true
    :rtc/ignore-attr-when-syncing true})
 
+(def ^:private property-ignore-rtc-upload-sync
+  {:rtc/ignore-attr-when-init-upload true
+   :rtc/ignore-attr-when-syncing true})
+
 ;; Main property vars
 ;; ==================
 
@@ -528,7 +532,7 @@
                                               :public? false}
                                              :properties
                                              {:logseq.property/description "Metadata of asset in remote storage"}
-                                             :rtc property-ignore-rtc}
+                                             :rtc property-ignore-rtc-upload-sync}
      :logseq.property.asset/resize-metadata {:title "Asset resize metadata"
                                              :schema {:type :map
                                                       :hide? true

+ 19 - 8
deps/graph-parser/src/logseq/graph_parser/block.cljs

@@ -455,13 +455,14 @@
                result))))) col)))
 
 (defn- with-page-refs-and-tags
-  [{:keys [title body tags refs marker priority] :as block} db date-formatter]
+  [{:keys [title body tags refs marker priority] :as block} db date-formatter {:keys [structured-tags]
+                                                                               :or {structured-tags #{}}}]
   (let [db-based? (and (ldb/db-based-graph? db) (not @*export-to-db-graph?))
         refs (->> (concat tags refs (when-not db-based? [marker priority]))
                   (remove string/blank?)
                   (distinct))
         *refs (atom refs)
-        *structured-tags (atom #{})]
+        *structured-tags (atom (set structured-tags))]
     (walk/prewalk
      (fn [form]
        ;; skip custom queries
@@ -556,9 +557,9 @@
     (map (fn [page] (page-name->map page db true date-formatter)) page-refs)))
 
 (defn- with-page-block-refs
-  [block db date-formatter]
+  [block db date-formatter opts]
   (some-> block
-          (with-page-refs-and-tags db date-formatter)
+          (with-page-refs-and-tags db date-formatter opts)
           with-block-refs
           (update :refs (fn [col] (remove nil? col)))))
 
@@ -612,7 +613,7 @@
                                 :block/macros (extract-macros-from-ast body)
                                 :block.temp/ast-body body}
                          {:keys [tags refs]}
-                         (with-page-block-refs {:body body :refs property-refs} db date-formatter)]
+                         (with-page-block-refs {:body body :refs property-refs} db date-formatter {})]
                      (cond-> block
                        tags
                        (assoc :block/tags tags)
@@ -630,8 +631,17 @@
     properties))
 
 (defn- construct-block
-  [block properties timestamps body encoded-content format pos-meta {:keys [block-pattern db date-formatter remove-properties? db-graph-mode? export-to-db-graph?]}]
-  (let [id (get-custom-id-or-new-id properties)
+  [block properties* timestamps body encoded-content format pos-meta {:keys [block-pattern db date-formatter remove-properties? db-graph-mode? export-to-db-graph?]}]
+  (let [id (get-custom-id-or-new-id properties*)
+        block-tags (and export-to-db-graph? (get-in properties* [:properties :tags]))
+        ;; For export, remove tags from properties as they are being converted to classes
+        properties (if (seq block-tags)
+                     (-> properties*
+                         (update :properties #(dissoc % :tags))
+                         (update :properties-text-values #(dissoc % :tags))
+                         (update :properties-order (fn [v] (remove #(= :tags %) v)))
+                         (update :page-refs (fn [v] (remove #(= "tags" %) v))))
+                     properties*)
         ref-pages-in-properties (->> (:page-refs properties)
                                      (remove string/blank?))
         block (second block)
@@ -671,7 +681,8 @@
         db-based? (or db-graph-mode? export-to-db-graph?)
         block (-> block
                   (assoc :body body)
-                  (with-page-block-refs db date-formatter))
+                  (with-page-block-refs db date-formatter
+                    (cond-> {} (seq block-tags) (assoc :structured-tags block-tags))))
         block (if db-based? block
                   (-> block
                       (update :tags (fn [tags] (map #(assoc % :block/format format) tags)))

+ 6 - 2
deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs

@@ -210,7 +210,7 @@
 
       ;; Counts
       ;; Includes journals as property values e.g. :logseq.property/deadline
-      (is (= 30 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Journal]] @conn))))
+      (is (= 31 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Journal]] @conn))))
 
       (is (= 5 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Asset]] @conn))))
       (is (= 4 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Task]] @conn))))
@@ -231,7 +231,7 @@
                   #_(map #(select-keys % [:block/title :block/tags]))
                   count))
           "Correct number of pages with block content")
-      (is (= 12 (->> @conn
+      (is (= 14 (->> @conn
                      (d/q '[:find [?ident ...]
                             :where [?b :block/tags :logseq.class/Tag] [?b :db/ident ?ident] (not [?b :logseq.property/built-in?])])
                      count))
@@ -493,6 +493,10 @@
              (mapv :db/ident (:block/tags (db-test/find-block-by-content @conn #"with namespace tag"))))
           "Block tagged with namespace tag is only associated with leaf child tag")
 
+      (is (= #{:user.class/ai :user.class/block-tag :user.class/p1}
+             (set (map :db/ident (:block/tags (db-test/find-block-by-content @conn #"Block tags")))))
+          "Block with tags through tags property")
+
       (is (= []
              (->> (d/q '[:find (pull ?b [:block/title {:block/tags [:db/ident]}])
                          :where [?b :block/tags :logseq.class/Tag]]

+ 2 - 0
deps/graph-parser/test/resources/exporter-test-graph/journals/2025_12_17.md

@@ -0,0 +1,2 @@
+- Block tags #p1
+  tags:: ai, #block-tag

+ 2 - 2
deps/outliner/src/logseq/outliner/core.cljs

@@ -409,7 +409,7 @@
         (swap! *txs-state concat [[:db/retract (:db/id block) :block/parent]
                                   [:db/retract (:db/id block) :block/order]
                                   [:db/retract (:db/id block) :block/page]])
-        (let [ids (cons (:db/id this) (ldb/get-block-full-children-ids db block-id))
+        (let [ids (cons (:db/id this) (ldb/get-block-full-children-ids db (:db/id block)))
               txs (map (fn [id] [:db.fn/retractEntity id]) ids)
               page-tx (let [block (d/entity db [:block/uuid block-id])]
                         (when (:block/pre-block? block)
@@ -979,7 +979,7 @@
                        (not (ldb/page? block))
                        (assoc :block/page target-page))]
             children-page-tx (when (and not-same-page? (not (ldb/page? block)))
-                               (let [children-ids (ldb/get-block-full-children-ids db (:block/uuid block))]
+                               (let [children-ids (ldb/get-block-full-children-ids db (:db/id block))]
                                  (keep (fn [id]
                                          (let [child (d/entity db id)]
                                            (when-not (ldb/page? child)

+ 1 - 1
scripts/src/logseq/tasks/lang.clj

@@ -208,7 +208,7 @@
    :pl #{:port :home :host :plugin/marketplace :whiteboard/link}
    :pt-BR #{:plugins :right-side-bar/flashcards :settings-page/enable-flashcards :page/backlinks
             :host :settings-page/tab-editor :shortcut.category/plugins :whiteboard/link :settings-of-plugins :whiteboard
-            :whiteboards :on-boarding/quick-tour-journal-page-desc-2 :plugin/downloads
+            :whiteboards :on-boarding/quick-tour-journal-page-desc-2 :plugin/downloads :plugin/popular
             :right-side-bar/whiteboards :search-item/whiteboard :settings-page/enable-whiteboards :settings-page/plugin-system
             :shortcut.category/whiteboard :command.whiteboard/zoom-in :command.whiteboard/zoom-out}
    :pt-PT #{:plugins :settings-of-plugins :plugin/downloads :right-side-bar/flashcards

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

@@ -89,7 +89,7 @@
                     (and (ldb/built-in? page) (ldb/private-built-in-page? page))))
          (notification/show! "Cannot go to an internal page." :warning)
          (if-let [source (and (not ignore-alias?) (db/get-alias-source-page (state/get-current-repo) (:db/id page)))]
-           (redirect-to-page! (:block/uuid source) opts)
+           (redirect-to-page! (:block/uuid source) (assoc opts :ignore-alias? true))
            (do
            ;; Always skip onboarding when loading an existing whiteboard
              (when-not new-whiteboard? (state/set-onboarding-whiteboard! true))

+ 1 - 0
src/main/frontend/worker/rtc/full_upload_download_graph.cljs

@@ -425,6 +425,7 @@
   (let [{:keys [remote-t init-tx-data tx-data]}
         (remote-all-blocks->tx-data+t all-blocks graph-uuid)]
     (m/sp
+      (rtc-log-and-state/clean-cached-graph-local-and-remote-t graph-uuid)
       (rtc-log-and-state/update-local-t graph-uuid remote-t)
       (rtc-log-and-state/update-remote-t graph-uuid remote-t)
       (c.m/<?

+ 6 - 0
src/main/frontend/worker/rtc/log_and_state.cljs

@@ -81,6 +81,12 @@
   (->> (m/latest vector (create-local-t-flow graph-uuid) (create-remote-t-flow graph-uuid))
        (m/eduction (filter (fn [[local-t remote-t]] (>= remote-t local-t))))))
 
+(defn clean-cached-graph-local-and-remote-t
+  [graph-uuid]
+  (let [graph-uuid (ensure-uuid graph-uuid)]
+    (swap! *graph-uuid->local-t dissoc graph-uuid)
+    (swap! *graph-uuid->remote-t dissoc graph-uuid)))
+
 (defn update-local-t
   [graph-uuid local-t]
   (let [graph-uuid (ensure-uuid graph-uuid)

+ 1 - 1
src/resources/dicts/it.edn

@@ -701,7 +701,7 @@
  :command.whiteboard/clone-down "Clona sotto"
  :command.whiteboard/clone-left "Clona a sinistra"
  :command.whiteboard/clone-right "Clona a destra"
- :command.whiteboard/clone-up "Clone sopra"
+ :command.whiteboard/clone-up "Clona sopra"
  :linked-references/filter-directions "Click per includere e Shift-Click per escludere. Clicca di nuovo per rimuovere."
  :linked-references/filter-excludes "Esclude: "
  :linked-references/filter-heading "Filtro"

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

@@ -447,7 +447,7 @@
  :flashcards/modal-welcome-desc-1                 "Możesz dodać \"{1}\" do dowolnego bloku, żeby zmienić go na fiszkę lub wpisz \"/cloze\" żeby dodać luki do wypełnienia."
  :flashcards/modal-welcome-desc-2                 "Możesz "
  :flashcards/modal-welcome-desc-3                 "kliknij ten link"
- :flashcards/modal-welcome-desc-4                 " żeby sprawdzić dokumentację."
+ :flashcards/modal-welcome-desc-4                 ", żeby sprawdzić dokumentację."
  :flashcards/modal-welcome-title                  "Czas na nową fiszkę!"
  :graph/all-graphs                                "Wszystkie grafy"
  :graph/local-graphs                              "Grafy lokalne:"
@@ -507,13 +507,13 @@
  :on-boarding/quick-tour-favorites-desc-1         "Przypnij swoje ulubione strony używając `...` menu na dowolnej stronie."
  :on-boarding/quick-tour-favorites-desc-2         "Dodaliśmy kilka przykładowych szablonów, żeby ci pomóc. Możesz je usunąć jak zaczniesz pisać swoje własne notatki."
  :on-boarding/quick-tour-favorites-title          "⭐️ Ulubione"
- :on-boarding/quick-tour-help-desc                "Możesz zawsze tu kliknąć, żeby poszukac pomocy lub innych informacji o Logseq."
+ :on-boarding/quick-tour-help-desc                "Możesz zawsze tu kliknąć, żeby poszukać pomocy lub innych informacji o Logseq."
  :on-boarding/quick-tour-help-title               "❓ Pomoc"
- :on-boarding/quick-tour-journal-page-desc-1      "To jest dzisiejsza strona dziennika. Możesz tu wrzucać swoje myśli, to czego się nauczyłeś, czy pomysły. Nie skupiaj się na organizowaniu treści. Po prosu pisz i"
+ :on-boarding/quick-tour-journal-page-desc-1      "To jest dzisiejsza strona dziennika. Możesz tu wrzucać swoje myśli, to czego się nauczyłeś, czy pomysły. Nie skupiaj się na organizowaniu treści. Po prostu pisz i"
  :on-boarding/quick-tour-journal-page-desc-2      "[[linkuj]]"
  :on-boarding/quick-tour-journal-page-desc-3      "swoje myśli"
  :on-boarding/quick-tour-journal-page-title       "📆 Codzienna strona dziennika"
- :on-boarding/quick-tour-left-sidebar-desc        "Otwórz lewy panel, żeby sprawdzić ważne elementy menu Logseq explore important menu items in Logseq."
+ :on-boarding/quick-tour-left-sidebar-desc        "Otwórz lewy panel, żeby sprawdzić ważne elementy menu Logseq."
  :on-boarding/quick-tour-left-sidebar-title       "👀 Lewy panel"
  :on-boarding/quick-tour-steps                    "KROK "
  :on-boarding/tour-whiteboard-btn-back            "Wstecz"
@@ -547,10 +547,10 @@
  :plugin/marketplace                              "Marketplace"
  :plugin/open-logseq-dir                          "Otwórz"
  :plugin/open-preferences                         "Otwórz preferencje"
- :plugin/remote-error                             "Zdalny błąd: "
+ :plugin/remote-error                             "Zdalny błąd:"
  :plugin/search-plugin                            "Szukaj pluginów"
  :plugin/security-warning                         "Pluginy mają dostęp do twojego grafu, plików lokalnych i mogą nawiązywać połączenia sieciowe.
-       Mogą też powodować uszkodzenia danych lub ich utratę. Pracujemy nad odpowiednimi regułami dostepu do twoich grafów.
+       Mogą też powodować uszkodzenia danych lub ich utratę. Pracujemy nad odpowiednimi regułami dostępu do twoich grafów.
        W międzyczasie upewnij się, że wykonujesz regularne kopie zapasowe swoich grafów i instalujesz pluginy tylko kiedy możesz sprawdzić i
        zrozumieć ich kod źródłowy."
  :plugin/title                                    "Tytuł ({1})"
@@ -598,14 +598,14 @@
  :settings-page/git-desc-2                        "Dla profesjonalistów, Logseq wspiera także używanie "
  :settings-page/git-desc-3                        " do kontroli wersji. Używasz Git na własne ryzyko, ponieważ problemy z Git nie są wspierane przez zespół Logseq."
  :settings-page/git-tip                           "Jeśli synchronizacja w Logseq jest włączona, możesz zobaczyć historię edycji stron bezpośrednio. Ta sekcja jest dla znających się na technologii."
- :settings-page/login-prompt                      "Żeby zyskać dostęp do nowych funkcjonalności przed innymi, musisz mieś status Open Collective Sponsor lub Logseq Backer i musisz się zalogować."
+ :settings-page/login-prompt                      "Żeby zyskać dostęp do nowych funkcjonalności przed innymi, musisz mieć status Open Collective Sponsor lub Logseq Backer i musisz się zalogować."
  :settings-page/native-titlebar                   "Natywny pasek tytułu"
  :settings-page/native-titlebar-desc              "Włącza natywny pasek tytułu w Windowsie lub Linuksie"
- :settings-page/preferred-outdenting-tip          "Lewa strona pokazuje zmniejszenie wcięcia z domyślnymi ustawieniami, a prawa z włączonym logicznym zmniejszeniem wcięć "
+ :settings-page/preferred-outdenting-tip          "Lewa strona pokazuje zmniejszenie wcięcia z domyślnymi ustawieniami, a prawa z włączonym logicznym zmniejszeniem wcięć."
  :settings-page/preferred-outdenting-tip-more     "→ Dowiedz się więcej"
  :settings-page/preferred-pasting-file            "Preferuj wklejanie pliku"
  :settings-page/preferred-pasting-file-hint       "Kiedy włączone, wklejanie obrazka z Internetu ściągnie i wstawi obrazek. Kiedy wyłączone, wklei link do obrazka."
- :settings-page/revision                          "Rewizja: "
+ :settings-page/revision                          "Rewizja:"
  :settings-page/show-full-blocks                  "Pokaż wszystkie linie referencji bloku"
  :settings-page/sync                              "Synchronizacja"
  :settings-page/sync-desc-1                       "Kliknij"

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

@@ -40,6 +40,7 @@
  :on-boarding/importing-desc "Se elas estiverem em formato JSON, EDN ou Markdown, o Logseq pode trabalhar com elas."
  :on-boarding/importing-roam-desc "Importar uma exportação JSON do seu grafo Roam"
  :on-boarding/importing-lsq-desc "Importar uma exportação EDN ou JSON do seu grafo Logseq"
+ :on-boarding/importing-sqlite-desc "Importar uma exportação de DB SQLite do seu grafo Logseq para um novo grafo DB"
  :on-boarding/importing-opml-desc "Importar arquivos OPML"
  :on-boarding/main-title (fn [] ["Bem-vindo ao " [:strong "Logseq!"]])
  :on-boarding/main-desc "Primeiro, você precisa escolher uma pasta onde o Logseq armazenará seus pensamentos, ideias, notas."
@@ -107,6 +108,7 @@
  :right-side-bar/page-graph "Grafo da página"
  :right-side-bar/block-ref "Referências de bloco"
  :right-side-bar/graph-view "Visualização de grafo"
+ :right-side-bar/all-pages "Páginas"
  :right-side-bar/flashcards "Flashcards"
  :right-side-bar/show-journals "Mostrar diários"
  :right-side-bar/separator "Manipulador de redimensionamento da barra lateral direita"
@@ -123,12 +125,16 @@
  :right-side-bar/pane-more "Mais"
  :left-side-bar/switch "Troque para:"
  :left-side-bar/journals "Diários"
+ :left-side-bar/assets "Imagens"
+ :left-side-bar/tasks "Tarefas"
  :left-side-bar/nav-favorites "Favoritos"
  :left-side-bar/nav-recent-pages "Recentes"
  :page/something-went-wrong "Algo deu errado"
  :page/logseq-is-having-a-problem "Logseq está com um problema. Para tentar fazê-lo voltar a funcionar, siga as etapas seguras a seguir:"
  :page/step "Etapa {1}"
  :page/try "Tentar"
+ :page/delete-confirmation "Você tem certeza que quer deletar essa página?"
+ :page/db-delete-confirmation "Você tem certeza que quer deletar essa página?"
  :page/open-in-finder "Abrir no diretório"
  :page/open-with-default-app "Abrir com o aplicativo padrão"
  :page/make-public "Torná-la pública para publicação"
@@ -142,6 +148,8 @@
  :page/copy-page-url "Copiar URL da página"
  :page/illegal-page-name "Nome de página inválido!"
  :page/page-already-exists "Página “{1}” já existe!"
+ :page/convert-to-tag "Converter para Tag"
+ :page/convert-tag-to-page "Converter Tag para Página"
  :page/whiteboard-to-journal-error "Páginas whiteboard não podem ser renomeadas para títulos de diário!"
  :file/name "Nome do arquivo"
  :file/last-modified-at "Última modificação em"
@@ -159,6 +167,7 @@
  :asset/delete "Excluir imagem"
  :asset/copy "Copiar imagem"
  :asset/maximize "Maximizar imagem"
+ :asset/ref-block "Bloco de referência de imagem"
  :asset/confirm-delete "Tem certeza de que deseja excluir este {1}?"
  :asset/physical-delete "Remover também o arquivo (observe que ele não pode ser restaurado)"
  :color/gray "Cinza"
@@ -195,6 +204,10 @@
  :context-menu/input-template-name "Qual é o nome do modelo?"
  :context-menu/template-include-parent-block "Incluir o bloco pai no modelo?"
  :context-menu/template-exists-warning "Modelo já existe!"
+ :settings-page/ai "IA"
+ :settings-page/tab-ai "IA"
+ :settings-page/enable-mcp-server "Servidor MCP"
+ :settings-page/enable-mcp-server-desc "Habilitar Servidor MCP para permitir comunicação com aplicações locais de IA. O Servidor MCP depende do servidor HTTP API."
  :settings-page/git-tip "Se você tiver a Sincronização do Logseq ativada, você pode visualizar o histórico de edições de uma página diretamente. Esta seção é apenas para usuários técnicos."
  :settings-page/git-desc-1 "Para visualizar o histórico de edições de uma página, clique nos três pontos horizontais no canto superior direito e selecione \"Ver histórico da página\"."
  :settings-page/git-desc-2 "Para usuários avançados, o Logseq também oferece suporte para uso do "
@@ -213,6 +226,10 @@
  :settings-page/custom-theme "Tema personalizado"
  :settings-page/export-theme "Exportar tema"
  :settings-page/show-brackets "Mostrar colchetes"
+ :settings-page/wide-mode "Modo amplo"
+ :settings-page/editor-font "Fonte"
+ :settings-page/accent-color "Cor de destaque"
+ :settings-page/accent-color-alert "Escolher uma cor de destaque pode sobrescrever qualquer tema selecionado."
  :settings-page/spell-checker "Verificador ortografo"
  :settings-page/auto-updater "Atualização automática"
  :settings-page/disable-sentry "Enviar dados de uso e diagnósticos para o Logseq"
@@ -250,6 +267,8 @@
  :settings-page/tab-advanced "Avançado"
  :settings-page/tab-assets "Ativos"
  :settings-page/tab-features "Recursos"
+ :settings-page/tab-collaboration "Colaboração"
+ :settings-page/tab-encryption "Criptografia de ponta a ponta"
  :settings-page/enable-flashcards "Flashcards"
  :settings-page/network-proxy "Proxy de rede"
  :settings-page/alpha-features "Recursos alfa"
@@ -405,11 +424,13 @@
 
  :home "Início"
  :new-page "Nova página:"
+ :new-tag "Nova tag:"
  :new-graph "Adicionar novo grafo"
  :graph "Grafo"
  :graph/all-graphs "Todos os grafos"
  :graph/local-graphs "Grafos locais:"
  :graph/remote-graphs "Grafos remotos:"
+ :graph/shared-graphs "Compartilhado por outros:"
  :export "Exportar"
  :export-graph "Exportar grafo"
  :export-page "Exportar página"
@@ -417,6 +438,9 @@
  :export-opml "Exportar como OPML"
  :export-public-pages "Exportar páginas públicas"
  :export-json "Exportar como JSON"
+ :export-db-edn "Exportar arquivo EDN"
+ :export-sqlite-db "Exportar SQLite DB"
+ :export-zip "Exportar ambos SQLite DB e imagens"
  :export-roam-json "Exportar como Roam JSON"
  :export-edn "Exportar como EDN"
  :export-transparent-background "Fundo transparente"
@@ -424,13 +448,18 @@
  :export-copied-to-clipboard "Copiado para a área de transferência!"
  :export-save-to-file "Salvar em arquivo"
  :all-graphs "Todos os grafos"
+ :all-pages "Páginas"
+ :all-pages/table-title (fn [total] (str total (if (= total 1) " Página" " Páginas")))
+ :all-pages/failed-to-delete-pages "Essas páginas tiveram seu conteúdo deletado, mas não podem ser deletadas:: {1}. Veja o console do JavaScript para mais detalhes."
  :all-whiteboards "Todos os whiteboards"
  :all-files "Todos os arquivos"
  :all-journals "Todos os diários"
  :settings "Configurações"
  :plugins "Plugins"
  :themes "Temas"
+ :appearance "Aparência"
  :relaunch-confirm-to-work "Necessita reiniciar o aplicativo para funcionar. Deseja reiniciá-lo agora?"
+ :import-notes "Importar notas existentes"
  :import "Importar"
  :importing "Importando"
  :help-shortcut-title "Clique para verificar os atalhos e outras dicas"
@@ -478,6 +507,7 @@
  :plugin/delete-alert "Tem certeza de que deseja desinstalar o plugin [{1}]?"
  :plugin/open-settings "Abrir configurações"
  :plugin/open-package "Abrir pacote"
+ :plugin/report-security "Reportar plugin"
  :plugin/load-unpacked "Carregar plugin descompactado"
  :plugin/restart "Reiniciar aplicativo"
  :plugin/unpacked-tips "Selecione o diretório do plugin"
@@ -495,6 +525,7 @@
  :plugin/checking-for-updates "Verificando atualizações de plugins ..."
  :plugin/list-of-updates "Atualizações de plugins: "
  :plugin/auto-check-for-updates "Verificação automática de atualizações"
+ :plugin/load-from-web-url "Carregar plugin de URL web"
  :plugin.install-from-file/menu-title "Instalar a partir de plugins.edn"
  :plugin.install-from-file/title "Instalar plugins a partir de plugins.edn"
  :plugin.install-from-file/notice "Os seguintes plugins substituirão os seus plugins:"
@@ -561,13 +592,21 @@
  :header/go-back                     "Voltar"
  :header/go-forward                  "Avançar"
 
+ :views.table/default-title (fn [total] (str total (if (<= total 1) " Nodo" " Nodos")))
+ :views.table/live-query-title (fn [total] (str "Consulta em tempo real (" total ")"))
+
  :command.auto-complete/complete "Auto-completar: Escolher item selecionado"
  :command.auto-complete/next "Auto-completar: Selecionar próximo item"
  :command.auto-complete/prev "Auto-completar: Selecionar item anterior"
  :command.auto-complete/shift-complete "Auto-completar: Abrir item selecionado na barra lateral"
+ :command.auto-complete/meta-complete "Auto-completar: Cmd + Enter para escolher o item selecionado"
  :command.cards/forgotten "Cartões: esquecidos"
  :command.cards/next-card "Cartões: próximo cartão"
  :command.cards/recall "Cartões: lembrar em algum tempo"
+ :command.cards/again "Cartões: novamente"
+ :command.cards/easy "Cartões: fácil"
+ :command.cards/good "Cartões: bom"
+ :command.cards/hard "Cartões: difícil"
  :command.cards/remembered "Cartões: lembrados"
  :command.cards/toggle-answers "Cartões: mostrar/ocultar respostas/ocultados"
  :command.command/run "Executar comando git"
@@ -576,6 +615,11 @@
  :command.dev/show-block-ast "(Dev) Mostrar AST do bloco"
  :command.dev/show-block-data "(Dev) Mostrar dados do bloco"
  :command.dev/show-page-ast "(Dev) Mostrar AST da página"
+ :command.dev/replace-graph-with-db-file "(Dev) Substituir grafo pelo seu arquivo db.sqlite"
+ :command.dev/validate-db "(Dev) Valide o atual grafo"
+ :command.dev/gc-graph "(Dev) Garbage collect no grafo (remover dados não utilizados no SQLite)"
+ :command.dev/rtc-start "(Dev) RTC Começar"
+ :command.dev/rtc-stop "(Dev) RTC Parar"
  :command.dev/show-page-data "(Dev) Mostrar dados da página"
  :command.editor/backspace "Backspace / Apagar"
  :command.editor/backward-kill-word "Excluir uma palavra com backspace"
@@ -584,6 +628,7 @@
  :command.editor/bold "Negrito"
  :command.editor/clear-block "Excluir conteúdo inteiro do bloco"
  :command.editor/collapse-block-children "Contrair"
+ :command.editor/toggle-block-children "Alternar expandir/recolher"
  :command.editor/copy "Copiar (copia seleção ou referência de bloco)"
  :command.editor/copy-current-file "Copiar arquivo atual"
  :command.editor/copy-embed "Copiar incorporação de bloco apontando para o bloco atual"
@@ -608,18 +653,21 @@
  :command.editor/kill-line-after "Excluir linha após a posição do cursor"
  :command.editor/kill-line-before "Excluir linha antes da posição do cursor"
  :command.editor/left "Mover cursor para a esquerda / Abrir bloco selecionado no início"
- :command.editor/move-block-down "Mover bloco para baixo"
  :command.editor/move-block-up "Mover bloco para cima"
+ :command.editor/move-block-down "Mover bloco para baixo"
+ :command.editor/move-blocks "Mover blocos"
  :command.editor/new-block "Criar novo bloco"
  :command.editor/new-line "Nova linha no bloco atual"
  :command.editor/new-whiteboard "Novo whiteboard"
  :command.editor/open-edit "Editar bloco selecionado"
+ :command.editor/open-selected-blocks-in-sidebar "Abra o(s) bloco(s) selecionado(s) na barra-lateral"
  :command.editor/open-file-in-default-app "Abrir arquivo no aplicativo padrão"
  :command.editor/open-file-in-directory "Abrir arquivo no diretório pai"
  :command.editor/open-link-in-sidebar "Abrir link na barra lateral"
  :command.editor/outdent "Diminuir recuo do bloco"
  :command.editor/paste-text-in-one-block-at-point "Colar texto em um bloco no lugar"
  :command.editor/redo "Refazer"
+ :command.editor/quick-add "Adicionar rapidamente"
  :command.editor/replace-block-reference-at-point "Substituir referência de bloco pelo seu conteúdo no lugar"
  :command.editor/right "Mover cursor para a direita / Abrir bloco selecionado no final"
  :command.editor/select-all-blocks "Selecionar todos os blocos"
@@ -630,12 +678,23 @@
  :command.editor/select-up "Selecionar conteúdo acima"
  :command.editor/strike-through "Riscar"
  :command.editor/toggle-number-list "Alternar lista numerada"
+ :command.editor/add-property "Adicionar propriedade"
+ :command.editor/set-tags "Definir tags para o(s) bloco(s) selecionado(s)"
+ :command.editor/add-property-deadline "Adicionar prazo da tarefa ao bloco selecionado"
+ :command.editor/add-property-status "Adicionar estado da tarefa ao bloco selecionado"
+ :command.editor/add-property-priority "Adicionar prioridade da tarefa ao bloco selecionado"
+ :command.editor/add-property-icon "Adicionar ícone"
+ :command.editor/jump "Pular para uma chave ou valor de propriedade"
  :command.editor/toggle-open-blocks "Alternar blocos abertos (contrair ou expandir todos os blocos)"
  :command.editor/undo "Desfazer"
  :command.editor/up "Mover cursor para cima / Selecionar para cima"
  :command.editor/zoom-in "Dar zoom no bloco em edição / Avançar caso contrário"
  :command.editor/zoom-out "Dar zoom out no bloco em edição / Retroceder caso contrário"
  :command.git/commit "Criar commit git com mensagem"
+ :command.misc/export-block-data "Exportar dados EDN do bloco"
+ :command.misc/export-page-data "Exportar dados EDN da página"
+ :command.misc/export-graph-ontology-data "Exportar tags e propriedades EDN do grafo"
+ :command.misc/import-edn-data "Importar dados EDN"
  :command.go/all-graphs "Ir para todos os grafos"
  :command.go/all-pages "Ir para todas as páginas"
  :command.go/backward "Retroceder"
@@ -651,10 +710,13 @@
  :command.go/next-journal "Ir para o próximo diário"
  :command.go/prev-journal "Ir para o diário anterior"
  :command.go/search "Pesquisar páginas e blocos"
+ :command.go/search-themes "Pesquisar temas"
  :command.go/search-in-page "Pesquisar blocos na página"
  :command.go/tomorrow "Ir para amanhã"
  :command.go/whiteboards "Ir para whiteboards"
  :command.graph/add "Adicionar um grafo"
+ :command.graph/db-add "Adicionar um grafo DB"
+ :command.graph/db-save "Salve o atual db para o disco (~/logseq/graphs/seu-atual-grafo)"
  :command.graph/export-as-html "Exportar páginas de grafos públicos como HTML"
  :command.graph/open "Selecionar grafo para abrir"
  :command.graph/re-index "Reindexar grafo atual"
@@ -671,9 +733,11 @@
  :command.ui/clear-all-notifications "Limpar todas as notificações"
  :command.ui/goto-plugins "Ir para o painel de plugins"
  :command.ui/install-plugins-from-file "Instalar plugins do arquivo plugins.edn"
+ :command.ui/install-plugin-from-github "Instalar plugin a partir de release do Github"
  :command.ui/select-theme-color "Selecionar cores de tema disponíveis"
  :command.ui/toggle-brackets "Alternar se deve exibir colchetes"
  :command.ui/toggle-contents "Alternar conteúdo na barra lateral"
+ :command.ui/customize-appearance "Personalizar aparência"
  :command.ui/toggle-document-mode "Alternar modo de documento"
  :command.ui/toggle-help "Alternar ajuda"
  :command.ui/toggle-left-sidebar "Alternar barra lateral esquerda"
@@ -681,6 +745,8 @@
  :command.ui/toggle-settings "Alternar configurações"
  :command.ui/toggle-theme "Alternar entre tema escuro/claro"
  :command.ui/toggle-wide-mode "Alternar modo amplo"
+ :command.ui/highlight-recent-blocks "Alternar destaque de blocos recentes"
+ :command.editor/toggle-display-hidden-properties "Alternar exibição de propriedades ocultas"
  :command.whiteboard/bring-forward "Mover para frente"
  :command.whiteboard/bring-to-front "Mover para a frente"
  :command.whiteboard/clone-down "Clonar para baixo"
@@ -720,6 +786,7 @@
  :whiteboards "Whiteboards"
  :on-boarding/quick-tour-journal-page-desc-2 "[[link]]"
  :plugin/downloads "Downloads"
+ :plugin/popular "Popular"
  :right-side-bar/whiteboards "Whiteboards"
  :search-item/whiteboard "Whiteboard"
  :settings-page/enable-whiteboards "Whiteboards"
@@ -731,5 +798,5 @@
 
  ;; Os comandos estão aninhados por enquanto para permanecerem sincronizados com o sistema de atalhos.
  ;; Outras linguagens não devem aninhar chaves em :commands
-
+ 
  }

+ 1 - 1
src/test/frontend/db/reference_test.cljs

@@ -67,7 +67,7 @@
   (d/transact! conn [[:db/retract foo-id :logseq.property.linked-references/includes]
                      [:db/retract foo-id :logseq.property.linked-references/excludes]]))
 
-(deftest ^:large-vars/cleanup-todo get-linked-references
+(deftest ^:large-vars/cleanup-todo ^:focus get-linked-references
   (let [conn (create-conn!)
         foo-id (:db/id (ldb/get-page @conn "foo"))
         _ (retract-filters! conn foo-id)