From b8a067ce9fc7442ee39bfacd5c190e639da0420b Mon Sep 17 00:00:00 2001 From: lix-2026 Date: Tue, 26 May 2026 02:48:30 +0800 Subject: [PATCH] refactor: split tree shell state runtime --- ...ime-module-maintainability-checklist-v1.md | 2 +- .../mnote-web/browser/tree-shell-runtime.js | 63 ++++--------------- .../browser/tree-shell-state-runtime.js | 54 ++++++++++++++++ rust/crates/mnote-web/src/routes/mod.rs | 5 ++ rust/crates/mnote-web/src/routes/web_shell.rs | 14 +++++ 5 files changed, 86 insertions(+), 52 deletions(-) create mode 100644 rust/crates/mnote-web/browser/tree-shell-state-runtime.js diff --git a/design/10-review/process/16-mnote-web-runtime-module-maintainability-checklist-v1.md b/design/10-review/process/16-mnote-web-runtime-module-maintainability-checklist-v1.md index 3899d4e5..0f26b341 100644 --- a/design/10-review/process/16-mnote-web-runtime-module-maintainability-checklist-v1.md +++ b/design/10-review/process/16-mnote-web-runtime-module-maintainability-checklist-v1.md @@ -204,7 +204,7 @@ cargo test --manifest-path rust/Cargo.toml -p mnote-web --lib ssr::pages::layout 拆分项: -- [ ] D1. `tree-shell-state-runtime.js`:state hydration、serialization、patch dispatch。 +- [x] D1. `tree-shell-state-runtime.js`:state hydration、serialization、patch dispatch。 - [x] D2. `tree-shell-page-runtime.js`:page tree keyboard、expand、focus、drag/drop intent。 - [ ] D3. `tree-shell-filetree-runtime.js`:filetree row normalization、resource meta、open target。 - [ ] D4. `tree-shell-picker-runtime.js`:picker search/focus/pick root。 diff --git a/rust/crates/mnote-web/browser/tree-shell-runtime.js b/rust/crates/mnote-web/browser/tree-shell-runtime.js index a969c40b..2aaf5756 100644 --- a/rust/crates/mnote-web/browser/tree-shell-runtime.js +++ b/rust/crates/mnote-web/browser/tree-shell-runtime.js @@ -10,6 +10,15 @@ import { createTreeShellActionButton, createTreeShellKindBadge as createKindBadge, } from "./tree-shell-icons-runtime.js"; +import { + normalizeTreeShellNumber as normalizeNumber, + normalizeTreeShellStringArray as normalizeStringArray, + normalizeTreeShellText as normalizeText, + parseTreeShellState, + parseTreeShellStateFromHtml, + resolveTreeShellMode, + resolveTreeShellTargetOrigin, +} from "./tree-shell-state-runtime.js"; function buildFileTreeRuntimeEnvironment(runtimeContext) { const fileTreeRowById = runtimeContext.fileTreeRowById || new Map(); @@ -37,15 +46,7 @@ function startTreeShellRuntime() { return; } - const parseState = () => { - try { - return JSON.parse(stateElement.textContent || "{}"); - } catch { - return {}; - } - }; - - const state = parseState(); + const state = parseTreeShellState(stateElement); const rendererInput = state.rendererInput && typeof state.rendererInput === "object" ? state.rendererInput @@ -77,12 +78,6 @@ function startTreeShellRuntime() { runtimeArtifact.runtimeApi && typeof runtimeArtifact.runtimeApi === "object" ? runtimeArtifact.runtimeApi : {}; - const normalizeStringArray = (value) => - Array.isArray(value) - ? value - .map((item) => (typeof item === "string" ? item.trim() : "")) - .filter(Boolean) - : []; const hostOverride = window.__MNOTE_TREE_SHELL_OVERRIDE__ && typeof window.__MNOTE_TREE_SHELL_OVERRIDE__ === "object" @@ -128,13 +123,7 @@ function startTreeShellRuntime() { : typeof state.activePickerItemKey === "string" && state.activePickerItemKey.trim() ? state.activePickerItemKey.trim() : ""; - const mode = (() => { - const rawMode = - typeof state.mode === "string" ? state.mode.trim() : ""; - if (rawMode === "picker") return "picker"; - if (rawMode === "filetree") return "filetree"; - return "page"; - })(); + const mode = resolveTreeShellMode(state); const allowRootPick = state.allowRootPick === true; const excludedIds = new Set( normalizeStringArray(rendererInput.excludedPickerIds).length > 0 @@ -171,20 +160,7 @@ function startTreeShellRuntime() { const titleElement = document.getElementById("tree-shell-title"); const summaryElement = document.getElementById("tree-shell-summary"); const toolbarElement = document.getElementById("tree-shell-toolbar"); - const targetOrigin = (() => { - try { - if (!document.referrer) return "*"; - return new URL(document.referrer).origin || "*"; - } catch { - return "*"; - } - })(); - - const normalizeText = (value, fallback = "") => { - if (typeof value !== "string") return fallback; - const trimmed = value.trim(); - return trimmed || fallback; - }; + const targetOrigin = resolveTreeShellTargetOrigin(document); const initialRenameRowId = (() => { try { return normalizeText(new URL(window.location.href).searchParams.get("renameRowId")); @@ -202,10 +178,6 @@ function startTreeShellRuntime() { return normalized || null; }; - const normalizeNumber = (value, fallback = Number.MAX_SAFE_INTEGER) => { - return Number.isFinite(value) ? Number(value) : fallback; - }; - const normalizeRowKind = (value) => { const normalized = normalizeText(value).toLowerCase(); if (normalized === "index") return "index"; @@ -1544,17 +1516,6 @@ function startTreeShellRuntime() { } }; - const parseTreeShellStateFromHtml = (html) => { - const doc = new DOMParser().parseFromString(html, "text/html"); - const nextStateElement = doc.getElementById("tree-shell-state"); - if (!nextStateElement) return null; - try { - return JSON.parse(nextStateElement.textContent || "{}"); - } catch { - return null; - } - }; - const applyTreeShellStateSnapshot = (nextState, options = {}) => { if (!nextState || typeof nextState !== "object") return false; mediaAssets = Array.isArray(nextState.mediaAssets) ? nextState.mediaAssets : []; diff --git a/rust/crates/mnote-web/browser/tree-shell-state-runtime.js b/rust/crates/mnote-web/browser/tree-shell-state-runtime.js new file mode 100644 index 00000000..d1b28c73 --- /dev/null +++ b/rust/crates/mnote-web/browser/tree-shell-state-runtime.js @@ -0,0 +1,54 @@ +// MNote debug /tree shell state parsing and normalization helpers. + +export function parseTreeShellState(stateElement) { + try { + return JSON.parse(stateElement?.textContent || "{}"); + } catch { + return {}; + } +} + +export function normalizeTreeShellText(value, fallback = "") { + if (typeof value !== "string") return fallback; + const trimmed = value.trim(); + return trimmed || fallback; +} + +export function normalizeTreeShellStringArray(value) { + return Array.isArray(value) + ? value + .map((item) => (typeof item === "string" ? item.trim() : "")) + .filter(Boolean) + : []; +} + +export function normalizeTreeShellNumber(value, fallback = Number.MAX_SAFE_INTEGER) { + return Number.isFinite(value) ? Number(value) : fallback; +} + +export function resolveTreeShellMode(state) { + const rawMode = typeof state?.mode === "string" ? state.mode.trim() : ""; + if (rawMode === "picker") return "picker"; + if (rawMode === "filetree") return "filetree"; + return "page"; +} + +export function resolveTreeShellTargetOrigin(documentRef) { + try { + if (!documentRef?.referrer) return "*"; + return new URL(documentRef.referrer).origin || "*"; + } catch { + return "*"; + } +} + +export function parseTreeShellStateFromHtml(html) { + const doc = new DOMParser().parseFromString(html, "text/html"); + const nextStateElement = doc.getElementById("tree-shell-state"); + if (!nextStateElement) return null; + try { + return JSON.parse(nextStateElement.textContent || "{}"); + } catch { + return null; + } +} diff --git a/rust/crates/mnote-web/src/routes/mod.rs b/rust/crates/mnote-web/src/routes/mod.rs index 129868b6..e6425aa8 100644 --- a/rust/crates/mnote-web/src/routes/mod.rs +++ b/rust/crates/mnote-web/src/routes/mod.rs @@ -170,6 +170,10 @@ pub fn build_router(state: AppState) -> Router { "/api/mnote-browser-runtime/tree-shell-page-runtime.js", get(web_shell::tree_shell_page_runtime_asset), ) + .route( + "/api/mnote-browser-runtime/tree-shell-state-runtime.js", + get(web_shell::tree_shell_state_runtime_asset), + ) .route( "/api/mnote-browser-runtime/tree-shell-icons-runtime.js", get(web_shell::tree_shell_icons_runtime_asset), @@ -626,6 +630,7 @@ mod tests { "/api/mnote-browser-runtime/tree-live-controller.js", "/api/mnote-browser-runtime/tree-shell-runtime.js", "/api/mnote-browser-runtime/tree-shell-page-runtime.js", + "/api/mnote-browser-runtime/tree-shell-state-runtime.js", "/api/mnote-browser-runtime/tree-shell-icons-runtime.js", "/api/mnote-browser-runtime/document-conflict-panel-runtime.js", "/api/mnote-browser-runtime/document-pane-runtime.js", diff --git a/rust/crates/mnote-web/src/routes/web_shell.rs b/rust/crates/mnote-web/src/routes/web_shell.rs index 3abb1e28..cf01577e 100644 --- a/rust/crates/mnote-web/src/routes/web_shell.rs +++ b/rust/crates/mnote-web/src/routes/web_shell.rs @@ -952,6 +952,20 @@ pub async fn tree_shell_page_runtime_asset() -> Response { .unwrap_or_else(|_| Response::new(Body::empty())) } +pub async fn tree_shell_state_runtime_asset() -> Response { + const JS: &str = include_str!("../../browser/tree-shell-state-runtime.js"); + Response::builder() + .status(StatusCode::OK) + .header( + header::CONTENT_TYPE, + "application/javascript; charset=utf-8", + ) + .header(header::CACHE_CONTROL, "no-store") + .header(HEADER_MNOTE_WEB_OWNER, "mnote-web") + .body(Body::from(JS)) + .unwrap_or_else(|_| Response::new(Body::empty())) +} + pub async fn tree_shell_icons_runtime_asset() -> Response { const JS: &str = include_str!("../../browser/tree-shell-icons-runtime.js"); Response::builder()