+ {/* Header */}
+
- {/* fill the rest of the space */}
-
-
-
-
-
-
+
+ {/* 移动端抽屉式侧边栏 (overlay) */}
+
setIsSidebarOpen(false)}
+ fileTree={fileTree()}
+ pathHeadings={pathHeadings()}
+ onDataSourceOpen={() => setIsDataSourceOpen(true)}
+ />
+
+ {/* Body: sidebar + main + journal */}
+
+ {/* 桌面端固定侧边栏 (flex child) */}
+
setIsDataSourceOpen(true)}
+ />
+
+ {/* Main content — the scrollable region */}
+ mainRef}>
+
+
+
+
+
+ {/* Journal panel wrapper — on desktop: flex child with width transition;
+ on mobile: passes through to JournalPanel's own overlay */}
+
+ setIsJournalOpen(false)}
+ />
+
+
setIsDocOpen(false)} />
setIsDataSourceOpen(false)}
onSourceChanged={handleSourceChanged}
/>
- setIsJournalOpen(false)}
- />
);
};
diff --git a/src/components/FileTree.tsx b/src/components/FileTree.tsx
index d8a083f..df0e96f 100644
--- a/src/components/FileTree.tsx
+++ b/src/components/FileTree.tsx
@@ -1,6 +1,7 @@
import { Component, createMemo, createSignal, Show } from "solid-js";
import { type FileNode, type TocNode } from "../data-loader";
import { useNavigateWithParams } from "./useNavigateWithParams";
+import { useScrollContainer } from "../App";
/**
* 检查当前文件路径是否在文件夹内
@@ -98,6 +99,8 @@ export const HeadingNode: Component<{
setIsExpanded(!isExpanded());
}
};
+ const scrollContainer = useScrollContainer();
+
const handleClick = (e: MouseEvent) => {
e.preventDefault();
navigate(href);
@@ -105,10 +108,18 @@ export const HeadingNode: Component<{
requestAnimationFrame(() => {
const element = document.getElementById(anchor);
if (element) {
+ const scroller = scrollContainer();
const navBarHeight = 80;
const elementPosition = element.getBoundingClientRect().top;
- const offsetPosition = window.scrollY + elementPosition - navBarHeight;
- window.scrollTo({ top: offsetPosition, behavior: "instant" });
+ const containerTop = scroller?.getBoundingClientRect().top ?? 0;
+ const relativePosition = elementPosition - containerTop;
+ const currentScroll = scroller?.scrollTop ?? window.scrollY;
+ const offsetPosition = currentScroll + relativePosition - navBarHeight;
+ if (scroller) {
+ scroller.scrollTo({ top: offsetPosition, behavior: "instant" });
+ } else {
+ window.scrollTo({ top: offsetPosition, behavior: "instant" });
+ }
}
});
};
diff --git a/src/components/RevealManager.tsx b/src/components/RevealManager.tsx
index ad8ecea..e5e8857 100644
--- a/src/components/RevealManager.tsx
+++ b/src/components/RevealManager.tsx
@@ -18,6 +18,7 @@ import {
import { Portal } from "solid-js/web";
import { useLocation } from "@solidjs/router";
import { useArticleDom } from "./Article";
+import { useScrollContainer } from "../App";
import { journalStreamState } from "./stores/journalStream";
import { useJournalCompletions } from "./journal/completions";
import {
@@ -75,6 +76,7 @@ function hide() {
export const RevealManager: Component = () => {
const contentDom = useArticleDom();
+ const scrollContainer = useScrollContainer();
const location = useLocation();
const comp = useJournalCompletions();
@@ -179,8 +181,16 @@ export const RevealManager: Component = () => {