diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index 3b81af0ea4..5a8472b92c 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -495,10 +495,14 @@ (rum/local nil ::src) [state config title href metadata full_text] (let [src (::src state) - ^js js-url (:link-js-url config) href (cond-> href - (nil? js-url) - (config/get-local-asset-absolute-path))] + (common-config/local-relative-asset? href) + (config/get-local-asset-absolute-path)) + ^js js-url (or (:link-js-url config) + (when (path/protocol-url? href) + (try + (js/URL. href) + (catch :default _ nil))))] (when (nil? @src) (-> (assets-handler//... - (path/path-join repo-dir (string/replace ext-url #"^[./]+" "")) - (path/path-join repo-dir (str "assets/" (:block/uuid asset-block) "." (name ext))))] - (js/window.apis.openPath file-fpath)))} + ext-url (:logseq.property.asset/external-url asset-block) + remote-ext-url? (and (not (string/blank? ext-url)) + (path/protocol-url? ext-url) + (not (common-config/local-protocol-asset? ext-url))) + local-ext-url? (and (not (string/blank? ext-url)) + (common-config/local-relative-asset? ext-url)) + file-fpath (if local-ext-url? + ;; Plugin-sourced asset stored under assets/storages//... + (path/path-join repo-dir (string/replace ext-url #"^[./]+" "")) + (path/path-join repo-dir (str "assets/" (:block/uuid asset-block) "." (name ext))))] + (if remote-ext-url? + (js/window.apis.openExternal ext-url) + (js/window.apis.openPath file-fpath))))} file-name]) :else diff --git a/src/main/frontend/handler/editor.cljs b/src/main/frontend/handler/editor.cljs index d8ed2a0077..47658a3905 100644 --- a/src/main/frontend/handler/editor.cljs +++ b/src/main/frontend/handler/editor.cljs @@ -1094,6 +1094,17 @@ (let [value (gobj/get input "value")] (extract-nearest-link-from-text value pos)))))))) +(defn- (follow-page-link-result {:page-title "Project" + :existing-page? true}) + (p/then + (fn [{:keys [events redirects]}] + (is (empty? events)) + (is (= [["Project"]] redirects)) + (done)))))) + +(deftest follow-link-under-cursor-creates-missing-page-test + (async done + (-> (follow-page-link-result {:page-title "May 15th, 2026" + :existing-page? false}) + (p/then + (fn [{:keys [events redirects]}] + (is (= [[:page/create "May 15th, 2026"]] events)) + (is (empty? redirects)) + (done)))))) + +(deftest follow-link-under-cursor-uses-worker-page-before-creating-test + (async done + (-> (follow-page-link-result {:page-title "May 15th, 2026" + :existing-page? false + :worker-page? true}) + (p/then + (fn [{:keys [events redirects]}] + (is (empty? events)) + (is (= [["May 15th, 2026"]] redirects)) + (done)))))) + (defn- keyup-handler "Spied version of editor/keyup-handler" [{:keys [value cursor-pos action commands] diff --git a/src/test/frontend/worker/search_test.cljs b/src/test/frontend/worker/search_test.cljs index 406c7d7733..e1b94700b5 100644 --- a/src/test/frontend/worker/search_test.cljs +++ b/src/test/frontend/worker/search_test.cljs @@ -209,6 +209,23 @@ (is (some? result)) (is (empty? result))))) +(deftest search-blocks-escapes-quotes-for-fts + (testing "user quote characters are escaped before SQLite FTS receives them" + (let [fts-binds (atom []) + db #js {:exec (fn [opts] + (let [sql (aget opts "sql") + bind (js->clj (aget opts "bind"))] + (when (string/includes? sql "title match ?") + (swap! fts-binds conj (first bind))) + #js []))}] + (with-redefs [search/combine-results (fn [_db results] results) + search/search-result->block-result + (fn [_conn _q _code-class _option result] + result)] + (is (empty? (search/search-blocks (atom :large-db) db "\"" {:limit 10}))) + (is (empty? (search/search-blocks (atom :large-db) db "foo \"bar" {:limit 10}))) + (is (= ["\"\"\"\"*" "\"foo \"\"bar\"*"] @fts-binds)))))) + (deftest search-blocks-large-graph-benchmark-regression (testing "cmd-k and autocomplete queries must not scan the full Datascript graph while typing" (let [calls (atom [])