refactor: preserve URL search params during navigation

Introduce `useNavigateWithParams` to ensure that existing URL search
parameters are maintained when navigating between pages. This is
applied to the Article component, FileTree, and journal links to
prevent losing state (like session or player info) during client-side
navigation.

Also intercepts markdown anchor links in the Article component to use
client-side navigation.
This commit is contained in:
2026-07-07 23:26:44 +08:00
parent 48776424a7
commit 779722fe85
4 changed files with 54 additions and 9 deletions
+26 -3
View File
@@ -15,7 +15,7 @@ import { extractSection } from "../data-loader";
import mermaid from "mermaid";
import { getIndexedData } from "../data-loader/file-index";
import { resolvePath } from "./utils/path";
import { useLocation } from "@solidjs/router";
import { useNavigateWithParams } from "./useNavigateWithParams";
export interface ArticleProps {
src: string;
@@ -76,7 +76,7 @@ function scrollToHash(hash: string) {
* 用于将特定 src 位置的 md 文件显示为 markdown 文章
*/
export const Article: Component<ArticleProps & ParentProps> = (props) => {
const location = useLocation();
const navigate = useNavigateWithParams();
const [content, { refetch }] = createResource(
() => ({ src: props.src, section: props.section }),
fetchArticleContent,
@@ -97,10 +97,33 @@ export const Article: Component<ArticleProps & ParentProps> = (props) => {
void mermaid.run();
// 内容渲染后检查 hash 并滚动
scrollToHash(location.hash);
scrollToHash(window.location.hash);
}
});
// Intercept markdown <a> links to use client-side navigation with
// preserved URL search params.
createEffect(() => {
const dom = contentDom();
if (!dom) return;
const onClick = (e: MouseEvent) => {
const anchor = (e.target as HTMLElement).closest("a[href]");
if (!anchor) return;
const href = anchor.getAttribute("href");
if (!href) return;
// Only intercept same-origin navigation links (not external, not anchors)
if (href.startsWith("http") || href.startsWith("//")) return;
if (href.startsWith("#")) return;
e.preventDefault();
navigate(href);
};
dom.addEventListener("click", onClick);
onCleanup(() => dom.removeEventListener("click", onClick));
});
onCleanup(() => {
// 清理时清空内容,触发内部组件的销毁
});