fix(web): remember document outline visibility

Default the outline to collapsed and persist the user's last choice across reloads.\n\nRefs #345
This commit is contained in:
tianma-if committed 2026-09-04 12:24:01 +08:00
1 parent c7bee0afc9
commit ae464e88c4
3 files changed
+78 -4

No files matched your search

+19 -4
View File
@@ -158,7 +158,9 @@ import {
getEditableMemoTitle,
getNotebookMoveOptions,
readDesktopReadingProtectionPreference,
readEditorOutlineCollapsedPreference,
writeDesktopReadingProtectionPreference,
writeEditorOutlineCollapsedPreference,
type EditorContentAlignment,
type MemoDocumentActionRequest,
type ShortcutSettings,
@@ -640,7 +642,7 @@ const RichEditorPane = ({
const [markdownSource, setMarkdownSource] = useState("");
const [isMarkdownMode, setIsMarkdownMode] = useState(false);
const [mobileToolbarOpen, setMobileToolbarOpen] = useState(false);
const [editorOutlineCollapsed, setEditorOutlineCollapsed] = useState(false);
const [editorOutlineCollapsed, setEditorOutlineCollapsed] = useState(readEditorOutlineCollapsedPreference);
const [wechatCopyState, setWechatCopyState] = useState<"idle" | "copying" | "copied" | "error">("idle");
const [memoIdCopyNotice, setMemoIdCopyNotice] = useState<{ status: "copied" | "error"; id: string } | null>(null);
const handledSaveAndSyncTokenRef = useRef(saveAndSyncToken);
@@ -714,6 +716,19 @@ const RichEditorPane = ({
});
}, []);
const handleEditorOutlineCollapsedChange = useCallback((collapsed: boolean) => {
setEditorOutlineCollapsed(collapsed);
writeEditorOutlineCollapsedPreference(collapsed);
}, []);
const toggleEditorOutline = useCallback(() => {
setEditorOutlineCollapsed((collapsed) => {
const nextCollapsed = !collapsed;
writeEditorOutlineCollapsedPreference(nextCollapsed);
return nextCollapsed;
});
}, []);
useEffect(() => {
if (!isMobileViewport && mobileDefaultEditRequested && desktopReadingProtection) {
setDesktopReadingProtection(false);
@@ -3121,8 +3136,8 @@ const RichEditorPane = ({
return;
}
setEditorOutlineCollapsed((current) => !current);
}, [editorShortcutBlocked, isMobileViewport, outlineToggleToken, useMarkdownSourceEditor, useMobilePlainTextEditor]);
toggleEditorOutline();
}, [editorShortcutBlocked, isMobileViewport, outlineToggleToken, toggleEditorOutline, useMarkdownSourceEditor, useMobilePlainTextEditor]);
useEffect(() => {
if (handledSaveAndSyncTokenRef.current === saveAndSyncToken || saveMutationPending) {
@@ -4492,7 +4507,7 @@ const RichEditorPane = ({
scrollContainer={editorScrollContainer}
collapsed={editorOutlineCollapsed}
shortcutLabel={formatShortcutBinding(shortcutSettings.toggleOutline)}
onCollapsedChange={setEditorOutlineCollapsed}
onCollapsedChange={handleEditorOutlineCollapsedChange}
/>
)}
</div>
+42
View File
@@ -3,6 +3,7 @@ import {
DEFAULT_SHORTCUT_SETTINGS,
DESKTOP_FOCUS_MODE_STORAGE_KEY,
DESKTOP_READING_PROTECTION_STORAGE_KEY,
EDITOR_OUTLINE_COLLAPSED_STORAGE_KEY,
EDITOR_CONTENT_ALIGNMENT_STORAGE_KEY,
NOTEBOOK_SORT_STORAGE_KEY,
SHORTCUT_SETTINGS_STORAGE_KEY,
@@ -13,11 +14,13 @@ import {
readNotebookSortPreference,
readDesktopFocusModePreference,
readDesktopReadingProtectionPreference,
readEditorOutlineCollapsedPreference,
readShortcutSettingsPreference,
writeEditorContentAlignmentPreference,
writeNotebookSortPreference,
writeDesktopFocusModePreference,
writeDesktopReadingProtectionPreference,
writeEditorOutlineCollapsedPreference,
} from "./app-helpers.ts";
const originalWindow = globalThis.window;
@@ -130,6 +133,45 @@ describe("desktop reading protection preference", () => {
});
});
describe("editor outline preference", () => {
test("defaults to collapsed and only expands for an explicit false value", () => {
const values = installLocalStorage();
expect(readEditorOutlineCollapsedPreference()).toBe(true);
values.set(EDITOR_OUTLINE_COLLAPSED_STORAGE_KEY, "true");
expect(readEditorOutlineCollapsedPreference()).toBe(true);
values.set(EDITOR_OUTLINE_COLLAPSED_STORAGE_KEY, "false");
expect(readEditorOutlineCollapsedPreference()).toBe(false);
});
test("persists collapsed and expanded states", () => {
const values = installLocalStorage();
writeEditorOutlineCollapsedPreference(false);
expect(values.get(EDITOR_OUTLINE_COLLAPSED_STORAGE_KEY)).toBe("false");
writeEditorOutlineCollapsedPreference(true);
expect(values.get(EDITOR_OUTLINE_COLLAPSED_STORAGE_KEY)).toBe("true");
});
test("falls back to collapsed when local storage is unavailable", () => {
globalThis.window = {
localStorage: {
getItem: () => {
throw new Error("blocked");
},
setItem: () => {
throw new Error("blocked");
},
},
};
expect(readEditorOutlineCollapsedPreference()).toBe(true);
expect(() => writeEditorOutlineCollapsedPreference(false)).not.toThrow();
});
});
describe("editor content alignment preference", () => {
test("defaults to left aligned and persists both supported alignments", () => {
const values = installLocalStorage();
+17
View File
@@ -131,6 +131,7 @@ export { DEFAULT_MEMO_TITLE };
export const IMAGE_COMPRESSION_STORAGE_KEY = "edgeever.imageCompressionEnabled";
export const DESKTOP_FOCUS_MODE_STORAGE_KEY = "edgeever.desktopFocusMode";
export const DESKTOP_READING_PROTECTION_STORAGE_KEY = "edgeever.desktopReadingProtection";
export const EDITOR_OUTLINE_COLLAPSED_STORAGE_KEY = "edgeever.editorOutlineCollapsed";
export const EDITOR_CONTENT_ALIGNMENT_STORAGE_KEY = "edgeever.editorContentAlignment";
export const MEMO_LIST_DENSITY_STORAGE_KEY = "edgeever.memoListDensity";
export const MEMO_LIST_WIDTH_STORAGE_KEY = "edgeever.memoListWidth";
@@ -355,6 +356,22 @@ export const writeDesktopReadingProtectionPreference = (enabled: boolean) => {
}
};
export const readEditorOutlineCollapsedPreference = () => {
try {
return window.localStorage.getItem(EDITOR_OUTLINE_COLLAPSED_STORAGE_KEY) !== "false";
} catch {
return true;
}
};
export const writeEditorOutlineCollapsedPreference = (collapsed: boolean) => {
try {
window.localStorage.setItem(EDITOR_OUTLINE_COLLAPSED_STORAGE_KEY, collapsed ? "true" : "false");
} catch {
// Local storage can be unavailable in private or restricted browser contexts.
}
};
export const readEditorContentAlignmentPreference = (): EditorContentAlignment => {
try {
return window.localStorage.getItem(EDITOR_CONTENT_ALIGNMENT_STORAGE_KEY) === "center" ? "center" : "start";