diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index d395ae4..e2d2ef3 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1538,6 +1538,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-range" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" + [[package]] name = "httparse" version = "1.10.1" @@ -3746,6 +3752,7 @@ dependencies = [ "gtk", "heck 0.5.0", "http", + "http-range", "jni 0.21.1", "libc", "log", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 3b53021..03756de 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -18,7 +18,7 @@ crate-type = ["staticlib", "cdylib", "rlib"] tauri-build = { version = "2", features = [] } [dependencies] -tauri = { version = "2", features = ["macos-private-api"] } +tauri = { version = "2", features = ["macos-private-api", "protocol-asset"] } tauri-plugin-single-instance = "2" tauri-plugin-opener = "2" tauri-plugin-fs = { version = "2", features = ["watch"] } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 649f6bf..a987e56 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -23,7 +23,11 @@ } ], "security": { - "csp": null + "csp": null, + "assetProtocol": { + "enable": true, + "scope": ["**"] + } } }, "plugins": { diff --git a/src/components/overlays/file-preview-overlay.tsx b/src/components/overlays/file-preview-overlay.tsx index 305600c..3b95548 100644 --- a/src/components/overlays/file-preview-overlay.tsx +++ b/src/components/overlays/file-preview-overlay.tsx @@ -1,8 +1,9 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { convertFileSrc } from "@tauri-apps/api/core"; import { readFile, readTextFile, stat } from "@tauri-apps/plugin-fs"; import { openPath } from "@tauri-apps/plugin-opener"; import { useI18n } from "@/lib"; -import { basename, isSupportedTextPath, previewKindForPath, previewMimeForPath, validatePlainTextFile } from "@/lib"; +import { basename, dirname, htmlDocWithBase, isSupportedTextPath, previewKindForPath, previewMimeForPath, validatePlainTextFile } from "@/lib"; const CLOSE_ICON_SVG = ``; const EYE_ICON_SVG = ``; @@ -54,6 +55,13 @@ export function FilePreviewOverlay({ path, onClose, onOpenAsText }: Props) { const [size, setSize] = useState(null); const [errMsg, setErrMsg] = useState(""); + // html previews get a pointing at the file's directory (via the asset + // protocol) so relative images/css/scripts resolve. + const htmlDoc = useMemo( + () => (path && kind === "html" ? htmlDocWithBase(text, convertFileSrc(dirname(path))) : ""), + [path, kind, text], + ); + const urlRef = useRef(null); const revokeUrl = useCallback(() => { @@ -102,7 +110,7 @@ export function FilePreviewOverlay({ path, onClose, onOpenAsText }: Props) { return; } - if (kind === "text") { + if (kind === "text" || kind === "html") { const check = await validatePlainTextFile(path); if (cancelled) return; if (!check.ok) { @@ -151,7 +159,7 @@ export function FilePreviewOverlay({ path, onClose, onOpenAsText }: Props) { if (!path) return null; const title = basename(path); - const showOpenAsText = kind === "text" && isSupportedTextPath(path) === false; + const showOpenAsText = (kind === "text" || kind === "html") && isSupportedTextPath(path) === false; return (
) : kind === "pdf" && url ? (