Fix Windows file URL opening for block links (#12646)

* fix: preserve Windows file URL handling in path functions

* fix: improve file path handling for Windows file URLs

* fix: correct regex for Windows file URL handling

* improve file path compatibility
This commit is contained in:
megayu
2026-05-14 19:13:07 +08:00
committed by GitHub
parent 2169a86f03
commit e286370c36
3 changed files with 53 additions and 10 deletions

View File

@@ -130,6 +130,12 @@
[])
(join-fn))))
(defn- preserve-file-url-win-drive
[scheme encoded-path]
(cond-> encoded-path
(= scheme "file:")
(string/replace #"^/([a-zA-Z])%3[Aa](?=/|$)" "/$1:")))
(defn url-join
"Segments are not URL-encoded"
[base-url & segments]
@@ -138,7 +144,8 @@
path (.-pathname url)
domain (or (not-empty (.-host url))
(if (string/starts-with? path "/") "" "/"))
encoded-new-path (apply uri-path-join-internal path segments)]
encoded-new-path (->> (apply uri-path-join-internal path segments)
(preserve-file-url-win-drive scheme))]
(str scheme "//" domain encoded-new-path)))
(defn path-join
@@ -180,7 +187,8 @@
scheme (.-protocol url)
domain (or (not-empty (.-host url)) "/")
path (.-pathname url)
encoded-new-path (uri-path-join-internal path)]
encoded-new-path (->> (uri-path-join-internal path)
(preserve-file-url-win-drive scheme))]
(str scheme "//" domain encoded-new-path)))
(defn path-normalize
@@ -219,6 +227,13 @@
(str "//" host path)))
original-url))
(defn file-url-or-path->path
"Converts a file URL to a local filesystem path."
[s]
(if (is-file-url? s)
(url-to-path s)
s))
(defn trim-dir-prefix
"Trim dir prefix from path"
[base-path sub-path]

View File

@@ -21,15 +21,28 @@
(is (= "/foo/bar/baz/asdf" (path/path-join "/foo/bar//baz/asdf/quux/..")))
(is (= "assets:///foo.bar/baz" (path/path-join "assets:///foo.bar" "baz")))
(is (= "assets:///foo.bar/baz" (path/path-join "assets:///foo.bar/" "baz")))
(is (= "file:///D:/a.txt" (path/path-join "file://" "D:/a.txt")))
(is (= "file:///c:/x" (path/path-join "file://" "c:/x")))
(is (= "//NAS/MyGraph/logseq/config.edn" (path/path-join "//NAS/MyGraph" "logseq/config.edn")))))
(deftest prepend-protocol
(testing "prepend-protocol"
(is (= "file:///home/logseq/graph" (path/prepend-protocol "file:" "/home/logseq/graph")))
(is (= "file:///C%3A/Graph/pages" (path/prepend-protocol "file:" "C:/Graph/pages")))
(is (= "file:///C:/Graph/pages" (path/prepend-protocol "file:" "C:/Graph/pages")))
(is (= "file://NAS/MyGraph" (path/prepend-protocol "file:" "//NAS/MyGraph"))
"Windows UNC URL")))
(deftest file-url-or-path->path
(testing "file URL paths"
(is (= "D:/a.txt" (path/file-url-or-path->path "file:///D:/a.txt")))
(is (= "c:/x" (path/file-url-or-path->path "file:///c:/x")))
(is (= "//nas/share/x" (path/file-url-or-path->path "file://NAS/share/x")))
(is (= "D:/a.txt" (path/file-url-or-path->path "file:///D%3A/a.txt")))
(is (= "/home/admin/a.txt" (path/file-url-or-path->path "file:///home/admin/a.txt")))
(is (= "/D:/a.txt" (path/file-url-or-path->path "/D:/a.txt")))
(is (= "/D:\\a.txt" (path/file-url-or-path->path "/D:\\a.txt")))
(is (= "/home/admin/a.txt" (path/file-url-or-path->path "/home/admin/a.txt")))))
(deftest path-absolute
(testing "absolute"
(is (true? (path/absolute? "D:\\sources\\sources.md")))

View File

@@ -1309,6 +1309,19 @@
(join (config/get-repo-dir (state/get-current-repo))
(config/get-local-asset-absolute-path path)))))
(defn- file-link-path->open-path
[file-path]
(let [file-path (path/file-url-or-path->path file-path)
file-path (if (and util/win32?
(string? file-path)
(re-find #"^/[A-Za-z]:(?:[/\\]|$)" file-path))
(subs file-path 1)
file-path)]
(if (or (path/absolute? file-path)
(path/protocol-url? file-path))
file-path
(relative-assets-path->absolute-path file-path))))
(rum/defc audio-link
[config url href _label metadata full_text]
(if (common-config/local-relative-asset? href)
@@ -1391,13 +1404,14 @@
(util/electron?)
(let [path (cond
(string/starts-with? s "file://")
(string/replace s "file://" "")
s
(string/starts-with? s "/")
s
:else
(relative-assets-path->absolute-path s))]
(relative-assets-path->absolute-path s))
path (file-link-path->open-path path)]
(->elem
:a
(cond->
@@ -1432,8 +1446,8 @@
:else
(let [href (string-of-url url)
[protocol path] (or (and (= "Complex" (first url)) [(:protocol (second url)) (:link (second url))])
(and (= "File" (first url)) ["file" (second url)]))
[protocol _path] (or (and (= "Complex" (first url)) [(:protocol (second url)) (:link (second url))])
(and (= "File" (first url)) ["file" (second url)]))
config (cond-> config
(not (string/blank? protocol))
(assoc :link-js-url (try (js/URL. href)
@@ -1442,8 +1456,9 @@
(= protocol "file")
(if (show-link? href full_text)
(media-link config url href label metadata full_text)
(let [href* (if (util/electron?)
(relative-assets-path->absolute-path href)
(let [file-path (file-link-path->open-path href)
href* (if (util/electron?)
file-path
href)]
[:div.flex.flex-row.items-center
(ui/icon "file" {:class "opacity-50"})
@@ -1452,7 +1467,7 @@
(cond-> (if (util/electron?)
{:on-click (fn [e]
(util/stop e)
(js/window.apis.openPath path))
(js/window.apis.openPath file-path))
:data-href href*}
{:href (path/path-join "file://" href*)
:data-href href*