//! MNOTE 通用页面布局组件（Wolai / Notion 风格）

use crate::ssr::pages::admin::AdminAccessPolicyPanel;
use leptos::prelude::*;

const SIDEBAR_TREE_JS: &str = r##"
(function(){
  if (window.__mnoteSidebarTreeRuntimeStarted) return;
  // sidebar-tree-runtime.js module has NOT loaded yet.
  // This inline script provides a minimal bootstrap until the module arrives.
  // Module script is loaded via type="module" before this inline block.
  window.__mnoteSidebarTreeRuntimeLoading = true;
  var _checkInterval = setInterval(function() {
    if (window.__mnoteSidebarTreeRuntimeStarted) {
      clearInterval(_checkInterval);
      window.__mnoteSidebarTreeRuntimeLoading = false;
    }
  }, 100);
  // If module hasn't loaded after 5 seconds, stop polling
  setTimeout(function() {
    if (_checkInterval) clearInterval(_checkInterval);
  }, 5000);
})();

"##;

fn browser_runtime_src(asset: &str) -> String {
    let base = format!("/api/mnote-browser-runtime/{asset}");
    if let Some(cache_buster) = crate::routes::dev_hot::dev_hot_cache_buster() {
        format!("{base}?devHot={cache_buster}")
    } else {
        base
    }
}

/// MNOTE Wolai 风格页面布局
///
/// 包含左侧栏 + 内容区的双栏布局。
/// 侧栏显示品牌、导航链接和可选的页面树。
///
/// # 用法
///
/// ```ignore
/// view! {
///     <PageLayout current_nav="home" sidebar_tree_html={None}>
///         <section>...</section>
///     </PageLayout>
/// }
/// ```
#[component]
pub fn PageLayout(
    children: Children,
    current_nav: &'static str,
    /// 侧栏页面树 HTML（可选），由路由 handler 渲染
    #[prop(optional)]
    sidebar_tree_html: Option<String>,
    /// 工作区名称（可选），显示在侧栏顶部
    #[prop(optional)]
    workspace_name: Option<String>,
    /// workspace shell 侧栏 sections HTML（可选），由 projection 渲染
    #[prop(optional)]
    workspace_sidebar_html: Option<String>,
    /// 顶栏当前页面标题（可选）
    #[prop(optional)]
    topbar_title: Option<String>,
    /// 是否显示管理员授权能力
    #[prop(optional)]
    show_admin_access_policy: bool,
    /// 是否启用树实时流
    #[prop(optional, default = true)]
    enable_tree_live: bool,
) -> impl IntoView {
    let _ = show_admin_access_policy;
    let sidebar_tree_html = sidebar_tree_html.unwrap_or_default();

    let ws_name = workspace_name
        .map(|value| value.trim().to_string())
        .filter(|value| !value.is_empty())
        .unwrap_or_else(|| "开发用户 的空间".to_string());
    let topbar_title = topbar_title
        .map(|value| value.trim().to_string())
        .filter(|value| !value.is_empty())
        .unwrap_or_else(|| "个人".to_string());
    let sidebar_sections_html = workspace_sidebar_html
        .map(|value| value.trim().to_string())
        .filter(|value| !value.is_empty())
        .unwrap_or_else(|| {
            let dataset = serde_json::json!({
                "workspaces": [{ "id": "default", "name": ws_name.clone() }],
                "documents": []
            });
            let projection = crate::workspace_shell::build_workspace_shell_projection(
                &dataset, "default", None, &ws_name,
            );
            crate::workspace_shell::render_workspace_shell_sidebar_html(
                &projection,
                Some(sidebar_tree_html.as_str()),
                None,
                None,
                None,
            )
        });
    let tree_live_bootstrap = serde_json::json!({
        "schema": "mnote.tree_live_bootstrap.v1",
        "disabled": !enable_tree_live,
        "transport": if enable_tree_live { "tree-live-ws" } else { "disabled" },
        "workspaceId": null,
        "rootIds": [],
        "initialRevision": null,
        "endpoint": "/api/tree/events",
        "wsEndpoint": "/api/realtime/ws",
        "views": ["page-tree", "file-tree"]
    })
    .to_string();
    let admin_access_policy_template = crate::ssr::render_view(leptos::view! {
        <AdminAccessPolicyPanel workspace_name={ws_name.clone()} is_admin=true boot_script=false />
    });
    let user_access_policy_template = crate::ssr::render_view(leptos::view! {
        <AdminAccessPolicyPanel workspace_name={ws_name.clone()} is_admin=false boot_script=false />
    });

    view! {
        <div class="mnote-shell wolai-workspace-shell" data-mnote-web-owner="mnote-web" data-mnote-shell="workspace">
            <aside class="mnote-sidebar wolai-sidebar" data-testid="wolai-sidebar">
                <div class="mnote-sidebar-header wolai-sidebar-header" data-testid="wolai-workspace-identity">
                    <a href="/" class="mnote-sidebar-brand wolai-avatar" aria-label="工作区首页">"L"</a>
                    <button
                        type="button"
                        class="mnote-workspace-source-trigger"
                        data-testid="mnote-workspace-source-trigger"
                        data-mnote-action="open-workspace-source-menu"
                        aria-haspopup="menu"
                        aria-expanded="false"
                        title="切换我的空间或本地文件夹"
                    >
                        <span class="sidebar-workspace-name">{ws_name.clone()}</span>
                        <span class="wolai-sidebar-chevron" aria-hidden="true">"⌄"</span>
                    </button>
                </div>
                <nav class="mnote-sidebar-nav wolai-quick-actions" aria-label="快捷操作" data-testid="wolai-sidebar-quick-actions">
                    <a href="/search" class:active={current_nav == "search"} title="搜索" aria-label="搜索" data-mnote-action="open-search-modal"><span class="material-symbols-outlined nav-icon" data-icon="search" aria-hidden="true"></span></a>
                    <a href="/graph" title="关系图" aria-label="关系图"><span class="material-symbols-outlined nav-icon" data-icon="account_tree" aria-hidden="true"></span></a>
                    <button type="button" title="导航页" aria-label="导航页" data-mnote-action="open-navigation-page"><span class="material-symbols-outlined nav-icon" data-icon="home" aria-hidden="true"></span></button>
                    <a href="/help" title="帮助" aria-label="帮助"><span class="material-symbols-outlined nav-icon" data-icon="help" aria-hidden="true"></span></a>
                    <a href="/files" title="文件" aria-label="文件"><span class="material-symbols-outlined nav-icon" data-icon="inventory_2" aria-hidden="true"></span></a>
                    <button type="button" title="打开本地文件夹" aria-label="打开本地文件夹" data-mnote-action="open-local-folder"><span class="material-symbols-outlined nav-icon" data-icon="folder_open" aria-hidden="true"></span></button>
                    <button
                        type="button"
                        title="更多"
                        aria-label="更多"
                        data-testid="mnote-account-menu-trigger"
                        data-mnote-action="open-account-menu"
                        aria-haspopup="menu"
                        aria-expanded="false"
                    >
                        <span class="material-symbols-outlined nav-icon" data-icon="more_horiz" aria-hidden="true"></span>
                    </button>
                </nav>
                <div class="wolai-sidebar-body" inner_html={sidebar_sections_html}></div>
                <script id="__MNOTE_TREE_LIVE_BOOTSTRAP__" type="application/json" inner_html={tree_live_bootstrap}></script>
                <div hidden data-testid="mnote-admin-access-policy-template-admin" inner_html={admin_access_policy_template}></div>
                <div hidden data-testid="mnote-admin-access-policy-template-user" inner_html={user_access_policy_template}></div>
                <script inner_html={crate::ssr::pages::admin::ADMIN_POLICY_SCRIPT.to_string()}></script>
                <script type="module" src={browser_runtime_src("resource-open-runtime.js")}></script>
                <script type="module" src={browser_runtime_src("local-upload-runtime.js")}></script>
                <script type="module" src={browser_runtime_src("filetree-runtime.js")}></script>
                <script type="module" src={browser_runtime_src("filetree-selection-runtime.js")}></script>
                <script type="module" src={browser_runtime_src("filetree-context-menu-runtime.js")}></script>
                <script type="module" src={browser_runtime_src("filetree-dnd-runtime.js")}></script>
                <script type="module" src={browser_runtime_src("filetree-keyboard-runtime.js")}></script>
                <script type="module" src={browser_runtime_src("sidebar-shell-runtime.js")}></script>
                <script type="module" src={browser_runtime_src("sidebar-tree-runtime.js")}></script>
                <script inner_html={SIDEBAR_TREE_JS.to_string()}></script>
                <script type="module" src={browser_runtime_src("local-folder-event-bus-runtime.js")}></script>
                <script type="module" src={browser_runtime_src("tree-live-controller.js")}></script>
            </aside>
            <div class="mnote-sidebar-resizer" data-mnote-sidebar-resizer="true" role="separator" aria-orientation="vertical" aria-label="调整侧栏宽度"></div>
            <div class="mnote-main">
                <header class="wolai-topbar" data-testid="wolai-topbar">
                    <div class="wolai-topbar-left">
                        <button type="button" class="wolai-icon-button wolai-menu-button" aria-label="切换侧栏" aria-pressed="false" data-mnote-action="toggle-sidebar" data-testid="wolai-sidebar-toggle"><span class="material-symbols-outlined" data-icon="menu" aria-hidden="true"></span></button>
                        <nav class="wolai-breadcrumb" aria-label="页面路径">
                            <span class="wolai-breadcrumb-root">{ws_name.clone()}</span>
                            <span class="wolai-breadcrumb-separator" aria-hidden="true">"›"</span>
                            <span class="wolai-breadcrumb-current"><span class="material-symbols-outlined wolai-home-icon" data-icon="home" aria-hidden="true"></span><span data-page-title-current="true">{topbar_title}</span></span>
                        </nav>
                    </div>
                    <div class="wolai-topbar-actions" aria-label="页面操作">
                        <span class="wolai-public-pill" data-testid="wolai-public-state" hidden></span>
                        <button type="button" class="wolai-icon-button" title="星标置顶" aria-label="星标置顶" data-mnote-action="toggle-sidebar-shortcut" data-mnote-shortcut-kind="page"><span class="material-symbols-outlined" data-icon="star" aria-hidden="true"></span></button>
                        <button type="button" class="wolai-icon-button" title="演示" aria-label="演示"><span class="material-symbols-outlined" data-icon="slideshow" aria-hidden="true"></span></button>
                        <button type="button" class="wolai-icon-button" title="资料库问答" aria-label="资料库问答" data-testid="mnote-knowledge-rag-settings-toggle" data-mnote-action="open-knowledge-rag-settings"><span class="material-symbols-outlined" data-icon="travel_explore" aria-hidden="true"></span></button>
                        <button type="button" class="wolai-icon-button" title="评论" aria-label="评论"><span class="material-symbols-outlined" data-icon="comment" aria-hidden="true"></span></button>
                        <button type="button" class="wolai-icon-button" title="分享" aria-label="分享"><span class="material-symbols-outlined" data-icon="share" aria-hidden="true"></span></button>
                        <button type="button" class="wolai-icon-button" title="成员" aria-label="成员"><span class="material-symbols-outlined" data-icon="person_add" aria-hidden="true"></span></button>
                        <button type="button" class="wolai-icon-button" title="页面历史" aria-label="历史" data-mnote-action="open-page-history"><span class="material-symbols-outlined" data-icon="history" aria-hidden="true"></span></button>
                        <button type="button" class="wolai-icon-button" title="页面选项和全局选项" aria-label="更多" data-testid="wolai-page-settings-trigger" data-mnote-action="open-page-settings"><span class="material-symbols-outlined" data-icon="more_horiz" aria-hidden="true"></span></button>
                    </div>
                </header>
                <article class="mnote-content">
                    {children()}
                </article>
                <div class="wolai-floating-actions" aria-label="浮动操作">
                    <button type="button" data-testid="wolai-floating-ai" class="wolai-floating-button wolai-floating-button--ai" aria-label="AI 助手" title="点击 进入空间智能问答" data-mnote-action="open-page-ai"><span class="material-symbols-outlined material-symbols-filled" data-icon="auto_awesome" aria-hidden="true"></span></button>
                </div>
            </div>
        </div>
    }
}

#[cfg(test)]
mod tests {
    use leptos::prelude::ElementChild;

    const SIDEBAR_TREE_RUNTIME_JS: &str = include_str!("../../../browser/sidebar-tree-runtime.js");
    const SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-tree-live-apply-runtime.js");
    const SIDEBAR_FILETREE_OPEN_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-filetree-open-runtime.js");
    const SIDEBAR_FILETREE_COMMAND_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-filetree-command-runtime.js");
    const SIDEBAR_FILETREE_UPLOAD_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-filetree-upload-runtime.js");
    const SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-attachment-open-runtime.js");
    const SIDEBAR_SHELL_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-shell-runtime.js");
    const SIDEBAR_WORKSPACE_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-workspace-runtime.js");
    const SIDEBAR_PAGE_TREE_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-page-tree-runtime.js");
    // Browser Page AI runtime is served as a module; keep this include explicit so
    // resume/journal contract checks inspect the actual shipped JS.
    const SIDEBAR_PAGE_AI_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-page-ai-runtime.js");
    const SIDEBAR_PAGE_AI_MARKDOWN_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-page-ai-markdown-runtime.js");
    const SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-page-ai-render-runtime.js");
    const SIDEBAR_PAGE_AI_PERMISSION_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-page-ai-permission-runtime.js");
    const SIDEBAR_PAGE_AI_PROFILE_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-page-ai-profile-runtime.js");
    const SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-page-ai-session-runtime.js");
    const SIDEBAR_PAGE_AI_SKILL_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-page-ai-skill-runtime.js");
    const SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-page-ai-target-runtime.js");
    const SIDEBAR_PAGE_SETTINGS_RUNTIME_JS: &str =
        include_str!("../../../browser/sidebar-page-settings-runtime.js");
    const FILETREE_CONTEXT_MENU_RUNTIME_JS: &str =
        include_str!("../../../browser/filetree-context-menu-runtime.js");
    const FILETREE_DND_RUNTIME_JS: &str = include_str!("../../../browser/filetree-dnd-runtime.js");
    const FILETREE_KEYBOARD_RUNTIME_JS: &str =
        include_str!("../../../browser/filetree-keyboard-runtime.js");
    const FILETREE_RUNTIME_JS: &str = include_str!("../../../browser/filetree-runtime.js");
    const FILETREE_SELECTION_RUNTIME_JS: &str =
        include_str!("../../../browser/filetree-selection-runtime.js");
    const LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS: &str =
        include_str!("../../../browser/local-folder-event-bus-runtime.js");
    const TREE_LIVE_CONTROLLER_JS: &str = include_str!("../../../browser/tree-live-controller.js");

    fn js_function_body(source: &str, name: &str) -> String {
        let marker = format!("function {name}(");
        let start = source.find(&marker).expect("js function exists");
        let rest = &source[start..];
        let brace = rest.find('{').expect("js function body starts");
        let mut depth = 0usize;
        let mut end = None;
        for (offset, ch) in rest[brace..].char_indices() {
            if ch == '{' {
                depth += 1;
            } else if ch == '}' {
                depth -= 1;
                if depth == 0 {
                    end = Some(brace + offset + 1);
                    break;
                }
            }
        }
        rest[..end.expect("js function body ends")].to_string()
    }

    #[test]
    fn sidebar_tree_runtime_handles_navigation_drag_and_filetree_actions() {
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("mnoteNavigationInFlight"));
        assert!(SIDEBAR_PAGE_TREE_RUNTIME_JS.contains("data-mnote-navigation-pending"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("MNOTE_SIDEBAR_TREE_MODE_KEY"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("MNOTE_GLOBAL_SHOW_HEADING_NUMBERS_KEY"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("MNOTE_LAST_CLOUD_WORKSPACE_KEY"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS
            .contains("data-global-option-checkbox=\"showHeadingNumbers\""));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("/api/ui/preferences"));
        assert!(!SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("localStorage.setItem"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("restoreSidebarTreeTab"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("treeView"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("tree:local-command"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("applyCreatedDocumentLocally"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("data-mnote-tree-local-command-applied', 'create"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("data-mnote-tree-local-command-applied', 'remove"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("data-mnote-tree-local-command-applied', 'rename"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("data-mnote-tree-local-command-applied', 'move"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function sortOrderFromDelta(data)"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("data.sortOrder ?? data.sort_order"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function insertTreeNodeAtSortOrder"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("applyMoveDocumentDelta({ documentId: body.documentId, parentId: body.parentId || null, sortOrder: body.sortOrder })"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("wolai:assets-changed"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("applyAssetsChangedToFileTree"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("installMindmapAssetFetchObserver"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("parseMindmapApiTarget"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-mnote-assets-local-applied"));
        assert!(SIDEBAR_PAGE_TREE_RUNTIME_JS.contains("application/x-mnote-page-tree-node"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("dragstart"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("drop"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-drop-feedback"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("action: 'move'"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("sidebar-file-tree-root"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("tree.filetree.open"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("tree.asset.open"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("openConvexAssetFromFileTree"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("buildMindmapOpenPath"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("isMindmapAssetDetail"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("^思维导图"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("openMindmapAssetInDocumentShell"));
        assert!(
            SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("data-mnote-last-mindmap-asset-open-mode")
        );
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("mindmap-object-shell"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS
            .contains("navigateToMindmapObject(documentId, assetId"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS
            .contains("__mnoteDocumentPaneRuntime?.openPrimaryMindmap"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("shortMindmapFileName"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("assetType: assetType || null"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-object-identity"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("readFileTreeObjectIdentity"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("objectIdentity: objectIdentity"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("workspaceId: resolveWorkspaceId(fileRow)"));
        assert!(!SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("/api/media/sign?assetId="));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("data-mnote-media-sign-retired"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("/api/media/sign?assetId="));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-mnote-media-sign-retired"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("/api/media/upload"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-mnote-media-upload-retired"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("function localFilePathFromAssetId"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("/api/local-folder/files/open"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("local-file:"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS
            .contains("value.indexOf('local:asset:') === 0 ? value.slice('local:asset:'.length)"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("fetchCurrentOnlyOfficeUserId"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("/api/auth/whoami"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("buildOnlyOfficeOpenUrl"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("target.searchParams.set('userId'"));
        assert!(!SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("window.open(officeUrl,"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("tree.filetree.internal-drop"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("tree.filetree.external-drop"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("beginFileTreeInlineRename"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("tree-rename-input"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("sidebarFileTreeClipboard"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("pasteSidebarFileTreeClipboard"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("MNOTE_RECENT_LOCAL_ROOTS_KEY_PREFIX"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("function recentLocalRootsStorageKey"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("data-mnote-actor-id"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("requestOpenLocalFolder"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("autoOpenRecentLocalRootOnHome"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("sourceKind', 'local_folder"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("switchToCloudWorkspace"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("mnote-switch-cloud-workspace"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("mnote-recent-local-root"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("mnote-local-folder-authorized-roots"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("mnote-local-folder-authorized-root"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("已授权文件夹"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("fetch('/api/user/access-policy'"));
    }

    #[test]
    fn sidebar_live_apply_runtime_uses_persisted_tree_view_state() {
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("SIDEBAR_TREE_VIEW_STATE_KEY"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function loadSidebarTreeViewState"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function persistSidebarTreeViewState"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function applySidebarTreeViewState"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("/api/tree/view-state"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("mnote.sidebarTreeViewState.v1:{userId}:{workspaceId}:{sourceKind}:{treeKind}:{rootUriHash}:{scopeHash}"));

        let render_page_rows =
            js_function_body(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS, "renderPageRows");
        assert!(render_page_rows.contains("sidebarTreeViewStateFor('pagetree'"));
        assert!(render_page_rows.contains("expandedIds"));
        assert!(
            !render_page_rows
                .contains("var expanded = expandable && item.expandedByDefault !== false"),
            "PageTree 不能只依赖 projection expandedByDefault 决定展开"
        );

        let render_file_rows =
            js_function_body(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS, "renderFileRows");
        assert!(render_file_rows.contains("sidebarTreeViewStateFor('filetree'"));
        assert!(render_file_rows.contains("expandedRelativePaths"));
    }

    #[test]
    fn page_layout_hides_public_state_and_exposes_sidebar_shortcut_star() {
        let html = crate::ssr::render_view(leptos::view! {
            <super::PageLayout current_nav="documents" topbar_title={"个人".to_string()}>
                <main>"正文"</main>
            </super::PageLayout>
        });

        assert!(!html.contains(r#"data-testid="wolai-public-state">全网公开"#));
        assert!(html.contains(r#"data-mnote-action="toggle-sidebar-shortcut""#));
        assert!(html.contains(r#"data-mnote-shortcut-kind="page""#));
    }

    #[test]
    fn page_layout_adds_dev_hot_cache_buster_to_browser_runtime_scripts() {
        let _guard = crate::test_support::hermes_env_lock()
            .lock()
            .expect("env lock");
        std::env::set_var("MNOTE_WEB_DEV_HOT_RELOAD", "1");
        let html = crate::ssr::render_view(leptos::view! {
            <super::PageLayout current_nav="documents" topbar_title={"个人".to_string()}>
                <main>"正文"</main>
            </super::PageLayout>
        });
        std::env::remove_var("MNOTE_WEB_DEV_HOT_RELOAD");

        assert!(
            html.contains("/api/mnote-browser-runtime/tree-live-controller.js?devHot="),
            "dev:hot 下 tree live controller URL 必须带 cache buster，避免浏览器继续执行旧 WS/SSE 逻辑"
        );
        assert!(
            html.contains("/api/mnote-browser-runtime/sidebar-tree-runtime.js?devHot="),
            "dev:hot 下 sidebar runtime URL 也必须带 cache buster"
        );
        assert!(
            html.contains("/api/mnote-browser-runtime/local-folder-event-bus-runtime.js?devHot="),
            "dev:hot 下 local-folder event bus URL 必须带 cache buster，避免复用旧连接编排逻辑"
        );
    }

    #[test]
    fn page_layout_loads_local_folder_event_bus_before_tree_live_controller() {
        let html = crate::ssr::render_view(leptos::view! {
            <super::PageLayout current_nav="documents" topbar_title={"个人".to_string()}>
                <main>"正文"</main>
            </super::PageLayout>
        });
        let bus_index = html
            .find("/api/mnote-browser-runtime/local-folder-event-bus-runtime.js")
            .expect("local-folder event bus runtime should be loaded");
        let controller_index = html
            .find("/api/mnote-browser-runtime/tree-live-controller.js")
            .expect("tree live controller runtime should be loaded");
        assert!(
            bus_index < controller_index,
            "local-folder event bus 必须先于 tree-live-controller 加载，确保 local-folder watcher 连接由 bus 接管"
        );
    }

    #[test]
    fn sidebar_runtime_supports_shortcuts_and_scoped_filetree() {
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("/api/sidebar/shortcuts"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("toggle-sidebar-shortcut"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("openStarredFolderShortcut"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function openNavigationPageForFolder"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function openCurrentNavigationPage"));
        assert!(
            SIDEBAR_TREE_RUNTIME_JS.contains("openCurrentNavigationPage(navigationPageTrigger)")
        );
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("/api/navigation/recent"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("await renderPageProjection(sidebarProjection)"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("authRedirectUrl"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("window.location.assign(authRedirectUrl())"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("window.location.assign(url)"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("rootUri: currentRootUri()"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("var rootUri = readShortcutRootUri(row);"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("inferLocalRootUriFromWorkspaceId"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-mnote-filetree-scope"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("toggle-sidebar-folder-shortcut"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("/api/tree/projections/sidebar"));
        let scope_start = SIDEBAR_TREE_RUNTIME_JS
            .find("persistStarredFolderScope(workspaceId, rootUri, relativePath);")
            .expect("starred folder should persist scope before loading");
        let sidebar_fetch_start = SIDEBAR_TREE_RUNTIME_JS[scope_start..]
            .find("fetch(sidebarUrl.toString()")
            .expect("starred folder should fetch local page projection")
            + scope_start;
        let fetch_start = SIDEBAR_TREE_RUNTIME_JS[scope_start..]
            .find("fetch(url.toString()")
            .expect("starred folder should fetch scoped projection")
            + scope_start;
        assert!(
            sidebar_fetch_start < fetch_start,
            "星标文件夹请求 scoped filetree 前必须先拉本地页面树投影，避免我的空间旧页面残留"
        );
        assert!(
            scope_start < fetch_start,
            "星标文件夹应先写入 fileTreeScope，再请求大目录，避免 live refresh 把视图折回根目录"
        );
    }

    #[test]
    fn page_layout_quick_action_opens_current_navigation_page() {
        let html = crate::ssr::render_view(leptos::view! {
            <super::PageLayout current_nav="documents" topbar_title={"个人".to_string()}>
                <main>"正文"</main>
            </super::PageLayout>
        });

        assert!(html.contains(r#"data-mnote-action="open-navigation-page""#));
        assert!(html.contains(r#"title="导航页""#));
        assert!(!html.contains(r#"href="/actions""#));
    }

    #[test]
    fn page_layout_exposes_lightrag_knowledge_rag_settings_only() {
        let html = crate::ssr::render_view(leptos::view! {
            <super::PageLayout current_nav="documents" topbar_title={"个人".to_string()}>
                <main>"正文"</main>
            </super::PageLayout>
        });

        assert!(!html.contains(r#"data-testid="mnote-local-index-settings-toggle""#));
        assert!(!html.contains(r#"data-mnote-action="open-index-settings""#));
        assert!(html.contains(r#"data-testid="mnote-knowledge-rag-settings-toggle""#));
        assert!(html.contains(r#"data-mnote-action="open-knowledge-rag-settings""#));
        assert!(html.contains(r#"data-icon="travel_explore""#));
        assert!(crate::ssr::styles::MNOTE_CSS.contains(r#"data-icon="travel_explore""#));
        assert!(!html.contains(r#"data-testid="mnote-local-ocr-task-toggle""#));
        assert!(!html.contains(r#"data-mnote-action="open-ocr-settings""#));
        assert!(!html.contains(r#"data-testid="mnote-floating-task-toggle""#));
        assert!(!html.contains(r#"data-mnote-action="toggle-ocr-tasks""#));
    }

    #[test]
    fn sidebar_settings_runtime_routes_index_and_ocr_to_lightrag_settings() {
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("mnote-knowledge-rag-settings-popover"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("/api/knowledge-rag/status?"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("/api/knowledge-rag/pipeline-events"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("/api/knowledge-rag/ingest"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("/api/knowledge-rag/prune-registry"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("use-filetree-selection"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("data-knowledge-rag-action=\"prune\""));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("mnote-knowledge-rag-source-filters"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("filter-sources"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("LightRAG 未映射"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("Rerank"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("scheduleKnowledgeRagStatusBridge"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("knowledgeRagStatusHasInFlight"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("new EventSource(url.toString())"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("pipeline_status"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("pipeline.pendingEnqueues"));
        assert!(
            SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("data-mnote-knowledge-rag-watcher-required")
        );
        assert!(!SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("runKnowledgeRagStatusBridge"));
        assert!(!SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("knowledgeRagBridgeDelays"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("knowledgeRagRegistryStatusDigest"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("registry-status-changed"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("knowledgeRagIndeterminateProgress"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("data-progress-mode"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("indeterminate"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("analyzing"));
        assert!(crate::ssr::styles::MNOTE_CSS
            .contains("mnote-knowledge-rag-source-progress[data-progress-mode=\"indeterminate\"]"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("delete_submitted"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("delete_completed"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("publicKnowledgeRagDashboardUrl"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("label.hidden = status === 'idle'"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("data-knowledge-rag-input-status-label"));
        assert!(!SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("localOcrAutoEnabled"));
        assert!(SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("data-mnote-local-ocr-auto-retired"));
        assert!(
            SIDEBAR_TREE_RUNTIME_JS.contains("[data-mnote-action=\"open-knowledge-rag-settings\"]")
        );
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("[data-knowledge-rag-action]"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("useKnowledgeRagFileTreeSelection"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("reindex-source"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("deleteKnowledgeRagSource"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("pruneKnowledgeRagRegistry"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("setKnowledgeRagSourceFilter"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("mnote:knowledge-rag-source-updated"));
        assert!(
            SIDEBAR_TREE_RUNTIME_JS.contains(r#"<option value="exact" selected>关键词</option>"#)
        );
        assert!(SIDEBAR_TREE_RUNTIME_JS
            .contains(r#"data-search-switch="knowledge" role="switch" aria-checked="true""#));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("knowledgeRagSourceRelativePath"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("supportsKnowledgeRagSource"));
        assert!(!SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("runLocalOcr"));
        assert!(!SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("mnote:local-ocr-job-updated"));
        assert!(!SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("data-mnote-local-ocr-menu-status"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("openPageIndexSettingsPopover();"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("openLocalOcrSettingsPopover();"));
        assert!(!SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("data-page-settings-tab=\"index\""));
        assert!(!SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("本地索引仅在本地工作区页面可用"));
        assert!(
            !SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("wolai-page-settings-local-index-backlinks")
        );
        assert!(!SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("wolai-page-settings-local-index-tags"));
        assert!(!SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("backlinksUrl"));
        assert!(!SIDEBAR_PAGE_SETTINGS_RUNTIME_JS.contains("tagsUrl"));
    }

    #[test]
    fn sidebar_filetree_folder_click_toggles_instead_of_navigation_page() {
        let folder_branch = SIDEBAR_TREE_RUNTIME_JS
            .split("if (fileAction === 'open' && rowKind === 'folder') {")
            .nth(1)
            .and_then(|rest| rest.split("return;").next())
            .expect("filetree folder open branch exists");
        assert!(folder_branch.contains("toggleChildren(fileRow"));
        assert!(!folder_branch.contains("openNavigationPageForFolder"));
        assert!(!folder_branch.contains("window.location.assign"));
    }

    #[test]
    fn page_ai_context_uses_page_aggregate_subtree_as_single_truth() {
        assert!(
            !SIDEBAR_TREE_RUNTIME_JS.contains("pageSubtreeSource: 'local'"),
            "页面 AI context 不能从编辑器 DOM 派生 local page subtree；必须以 Rust Page Aggregate projection 为准"
        );
        assert!(
            !SIDEBAR_TREE_RUNTIME_JS.contains("buildPageAiLocalSubtree(localBlocks"),
            "页面 AI context 不应调用本地 page subtree builder"
        );
    }

    #[test]
    fn page_ai_fast_path_is_not_local_first_main_path() {
        assert!(
            SIDEBAR_PAGE_AI_RUNTIME_JS
                .contains("if (currentSourceKind() === 'local_folder') return false;"),
            "local-first 页面 AI 不应继续加厚 page-ai fast-path；本地编辑应走受控文件工具"
        );
    }

    #[test]
    fn page_ai_local_source_passes_file_reference_fields_to_agent_run() {
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("function currentRootUri()"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("sourceKind: currentSourceKind()"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("rootUri: currentRootUri()"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS
            .contains("if (sourceKind && !payload.sourceKind) payload.sourceKind = sourceKind"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS
            .contains("if (rootUri && !payload.rootUri) payload.rootUri = rootUri"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageContext: scopedContext.pageContext"));
        assert!(
            SIDEBAR_PAGE_AI_RUNTIME_JS
                .contains("if (currentSourceKind() === 'local_folder') return false;"),
            "local source 不应进入 page-ai fast-path，后端会把 run 收敛为文件引用 scope"
        );
        assert!(
            !SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiEnrichOcrContextRefs"),
            "Page AI 目标包不应保留已退役 OCR sidecar context enrichment"
        );
        assert!(
            !SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS.contains("ocrRootRelativePath"),
            "Page AI target runtime 不应再注入旧 OCR sidecar 路径"
        );
    }

    #[test]
    fn page_ai_agent_target_picker_contract_is_visible_and_serialized() {
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiRenderRuntime"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS
            .contains("export function createSidebarPageAiRenderRuntime"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-target-button"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-target-popover"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-target-option"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-target-chip"));
        assert!(SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS.contains("primaryTargetId"));
        assert!(SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS.contains("targets: ["));
        assert!(SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS.contains("policy: {"));
    }

    #[test]
    fn page_ai_uses_backend_acp_session_runtime_store() {
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiSessionRuntime"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiSkillRuntime"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiTargetRuntime"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("function renderPageAiControls"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("function ensurePageAiDrawer"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("function renderPageAiConversation"));
        assert!(SIDEBAR_PAGE_AI_SKILL_RUNTIME_JS
            .contains("export function createSidebarPageAiSkillRuntime"));
        assert!(SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS
            .contains("export function createSidebarPageAiTargetRuntime"));
        assert!(
            SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS.contains("function currentPageAiOpenEditorsSnapshot")
        );
        assert!(SIDEBAR_PAGE_AI_TARGET_RUNTIME_JS.contains("mnote.agent_target_package.v1"));
        assert!(SIDEBAR_PAGE_AI_SKILL_RUNTIME_JS.contains("function pageAiSkillSourceOptions"));
        assert!(SIDEBAR_PAGE_AI_SKILL_RUNTIME_JS.contains("function pageAiSkillPreferenceTable"));
        assert!(SIDEBAR_PAGE_AI_SKILL_RUNTIME_JS.contains("ai.agent.reasonix.memory_enabled"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("function pageAiLoadBackendSessions"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("/api/hermes/client/sessions?"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("params.set('source', 'acp')"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains(
            "var draftSessions = pageAiNormalizeArray(pageUiState.pageAiSessions).filter"
        ));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS
            .contains("pageAiDedupeSessions(backendSessions.concat(draftSessions))"));
        assert!(
            SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("function pageAiLoadBackendSessionDetail")
        );
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("function pageAiSearchBackendSessions"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS
            .contains("function pageAiDeleteSelectedBackendSessions"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("function pageAiCheckActiveRun"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("/api/page-ai/sessions/"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("/active-run?"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-session-rename"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-session-delete"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-session-select"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-session-delete-selected"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-session-resume"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-session-search"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("function pageAiRenderThoughtGroup"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-thought-card"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-collapse-card"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiUsageSummary"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("usage.updated"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("thought.delta"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("permission.requested"));
        assert!(SIDEBAR_PAGE_AI_PERMISSION_RUNTIME_JS
            .contains("data-page-ai-permission-action=\"allow\""));
        assert!(SIDEBAR_PAGE_AI_PERMISSION_RUNTIME_JS
            .contains("data-page-ai-permission-action=\"deny\""));
        assert!(
            SIDEBAR_PAGE_AI_PERMISSION_RUNTIME_JS.contains("function pageAiHidePermissionDialog")
        );
        assert!(SIDEBAR_PAGE_AI_PERMISSION_RUNTIME_JS.contains("if (!message.resolved)"));
        assert!(
            !SIDEBAR_PAGE_AI_RUNTIME_JS.contains("(item.resolved ? ' disabled' : '')"),
            "已决 ACP permission 事件不能继续展示假审批按钮"
        );
        assert!(
            SIDEBAR_PAGE_AI_RUNTIME_JS.contains("session.info.updated"),
            "ACP SessionInfoUpdate 事件应通过 session.info.updated SSE 转发到前端"
        );
        assert!(
            SIDEBAR_PAGE_AI_RUNTIME_JS.contains("plan.updated"),
            "ACP PlanUpdate 事件应通过 plan.updated SSE 转发到前端"
        );
        assert!(
            SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-plan"),
            "plan 消息应渲染为 data-page-ai-plan 标记的轻量系统状态面板"
        );
        assert!(
            SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("执行计划 · "),
            "plan 面板标题应显示执行计划和步数"
        );
    }

    #[test]
    fn page_ai_session_ui_labels_local_shared_and_cloud_storage() {
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("function pageAiSessionStorageLabel"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("local_private"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("local_shared"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("sqlite_control_plane"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("convex_acp_runtime_store"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("本地私有"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("共享会话"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("账号会话"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("云端会话"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("sessionStorage:"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("permissionLevel:"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS.contains("shareId:"));
    }

    #[test]
    fn page_ai_acp_runtime_defaults_to_reasonix_and_keeps_hermes_switch() {
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("var PAGE_AI_SESSION_STORAGE_VERSION = 3"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function ensurePageAiStateFacade"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiAcpRuntime: 'reasonix'"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("pageAiAcpRuntime: 'reasonix'"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiPermissionRuntime"));
        assert!(SIDEBAR_PAGE_AI_PERMISSION_RUNTIME_JS.contains("function pageAiResolvePermission"));
        assert!(SIDEBAR_PAGE_AI_PERMISSION_RUNTIME_JS.contains("resolve-permission"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("createSidebarPageAiProfileRuntime"));
        assert!(SIDEBAR_PAGE_AI_PROFILE_RUNTIME_JS.contains("function pageAiNormalizeAcpRuntimes"));
        assert!(SIDEBAR_PAGE_AI_PROFILE_RUNTIME_JS.contains("return ['reasonix', 'hermes']"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiNormalizeAcpRuntimes(acpRuntimes)"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiAcpRuntimeSelect.value || 'reasonix'"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function handlePageAiClick"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function handlePageAiPointerDown"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("mnote.page_ai.drawer_width"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-resize-handle"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function pageAiOpenCitationUrl"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("a[data-page-ai-citation-link=\"true\"]"));
        assert!(SIDEBAR_PAGE_AI_MARKDOWN_RUNTIME_JS.contains("data-page-ai-citation-link=\"true\""));
        assert!(SIDEBAR_PAGE_AI_MARKDOWN_RUNTIME_JS.contains("renderPageAiMarkdownTable"));
        assert!(SIDEBAR_PAGE_AI_MARKDOWN_RUNTIME_JS.contains("wolai-page-ai-markdown-table-wrap"));
        assert!(SIDEBAR_PAGE_AI_MARKDOWN_RUNTIME_JS.contains("target=\"_blank\""));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function handlePageAiChange"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function installPageAiDelegates"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("document.addEventListener('visibilitychange'"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiCheckActiveRun(sessionId || undefined)"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function pageAiCheckAndResumeActiveRun"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function pageAiResumeActiveRunJournal"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function pageAiTrackStreamingRunEvent"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("void pageAiCheckAndResumeActiveRun().catch"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("mnote.page_ai_active_run_snapshot.v1"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("data-mnote-page-ai-active-run-last-seq"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("/api/page-ai/runs/"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("afterSeq="));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("pageAiCheckAndResumeActiveRun()"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("/api/page-ai/agents/descriptors"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("function pageAiLoadAgentDescriptors"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("data-page-ai-descriptor-field"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("function pageAiRenderDescriptorField"));
        assert!(SIDEBAR_PAGE_AI_RENDER_RUNTIME_JS.contains("data-page-ai-agent-descriptor-card"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("sidebarPageAi.installPageAiDelegates()"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("sidebarPageAi.handlePageAi"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("closestAction(e.target, '[data-page-ai-action"));
        assert!(SIDEBAR_PAGE_AI_SESSION_RUNTIME_JS
            .contains("activeAcpRuntime: pageUiState.pageAiAcpRuntime || 'reasonix'"));
        assert!(!SIDEBAR_PAGE_AI_RUNTIME_JS.contains("默认 (Hermes HTTP)"));
    }

    #[test]
    fn sidebar_tree_runtime_does_not_use_retired_query_preferred_snapshot_selector() {
        assert!(TREE_LIVE_CONTROLLER_JS.contains("tree:snapshot"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("tree:delta"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("usePreferredSidebarSnapshot"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("preferredSidebarSnapshot"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("liveSidebarTitle"));
    }

    #[test]
    fn page_layout_sidebar_toggle_is_wired_to_shell_state() {
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function toggleWorkspaceSidebar"));
        assert!(SIDEBAR_TREE_RUNTIME_JS
            .contains("sidebarShellRuntimeFunction('toggleWorkspaceSidebar')"));
        assert!(SIDEBAR_SHELL_RUNTIME_JS.contains("function toggleWorkspaceSidebar"));
        assert!(SIDEBAR_SHELL_RUNTIME_JS.contains("data-mnote-sidebar-collapsed"));
        assert!(SIDEBAR_SHELL_RUNTIME_JS.contains("function installWorkspaceSidebarResizer"));
        assert!(SIDEBAR_SHELL_RUNTIME_JS.contains("function restoreSidebarTreeTab"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("[data-mnote-action=\"toggle-sidebar\"]"));
    }

    #[test]
    fn sidebar_workspace_source_switch_remembers_real_cloud_workspace_before_local_folder() {
        assert!(
            !SIDEBAR_TREE_RUNTIME_JS
                .contains("rememberCloudWorkspaceId(resolveWorkspaceId(document.body))"),
            "进入本地文件夹前不能用 document.body 推导 workspaceId；它会在无 workspaceId URL 时回退成 default"
        );
        assert!(
            SIDEBAR_WORKSPACE_RUNTIME_JS.contains("rememberCurrentCloudWorkspaceId()"),
            "进入本地文件夹前应从 URL 或 DOM 子树读取真实 workspaceId"
        );
        assert!(
            SIDEBAR_WORKSPACE_RUNTIME_JS.contains("normalized.indexOf('local-ws:') === 0"),
            "本地文件夹 workspaceId 不能写入 lastCloudWorkspaceId"
        );
        assert!(
            SIDEBAR_WORKSPACE_RUNTIME_JS.contains("stored.trim().indexOf('local-ws:') !== 0"),
            "切回工作区时不能复用 local-ws:* 作为旧云 workspace"
        );
        assert!(
            !SIDEBAR_TREE_RUNTIME_JS
                .contains("targetUrl.searchParams.set('sourceKind', 'convex_workspace')"),
            "默认来源菜单不能跳到已退出 dev 主链的 convex_workspace"
        );
    }

    #[test]
    fn sidebar_tree_runtime_uses_local_folder_events_without_browser_reload() {
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("startLocalFolderSidebarWatch"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("refreshLocalFolderSidebarSnapshot"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("function currentWorkspaceId()"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("var rootUri = currentRootUri();"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("/api/tree/projections/sidebar"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("/api/tree/projections/file"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("var resolvedSidebarPayload = sidebarPayload ? (sidebarPayload.result || sidebarPayload) : null"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("var renderedPage = false;"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("renderedPage = await renderPageProjection(resolvedSidebarPayload);"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("refreshFileTreeParent(fileTreeScope)"));
        assert!(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("data-mnote-local-folder-watch-applied")
        );
        assert!(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("data-mnote-local-folder-watch-disabled")
        );
        assert!(
            TREE_LIVE_CONTROLLER_JS.contains("body.getAttribute('data-mnote-source-kind')"),
            "根入口由 SSR body 暴露 local_folder 时，tree live 不能误走 retired Convex WS/SSE"
        );
        assert!(
            TREE_LIVE_CONTROLLER_JS.contains("bootstrap.transport === 'local-folder-events'"),
            "本地文件夹 bootstrap transport 应直接选择 /api/local-folder/events"
        );
        assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("__mnoteLocalFolderEventBus"));
        assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("startLocalFolderWatcher"));
        assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("connections.has(key)"));
        assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("function pathArrayOf"));
        assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("function queueSidebarRefresh"));
        assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS
            .contains("mnote:local-folder:sidebar-refresh-requested"));
        assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS
            .contains("data-mnote-local-folder-event-bus-connections"));
        assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("tree:local-folder-watch-batch"));
        assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("viaEventBus: true"));
        assert!(LOCAL_FOLDER_EVENT_BUS_RUNTIME_JS.contains("emitSyntheticWatchBatch"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("__mnoteLocalFolderEventBus"));
        assert!(SIDEBAR_PAGE_AI_RUNTIME_JS.contains("synthetic_page_ai_receipt"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("__mnoteLocalFolderEventBus"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("startLocalFolderWatcher"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("mnote:local-folder:sidebar-refresh-requested"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("event.detail.viaEventBus === true"));
        assert!(!SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("/api/tree/local-folder-watch"));
        assert!(!SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("fetch(window.location.href, { headers: { accept: 'text/html' } })"));
        assert!(!SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("replaceSidebarTreeFromDocument(nextDocument, 'sidebar-tree-root')"));
        assert!(!SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("window.location.reload"));
    }

    #[test]
    fn sidebar_upload_runtime_routes_local_markdown_assets_to_local_folder() {
        let current_document_function =
            js_function_body(SIDEBAR_TREE_RUNTIME_JS, "currentDocumentId");
        let upload_function = js_function_body(SIDEBAR_TREE_RUNTIME_JS, "uploadFileToMediaAsset");
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("currentSourceKind() === 'local_folder'"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("/api/local-folder/assets/upload"));
        assert!(
            upload_function.contains("var rootUri = currentRootUri();"),
            "本地上传必须复用 currentRootUri()，否则 / 页面由 body dataset 提供 rootUri 时 .md 上传会失败"
        );
        assert!(
            current_document_function.contains("params.get('pageId')"),
            "SQLite/local-first 根入口可能通过 pageId 或 DOM 暴露当前页面，不能只解析 /documents/:id"
        );
        assert!(
            current_document_function.contains("data-pane-document-id")
                && current_document_function.contains("data-document-id"),
            "主编辑器上传应能从当前文档 DOM 回退解析 documentId"
        );
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("localForm.append('rootUri', rootUri)"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("localForm.append('uploadIntent', uploadIntent)"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("uploadIntent: 'editor.markdown.attach'"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("uploadIntent: 'filetree.folder.drop'"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("localForm.append('documentId', documentId)"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("isLocalUploadedAsset(asset)"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("buildLocalOnlyOfficeOpenUrl"));
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("localOfficeUrl"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("fileTreeIconKindForFileName(title)"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("refreshLocalFolderSidebarSnapshot"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("wolai:local-assets-changed"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function resolveEditorUploadContext"));
        assert!(SIDEBAR_TREE_RUNTIME_JS
            .contains("localUploadRuntimeFunction('resolveEditorUploadContext')"));
        assert!(SIDEBAR_TREE_RUNTIME_JS
            .contains("localUploadRuntimeFunction('openEditorUploadFilePicker')"));
        assert!(SIDEBAR_TREE_RUNTIME_JS
            .contains("localUploadRuntimeFunction('insertUploadedAssetIntoEditor')"));
        assert!(SIDEBAR_TREE_RUNTIME_JS
            .contains("localUploadRuntimeFunction('uploadFileToMediaAsset')"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("localUploadRuntimeFunction('uploadMediaAsset')"));
        assert!(SIDEBAR_TREE_RUNTIME_JS
            .contains("localUploadRuntimeFunction('uploadFilesWithResolvedTarget')"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("__mnoteLastEditorUploadRoot"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-pane-role=\"primary\""));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("insertUploadedAssetIntoEditor(localPayload.asset, editorRootFromUploadOptions(options))"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("editorRoot: uploadContext.root"));
    }

    #[test]
    fn sidebar_attachment_open_runtime_receives_closest_action_dependency() {
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function closestAction"));
        let injection_start = SIDEBAR_TREE_RUNTIME_JS
            .find("const sidebarAttachmentOpen = createSidebarAttachmentOpenRuntime({")
            .expect("attachment runtime injection");
        let injection_end = SIDEBAR_TREE_RUNTIME_JS[injection_start..]
            .find("  });")
            .map(|offset| injection_start + offset)
            .expect("attachment runtime injection end");
        let injection = &SIDEBAR_TREE_RUNTIME_JS[injection_start..injection_end];
        assert!(
            injection.contains("closestAction"),
            "attachment open runtime 需要显式注入 closestAction，避免首屏点击监听安装时 ReferenceError"
        );
        assert!(
            injection.contains("hydrateEditorAttachmentMeta"),
            "attachment open runtime 需要显式注入 hydrateEditorAttachmentMeta，避免上传附件增强时 ReferenceError"
        );
        assert!(
            SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
                .contains("closestAction = typeof injectedClosestAction === 'function'")
                || SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("dependencies.closestAction"),
            "attachment open runtime 应从 dependencies 读取 closestAction，而不是依赖外层闭包"
        );
    }

    #[test]
    fn local_upload_runtime_contains_editor_upload_context_helpers() {
        const LOCAL_UPLOAD_RUNTIME_JS: &str =
            include_str!("../../../browser/local-upload-runtime.js");
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function resolveEditorUploadContext"));
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function editorRootFromUploadOptions"));
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function openEditorUploadFilePicker"));
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function fetchWithTimeout"));
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function uploadLocalFolderAsset"));
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function uploadMediaAsset"));
        assert!(!LOCAL_UPLOAD_RUNTIME_JS.contains("/api/media/upload"));
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("data-mnote-media-upload-retired"));
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function insertUploadedAssetIntoEditor"));
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function dispatchUploadedEditorChange"));
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function persistUploadedEditorChange"));
        assert!(
            LOCAL_UPLOAD_RUNTIME_JS.contains("function persistLocalFolderSelfChangeSuppression")
        );
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("editorSource: 'local-upload-runtime'"));
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("mnote:leptos-tiptap-spike:change"));
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function uploadFileToMediaAsset"));
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("function uploadFilesWithResolvedTarget"));
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("data-mnote-last-upload-inserted"));
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("uploadFileToMediaAsset: uploadFileToMediaAsset"));
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("uploadFilesWithResolvedTarget"));
        assert!(LOCAL_UPLOAD_RUNTIME_JS.contains("__mnoteLastEditorUploadRoot"));
        assert!(
            LOCAL_UPLOAD_RUNTIME_JS
                .contains("var imageUrl = localOpenUrl || fallbackUrl || markdownHref;"),
            "图片上传首屏必须使用本地 open URL 渲染，避免相对 href 在 /documents 下命中退役 Convex 兼容路径"
        );
        assert!(
            LOCAL_UPLOAD_RUNTIME_JS.contains("setImage({ src: imageUrl"),
            "插入图片时不能直接把 ./asset.png 作为 img.src，否则刷新前会显示破损"
        );
    }

    #[test]
    fn sidebar_filetree_runtime_helpers_are_externalized_with_inline_fallback() {
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function fileTreeRuntimeFunction"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("window.__mnoteFileTreeRuntime"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('fileTreeRowDocumentId')"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('fileTreeRowAssetId')"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('fileTreeRowLocalRelativePath')"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('fileTreeAssetDownloadDetail')"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('fileTreeRowsByRowIds')"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('fileTreeChildCount')"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("fileTreeRuntimeFunction('revealFileTreeRow')"));
        assert!(
            SIDEBAR_TREE_RUNTIME_JS.contains("fileTreeRuntimeFunction('revealFileTreeAssetRow')")
        );
        assert!(
            SIDEBAR_TREE_RUNTIME_JS.contains("fileTreeRuntimeFunction('appendUploadedAssetRow')")
        );
        assert!(SIDEBAR_FILETREE_UPLOAD_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('fileTreeUploadTargetFallback')"));
        assert!(SIDEBAR_FILETREE_UPLOAD_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('fileTreeRowsForUploadPreflight')"));
        assert!(SIDEBAR_FILETREE_UPLOAD_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('fileTreeDocumentParentsForPreflight')"));
        assert!(SIDEBAR_FILETREE_UPLOAD_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('fileTreeTargetChildrenForPreflight')"));
        assert!(SIDEBAR_FILETREE_UPLOAD_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('fileTreeDropPreflightRows')"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('readProjection')"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('readSidebarDataset')"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('readDatasetProjection')"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('projectionItems')"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('hasProjectionItems')"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("fileTreeRuntimeFunction('nodeIdOf')"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("fileTreeRuntimeFunction('rowIdOf')"));
        assert!(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("fileTreeRuntimeFunction('parentIdOf')")
        );
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("fileTreeRuntimeFunction('titleOf')"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('fileWorkspaceRelativePath')"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('groupRowsByParent')"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('replaceSidebarTreeFromDocument')"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('applyLocalFolderSidebarSnapshot')"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('markLocalFolderWatchApplied')"));
        let document_id_body =
            js_function_body(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS, "fileTreeRowDocumentId");
        assert!(
            document_id_body.contains("data-document-id")
                && document_id_body.contains("data-doc-id"),
            "inline fallback 必须保留旧 data attribute 解析"
        );
        let replace_body = js_function_body(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS,
            "replaceSidebarTreeFromDocument",
        );
        assert!(
            replace_body.contains("current.innerHTML = next.innerHTML")
                && replace_body.contains("current.setAttribute(attr.name, attr.value)"),
            "inline fallback 必须保留 DOM copy 行为"
        );
        let apply_body = js_function_body(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS,
            "applyLocalFolderSidebarSnapshot",
        );
        assert!(
            apply_body.contains("options.pageRootId || 'sidebar-tree-root'")
                && apply_body.contains("options.fileRootId || 'sidebar-file-tree-root'")
                && apply_body.contains("replaceSidebarTreeFromDocument(nextDocument, pageRootId)")
                && apply_body.contains("replaceSidebarTreeFromDocument(nextDocument, fileRootId)"),
            "inline fallback 必须保留 sidebar/page tree DOM 应用行为"
        );
        let watch_applied_body = js_function_body(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS,
            "markLocalFolderWatchApplied",
        );
        assert!(
            watch_applied_body.contains("data-mnote-local-folder-watch-applied")
                && watch_applied_body.contains("String(value || 'projection')"),
            "inline fallback 必须保留 local folder watch projection 标记"
        );
    }

    #[test]
    fn filetree_runtime_contains_row_accessor_helpers() {
        assert!(FILETREE_RUNTIME_JS.contains("window.__mnoteFileTreeRuntime"));
        assert!(FILETREE_RUNTIME_JS.contains("function fileTreeRowDocumentId"));
        assert!(FILETREE_RUNTIME_JS.contains("function fileTreeRowAssetId"));
        assert!(FILETREE_RUNTIME_JS.contains("function decodeLocalEncodedPath"));
        assert!(FILETREE_RUNTIME_JS.contains("function fileTreeRowLocalRelativePath"));
        assert!(FILETREE_RUNTIME_JS.contains("function fileTreeRowLocalUploadTargetRelativePath"));
        assert!(FILETREE_RUNTIME_JS.contains("function fileTreeAssetDownloadDetail"));
        assert!(FILETREE_RUNTIME_JS.contains("function fileTreeRowsByRowIds"));
        assert!(FILETREE_RUNTIME_JS.contains("function fileTreeChildCount"));
        assert!(FILETREE_RUNTIME_JS.contains("function revealFileTreeRow"));
        assert!(FILETREE_RUNTIME_JS.contains("function revealFileTreeAssetRow"));
        assert!(FILETREE_RUNTIME_JS.contains("function appendUploadedAssetRow"));
        assert!(FILETREE_RUNTIME_JS.contains("function fileTreeUploadTargetFallback"));
        assert!(FILETREE_RUNTIME_JS.contains("function fileTreeRowsForUploadPreflight"));
        assert!(FILETREE_RUNTIME_JS.contains("function fileTreeDocumentParentsForPreflight"));
        assert!(FILETREE_RUNTIME_JS.contains("function fileTreeTargetChildrenForPreflight"));
        assert!(FILETREE_RUNTIME_JS.contains("function fileTreeDropPreflightRows"));
        assert!(
            FILETREE_RUNTIME_JS.contains("function fileTreeDocumentWorkspacesForUploadPreflight")
        );
        assert!(FILETREE_RUNTIME_JS.contains("function fileTreeUploadTargetPreflightBody"));
        assert!(FILETREE_RUNTIME_JS.contains("function shortMindmapFileName"));
        assert!(FILETREE_RUNTIME_JS.contains("function parseMindmapApiTarget"));
        assert!(FILETREE_RUNTIME_JS.contains("function requestMethod"));
        assert!(FILETREE_RUNTIME_JS.contains("function requestBodyHasMindmapCreateOnly"));
        assert!(FILETREE_RUNTIME_JS.contains("function readProjection"));
        assert!(FILETREE_RUNTIME_JS.contains("function readSidebarDataset"));
        assert!(FILETREE_RUNTIME_JS.contains("function readDatasetProjection"));
        assert!(FILETREE_RUNTIME_JS.contains("function projectionItems"));
        assert!(FILETREE_RUNTIME_JS.contains("function hasProjectionItems"));
        assert!(FILETREE_RUNTIME_JS.contains("function nodeIdOf"));
        assert!(FILETREE_RUNTIME_JS.contains("function rowIdOf"));
        assert!(FILETREE_RUNTIME_JS.contains("function parentIdOf"));
        assert!(FILETREE_RUNTIME_JS.contains("function titleOf"));
        assert!(FILETREE_RUNTIME_JS.contains("function fileWorkspaceRelativePath"));
        assert!(FILETREE_RUNTIME_JS.contains("function groupRowsByParent"));
        assert!(FILETREE_RUNTIME_JS.contains("function replaceSidebarTreeFromDocument"));
        assert!(FILETREE_RUNTIME_JS.contains("function applyLocalFolderSidebarSnapshot"));
        assert!(FILETREE_RUNTIME_JS.contains("function markLocalFolderWatchApplied"));
        assert!(FILETREE_RUNTIME_JS.contains("appendUploadedAssetRow: appendUploadedAssetRow"));
        assert!(FILETREE_RUNTIME_JS.contains("groupRowsByParent: groupRowsByParent"));
        assert!(FILETREE_RUNTIME_JS
            .contains("replaceSidebarTreeFromDocument: replaceSidebarTreeFromDocument"));
        assert!(FILETREE_RUNTIME_JS
            .contains("applyLocalFolderSidebarSnapshot: applyLocalFolderSidebarSnapshot"));
        assert!(FILETREE_RUNTIME_JS
            .contains("markLocalFolderWatchApplied: markLocalFolderWatchApplied"));
        assert!(FILETREE_RUNTIME_JS
            .contains("fileTreeRowsForUploadPreflight: fileTreeRowsForUploadPreflight"));
        assert!(FILETREE_RUNTIME_JS
            .contains("fileTreeDocumentParentsForPreflight: fileTreeDocumentParentsForPreflight"));
        assert!(FILETREE_RUNTIME_JS
            .contains("fileTreeTargetChildrenForPreflight: fileTreeTargetChildrenForPreflight"));
        assert!(
            FILETREE_RUNTIME_JS.contains("fileTreeDropPreflightRows: fileTreeDropPreflightRows")
        );
        assert!(FILETREE_RUNTIME_JS.contains(
            "fileTreeDocumentWorkspacesForUploadPreflight: fileTreeDocumentWorkspacesForUploadPreflight"
        ));
        assert!(FILETREE_RUNTIME_JS
            .contains("fileTreeUploadTargetPreflightBody: fileTreeUploadTargetPreflightBody"));
        assert!(FILETREE_RUNTIME_JS.contains("shortMindmapFileName: shortMindmapFileName"));
        assert!(FILETREE_RUNTIME_JS.contains("parseMindmapApiTarget: parseMindmapApiTarget"));
        assert!(FILETREE_RUNTIME_JS.contains("requestMethod: requestMethod"));
        assert!(FILETREE_RUNTIME_JS
            .contains("requestBodyHasMindmapCreateOnly: requestBodyHasMindmapCreateOnly"));
        assert!(SIDEBAR_FILETREE_UPLOAD_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('fileTreeDocumentWorkspacesForUploadPreflight')"));
        assert!(SIDEBAR_FILETREE_UPLOAD_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('fileTreeUploadTargetPreflightBody')"));
        assert!(SIDEBAR_FILETREE_UPLOAD_RUNTIME_JS
            .contains("fileTreeRowsForUploadPreflight: fileTreeRowsForUploadPreflight"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("fileTreeRuntimeFunction('shortMindmapFileName')"));
        assert!(
            SIDEBAR_TREE_RUNTIME_JS.contains("fileTreeRuntimeFunction('parseMindmapApiTarget')")
        );
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("fileTreeRuntimeFunction('requestMethod')"));
        assert!(SIDEBAR_TREE_RUNTIME_JS
            .contains("fileTreeRuntimeFunction('requestBodyHasMindmapCreateOnly')"));
        assert!(FILETREE_RUNTIME_JS
            .contains("uploadIntent: String(detail.uploadIntent || 'filetree.folder.drop')"));
        assert!(FILETREE_RUNTIME_JS
            .contains("uploadIntent: String(detail.uploadIntent || 'editor.markdown.attach')"));
    }

    #[test]
    fn sidebar_filetree_selection_runtime_helpers_are_externalized_with_inline_fallback() {
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function fileTreeSelectionRuntimeFunction"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("window.__mnoteFileTreeSelectionRuntime"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("fileTreeSelectionRuntimeFunction('visibleFileTreeRows')"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("fileTreeSelectionRuntimeFunction('syncSidebarFileTreeSelection')"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("fileTreeSelectionRuntimeFunction('selectSidebarFileTreeRow')"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("fileTreeSelectionRuntimeFunction('selectedSidebarFileTreeRowIdsForDrag')"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("fileTreeSelectionRuntimeFunction('selectedSidebarFileTreeRows')"));
        let select_body = js_function_body(
            SIDEBAR_FILETREE_COMMAND_RUNTIME_JS,
            "selectSidebarFileTreeRow",
        );
        assert!(
            select_body.contains("shiftKey && sidebarFileTreeSelection.anchorRowId")
                && select_body.contains("ctrlKey"),
            "inline fallback 必须保留 shift range 与 ctrl/meta toggle"
        );
        let select_document_body = js_function_body(
            SIDEBAR_FILETREE_COMMAND_RUNTIME_JS,
            "selectSidebarFileTreeDocument",
        );
        assert!(
            select_document_body.contains("activateSidebarFileTreeRow(row, options)")
                && SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("function activateSidebarFileTreeRow")
                && SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("selectSidebarFileTreeRow(row, { ctrlKey: false, metaKey: false, shiftKey: false })"),
            "document selection 应复用 filetree row selection runtime/fallback"
        );
    }

    #[test]
    fn filetree_selection_runtime_contains_selection_helpers() {
        assert!(FILETREE_SELECTION_RUNTIME_JS.contains("window.__mnoteFileTreeSelectionRuntime"));
        assert!(FILETREE_SELECTION_RUNTIME_JS.contains("function visibleFileTreeRows"));
        assert!(FILETREE_SELECTION_RUNTIME_JS.contains("function syncSidebarFileTreeSelection"));
        assert!(FILETREE_SELECTION_RUNTIME_JS.contains("function selectSidebarFileTreeRow"));
        assert!(
            FILETREE_SELECTION_RUNTIME_JS.contains("function selectedSidebarFileTreeRowIdsForDrag")
        );
        assert!(FILETREE_SELECTION_RUNTIME_JS.contains("function selectedSidebarFileTreeRows"));
        assert!(FILETREE_SELECTION_RUNTIME_JS.contains("tree.filetree.selection.changed"));
    }

    #[test]
    fn sidebar_tree_runtime_focuses_restored_local_folder_row() {
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("applyPendingLocalFolderRestoreFocusOnce"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("mnote.pendingLocalFolderRestoreFiletreeRowId"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("data-mnote-local-folder-restore-focused-row-id"));
    }

    #[test]
    fn sidebar_runtime_routes_local_mindmap_and_office_assets() {
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("withLocalMindmapSourceParams"));
        assert!(
            SIDEBAR_TREE_RUNTIME_JS.contains("url.searchParams.set('sourceKind', 'local_folder')")
        );
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("url.searchParams.set('rootUri', rootUri)"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("if (!parentRow && currentSourceKind() === 'local_folder' && objectKind === 'mindmap') return false;"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("if (!appended && currentSourceKind() === 'local_folder') void refreshLocalFolderSidebarSnapshot();"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS
            .contains("data-mnote-last-mindmap-asset-open-mode', 'local-mindmap-object-shell'"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("buildLocalOnlyOfficeOpenUrl"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS
            .contains("var localOfficeUrl = buildLocalOnlyOfficeOpenUrl"));
    }

    #[test]
    fn sidebar_local_folder_authorized_root_normalizes_plain_path_root_uri() {
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS
            .contains("function normalizeGrantedLocalFolderRootUri(grant)"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS
            .contains("if (rootPath) return pathToFileRootUri(rootPath);"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS
            .contains("if (rootUri) return pathToFileRootUri(rootUri);"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS
            .contains("var rootUri = normalizeGrantedLocalFolderRootUri(grant);"));
        assert!(
            SIDEBAR_WORKSPACE_RUNTIME_JS.contains("function isDefaultWorkspaceAutoGrant(grant)")
        );
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS
            .contains("if (isDefaultWorkspaceAutoGrant(grant)) return;"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS.contains("function recentLocalRootLabel(rootUri)"));
        assert!(SIDEBAR_WORKSPACE_RUNTIME_JS
            .contains("button.textContent = recentLocalRootLabel(rootUri);"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS
            .contains("button.textContent = rootUri.replace(/^file:\\/\\//, '')"));
    }

    #[test]
    fn sidebar_runtime_supports_resizable_filetree_and_hover_titles() {
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function installWorkspaceSidebarResizer()"));
        assert!(SIDEBAR_TREE_RUNTIME_JS
            .contains("sidebarShellRuntimeFunction('installWorkspaceSidebarResizer')"));
        assert!(SIDEBAR_SHELL_RUNTIME_JS.contains("data-mnote-sidebar-resizer"));
        assert!(SIDEBAR_SHELL_RUNTIME_JS.contains("mnote.workspace.sidebarWidth.v1"));
        assert!(
            SIDEBAR_SHELL_RUNTIME_JS.contains("shell.style.setProperty('--mnote-sidebar-width'")
        );
        assert!(FILETREE_DND_RUNTIME_JS.contains("window.__mnoteFileTreeDndRuntime"));
        assert!(FILETREE_KEYBOARD_RUNTIME_JS.contains("window.__mnoteFileTreeKeyboardRuntime"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("installWorkspaceSidebarResizer();"));
        assert!(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains(r#"" title="' + escapeHtml(title) + '""#)
        );
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains(r#"class="tree-link-title" title="' + escapeHtml(title) + '""#));
    }

    #[test]
    fn sidebar_runtime_opens_local_pdf_assets_in_active_resource_tab_by_default() {
        assert!(SIDEBAR_TREE_RUNTIME_JS
            .contains("function shouldOpenLocalResourceInNewWindow(fileName)"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("return false;"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("var shouldOpenInNewWindow = forceNewWindow || shouldOpenLocalResourceInNewWindow(localFileName);"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS
            .contains("if (!shouldOpenInNewWindow && await openLocalResourceInActiveTab({"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains(
            "openTarget: e.shiftKey || e.ctrlKey || e.metaKey ? 'new-window' : 'active-tab'"
        ));
    }

    #[test]
    fn sidebar_tree_runtime_contains_dev_hot_reload_client() {
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("installMnoteDevHotReload"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("/api/dev/hot-reload"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("data-mnote-dev-hot-reload"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("import.meta.url"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("mnoteDevHotReloadEnabled()"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("\n  installMnoteDevHotReload();\n"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("window.clearInterval(timer)"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("mnote:primary-document-activated"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("mnote:page-aggregate-synced"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("pageUiState.pageOptions = null"));
    }

    #[test]
    fn sidebar_tree_runtime_renders_context_menu_and_scoped_title_updates() {
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("openTreeContextMenu"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("mnote-tree-context-menu"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("复制访问链接"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("删除到垃圾桶"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("function createFileTreeFolder"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("action: 'create_folder'"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("function fileTreeMenuTargetParentId"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("if (action === 'new-folder')"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("fileTreeCopyPath(detail, trigger)"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("if (detail.contextKind === 'filetree' && action === 'download')"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("downloadSelectedFileTreeAssetRows(detail, trigger)"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("function selectedSidebarFileTreeRowsForDownload"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("function selectedSidebarFileTreeAssetRowsForDownload"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("data-local-relative-path"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("targetRelativePath"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains(
            "recordFileTreeAction(downloads.length > 1 ? 'bulk-download' : 'download', primary)"
        ));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("data-mnote-filetree-download-count"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("function triggerBrowserDownload"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("openEditorAttachmentMenu(editorAttachmentLink, editorAttachmentLink, { x: event.clientX, y: event.clientY });"));
        assert!(SIDEBAR_TREE_RUNTIME_JS
            .contains("var localFilePath = localFilePathFromAssetId(assetId);"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("attachmentMetaCache[assetId] = localMeta;"));
        assert!(SIDEBAR_PAGE_TREE_RUNTIME_JS.contains(
            "var pageRow = closestAction(event.target, '.tree-row[data-shell-mode=\"page\"]');"
        ));
        assert!(SIDEBAR_PAGE_TREE_RUNTIME_JS
            .contains("var action = btn ? btn.getAttribute('data-rust-action') : 'open';"));
        assert!(SIDEBAR_PAGE_TREE_RUNTIME_JS.contains("var openTrigger = btn || pageRow;"));
        assert!(SIDEBAR_PAGE_TREE_RUNTIME_JS
            .contains("var workspaceId = resolveWorkspaceId(openTrigger);"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains(
            r#".tree-row[data-shell-mode="page"][data-node-id="' + escaped + '"] > .tree-link > .tree-link-title"#
        ));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains(
            r#".tree-row[data-shell-mode="filetree"][data-row-id="' + escapedDocRowId + '"] > .tree-link > .tree-link-title"#
        ));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains(r#"[data-page-title-input="true"][data-document-id="' + escaped + '"]"#));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains(r#".wolai-breadcrumb-current [data-page-title-current]"#));
        assert!(!SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains(
            r#".tree-row[data-document-id="' + escaped + '"] > .tree-link > .tree-link-title"#
        ));
        assert!(!SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains(r#"[data-node-id="' + cssEscape(documentId) + '"] .tree-link-title"#));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("renderSidebarSnapshot"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("kernel_file_tree_projection"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("deltaNeedsProjectionRefresh"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("upsert_documents"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("setTreeLiveApplyError"));
        assert!(!SIDEBAR_TREE_RUNTIME_JS.contains("scheduleProjectionRefresh"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("/api/tree/projections/sidebar"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("/api/tree/projections/file"));
    }

    #[test]
    fn sidebar_filetree_runtime_uses_markdown_page_row_without_local_index_child() {
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("fileTreePageTitle(title)"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("normalizeFileTreePageRenameTitle"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("validateFileTreeRename"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("同级已存在同名页面"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("文件名不能包含"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("rowId === 'doc:' + activeId"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("currentFileTreeActiveRowId"));
        assert!(SIDEBAR_TREE_RUNTIME_JS
            .contains("window.location.pathname.match(/^\\/mindmap\\/([^\\/]+)\\/([^\\/]+)/)"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains(
            "var selected = activeRowId ? rowId === activeRowId : rowId === 'doc:' + activeId"
        ));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("renderFileRows('', groupRowsByParent(rows), activeId, activeRowId)"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("renderFileRows(nodeId, grouped, activeId, activeRowId)"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("normalizeMindmapFileTreeTitle"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("shortMindmapFileName(assetId)"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("var title = isFileTreeProjectionPageRow(rowKind, assetId)"));
        assert!(!SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("title: 'index.md'"));
        assert!(!SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("rowId === 'index:' + activeId"));
    }

    #[test]
    fn sidebar_filetree_runtime_supports_lazy_children_projection() {
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("/api/tree/projections/file/children"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("parentRelativePath"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("data-filetree-children-loaded"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("rowsByParent: new Map()"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("loadedParents: new Set()"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("loadingParents: new Map()"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("dirtyParents: new Set()"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("fileTreeExpandedRelativePaths"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("if (fileTreeState.loadingParents.has(key))"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("return fileTreeState.loadingParents.get(key);"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("function markExistingFileTreeChildrenLoaded"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("if (!stale && markExistingFileTreeChildrenLoaded(row, button, options))"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("scheduleRestorePersistedFileTreeExpansionState();"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function patchFileTreeParentChildren"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("return patchFileTreeParentChildren(parentRelativePath, rows);"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("var renderedPage = false;"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("renderedPage = await renderPageProjection(resolvedSidebarPayload);"));
        let render_start = SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .find("function renderFileProjection(projection)")
            .expect("renderFileProjection");
        let render_end = SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS[render_start..]
            .find("function renderSidebarSnapshot(payload)")
            .map(|offset| render_start + offset)
            .expect("renderFileProjection end");
        let render_body = &SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS[render_start..render_end];
        assert!(!render_body.contains("tree.innerHTML ="));
        assert!(render_body.contains("tree.replaceChildren"));
        let patch_branch = render_body
            .find("patchFileTreeParentChildren(parentRelativePath, rows)")
            .expect("non-root parent patch");
        let root_replace = render_body
            .find("tree.replaceChildren")
            .expect("root replacement");
        assert!(
            patch_branch < root_replace,
            "非 scope parent projection 必须先局部 patch，不能替换整棵 filetree"
        );
        let load_start = SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .find("async function loadFileTreeChildren(row, button, options)")
            .expect("lazy children loader");
        let optimistic_expand = SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS[load_start..]
            .find("setTreeRowExpanded(row, button, true, options);")
            .expect("lazy loading should mark the requested folder expanded before fetch")
            + load_start;
        let fetch_start = SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS[load_start..]
            .find("getFileTreeChildren(relativePath)")
            .expect("lazy children datasource call")
            + load_start;
        assert!(
            optimistic_expand < fetch_start,
            "慢目录加载期间必须先保存 expanded 状态，否则 refresh/create-page 会把刚点开的文件夹折叠"
        );
    }

    #[test]
    fn sidebar_filetree_runtime_discards_stale_generation_results() {
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("staleParents: new Set()"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("requestGeneration: 0"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function beginFileTreeRequest"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function isLatestFileTreeRequest"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("if (!isLatestFileTreeRequest(key, generation))"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("fileTreeState.staleParents.add(key)"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("fileTreeState.requestGeneration += 1"));
    }

    #[test]
    fn sidebar_runtime_applies_page_projection_with_generation_and_yield() {
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("pageTreeRequestGeneration: 0"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function beginPageTreeRequest"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function isLatestPageTreeRequest"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("async function renderPageProjection"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("await yieldForPageTreeRender()"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("if (!isLatestPageTreeRequest(generation))"));
    }

    #[test]
    fn sidebar_filetree_runtime_refreshes_scoped_page_tree_projection() {
        assert!(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
                .contains("sidebarUrl.searchParams.set('parentRelativePath', fileTreeScope)"),
            "scoped filetree 入口刷新 PageTree 时必须请求同 scope projection"
        );
        assert!(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("sidebarTreeViewStateFor('pagetree')"),
            "SSR 初始 PageTree 也必须触发 pagetree 用户保存态加载"
        );
    }

    #[test]
    fn sidebar_page_create_does_not_full_refresh_local_folder_sidebar() {
        assert!(
            !SIDEBAR_PAGE_TREE_RUNTIME_JS.contains("await refreshLocalFolderSidebarSnapshot();"),
            "本地新建页面不能全量刷新 sidebar，否则会折叠/重建其它已展开节点"
        );
        assert!(
            SIDEBAR_PAGE_TREE_RUNTIME_JS.contains("await refreshLocalFolderAfterCommand('create'"),
            "本地新建页面应等待局部 parent refresh，而不是全量刷新 sidebar"
        );
        assert!(
            SIDEBAR_PAGE_TREE_RUNTIME_JS.contains("selectSidebarFileTreeDocument(nextDocumentId"),
            "本地新建页面仍应只 reveal/select 新节点"
        );
    }

    #[test]
    fn sidebar_filetree_runtime_prefers_command_affected_parents() {
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("function addAffectedParentsFromCommandResult"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("Array.isArray(result && result.affectedParents)"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("result && result.execution && result.execution.affectedParents"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("addAffectedParentsFromCommandResult(parents, result)"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("data-mnote-filetree-command-refresh-fallback"));
    }

    #[test]
    fn sidebar_filetree_runtime_batches_local_command_refresh() {
        assert!(
            SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("function nextFileTreeOperationBatchId")
        );
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("removeFileTreeAssetRow"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("removeFileTreeAssetRow,"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("batchId: batchId"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("tree:local-command-batch-complete"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function queueFileTreeBatchRefresh"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function flushFileTreeBatchRefresh"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("data-mnote-filetree-batch-refresh-pending"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("data-mnote-filetree-batch-refresh-applied"));
    }

    #[test]
    fn sidebar_filetree_runtime_handles_watch_batch_and_structured_error() {
        assert!(TREE_LIVE_CONTROLLER_JS.contains("watch_batch"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("tree:local-folder-watch-batch"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("tree_error"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("tree:error"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function applyLocalFolderWatchBatch"));
        assert!(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function watchBatchNeedsPageTreeRefresh")
        );
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("data-mnote-local-folder-watch-sidebar-refresh-skipped"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("data-mnote-local-folder-watch-batch-applied"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("data-mnote-tree-live-error-schema"));
    }

    #[test]
    fn sidebar_filetree_runtime_has_view_state_namespace() {
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("var fileTreeViewState = {"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("expandedParents: new Set()"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("selectedRowIds: new Set()"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("focusedRowId: ''"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("activeRowId: ''"));
        assert!(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("var fileTreeState = fileTreeViewState;")
        );
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("var fileTreeExpandedRelativePaths = fileTreeViewState.expandedParents;"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains(
            "if (rootUri === fileTreeLazyCacheRootUri && scope === fileTreeState.scope) return;"
        ));
    }

    #[test]
    fn sidebar_filetree_runtime_hydrates_visible_expanded_rows_on_idle() {
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("function scheduleHydrateVisibleExpandedFileTreeRows"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("function hydrateVisibleExpandedFileTreeRows"));
        assert!(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("FILETREE_VISIBLE_EXPANDED_HYDRATE_MAX")
        );
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("requestIdleCallback"));
        assert!(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("data-mnote-filetree-idle-hydrate-applied")
        );
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("row.getAttribute('aria-expanded') === 'true'"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("row.getAttribute('data-filetree-children-loaded') !== 'true'"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("loadFileTreeChildren(row, button, { persist: false, idleHydrate: true })"));

        let restore_start = SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .find("function restorePersistedFileTreeExpansionState")
            .expect("restorePersistedFileTreeExpansionState");
        let restore_end = SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS[restore_start..]
            .find("function installTreeLiveApplyEventListeners")
            .map(|offset| restore_start + offset)
            .expect("restore function end");
        let restore_body = &SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS[restore_start..restore_end];
        assert!(restore_body.contains("scheduleHydrateVisibleExpandedFileTreeRows('restore')"));
        assert!(
            !restore_body.contains("then(function(loaded)"),
            "恢复 view-state 不能递归拉取历史 expanded path"
        );
    }

    #[test]
    fn sidebar_filetree_runtime_reprojects_selection_focus_after_patch() {
        assert!(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function rememberFileTreeSelectionState")
        );
        assert!(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("function reprojectFileTreeSelectionState")
        );
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("fileTreeViewState.selectedRowIds.add(rowId)"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains(
            "row.setAttribute('data-selected', String(fileTreeViewState.selectedRowIds.has(rowId)))"
        ));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains(
            "row.setAttribute('data-focused', String(rowId === fileTreeViewState.focusedRowId))"
        ));
    }

    #[test]
    fn sidebar_filetree_runtime_can_reveal_unloaded_resource_path() {
        assert!(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("async function revealFileTreeResource")
        );
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("function fileTreeParentChainForRelativePath"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("await getFileTreeChildren(parentRelativePath)"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
            .contains("fileTreeViewState.selectedRowIds = new Set([targetRowId])"));
        assert!(SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS.contains("revealFileTreeResource,"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("const revealFileTreeResource = (...args) => sidebarTreeLiveApply.revealFileTreeResource(...args);"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("revealFileTreeResource({"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("decodeLocalEncodedPath(id.slice('local-md:'.length))"));
        assert!(
            SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("function localMarkdownBundleParentPath")
        );
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("visibleFileTreeRowByRelativePath(bundleParentPath)"));
    }

    #[test]
    fn sidebar_tree_delete_to_trash_dispatches_archive_not_purge() {
        let delete_trash_start = SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .find("if (action === 'delete-trash'")
            .expect("delete-trash branch");
        let delete_trash_end = SIDEBAR_FILETREE_COMMAND_RUNTIME_JS[delete_trash_start..]
            .find("function appendTreeContextMenuButton")
            .map(|offset| delete_trash_start + offset)
            .expect("delete-trash branch end");
        let delete_trash_branch =
            &SIDEBAR_FILETREE_COMMAND_RUNTIME_JS[delete_trash_start..delete_trash_end];
        assert!(delete_trash_branch.contains("action: 'archive'"));
        assert!(!delete_trash_branch.contains("action: 'purge'"));
    }

    #[test]
    fn sidebar_filetree_delete_keys_support_mixed_doc_and_asset_selection() {
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("function deleteSelectedSidebarFileTreeRows"));
        assert!(
            SIDEBAR_TREE_RUNTIME_JS.contains("event.key === 'Delete' || event.key === 'Backspace'")
        );
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("selectedFileTreeRows.length > 1"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("deleteSelectedSidebarFileTreeRows(trigger || document.body)"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains(
            "currentSourceKind() === 'local_folder' && fileTreeRowKind(row) === 'asset'"
        ));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("if (currentSourceKind() === 'local_folder')"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("documentId: fileAssetIds[lf]"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("/api/media/batch"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("/api/mindmap/"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("/api/tables/"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("确认删除选中的 "));
    }

    #[test]
    fn sidebar_filetree_bulk_delete_refreshes_local_folder_after_success() {
        let bulk_delete_start = SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .find("async function deleteSelectedSidebarFileTreeRows")
            .expect("bulk delete function");
        let bulk_delete_end = SIDEBAR_FILETREE_COMMAND_RUNTIME_JS[bulk_delete_start..]
            .find("return {")
            .map(|offset| bulk_delete_start + offset)
            .expect("bulk delete function end");
        let bulk_delete = &SIDEBAR_FILETREE_COMMAND_RUNTIME_JS[bulk_delete_start..bulk_delete_end];
        assert!(bulk_delete.contains(
            "if (currentSourceKind() === 'local_folder') void refreshLocalFolderSidebarSnapshot();"
        ));
        assert!(!bulk_delete.contains(
            "if (currentSourceKind() !== 'local_folder') void refreshLocalFolderSidebarSnapshot();"
        ));
    }

    #[test]
    fn sidebar_filetree_blocks_readonly_paste_and_drop_with_action_status() {
        assert!(SIDEBAR_FILETREE_UPLOAD_RUNTIME_JS.contains("function blockReadonlyFileTreeAction"));
        assert!(SIDEBAR_FILETREE_UPLOAD_RUNTIME_JS.contains("data-mnote-filetree-readonly-blocked"));
        assert!(SIDEBAR_FILETREE_UPLOAD_RUNTIME_JS.contains("data-mnote-filetree-readonly-message"));
        assert!(
            SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("recordFileTreeActionStatus('blocked'")
        );
        assert!(SIDEBAR_FILETREE_UPLOAD_RUNTIME_JS.contains("/api/tree/filetree/drop-preflight"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("moveSidebarFileTreeRows(runtimeState.sidebarFileTreeClipboard.rowIds"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("ensureFileTreeWritableTarget('drop'"));
    }

    #[test]
    fn sidebar_filetree_command_context_helper_functions_exist() {
        assert!(
            SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("function buildSidebarFileTreeContext")
        );
        assert!(
            SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("function evaluateSidebarFileTreeWhen")
        );
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("__mnoteFileTreeContextMenuRuntime"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("'workspace.sourceKind'"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("'workspace.readonly'"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("'tree.focusKind'"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("'tree.selectionCount'"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("'tree.selectionResourceKind'"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("data-mnote-workspace-readonly"));
    }

    #[test]
    fn filetree_context_menu_runtime_contains_command_context_helpers() {
        assert!(FILETREE_CONTEXT_MENU_RUNTIME_JS.contains("function buildSidebarFileTreeContext"));
        assert!(FILETREE_CONTEXT_MENU_RUNTIME_JS.contains("function evaluateSidebarFileTreeWhen"));
        assert!(FILETREE_CONTEXT_MENU_RUNTIME_JS.contains("function tokenizeWhenExpression"));
        assert!(FILETREE_CONTEXT_MENU_RUNTIME_JS.contains("__mnoteFileTreeContextMenuRuntime"));
        assert!(FILETREE_CONTEXT_MENU_RUNTIME_JS.contains("'workspace.sourceKind'"));
        assert!(FILETREE_CONTEXT_MENU_RUNTIME_JS.contains("'workspace.readonly'"));
        assert!(FILETREE_CONTEXT_MENU_RUNTIME_JS.contains("'tree.selectionCount'"));
    }

    #[test]
    fn sidebar_filetree_context_menu_items_have_when_for_readonly() {
        // FileTree 分支的写操作必须带 when: '!workspace.readonly'。
        assert!(
            SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains("when: '!workspace.readonly'"),
            "上下文菜单写操作应携带 when: '!workspace.readonly'"
        );
        // Verify at least one delete-trash item has the when expression
        let delete_trash_items = SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .match_indices("'delete-trash'")
            .count();
        assert!(delete_trash_items > 0, "应至少有一个 delete-trash 菜单项");
    }

    #[test]
    fn sidebar_filetree_context_evaluator_uses_readonly_and_selection_count_for_delete_key() {
        assert!(
            SIDEBAR_TREE_RUNTIME_JS.contains(
                "evaluateSidebarFileTreeWhen(delCtx, '!workspace.readonly && !editor.dirty')"
            ),
            "Delete/Backspace 快捷键应评估 workspace.readonly 与 editor.dirty 上下文"
        );
        assert!(
            SIDEBAR_TREE_RUNTIME_JS.contains(
                "evaluateSidebarFileTreeWhen(renameCtx, '!workspace.readonly && !editor.dirty')"
            ),
            "F2 快捷键应与菜单重命名共用 command context"
        );
    }

    #[test]
    fn sidebar_filetree_local_table_bulk_delete_uses_tree_command() {
        let table_loop_start = SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .find("for (var t = 0; t < plan.tableRows.length; t += 1)")
            .expect("table bulk delete loop");
        let table_loop_end = SIDEBAR_FILETREE_COMMAND_RUNTIME_JS[table_loop_start..]
            .find("sidebarFileTreeSelection.selectedRowIds")
            .map(|offset| table_loop_start + offset)
            .expect("table bulk delete loop end");
        let table_loop = &SIDEBAR_FILETREE_COMMAND_RUNTIME_JS[table_loop_start..table_loop_end];
        assert!(table_loop.contains("currentSourceKind() === 'local_folder'"));
        assert!(table_loop.contains("dispatchTreeCommand(trigger || tableRow"));
        assert!(table_loop.contains("action: 'archive'"));
        assert!(table_loop.contains("documentId: tableId"));
        assert!(table_loop.contains("/api/tables/"));
    }

    #[test]
    fn sidebar_filetree_runtime_does_not_keep_retired_table_engine_branches() {
        let retired_table_engine = ["lucky", "sheet"].concat();
        assert!(!SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains(&retired_table_engine));
        let retired_api = ["/api/", "lucky"].concat();
        let retired_constructor = ["create", "Luck"].concat();
        assert!(!SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains(&retired_constructor));
        assert!(!SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains(&retired_api));
    }

    #[test]
    fn sidebar_filetree_asset_open_uses_owner_document_id() {
        assert!(
            SIDEBAR_TREE_RUNTIME_JS.contains(
                "var ownerDocumentId = fileRow.getAttribute('data-owner-document-id') || documentId;"
            ),
            "打开资源行时必须优先使用资源 owner document，不能用当前页面 documentId"
        );
        assert!(
            SIDEBAR_TREE_RUNTIME_JS.contains("documentId: ownerDocumentId || null"),
            "tree.asset.open detail 应携带资源 owner document"
        );
        assert!(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
                .contains("data-owner-document-id=\"' + escapeHtml(ownerDocumentId)"),
            "SSR filetree 行应输出 data-owner-document-id"
        );
        assert!(
            SIDEBAR_TREE_LIVE_APPLY_RUNTIME_JS
                .contains("return 'local-md:' + bundleName + '~2F' + bundleName + '.md';"),
            "local_folder bundle 资源应从 local-file 路径推导 owner markdown document"
        );
    }

    #[test]
    fn sidebar_tree_runtime_keeps_pdf_and_code_assets_out_of_onlyoffice() {
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("function inferOnlyOfficeFileType"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("isNonOfficeAttachmentName(name, ext)"));
        assert!(!SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("if (ext === 'pdf') return ext;"));
        assert!(!SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("mt.indexOf('pdf') >= 0"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("mnote-uploaded-attachment-pdf"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("mnote-uploaded-attachment-code"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("'toml'"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("'json'"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("'yaml'"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("'md'"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("'vue'"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("'svelte'"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("'proto'"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("'dockerfile'"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("'.gitignore'"));
    }

    #[test]
    fn sidebar_tree_runtime_opens_office_assets_through_resource_shell() {
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("function buildOnlyOfficeOpenPath"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains(
            "return '/office/' + encodeURIComponent(input.documentId) + '/' + encodeURIComponent(input.assetId)"
        ));
        assert!(
            SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("return '/onlyoffice?' + params.toString();")
        );
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS
            .contains("target.searchParams.set('mode', input.mode || 'view');"));
        assert!(
            SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("params.set('mode', input.mode || 'view');")
        );
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS.contains(
            "{ action: 'open-edit-mode', icon: 'edit_note', label: '使用编辑模式打开' }"
        ));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("{ action: 'open-edit-mode', icon: 'edit_note', label: '弹窗编辑' }"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("{ action: 'new-window-edit', icon: 'open_in_new', label: '新窗口编辑' }"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("function withOfficeEditModeGuard(callback)"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("data-mnote-last-office-edit-mode-requested"));
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
            .contains("function openEditorAttachmentEditTab(detail)"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("void openEditorAttachmentEditTab(detail);"));
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
            .contains("openLocalOfficeFileInActiveTab(detail, 'edit')"));
        assert!(SIDEBAR_FILETREE_COMMAND_RUNTIME_JS
            .contains("openEditorAttachmentNewWindow(detail, 'edit')"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS
            .contains("var requestedOfficeMode = forceEditMode ? 'edit' : 'view';"));
        assert!(!SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains(
            "var requestedOfficeMode = forceNewWindow || forceEditMode ? 'edit' : 'view';"
        ));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("forceEditMode ? 'edit' : 'view'"));
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
            .contains("url.searchParams.set('mode', requestedMode);"));
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("mode: requestedMode"));
    }

    #[test]
    fn sidebar_tree_runtime_opens_pdf_and_code_assets_with_builtin_tools() {
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("function openPdfEditorAttachment"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("function buildPdfPreviewOpenUrl"));
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("buildPdfPreviewOpenUrl"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS
            .contains("var localOpenUrl = isPdfAttachmentFileName(localFileName) ? buildPdfPreviewOpenUrl(localFileUrl, localFileName) : localFileUrl;"));
        let open_pdf_body = js_function_body(
            SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS,
            "openPdfEditorAttachment",
        );
        assert!(open_pdf_body.contains("buildPdfPreviewOpenUrl(resolved.url, title)"));
        assert!(
            !open_pdf_body.contains("window.open(resolved.url, '_blank', 'noopener,noreferrer');")
        );
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("function openCodeEditorAttachment"));
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("function resolveEditorAttachmentUrl"));
        assert!(!SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("/api/media/sign?assetId="));
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("data-mnote-media-sign-retired"));
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
            .contains("var localFilePath = localFilePathFromAssetId(assetId)"));
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains(
            "var localDownloadUrl = buildLocalFileOpenUrlForRoot(localFilePath, detail.localRootUri || currentRootUri(), true)"
        ));
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("type: 'codeBlock'"));
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("attrs: { language: language }"));
        assert!(SIDEBAR_TREE_RUNTIME_JS.contains("inferCodeAttachmentLanguage"));
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
            .contains("if (isPdfAttachmentFileName(detail.fileName))"));
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
            .contains("if (isCodeAttachmentFileName(detail.fileName))"));
        assert!(!SIDEBAR_FILETREE_OPEN_RUNTIME_JS
            .contains("if (isCodeAttachmentFileName(fileName) && String(asset.document_id"));
        assert!(!SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("await openCodeEditorAttachment({"));
        assert!(SIDEBAR_FILETREE_OPEN_RUNTIME_JS.contains("data-mnote-media-sign-retired"));
        let attachment_class_index = SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
            .find("attachmentClassForFileName(fileName).split")
            .expect("editor attachment links apply type-specific classes");
        let local_refresh_index = SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS
            .find("if (localFilePath && !isOnlyOfficeAttachmentHref(href))")
            .expect("local file links refresh existence after enhancement");
        assert!(attachment_class_index < local_refresh_index);
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("'mnote:leptos-tiptap-spike:ready'"));
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("window.setTimeout(function()"));
        assert!(SIDEBAR_ATTACHMENT_OPEN_RUNTIME_JS.contains("enhanceEditorAttachmentLinks();"));
    }

    #[test]
    fn tree_live_controller_marks_transport_and_closes_source_on_pagehide() {
        assert!(TREE_LIVE_CONTROLLER_JS.contains("tree-live-sse"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("tree-live-ws"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("new WebSocket(url.toString())"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("startWithSseFallback"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("data-mnote-tree-live-transport"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("pagehide"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("__mnoteTreeLiveEventSource"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("__mnoteLocalFolderEventBus"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("tree:snapshot"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("tree:delta"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("tree:resync"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("liveDeltaNeedsResync(payload)"));
        assert!(TREE_LIVE_CONTROLLER_JS.contains("op === 'resync_required'"));
        assert!(!TREE_LIVE_CONTROLLER_JS.contains("resyncEndpoint"));
        assert!(!TREE_LIVE_CONTROLLER_JS.contains("tree:resync-requested"));
        assert!(!TREE_LIVE_CONTROLLER_JS.contains("Delta indicates something changed"));
    }
}
