"use client";

import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
import type {
  FileTreeShellDeleteSelectionPayload,
  FileTreeShellExternalDropPayload,
  FileTreeShellInternalDropPayload,
  TreeShellHostMode,
  TreeShellMutationPayload,
  TreeShellPickerCommand,
} from "@/components/sidebar/tree-shell-host";
import {
  buildTreeShellDomChildrenByParent,
  buildTreeShellDomFiletreeSelection,
  buildTreeShellDomKernelFileTreeItems,
  buildTreeShellDomPageItems,
  buildTreeShellDomPickerItems,
  type TreeShellDomProjectionItem,
  type TreeShellPickerItem,
} from "@/components/sidebar/tree-shell-dom-model";
import type { KernelFileTreeProjectionItem } from "@/lib/kernel-file-tree";
import type { PageTreeProjectionItem } from "@/lib/tree-projection";

type TreeShellBridgeContextMenuPayload = {
  documentId: string | null;
  assetId: string | null;
  rowId: string | null;
  rowKind: string | null;
  x: number;
  y: number;
};

type TreeShellBridgePageExpandPayload = {
  documentId: string | null;
  expanded: boolean;
};

type TreeShellBridgeMutationPayload = TreeShellMutationPayload;

type TreeShellBridgeSelectionPayload = {
  selectedRowIds: string[];
  anchorRowId: string | null;
  focusedRowId: string | null;
};

type TreeShellStatePatchMessage = {
  channel: string;
  type: "tree.shell.state.patch";
  activeDocumentId: string | null;
  focusedDocumentId: string | null;
  activePickerItemKey: string | null;
};

type TreeShellPickerCommandMessage = {
  channel: string;
  type: "tree.picker.command";
  command: TreeShellPickerCommand["kind"];
};

export type { TreeShellPickerItem } from "@/components/sidebar/tree-shell-dom-model";

type TreeShellInlineProjectionItem = TreeShellDomProjectionItem;

type TreeShellInlineRendererInput = {
  mode: "page" | "fileTree" | "picker";
  projectionItemIds: string[];
  expandedIds: string[];
  focusedId: string | null;
  pageFocusKeyboardReducer?: {
    contractName: "rust_page_focus_keyboard_reducer_v1";
    actions: string[];
  };
  filetreeSelection: {
    selectedRowIds: string[];
    anchorRowId: string | null;
    focusedRowId: string | null;
  };
  filetreeSelectionReducer?: {
    contractName: "rust_filetree_selection_reducer_v1";
    actions: string[];
  };
  activePickerItem: string | null;
  excludedPickerIds: string[];
  pickerStateReducer?: {
    contractName: "rust_picker_state_reducer_v1";
    actions: string[];
  };
  commandDispatcher: {
    channel: string;
    commandNames: string[];
  };
  runtimeArtifact: {
    contractName: "rust_tree_shell_runtime_artifact_v1";
    family: "rust_family";
    version: number;
    executionStrategy: "browser_bridge";
    browserBridge: "iframe_srcdoc";
    wasmModuleUrl: string | null;
    jsGlueUrl: string | null;
    inputFields: string[];
    outputChannels: string[];
    eventKinds: string[];
    runtimeApi: {
      requestContract: "TreeShellRuntimeRequest";
      resultContract: "TreeShellRuntimeResult";
      reduceEndpoint: "/api/tree/runtime/reduce";
      stateSnapshots: string[];
      domPatchKinds: string[];
      hostEventKinds: string[];
      commandEventKinds: string[];
    };
  };
};

export type TreeShellIframeHostProps = {
  mode: TreeShellHostMode;
  surfaceTestId: string;
  workspaceId: string;
  rootNodeId?: string | null;
  activeDocumentId?: string | null;
  focusedDocumentId?: string | null;
  activePickerItemKey?: string | null;
  allowRootPick?: boolean;
  excludeIds?: string[];
  pickerCommand?: TreeShellPickerCommand | null;
  pickerItems?: TreeShellPickerItem[];
  pageTreeItems?: PageTreeProjectionItem[];
  inlineFileTreeItems?: KernelFileTreeProjectionItem[];
  channel?: string;
  host?: string;
  onNavigate?: (documentId: string) => void;
  onPick?: (targetId: string | null) => void;
  onPickerFocusChange?: (payload: { itemKey: string | null; documentId: string | null }) => void;
  onPageContextMenu?: (payload: { documentId: string; x: number; y: number }) => void;
  onPageExpandChange?: (payload: TreeShellBridgePageExpandPayload) => void;
  onPageFocusChange?: (payload: { documentId: string | null }) => void;
  onFileTreeContextMenu?: (payload: TreeShellBridgeContextMenuPayload) => void;
  onFileTreeSelectionChange?: (payload: TreeShellBridgeSelectionPayload) => void;
  onFileTreeDeleteSelection?: (payload: FileTreeShellDeleteSelectionPayload) => void;
  onInternalDrop?: (payload: FileTreeShellInternalDropPayload) => void;
  onDropFiles?: (payload: FileTreeShellExternalDropPayload) => void;
  onAssetOpen?: (payload: { assetId: string; documentId: string | null }) => void;
  onTreeMutation?: (payload: TreeShellBridgeMutationPayload) => void;
};

type TreeShellBridgeMessage = {
  channel: string;
  type: string;
  documentId: string | null;
  assetId: string | null;
  rowId: string | null;
  rowKind: string | null;
  x: number;
  y: number;
  expanded: boolean;
  selectedRowIds: string[];
  anchorRowId: string | null;
  focusedRowId: string | null;
  rowIds: string[];
  copy: boolean;
  files: File[];
  itemKey: string | null;
  parentId?: string | null;
  title?: string | null;
  sortOrder?: number | null;
  workspaceId?: string | null;
  updatedAt?: string | null;
  execution?: TreeShellMutationPayload["execution"];
};

const INLINE_TREE_SHELL_LOADING_HTML = [
  "<!doctype html>",
  '<html lang="zh-CN">',
  "<head>",
  '  <meta charset="utf-8" />',
  '  <meta name="viewport" content="width=device-width, initial-scale=1" />',
  "  <title>Tree Shell Loading</title>",
  "  <style>",
  "    body { margin: 0; font-family: \"Noto Sans CJK SC\", \"Source Han Sans SC\", sans-serif; background: #f8fafc; color: #475569; }",
  "    main { min-height: 100vh; display: grid; place-items: center; }",
  "    p { margin: 0; font-size: 13px; }",
  "  </style>",
  "</head>",
  "<body>",
  "  <main><p>正在加载树结果…</p></main>",
  "</body>",
  "</html>",
].join("");

const subscribeClientSnapshot = () => () => undefined;
const getClientSnapshot = () => true;
const getServerSnapshot = () => false;
const TREE_SHELL_COMMAND_NAMES = [
  "tree.node.create",
  "tree.node.rename",
  "tree.subtree.move",
  "tree.resource.copy",
  "tree.resource.move",
  "tree.resource.upload",
] as const;
const PAGE_FOCUS_KEYBOARD_REDUCER_ACTIONS = [
  "normalize",
  "focus",
  "move_next",
  "move_previous",
  "move_home",
  "move_end",
  "expand",
  "collapse",
  "toggle",
  "open",
  "context_menu",
  "dispatch_create",
  "dispatch_rename",
] as const;
const FILETREE_SELECTION_REDUCER_ACTIONS = [
  "select_row",
  "select_context_row",
  "normalize_visible_rows",
  "clear",
  "resolve_drag_rows",
  "update_drop_target",
  "dispatch_internal_drop",
  "dispatch_external_drop",
] as const;
const PICKER_STATE_REDUCER_ACTIONS = [
  "normalize",
  "focus",
  "next",
  "previous",
  "home",
  "end",
  "pick",
] as const;
const TREE_SHELL_RUNTIME_ARTIFACT = {
  contractName: "rust_tree_shell_runtime_artifact_v1",
  family: "rust_family",
  version: 1,
  executionStrategy: "browser_bridge",
  browserBridge: "iframe_srcdoc",
  wasmModuleUrl: "/api/tree-shell-runtime/mnote-tree-shell-runtime_bg.wasm",
  jsGlueUrl: "/api/tree-shell-runtime/mnote-tree-shell-runtime.js",
  inputFields: [
    "rendererInput",
    "projectionItems",
    "expandedIds",
    "selectedRowIds",
    "activePickerItem",
    "focusedId",
  ],
  outputChannels: ["domPatch", "intentEvent", "commandDispatchEvent"],
  eventKinds: ["focus", "keyboard", "expandCollapse", "selection", "contextMenu", "dragDrop", "pick"],
  runtimeApi: {
    requestContract: "TreeShellRuntimeRequest",
    resultContract: "TreeShellRuntimeResult",
    reduceEndpoint: "/api/tree/runtime/reduce",
    stateSnapshots: ["page", "fileTree", "picker"],
    domPatchKinds: ["pageState", "fileTreeState", "pickerState"],
    hostEventKinds: [
      "pageOpen",
      "pageContextMenu",
      "fileTreeOpen",
      "fileTreeContextMenu",
      "fileTreeInternalDrop",
      "fileTreeExternalDrop",
      "fileTreeKeyboardCommand",
      "pickerPickRoot",
      "pickerPickDocument",
    ],
    commandEventKinds: [
      "createNode",
      "renameNode",
      "moveSubtree",
      "copyResource",
      "moveResource",
      "uploadResource",
    ],
  },
} as const satisfies TreeShellInlineRendererInput["runtimeArtifact"];

const LOCAL_TREE_SHELL_TEMPLATE = `<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>mnote Tree Shell</title>
    <style>
      :root {
        color-scheme: light;
        --ink: #1f2328;
        --muted: #6b7280;
        --accent: #2563eb;
        --accent-soft: rgba(37, 99, 235, 0.12);
      }
      * { box-sizing: border-box; }
      body {
        margin: 0;
        font-family: "Noto Sans CJK SC", "Source Han Sans SC", sans-serif;
        background: #ffffff;
        color: var(--ink);
      }
      main {
        min-height: 100vh;
        display: grid;
        grid-template-rows: auto minmax(0, 1fr);
      }
      .tree-card {
        display: grid;
        grid-template-rows: auto minmax(0, 1fr);
        min-height: 0;
        overflow: hidden;
        background: #ffffff;
      }
      .tree-card-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        min-height: 40px;
        padding: 8px 10px 6px;
        border-bottom: 1px solid rgba(31, 35, 40, 0.08);
      }
      .tree-card-title {
        font-size: 12px;
        font-weight: 600;
        color: var(--muted);
      }
      .toolbar {
        display: flex;
        gap: 6px;
        justify-content: flex-end;
      }
      .toolbar button,
      .tree-toggle,
      .tree-action,
      .tree-link {
        font: inherit;
      }
      .toolbar button {
        border: 0;
        border-radius: 6px;
        padding: 4px 8px;
        background: transparent;
        color: var(--muted);
        font-size: 12px;
        cursor: pointer;
      }
      .toolbar button:hover {
        background: rgba(15, 23, 42, 0.05);
        color: var(--ink);
      }
      .tree-scroll {
        min-height: 0;
        overflow: auto;
        padding: 6px 4px 12px;
      }
      .tree-root,
      .tree-children {
        list-style: none;
        margin: 0;
        padding: 0;
      }
      .tree-children {
        margin-left: 18px;
        padding-left: 10px;
      }
      .tree-row {
        display: grid;
        grid-template-columns: auto auto minmax(0, 1fr) auto;
        gap: 4px;
        align-items: center;
        min-height: 26px;
        border-radius: 6px;
        border: 0;
        background: transparent;
        padding: 1px 4px;
        position: relative;
      }
      .tree-row:hover {
        background: rgba(15, 23, 42, 0.04);
      }
      .tree-row[data-focused="true"] {
        background: rgba(37, 99, 235, 0.08);
      }
      .tree-row[data-active="true"],
      .tree-row[data-selected="true"] {
        background: var(--accent-soft);
      }
      .tree-row[data-drop-target="true"],
      .tree-row[data-drop-feedback="true"] {
        background: rgba(15, 108, 132, 0.14);
        outline: 1px solid rgba(15, 108, 132, 0.35);
      }
      .tree-row[data-draggable="true"] {
        cursor: grab;
      }
      .tree-toggle,
      .tree-action {
        border: 0;
        border-radius: 4px;
        width: 20px;
        height: 20px;
        padding: 0;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        background: transparent;
        color: var(--muted);
        cursor: pointer;
      }
      .tree-toggle:hover,
      .tree-action:hover {
        background: rgba(15, 23, 42, 0.06);
        color: var(--ink);
      }
      .tree-spacer {
        width: 16px;
        height: 16px;
      }
      .tree-link {
        border: 0;
        background: transparent;
        padding: 2px 4px;
        text-align: left;
        cursor: pointer;
        display: grid;
        gap: 0;
        min-width: 0;
      }
      .tree-link-title {
        font-size: 13px;
        font-weight: 500;
        line-height: 1.35;
        color: var(--ink);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      .tree-link-meta {
        display: none;
      }
      .tree-kind-badge {
        min-width: 16px;
        width: 16px;
        height: 16px;
        border-radius: 4px;
        background: transparent;
        color: var(--muted);
        font-size: 10px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
      }
      .tree-actions {
        display: flex;
        gap: 2px;
        align-items: center;
        flex-wrap: nowrap;
        opacity: 0;
        pointer-events: none;
      }
      .tree-row:hover .tree-actions,
      .tree-row[data-focused="true"] .tree-actions,
      .tree-row:focus-within .tree-actions {
        opacity: 1;
        pointer-events: auto;
      }
      .tree-row[data-shell-mode="page"] .tree-link {
        padding-right: 42px;
      }
      .tree-row[data-shell-mode="page"] .tree-actions,
      .tree-row[data-shell-mode="filetree"] .tree-actions {
        position: absolute;
        right: 6px;
        top: 50%;
        transform: translateY(-50%);
      }
      .tree-empty {
        padding: 16px 10px;
        color: var(--muted);
      }
    </style>
  </head>
  <body>
    <main>
      <section class="tree-card">
        <div class="tree-card-header">
          <div class="tree-card-title" id="tree-shell-title">Sidebar / Page Tree</div>
          <div class="toolbar" id="tree-shell-toolbar">
            <button id="tree-create-root" data-testid="tree-create-root" type="button">新建</button>
          </div>
        </div>
        <div class="tree-scroll" id="tree-shell-app">__INITIAL_TREE_HTML__</div>
      </section>
      <div class="status-text" id="tree-shell-status" data-tone="normal" hidden>Tree shell 已加载</div>
      <div class="status-text" id="tree-shell-last-action" data-tone="normal" hidden></div>
    </main>
    <script id="tree-shell-state" type="application/json">__APP_STATE__</script>
    <script>
      (() => {
        const stateElement = document.getElementById("tree-shell-state");
        const appElement = document.getElementById("tree-shell-app");
        const statusElement = document.getElementById("tree-shell-status");
        const lastActionElement = document.getElementById("tree-shell-last-action");
        const createRootButton = document.getElementById("tree-create-root");
        if (!stateElement || !appElement || !statusElement || !lastActionElement || !createRootButton) return;

        const parseState = () => {
          try { return JSON.parse(stateElement.textContent || "{}"); } catch { return {}; }
        };
        const state = parseState();
        const rendererInput =
          state.rendererInput && typeof state.rendererInput === "object"
            ? state.rendererInput
            : {};
        const runtimeArtifact =
          rendererInput.runtimeArtifact && typeof rendererInput.runtimeArtifact === "object"
            ? rendererInput.runtimeArtifact
            : {};
        const runtimeApi =
          runtimeArtifact.runtimeApi && typeof runtimeArtifact.runtimeApi === "object"
            ? runtimeArtifact.runtimeApi
            : {};
        const rendererFiletreeSelection =
          rendererInput.filetreeSelection && typeof rendererInput.filetreeSelection === "object"
            ? rendererInput.filetreeSelection
            : {};
        const filetreeSelectionReducer =
          rendererInput.filetreeSelectionReducer && typeof rendererInput.filetreeSelectionReducer === "object"
            ? rendererInput.filetreeSelectionReducer
            : {};
        const pickerStateReducer =
          rendererInput.pickerStateReducer && typeof rendererInput.pickerStateReducer === "object"
            ? rendererInput.pickerStateReducer
            : {};
        const pageFocusKeyboardReducer =
          rendererInput.pageFocusKeyboardReducer && typeof rendererInput.pageFocusKeyboardReducer === "object"
            ? rendererInput.pageFocusKeyboardReducer
            : {};
        const normalizeStringArray = (value) =>
          Array.isArray(value)
            ? value
                .map((item) => (typeof item === "string" ? item.trim() : ""))
                .filter(Boolean)
            : [];
        const hostOverride =
          window.__MNOTE_TREE_SHELL_OVERRIDE__ && typeof window.__MNOTE_TREE_SHELL_OVERRIDE__ === "object"
            ? window.__MNOTE_TREE_SHELL_OVERRIDE__
            : {};
        const normalizeText = (value, fallback = "") => {
          if (typeof value !== "string") return fallback;
          const trimmed = value.trim();
          return trimmed || fallback;
        };
        const normalizeNumber = (value, fallback = Number.MAX_SAFE_INTEGER) =>
          Number.isFinite(value) ? Number(value) : fallback;
        const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value || {}, key);
        const normalizeRowKind = (value) => {
          const normalized = normalizeText(value).toLowerCase();
          if (normalized === "index") return "index";
          if (normalized === "asset") return "asset";
          if (normalized === "asset_folder") return "asset_folder";
          return "document";
        };
        const normalizeResourceMeta = (value) => {
          if (!value || typeof value !== "object") {
            return { resourceKind: "", documentId: "", assetId: "", assetKind: "" };
          }
          return {
            resourceKind: normalizeText(value.resourceKind),
            documentId: normalizeText(value.documentId),
            assetId: normalizeText(value.assetId),
            assetKind: normalizeText(value.assetKind),
            iconHint: normalizeText(value.iconHint),
          };
        };

        const channel = normalizeText(state.channel, "mnote-tree-shell-v1");
        const workspaceId = normalizeText(state.workspaceId);
        const mode = (() => {
          const raw = normalizeText(state.mode);
          if (raw === "picker") return "picker";
          if (raw === "filetree") return "filetree";
          return "page";
        })();
        const allowRootPick = state.allowRootPick === true;
        const excludedIds = new Set(
          normalizeStringArray(rendererInput.excludedPickerIds).length > 0
            ? normalizeStringArray(rendererInput.excludedPickerIds)
            : Array.isArray(state.excludeIds)
              ? state.excludeIds.map((item) => normalizeText(item)).filter(Boolean)
              : [],
        );
        const filetreeSelectionReducerContractName = normalizeText(filetreeSelectionReducer.contractName);
        const filetreeSelectionReducerActions = new Set(normalizeStringArray(filetreeSelectionReducer.actions));
        const runtimeReduceEndpoint = normalizeText(
          runtimeArtifact.runtimeApi && runtimeArtifact.runtimeApi.reduceEndpoint,
        );
        const pickerStateReducerContractName = normalizeText(pickerStateReducer.contractName);
        const pickerStateReducerActions = new Set(normalizeStringArray(pickerStateReducer.actions));
        const pageFocusKeyboardReducerContractName = normalizeText(pageFocusKeyboardReducer.contractName);
        const pageFocusKeyboardReducerActions = new Set(normalizeStringArray(pageFocusKeyboardReducer.actions));
        let currentActiveDocumentId = normalizeText(state.activeDocumentId);
        let currentFocusedDocumentId = normalizeText(state.focusedDocumentId);
        let currentActivePickerItemKey =
          typeof rendererInput.activePickerItem === "string" && rendererInput.activePickerItem.trim()
            ? rendererInput.activePickerItem.trim()
            : normalizeText(state.activePickerItemKey);
        const targetOrigin = (() => {
          try {
            if (!document.referrer) return "*";
            return new URL(document.referrer).origin || "*";
          } catch {
            return "*";
          }
        })();
        const postToHost = (type, extra = {}) => {
          if (window.parent === window) return;
          window.parent.postMessage(Object.assign({ channel, type }, extra), targetOrigin);
        };
        const setStatus = (message, tone = "normal") => {
          statusElement.textContent = message;
          statusElement.dataset.tone = tone;
        };
        const setLastAction = (message, tone = "normal") => {
          lastActionElement.textContent = message;
          lastActionElement.dataset.tone = tone;
        };
        const runTreeCommand = async (payload) => {
          const response = await fetch(normalizeText(state.commandPath, "/api/tree/commands"), {
            method: "POST",
            headers: { "content-type": "application/json" },
            credentials: "include",
            body: JSON.stringify(Object.assign({ workspaceId }, payload)),
          });
          const result = await response.json().catch(() => null);
          if (!response.ok) {
            const message = result && typeof result.error === "string" ? result.error : "tree command failed";
            throw new Error(message);
          }
          return result;
        };
        const runtimeArtifactContractName = normalizeText(runtimeArtifact.contractName);
        const runtimeRequestContract = normalizeText(runtimeApi.requestContract);
        const runtimeResultContract = normalizeText(runtimeApi.resultContract);
        const runtimeWasmModuleUrl = normalizeText(runtimeArtifact.wasmModuleUrl);
        const runtimeJsGlueUrl = normalizeText(runtimeArtifact.jsGlueUrl);
        let treeShellWasmRuntimePromise = null;
        const canUseTreeShellRuntimeArtifact = () =>
          runtimeArtifactContractName === "rust_tree_shell_runtime_artifact_v1" &&
          runtimeRequestContract === "TreeShellRuntimeRequest" &&
          runtimeResultContract === "TreeShellRuntimeResult";
        const buildTreeShellRuntimeRequestId = (prefix) =>
          prefix + "-" + Date.now() + "-" + Math.random().toString(36).slice(2);
        const loadTreeShellWasmRuntime = async () => {
          if (!runtimeJsGlueUrl || !runtimeWasmModuleUrl) return null;
          if (!treeShellWasmRuntimePromise) {
            treeShellWasmRuntimePromise = (async () => {
              const runtimeModule = await import(runtimeJsGlueUrl);
              const initRuntime =
                typeof runtimeModule.default === "function"
                  ? runtimeModule.default
                  : typeof runtimeModule.init === "function"
                    ? runtimeModule.init
                    : null;
              if (typeof initRuntime !== "function") {
                throw new Error("tree shell wasm glue missing init");
              }
              await initRuntime(runtimeWasmModuleUrl);
              const reduceTreeShellRuntime =
                typeof runtimeModule.reduce_tree_shell_runtime === "function"
                  ? runtimeModule.reduce_tree_shell_runtime
                  : typeof runtimeModule.reduceTreeShellRuntime === "function"
                    ? runtimeModule.reduceTreeShellRuntime
                    : null;
              if (typeof reduceTreeShellRuntime !== "function") {
                throw new Error("tree shell wasm glue missing reducer");
              }
              return { reduceTreeShellRuntime };
            })().catch((error) => {
              treeShellWasmRuntimePromise = null;
              throw error;
            });
          }
          try {
            return await treeShellWasmRuntimePromise;
          } catch {
            return null;
          }
        };
        const reduceTreeShellRuntimeViaWasm = async (request) => {
          const wasmRuntime = await loadTreeShellWasmRuntime();
          if (!wasmRuntime) return null;
          try {
            const runtimeResult = await wasmRuntime.reduceTreeShellRuntime(request);
            if (!runtimeResult || runtimeResult.requestId !== request.requestId) return null;
            return runtimeResult;
          } catch {
            return null;
          }
        };
        const reduceTreeShellRuntimeViaHttp = async (request) => {
          if (!runtimeReduceEndpoint) return null;
          try {
            const response = await fetch(runtimeReduceEndpoint, {
              method: "POST",
              credentials: "include",
              headers: {
                accept: "application/json",
                "content-type": "application/json",
              },
              body: JSON.stringify(request),
            });
            if (!response.ok) return null;
            const runtimeResult = await response.json().catch(() => null);
            if (!runtimeResult || runtimeResult.requestId !== request.requestId) return null;
            return runtimeResult;
          } catch {
            return null;
          }
        };
        const reduceTreeShellRuntimeWithArtifact = async ({
          mode,
          requestId,
          environment,
          state,
          action,
          fallbackResult,
          normalizeResult,
        }) => {
          if (!canUseTreeShellRuntimeArtifact()) return null;
          const request = {
            mode,
            requestId,
            environment,
            state,
            action,
          };
          const runtimeResult =
            (await reduceTreeShellRuntimeViaWasm(request)) ||
            (await reduceTreeShellRuntimeViaHttp(request));
          if (!runtimeResult) return null;
          return normalizeResult(runtimeResult, fallbackResult);
        };

        const rawItems = Array.isArray(state.items)
          ? state.items
          : Array.isArray(hostOverride.items)
            ? hostOverride.items
            : [];
        const normalizedItems = Array.isArray(rawItems)
          ? rawItems
              .map((item) => {
                const nodeId = normalizeText(item && item.nodeId);
                const resourceMeta = normalizeResourceMeta(item && item.resourceMeta);
                const rowKind = normalizeRowKind(item && item.rowKind);
                const fallbackRowId =
                  rowKind === "index"
                    ? \`index:\${resourceMeta.documentId || nodeId.replace(/^index:/, "")}\`
                    : rowKind === "asset"
                      ? \`asset:\${resourceMeta.assetId || nodeId.replace(/^asset:/, "")}\`
                      : rowKind === "asset_folder"
                        ? \`asset-folder:\${resourceMeta.assetId || nodeId.replace(/^asset-folder:/, "")}\`
                        : \`doc:\${resourceMeta.documentId || nodeId}\`;
                return {
                  rowId: normalizeText(item && item.rowId, fallbackRowId),
                  rowKind,
                  nodeId,
                  parentNodeId: normalizeText(item && item.parentNodeId) || null,
                  title: normalizeText(item && item.title, rowKind === "index" ? "index.md" : "无标题"),
                  depth: normalizeNumber(item && item.depth, 0),
                  childCount: normalizeNumber(item && item.childCount, 0),
                  position: normalizeNumber(item && item.position),
                  expandedByDefault: item && item.expandedByDefault !== false,
                  iconHint: normalizeText(item && item.iconHint),
                  capabilities: Array.isArray(item && item.capabilities)
                    ? item.capabilities.map((capability) => normalizeText(capability)).filter(Boolean)
                    : [],
                  resourceMeta,
                };
              })
              .filter((item) => item.nodeId && !excludedIds.has(item.nodeId))
          : [];

        const itemById = new Map(normalizedItems.map((item) => [item.nodeId, item]));
        const fileTreeRowById = new Map(normalizedItems.map((item) => [item.rowId, item]));
        const childrenByParentId = new Map();
        const roots = [];
        const compareItems = (left, right) => {
          const byPosition = left.position - right.position;
          if (byPosition !== 0) return byPosition;
          return left.title.localeCompare(right.title, "zh-CN");
        };
        normalizedItems.forEach((item) => {
          const parentId = item.parentNodeId && itemById.has(item.parentNodeId) ? item.parentNodeId : null;
          if (!parentId) {
            roots.push(item);
            return;
          }
          const bucket = childrenByParentId.get(parentId) || [];
          bucket.push(item);
          childrenByParentId.set(parentId, bucket);
        });
        roots.sort(compareItems);
        childrenByParentId.forEach((bucket) => bucket.sort(compareItems));

        const rendererExpandedIds = normalizeStringArray(rendererInput.expandedIds);
        const expanded = new Set(
          rendererExpandedIds.length > 0
            ? rendererExpandedIds
            : normalizedItems
                .filter((item) => item.childCount > 0 && item.expandedByDefault)
                .map((item) => item.nodeId),
        );
        let activeCursor = itemById.get(currentActiveDocumentId) || null;
        while (activeCursor && activeCursor.parentNodeId && itemById.has(activeCursor.parentNodeId)) {
          expanded.add(activeCursor.parentNodeId);
          activeCursor = itemById.get(activeCursor.parentNodeId) || null;
        }

        const getSiblings = (parentId) => {
          if (!parentId) return roots.slice();
          return (childrenByParentId.get(parentId) || []).slice();
        };
        const PAGE_DRAG_MIME = "application/x-mnote-page-tree-node";
        const resolveFocusedNodeIdFromHostState = () =>
          mode === "picker"
            ? currentActivePickerItemKey && currentActivePickerItemKey !== "__root__" && itemById.has(currentActivePickerItemKey)
              ? currentActivePickerItemKey
              : currentActiveDocumentId && itemById.has(currentActiveDocumentId)
                ? currentActiveDocumentId
                : roots[0]?.nodeId || ""
            : currentFocusedDocumentId && itemById.has(currentFocusedDocumentId)
              ? currentFocusedDocumentId
              : currentActiveDocumentId && itemById.has(currentActiveDocumentId)
                ? currentActiveDocumentId
                : roots[0]?.nodeId || "";
        let focusedNodeId = resolveFocusedNodeIdFromHostState();
        let draggingPageNodeId = "";
        let pageRuntimeState = {
          dropFeedback: null,
          dropTargetNodeId: null,
        };
        const rendererSelectedFileTreeRowIds = normalizeStringArray(
          rendererFiletreeSelection.selectedRowIds,
        );
        const rendererAnchorRowId =
          typeof rendererFiletreeSelection.anchorRowId === "string" &&
          rendererFiletreeSelection.anchorRowId.trim()
            ? rendererFiletreeSelection.anchorRowId.trim()
            : null;
        const rendererFocusedRowId =
          typeof rendererFiletreeSelection.focusedRowId === "string" &&
          rendererFiletreeSelection.focusedRowId.trim()
            ? rendererFiletreeSelection.focusedRowId.trim()
            : null;
        let selectedFileTreeRowIds = new Set(
          rendererSelectedFileTreeRowIds.length > 0
            ? rendererSelectedFileTreeRowIds
            : currentActiveDocumentId ? [\`index:\${currentActiveDocumentId}\`] : [],
        );
        let fileTreeAnchorRowId = rendererAnchorRowId || (currentActiveDocumentId ? \`index:\${currentActiveDocumentId}\` : null);
        let fileTreeFocusedRowId = rendererFocusedRowId || (currentActiveDocumentId ? \`index:\${currentActiveDocumentId}\` : null);
        let visibleFileTreeRowIds = [];

        const focusRowElement = (nodeId) => {
          if (!nodeId) return;
          window.requestAnimationFrame(() => {
            const row = appElement.querySelector(\`.tree-row[data-node-id="\${CSS.escape(nodeId)}"]\`);
            if (!(row instanceof HTMLElement)) return;
            row.focus({ preventScroll: true });
            row.scrollIntoView({ block: "nearest" });
          });
        };
        const handleNavigate = (nodeId) => {
          const item = itemById.get(nodeId);
          if (!item) return;
          if (mode === "picker") {
            postToHost("tree.pick", { documentId: nodeId, itemKey: nodeId, target: { documentId: nodeId } });
            return;
          }
          postToHost("tree.navigate", { documentId: nodeId, target: { documentId: nodeId } });
        };
        const toggleExpand = (nodeId) => {
          const nextExpanded = !expanded.has(nodeId);
          if (nextExpanded) expanded.add(nodeId);
          else expanded.delete(nodeId);
          if (mode === "page") {
            postToHost("tree.page.expand.changed", {
              documentId: nodeId,
              expanded: nextExpanded,
              target: { documentId: nodeId },
            });
          }
          if (mode === "page" && usedRustInitialRenderer && patchPageTreeExpansionDom(nodeId)) {
            focusRowElement(nodeId);
            return;
          }
          renderTree();
          focusRowElement(nodeId);
        };
        const applyPageExpandedIds = (expandedIds) => {
          const nextExpandedIds = new Set(normalizeStringArray(expandedIds));
          let changed = expanded.size !== nextExpandedIds.size;
          if (!changed) {
            changed = Array.from(expanded).some((nodeId) => !nextExpandedIds.has(nodeId));
          }
          if (!changed) return false;
          expanded.clear();
          nextExpandedIds.forEach((nodeId) => expanded.add(nodeId));
          return true;
        };
        const postPageFocusChange = (nodeId) => {
          if (mode !== "page" || !nodeId) return;
          postToHost("tree.page.focus.changed", { documentId: nodeId, target: { documentId: nodeId } });
        };
        const getVisiblePageItems = () => {
          const visible = [];
          const walk = (entries) => {
            entries.forEach((item) => {
              visible.push(item);
              if (item.childCount > 0 && expanded.has(item.nodeId)) {
                walk(getSiblings(item.nodeId));
              }
            });
          };
          walk(roots);
          return visible;
        };
        const focusNode = (nodeId) => {
          const nextFocusId = normalizeText(nodeId);
          if (!nextFocusId || !itemById.has(nextFocusId)) return;
          if (focusedNodeId === nextFocusId) {
            focusRowElement(nextFocusId);
            return;
          }
          focusedNodeId = nextFocusId;
          postPageFocusChange(nextFocusId);
          if (usedRustInitialRenderer) {
            patchPageTreeActiveDom();
            focusRowElement(nextFocusId);
            return;
          }
          renderTree();
          focusRowElement(nextFocusId);
        };
        const normalizePageDropPosition = (position) => {
          const normalizedPosition = normalizeText(position).toLowerCase();
          return normalizedPosition === "before" || normalizedPosition === "inside" || normalizedPosition === "after"
            ? normalizedPosition
            : null;
        };
        const normalizePageDropFeedback = (feedback) => {
          if (!feedback || typeof feedback !== "object") return null;
          const sourceNodeId = normalizeText(feedback.sourceNodeId);
          const position = normalizePageDropPosition(feedback.position);
          if (!sourceNodeId || !position) return null;
          return {
            sourceNodeId,
            targetNodeId: normalizeText(feedback.targetNodeId) || null,
            targetParentId: normalizeText(feedback.targetParentId) || null,
            position,
          };
        };
        const normalizePageRuntimeState = (runtimeState) => ({
          dropFeedback: normalizePageDropFeedback(runtimeState?.dropFeedback),
          dropTargetNodeId: normalizeText(runtimeState?.dropTargetNodeId) || null,
        });
        const arePageDropFeedbackEqual = (left, right) => {
          const normalizedLeft = normalizePageDropFeedback(left);
          const normalizedRight = normalizePageDropFeedback(right);
          return (
            normalizedLeft?.sourceNodeId === normalizedRight?.sourceNodeId &&
            normalizedLeft?.targetNodeId === normalizedRight?.targetNodeId &&
            normalizedLeft?.targetParentId === normalizedRight?.targetParentId &&
            normalizedLeft?.position === normalizedRight?.position
          );
        };
        const commitPageRuntimeState = (nextState) => {
          const normalizedRuntimeState = normalizePageRuntimeState({
            dropFeedback: hasOwn(nextState, "dropFeedback")
              ? nextState.dropFeedback
              : pageRuntimeState.dropFeedback,
            dropTargetNodeId: hasOwn(nextState, "dropTargetNodeId")
              ? nextState.dropTargetNodeId
              : pageRuntimeState.dropTargetNodeId,
          });
          pageRuntimeState = normalizedRuntimeState;
          return normalizedRuntimeState;
        };
        const resolvePageDropTargetNodeId = (element) => {
          const row = element instanceof Element
            ? element.closest('.tree-row[data-shell-mode="page"]')
            : null;
          if (!(row instanceof HTMLElement)) return "";
          return normalizeText(row.dataset.nodeId);
        };
        const readPageDragNodeId = (event) => {
          const raw =
            event.dataTransfer?.getData(PAGE_DRAG_MIME) ||
            event.dataTransfer?.getData("text/plain") ||
            draggingPageNodeId ||
            "";
          return normalizeText(raw);
        };
        const canSubmitPageDropCandidate = (sourceNodeId, targetNodeId) => {
          const normalizedSourceNodeId = normalizeText(sourceNodeId);
          const normalizedTargetNodeId = normalizeText(targetNodeId);
          return Boolean(
            normalizedSourceNodeId &&
            normalizedTargetNodeId &&
            normalizedSourceNodeId !== normalizedTargetNodeId,
          );
        };
        const buildPageRuntimeAction = (action) => {
          const actionKind = normalizeText(action?.kind);
          if (actionKind === "focus") {
            const nodeId = normalizeText(action.nodeId);
            return nodeId ? { kind: "focus", nodeId } : null;
          }
          if (actionKind === "move_next") return { kind: "moveNext" };
          if (actionKind === "move_previous") return { kind: "movePrevious" };
          if (actionKind === "move_home") return { kind: "moveHome" };
          if (actionKind === "move_end") return { kind: "moveEnd" };
          if (actionKind === "open") return { kind: "openFocused" };
          if (actionKind === "context_menu") return { kind: "contextMenuFocused" };
          if (actionKind === "dispatch_create") {
            return {
              kind: "dispatchCreate",
              parentNodeId: normalizeText(action.parentNodeId) || null,
            };
          }
          if (actionKind === "dispatch_rename") {
            const nodeId = normalizeText(action.nodeId);
            const title = normalizeText(action.title);
            if (!nodeId || !title) return null;
            return {
              kind: "dispatchRename",
              nodeId,
              title,
            };
          }
          if (actionKind === "expand" || actionKind === "collapse" || actionKind === "toggle") {
            const nodeId = normalizeText(action.nodeId);
            if (!nodeId) return null;
            return {
              kind: actionKind,
              nodeId,
            };
          }
          if (actionKind === "update_drop_feedback") {
            const feedback = normalizePageDropFeedback(action.feedback);
            return {
              kind: "updateDropFeedback",
              feedback,
            };
          }
          if (actionKind === "update_drop_feedback_for_target") {
            const sourceNodeId = normalizeText(action.sourceNodeId);
            const targetNodeId = normalizeText(action.targetNodeId);
            const position = normalizePageDropPosition(action.position);
            if (!sourceNodeId || !targetNodeId || !position) return null;
            return {
              kind: "updateDropFeedbackForTarget",
              sourceNodeId,
              targetNodeId,
              position,
            };
          }
          if (actionKind === "dispatch_move") {
            const sourceNodeId = normalizeText(action.sourceNodeId);
            const position = normalizePageDropPosition(action.position);
            if (!sourceNodeId || !position) return null;
            return {
              kind: "dispatchMove",
              sourceNodeId,
              targetParentId: normalizeText(action.targetParentId) || null,
              position,
            };
          }
          if (actionKind === "dispatch_move_to_target") {
            const sourceNodeId = normalizeText(action.sourceNodeId);
            const targetNodeId = normalizeText(action.targetNodeId);
            const position = normalizePageDropPosition(action.position);
            if (!sourceNodeId || !targetNodeId || !position) return null;
            return {
              kind: "dispatchMoveToTarget",
              sourceNodeId,
              targetNodeId,
              position,
            };
          }
          return null;
        };
        const buildPageRuntimeEnvironment = () => ({
          visibleNodeIds: getVisiblePageItems().map((entry) => entry.nodeId),
          expandableNodeIds: normalizedItems
            .filter((entry) => entry.childCount > 0 && getSiblings(entry.nodeId).length > 0)
            .map((entry) => entry.nodeId),
          rows: normalizedItems.map((entry) => ({
            nodeId: entry.nodeId,
            parentNodeId: normalizeText(entry.parentNodeId) || null,
            position: normalizeNumber(entry.position, 0),
          })),
        });
        const readPageRuntimeState = (action) => {
          const actionKind = normalizeText(action?.kind);
          const focusedId =
            (actionKind === "open" || actionKind === "context_menu") && itemById.has(normalizeText(action?.nodeId))
              ? normalizeText(action.nodeId)
              : focusedNodeId || null;
          return {
            focusedId,
            expandedIds: Array.from(expanded),
            dropFeedback: pageRuntimeState.dropFeedback,
          };
        };
        const normalizePageRuntimeResult = (runtimeResult, fallbackResult) => {
          if (!runtimeResult || runtimeResult.mode !== "page") return null;
          const stateSnapshot =
            runtimeResult.state && runtimeResult.state.mode === "page"
              ? runtimeResult.state.state
              : null;
          const pagePatch = Array.isArray(runtimeResult.domPatches)
            ? runtimeResult.domPatches.find((patch) => patch && patch.kind === "pageState")
            : null;
          const rawFocusedId =
            typeof pagePatch?.focusedId === "string"
              ? pagePatch.focusedId
              : typeof stateSnapshot?.focusedId === "string"
                ? stateSnapshot.focusedId
                : "";
          const rawExpandedIds = Array.isArray(pagePatch?.expandedIds)
            ? pagePatch.expandedIds
            : Array.isArray(stateSnapshot?.expandedIds)
              ? stateSnapshot.expandedIds
              : null;
          const rawDropFeedback = hasOwn(pagePatch || {}, "dropFeedback")
            ? pagePatch?.dropFeedback
            : hasOwn(stateSnapshot || {}, "dropFeedback")
              ? stateSnapshot?.dropFeedback
              : hasOwn(fallbackResult || {}, "dropFeedback")
                ? fallbackResult?.dropFeedback
                : undefined;
          const dropFeedback =
            typeof rawDropFeedback === "undefined"
              ? pageRuntimeState.dropFeedback
              : normalizePageDropFeedback(rawDropFeedback);
          return {
            focusedId: normalizeText(rawFocusedId),
            expandedIds: rawExpandedIds ? normalizeStringArray(rawExpandedIds) : null,
            dropFeedback,
            dropTargetNodeId: dropFeedback
              ? normalizeText(dropFeedback.targetNodeId) ||
                normalizeText(fallbackResult?.dropTargetNodeId) ||
                pageRuntimeState.dropTargetNodeId ||
                null
              : null,
            hostEvents: Array.isArray(runtimeResult.hostEvents) ? runtimeResult.hostEvents : [],
            commandEvents: Array.isArray(runtimeResult.commandEvents) ? runtimeResult.commandEvents : [],
          };
        };
        const buildPageContextMenuPayload = (nodeId, sourceElement) => {
          const normalizedNodeId = normalizeText(nodeId);
          if (!normalizedNodeId) return null;
          const rect = sourceElement?.getBoundingClientRect?.();
          return {
            documentId: normalizedNodeId,
            x: rect ? rect.left + Math.min(rect.width - 12, 28) : 0,
            y: rect ? rect.top + Math.min(rect.height - 12, 18) : 0,
            target: { documentId: normalizedNodeId },
          };
        };
        const replayPageRuntimeHostEvents = (runtimeResult, fallbackHostEvent) => {
          const runtimeHostEvents = Array.isArray(runtimeResult?.hostEvents)
            ? runtimeResult.hostEvents
            : [];
          let replayed = false;
          runtimeHostEvents.forEach((event) => {
            if (!event || typeof event !== "object") return;
            if (event.kind === "pageOpen") {
              const nodeId = normalizeText(event.nodeId);
              if (!nodeId) return;
              handleNavigate(nodeId);
              replayed = true;
              return;
            }
            if (event.kind === "pageContextMenu") {
              const payload = buildPageContextMenuPayload(event.nodeId, fallbackHostEvent?.sourceElement);
              if (!payload) return;
              postToHost("tree.page.context-menu", payload);
              replayed = true;
            }
          });
          if (replayed || !fallbackHostEvent) return replayed;
          if (fallbackHostEvent.kind === "pageOpen") {
            handleNavigate(fallbackHostEvent.nodeId);
            return true;
          }
          if (fallbackHostEvent.kind === "pageContextMenu") {
            const payload = buildPageContextMenuPayload(
              fallbackHostEvent.nodeId,
              fallbackHostEvent.sourceElement,
            );
            if (!payload) return false;
            postToHost("tree.page.context-menu", payload);
            return true;
          }
          return false;
        };
        const reducePageActionWithRuntime = async (
          action,
          runtimeState = readPageRuntimeState(action),
          runtimeEnvironment = buildPageRuntimeEnvironment(),
          fallbackResult = null,
        ) => {
          if (mode !== "page") {
            return null;
          }
          const runtimeAction = buildPageRuntimeAction(action);
          if (!runtimeAction) return null;
          return reduceTreeShellRuntimeWithArtifact({
            mode: "page",
            requestId: buildTreeShellRuntimeRequestId("page-state"),
            environment: runtimeEnvironment,
            state: runtimeState,
            action: runtimeAction,
            fallbackResult,
            normalizeResult: normalizePageRuntimeResult,
          });
        };
        const syncPageDropFeedbackDom = () => {
          if (mode !== "page") return;
          const dropTargetNodeId = pageRuntimeState.dropFeedback ? pageRuntimeState.dropTargetNodeId : null;
          appElement.querySelectorAll('[data-shell-mode="page"]').forEach((row) => {
            if (!(row instanceof HTMLElement)) return;
            const nodeId = normalizeText(row.dataset.nodeId);
            row.dataset.dropFeedback = String(Boolean(nodeId && dropTargetNodeId && nodeId === dropTargetNodeId));
          });
        };
        const dispatchPageMoveCommand = async (commandEvent, fallbackCommandEvent) => {
          const sourceNodeId = normalizeText(
            commandEvent?.sourceNodeId,
            normalizeText(fallbackCommandEvent?.sourceNodeId),
          );
          const targetNodeId = normalizeText(
            commandEvent?.targetNodeId,
            normalizeText(fallbackCommandEvent?.targetNodeId),
          );
          const position =
            normalizePageDropPosition(commandEvent?.position) ||
            normalizePageDropPosition(fallbackCommandEvent?.position) ||
            "before";
          if (!sourceNodeId) return false;
          const targetItem = targetNodeId ? itemById.get(targetNodeId) : null;
          let parentId =
            position === "inside"
              ? targetNodeId
              : normalizeText(commandEvent?.targetParentId) ||
                normalizeText(fallbackCommandEvent?.targetParentId) ||
                targetItem?.parentNodeId ||
                null;
          const runtimeSortOrder = Number.isFinite(commandEvent?.sortOrder)
            ? Math.max(0, Math.trunc(commandEvent.sortOrder))
            : null;
          let sortOrder = runtimeSortOrder ?? 0;
          if (runtimeSortOrder !== null) {
            // sortOrder 已由 Rust runtime 基于 page rows 解析，JS 只负责 transport 回放。
          } else if (position === "inside" && targetNodeId) {
            sortOrder = getSiblings(targetNodeId).length;
          } else {
            if (!targetItem) return false;
            const siblings = getSiblings(targetItem.parentNodeId);
            const targetIndex = siblings.findIndex((entry) => entry.nodeId === targetNodeId);
            if (targetIndex < 0) return false;
            sortOrder = position === "after" ? targetIndex + 1 : targetIndex;
          }
          try {
            const result = await runTreeCommand({
              action: "move",
              documentId: sourceNodeId,
              parentId,
              sortOrder,
            });
            const commandResult = result && result.result && typeof result.result === "object" ? result.result : {};
            setLastAction(targetItem ? \`页面已拖放到 \${targetItem.title}\` : "页面已完成拖放移动");
            postToHost("tree.subtree.moved", {
              documentId: sourceNodeId,
              parentId: normalizeText(commandResult.parentId, parentId || "") || null,
              sortOrder: Number.isFinite(commandResult.sortOrder) ? Number(commandResult.sortOrder) : sortOrder,
              workspaceId: normalizeText(commandResult.workspaceId, workspaceId) || workspaceId || null,
              updatedAt: normalizeText(commandResult.updatedAt) || null,
              execution: commandResult.execution && typeof commandResult.execution === "object" ? commandResult.execution : null,
              target: { documentId: sourceNodeId },
            });
            return true;
          } catch (error) {
            setLastAction(error instanceof Error ? error.message : "拖拽移动失败", "error");
            return false;
          }
        };
        const dispatchPageCreateCommand = async (commandEvent, fallbackCommandEvent) => {
          const parentId = normalizeText(
            commandEvent?.parentNodeId,
            normalizeText(fallbackCommandEvent?.parentNodeId),
          ) || null;
          try {
            const result = await runTreeCommand({
              action: "create",
              parentId,
            });
            const documentId = normalizeText(
              result && result.result && result.result.documentId,
              normalizeText(result && result.id),
            );
            setLastAction(documentId ? \`已创建子页面 \${documentId}\` : "已创建子页面");
            const commandResult = result && result.result && typeof result.result === "object" ? result.result : {};
            postToHost("tree.node.created", {
              documentId: documentId || null,
              parentId: normalizeText(commandResult.parentId, parentId || "") || null,
              title: normalizeText(commandResult.title, "无标题"),
              sortOrder: Number.isFinite(commandResult.sortOrder) ? Number(commandResult.sortOrder) : null,
              workspaceId: normalizeText(commandResult.workspaceId, workspaceId) || workspaceId || null,
              updatedAt: normalizeText(commandResult.updatedAt) || null,
              execution: commandResult.execution && typeof commandResult.execution === "object" ? commandResult.execution : null,
              target: { documentId: documentId || null },
            });
            return true;
          } catch (error) {
            setLastAction(error instanceof Error ? error.message : "创建页面失败", "error");
            return false;
          }
        };
        const dispatchPageRenameCommand = async (commandEvent, fallbackCommandEvent) => {
          const nodeId = normalizeText(
            commandEvent?.nodeId,
            normalizeText(fallbackCommandEvent?.nodeId),
          );
          const title = normalizeText(
            commandEvent?.title,
            normalizeText(fallbackCommandEvent?.title),
          );
          if (!nodeId || !title) return false;
          try {
            const result = await runTreeCommand({
              action: "rename",
              documentId: nodeId,
              title,
            });
            const commandResult = result && result.result && typeof result.result === "object" ? result.result : {};
            const item = itemById.get(nodeId);
            if (item) item.title = title;
            setLastAction(\`已重命名为 \${title}\`);
            postToHost("tree.node.renamed", {
              documentId: nodeId,
              title: normalizeText(commandResult.title, title),
              workspaceId: normalizeText(commandResult.workspaceId, workspaceId) || workspaceId || null,
              updatedAt: normalizeText(commandResult.updatedAt) || null,
              execution: commandResult.execution && typeof commandResult.execution === "object" ? commandResult.execution : null,
              target: { documentId: nodeId },
            });
            if (fallbackCommandEvent?.titleElement) {
              fallbackCommandEvent.titleElement.textContent = title;
            } else if (!usedRustInitialRenderer) {
              renderTree();
            }
            return true;
          } catch (error) {
            setLastAction(error instanceof Error ? error.message : "重命名失败", "error");
            return false;
          }
        };
        const replayPageRuntimeCommandEvents = (runtimeResult, fallbackCommandEvent) => {
          const runtimeCommandEvents = Array.isArray(runtimeResult?.commandEvents)
            ? runtimeResult.commandEvents
            : [];
          let replayed = false;
          runtimeCommandEvents.forEach((event) => {
            if (!event || typeof event !== "object") return;
            if (event.kind === "createNode") {
              replayed = true;
              void dispatchPageCreateCommand(event, fallbackCommandEvent);
              return;
            }
            if (event.kind === "renameNode") {
              replayed = true;
              void dispatchPageRenameCommand(event, fallbackCommandEvent);
              return;
            }
            if (event.kind === "moveSubtree") {
              replayed = true;
              void dispatchPageMoveCommand(event, fallbackCommandEvent);
            }
          });
          if (replayed || !fallbackCommandEvent) return replayed;
          if (runtimeResult && fallbackCommandEvent.runtimeRequired === true) {
            return false;
          }
          if (fallbackCommandEvent.kind === "createNode") {
            void dispatchPageCreateCommand(null, fallbackCommandEvent);
            return true;
          }
          if (fallbackCommandEvent.kind === "renameNode") {
            void dispatchPageRenameCommand(null, fallbackCommandEvent);
            return true;
          }
          if (fallbackCommandEvent.kind === "moveSubtree") {
            void dispatchPageMoveCommand(null, fallbackCommandEvent);
            return true;
          }
          return true;
        };
        const buildPageDropFeedbackAction = (sourceNodeId, targetNodeId) => {
          return {
            kind: "update_drop_feedback_for_target",
            sourceNodeId: normalizeText(sourceNodeId),
            targetNodeId: normalizeText(targetNodeId),
            position: "before",
          };
        };
        const buildPageMoveAction = (sourceNodeId, targetNodeId) => {
          const normalizedSourceNodeId = normalizeText(sourceNodeId);
          const normalizedTargetNodeId = normalizeText(targetNodeId);
          if (!normalizedSourceNodeId || !normalizedTargetNodeId || normalizedSourceNodeId === normalizedTargetNodeId) return null;
          const targetItem = itemById.get(normalizedTargetNodeId);
          return {
            kind: "dispatch_move_to_target",
            sourceNodeId: normalizedSourceNodeId,
            targetNodeId: normalizedTargetNodeId,
            targetParentId: targetItem?.parentNodeId || null,
            position: "before",
          };
        };
        const applyPageDropFeedbackAction = (action) => {
          const runtimeState = readPageRuntimeState(action);
          const runtimeEnvironment = buildPageRuntimeEnvironment();
          const fallbackResult = {
            dropFeedback: null,
            dropTargetNodeId: null,
          };
          void reducePageActionWithRuntime(
            action,
            runtimeState,
            runtimeEnvironment,
            fallbackResult,
          ).then((runtimeResult) => {
            reconcilePageRuntimeResult(runtimeResult);
          });
        };
        const setPageDropFeedback = (sourceNodeId, targetNodeId) => {
          applyPageDropFeedbackAction(buildPageDropFeedbackAction(sourceNodeId, targetNodeId));
        };
        const clearPageDropFeedback = () => {
          applyPageDropFeedbackAction({ kind: "update_drop_feedback", feedback: null });
        };
        const dispatchPageMoveWithRuntime = (sourceNodeId, targetNodeId) => {
          const action = buildPageMoveAction(sourceNodeId, targetNodeId);
          if (!action) return;
          const runtimeState = readPageRuntimeState(action);
          const runtimeEnvironment = buildPageRuntimeEnvironment();
          const fallbackCommandEvent = {
            kind: "moveSubtree",
            sourceNodeId: action.sourceNodeId,
            targetNodeId: action.targetNodeId,
            targetParentId: action.targetParentId,
            position: action.position,
            runtimeRequired: true,
          };
          void reducePageActionWithRuntime(action, runtimeState, runtimeEnvironment).then(
            (runtimeResult) => {
              replayPageRuntimeCommandEvents(runtimeResult, fallbackCommandEvent);
              reconcilePageRuntimeResult(runtimeResult);
            },
          );
        };
        const dispatchPageCreateWithRuntime = (parentNodeId) => {
          const action = {
            kind: "dispatch_create",
            parentNodeId: normalizeText(parentNodeId) || null,
          };
          const runtimeState = readPageRuntimeState(action);
          const runtimeEnvironment = buildPageRuntimeEnvironment();
          const fallbackCommandEvent = {
            kind: "createNode",
            parentNodeId: action.parentNodeId,
          };
          void reducePageActionWithRuntime(action, runtimeState, runtimeEnvironment).then(
            (runtimeResult) => {
              replayPageRuntimeCommandEvents(runtimeResult, fallbackCommandEvent);
              reconcilePageRuntimeResult(runtimeResult);
            },
          );
        };
        const dispatchPageRenameWithRuntime = (nodeId, title, titleElement = null) => {
          const action = {
            kind: "dispatch_rename",
            nodeId: normalizeText(nodeId),
            title: normalizeText(title),
          };
          if (!action.nodeId || !action.title) return;
          const runtimeState = readPageRuntimeState(action);
          const runtimeEnvironment = buildPageRuntimeEnvironment();
          const fallbackCommandEvent = {
            kind: "renameNode",
            nodeId: action.nodeId,
            title: action.title,
            titleElement,
          };
          void reducePageActionWithRuntime(action, runtimeState, runtimeEnvironment).then(
            (runtimeResult) => {
              replayPageRuntimeCommandEvents(runtimeResult, fallbackCommandEvent);
              reconcilePageRuntimeResult(runtimeResult);
            },
          );
        };
        const reconcilePageRuntimeResult = (runtimeResult) => {
          if (!runtimeResult) return;
          let shouldPatchTree = false;
          let shouldSyncDropFeedback = false;
          if (Array.isArray(runtimeResult.expandedIds)) {
            shouldPatchTree = applyPageExpandedIds(runtimeResult.expandedIds) || shouldPatchTree;
          }
          if (runtimeResult.focusedId && runtimeResult.focusedId !== focusedNodeId) {
            focusedNodeId = runtimeResult.focusedId;
            postPageFocusChange(runtimeResult.focusedId);
            shouldPatchTree = true;
          }
          const nextDropTargetNodeId = runtimeResult.dropFeedback
            ? runtimeResult.dropTargetNodeId || pageRuntimeState.dropTargetNodeId || null
            : null;
          if (
            !arePageDropFeedbackEqual(runtimeResult.dropFeedback, pageRuntimeState.dropFeedback) ||
            nextDropTargetNodeId !== pageRuntimeState.dropTargetNodeId
          ) {
            commitPageRuntimeState({
              dropFeedback: runtimeResult.dropFeedback,
              dropTargetNodeId: nextDropTargetNodeId,
            });
            shouldSyncDropFeedback = true;
          }
          if (usedRustInitialRenderer) {
            if (shouldPatchTree) {
              if (Array.isArray(runtimeResult.expandedIds)) {
                runtimeResult.expandedIds.forEach((nodeId) => {
                  patchPageTreeExpansionDom(nodeId);
                });
              }
              patchPageTreeActiveDom();
            }
            if (shouldSyncDropFeedback) {
              syncPageDropFeedbackDom();
            }
            if (runtimeResult.focusedId) focusRowElement(runtimeResult.focusedId);
            return;
          }
          if (shouldPatchTree || shouldSyncDropFeedback) {
            renderTree();
            if (runtimeResult.focusedId) focusRowElement(runtimeResult.focusedId);
          }
        };
        const applyPageKeyboardAction = (action, item, sourceElement) => {
          if (mode !== "page") return;
          const actionKind = normalizeText(action?.kind).toLowerCase();
          if (
            pageFocusKeyboardReducerContractName !== "rust_page_focus_keyboard_reducer_v1" ||
            !pageFocusKeyboardReducerActions.has(actionKind)
          ) {
            return;
          }
          const runtimeAction =
            item?.nodeId && (actionKind === "open" || actionKind === "context_menu")
              ? Object.assign({}, action, { nodeId: item.nodeId })
              : action;
          const runtimeState = readPageRuntimeState(runtimeAction);
          const runtimeEnvironment = buildPageRuntimeEnvironment();
          const dispatchRuntime = (nextAction = runtimeAction, fallbackHostEvent = null) => {
            void reducePageActionWithRuntime(nextAction, runtimeState, runtimeEnvironment).then(
              (runtimeResult) => {
                replayPageRuntimeHostEvents(runtimeResult, fallbackHostEvent);
                reconcilePageRuntimeResult(runtimeResult);
              },
            );
          };
          if (actionKind === "focus") {
            focusNode(action?.nodeId);
            dispatchRuntime();
            return;
          }
          const visible = getVisiblePageItems();
          const visibleIds = visible.map((entry) => entry.nodeId);
          const currentIndex = Math.max(0, visibleIds.indexOf(focusedNodeId));
          if (actionKind === "move_next") {
            const next = visible[currentIndex + 1];
            if (next) focusNode(next.nodeId);
            dispatchRuntime();
            return;
          }
          if (actionKind === "move_previous") {
            const previous = visible[currentIndex - 1];
            if (previous) focusNode(previous.nodeId);
            dispatchRuntime();
            return;
          }
          if (actionKind === "move_home") {
            if (visibleIds[0]) focusNode(visibleIds[0]);
            dispatchRuntime();
            return;
          }
          if (actionKind === "move_end") {
            if (visibleIds[visibleIds.length - 1]) focusNode(visibleIds[visibleIds.length - 1]);
            dispatchRuntime();
            return;
          }
          if (actionKind === "expand") {
            if (item?.childCount > 0 && !expanded.has(item.nodeId)) {
              toggleExpand(item.nodeId);
              dispatchRuntime({ kind: "expand", nodeId: item.nodeId });
              return;
            }
            const firstChild = item ? getSiblings(item.nodeId)[0] : null;
            if (firstChild) focusNode(firstChild.nodeId);
            dispatchRuntime();
            return;
          }
          if (actionKind === "collapse") {
            if (item?.childCount > 0 && expanded.has(item.nodeId)) {
              toggleExpand(item.nodeId);
              dispatchRuntime({ kind: "collapse", nodeId: item.nodeId });
              return;
            }
            if (item?.parentNodeId && itemById.has(item.parentNodeId)) {
              focusNode(item.parentNodeId);
            }
            dispatchRuntime();
            return;
          }
          if (actionKind === "toggle") {
            if (item?.childCount > 0) {
              toggleExpand(item.nodeId);
              dispatchRuntime({ kind: "toggle", nodeId: item.nodeId });
            }
            return;
          }
          if (actionKind === "open") {
            dispatchRuntime(runtimeAction, item?.nodeId ? {
              kind: "pageOpen",
              nodeId: item.nodeId,
            } : null);
            return;
          }
          if (actionKind === "context_menu") {
            if (!item?.nodeId) return;
            dispatchRuntime(runtimeAction, {
              kind: "pageContextMenu",
              nodeId: item.nodeId,
              sourceElement,
            });
          }
        };
        const createKindBadge = (kind) => {
          const badge = document.createElement("span");
          badge.className = "tree-kind-badge";
          badge.dataset.kind = kind || "page";
          badge.textContent =
            kind === "index" ? "MD" :
            kind === "mindmap" ? "图" :
            kind === "table" ? "表" :
            kind === "pdf" ? "PDF" :
            kind === "book" ? "书" :
            kind === "image" ? "图" :
            kind === "video" ? "影" :
            kind === "audio" ? "音" :
            kind === "file" ? "附" : "页";
          return badge;
        };
        const getFileTreeRowDocumentId = (item) => {
          if (!item) return "";
          if (item.resourceMeta && item.resourceMeta.documentId) return item.resourceMeta.documentId;
          if (item.rowKind === "document") return item.nodeId;
          if (item.rowKind === "index") return item.nodeId.replace(/^index:/, "");
          return "";
        };
        const getFileTreeRowAssetId = (item) => {
          if (!item) return "";
          if (item.resourceMeta && item.resourceMeta.assetId) return item.resourceMeta.assetId;
          if (item.rowKind === "asset") return item.nodeId.replace(/^asset:/, "");
          if (item.rowKind === "asset_folder") return item.nodeId.replace(/^asset-folder:/, "");
          return "";
        };
        const getFileTreeRowIconKind = (item) => {
          const metaKind = normalizeText(item.resourceMeta && (item.resourceMeta.iconHint || item.resourceMeta.assetKind || item.resourceMeta.resourceKind));
          if (item.rowKind === "index") return "index";
          if (metaKind) return metaKind;
          return item.rowKind === "document" ? "page" : "file";
        };
        const emitFileTreeSelectionChange = () => {
          if (mode !== "filetree") return;
          postToHost("tree.filetree.selection.changed", {
            selectedRowIds: Array.from(selectedFileTreeRowIds),
            anchorRowId: fileTreeAnchorRowId,
            focusedRowId: fileTreeFocusedRowId,
          });
        };
        const getFileTreeRangeRowIds = (fromId, toId) => {
          const fromIndex = visibleFileTreeRowIds.indexOf(fromId);
          const toIndex = visibleFileTreeRowIds.indexOf(toId);
          if (fromIndex < 0 || toIndex < 0) return [toId];
          const lo = Math.min(fromIndex, toIndex);
          const hi = Math.max(fromIndex, toIndex);
          return visibleFileTreeRowIds.slice(lo, hi + 1);
        };
        const normalizeFileTreeSelectionState = (selection) => {
          const selectedRowIds =
            selection?.selectedRowIds instanceof Set
              ? new Set(
                  Array.from(selection.selectedRowIds)
                    .map((rowId) => normalizeText(rowId))
                    .filter(Boolean),
                )
              : new Set(normalizeStringArray(selection?.selectedRowIds));
          return {
            selectedRowIds,
            anchorRowId: normalizeText(selection?.anchorRowId) || null,
            focusedRowId: normalizeText(selection?.focusedRowId) || null,
          };
        };
        const readFileTreeSelectionState = () =>
          normalizeFileTreeSelectionState({
            selectedRowIds: Array.from(selectedFileTreeRowIds),
            anchorRowId: fileTreeAnchorRowId,
            focusedRowId: fileTreeFocusedRowId,
          });
        const areFileTreeSelectionsEqual = (left, right) => {
          const normalizedLeft = normalizeFileTreeSelectionState(left);
          const normalizedRight = normalizeFileTreeSelectionState(right);
          return (
            normalizedLeft.selectedRowIds.size === normalizedRight.selectedRowIds.size &&
            Array.from(normalizedLeft.selectedRowIds).every((rowId) =>
              normalizedRight.selectedRowIds.has(rowId),
            ) &&
            normalizedLeft.anchorRowId === normalizedRight.anchorRowId &&
            normalizedLeft.focusedRowId === normalizedRight.focusedRowId
          );
        };
        const normalizeFileTreeDragEffect = (dragEffect) => {
          const normalizedDragEffect = normalizeText(dragEffect).toLowerCase();
          return normalizedDragEffect === "copy" || normalizedDragEffect === "move"
            ? normalizedDragEffect
            : null;
        };
        const normalizeFileTreeRuntimeState = (runtimeState) => ({
          selection: normalizeFileTreeSelectionState(runtimeState?.selection),
          dragRowIds: normalizeStringArray(runtimeState?.dragRowIds),
          dragEffect: normalizeFileTreeDragEffect(runtimeState?.dragEffect),
          dropTargetRowId: normalizeText(runtimeState?.dropTargetRowId) || null,
        });
        let fileTreeRuntimeState = normalizeFileTreeRuntimeState({
          selection: {
            selectedRowIds: Array.from(selectedFileTreeRowIds),
            anchorRowId: fileTreeAnchorRowId,
            focusedRowId: fileTreeFocusedRowId,
          },
          dragRowIds: [],
          dragEffect: null,
          dropTargetRowId: null,
        });
        const commitFileTreeRuntimeState = (nextState) => {
          const normalizedRuntimeState = normalizeFileTreeRuntimeState({
            selection: hasOwn(nextState, "selection")
              ? nextState.selection
              : {
                  selectedRowIds: Array.from(selectedFileTreeRowIds),
                  anchorRowId: fileTreeAnchorRowId,
                  focusedRowId: fileTreeFocusedRowId,
                },
            dragRowIds: hasOwn(nextState, "dragRowIds")
              ? nextState.dragRowIds
              : fileTreeRuntimeState.dragRowIds,
            dragEffect: hasOwn(nextState, "dragEffect")
              ? nextState.dragEffect
              : fileTreeRuntimeState.dragEffect,
            dropTargetRowId: hasOwn(nextState, "dropTargetRowId")
              ? nextState.dropTargetRowId
              : fileTreeRuntimeState.dropTargetRowId,
          });
          fileTreeRuntimeState = normalizedRuntimeState;
          return normalizedRuntimeState;
        };
        const commitFileTreeSelection = (nextSelection) => {
          const normalizedSelection = normalizeFileTreeSelectionState(nextSelection);
          selectedFileTreeRowIds = normalizedSelection.selectedRowIds;
          fileTreeAnchorRowId = normalizedSelection.anchorRowId;
          fileTreeFocusedRowId = normalizedSelection.focusedRowId;
          commitFileTreeRuntimeState({ selection: normalizedSelection });
          emitFileTreeSelectionChange();
          return normalizedSelection;
        };
        const buildFileTreeRuntimeAction = (action) => {
          const actionKind = normalizeText(action?.kind);
          if (actionKind === "select_row") {
            const rowId = normalizeText(action.rowId);
            if (!rowId) return null;
            return {
              kind: "selectRow",
              rowId,
              modifiers: {
                shiftKey: action.modifiers?.shiftKey === true,
                ctrlKey: action.modifiers?.ctrlKey === true,
                metaKey: action.modifiers?.metaKey === true,
              },
            };
          }
          if (actionKind === "select_context_row") {
            const rowId = normalizeText(action.rowId);
            return rowId ? { kind: "selectContextRow", rowId } : null;
          }
          if (actionKind === "normalize_visible_rows") {
            return { kind: "normalizeVisibleRows" };
          }
          if (actionKind === "clear") {
            return { kind: "clearSelection" };
          }
          if (actionKind === "resolve_drag_rows") {
            const rowId = normalizeText(action.rowId);
            return rowId
              ? {
                  kind: "resolveDragRows",
                  rowId,
                  hasExternalFiles: action.hasExternalFiles === true,
                  altKey: action.altKey === true,
                }
              : null;
          }
          if (actionKind === "update_drop_target") {
            return {
              kind: "updateDropTarget",
              rowId: normalizeText(action.rowId) || null,
            };
          }
          if (actionKind === "dispatch_internal_drop") {
            const rowIds = normalizeStringArray(action.rowIds);
            if (rowIds.length === 0) return null;
            return {
              kind: "dispatchInternalDrop",
              targetRowId: normalizeText(action.targetRowId) || null,
              rowIds,
              copy: action.copy === true,
            };
          }
          if (actionKind === "dispatch_external_drop") {
            const fileCount = Number.isFinite(action.fileCount) ? Math.max(0, Math.trunc(action.fileCount)) : 0;
            if (fileCount <= 0) return null;
            return {
              kind: "dispatchExternalDrop",
              targetRowId: normalizeText(action.targetRowId) || null,
              fileCount,
            };
          }
          if (actionKind === "open_row") {
            const rowId = normalizeText(action.rowId);
            return rowId ? { kind: "openRow", rowId } : null;
          }
          if (actionKind === "context_menu_row") {
            const rowId = normalizeText(action.rowId);
            return rowId ? { kind: "contextMenuRow", rowId } : null;
          }
          return null;
        };
        const buildFileTreeRuntimeEnvironment = (action) => {
          const actionVisibleRowIds = normalizeStringArray(action?.visibleRowIds);
          return {
            visibleRowIds:
              actionVisibleRowIds.length > 0 ? actionVisibleRowIds : visibleFileTreeRowIds.slice(),
            rows: normalizedItems.map((item) => ({
              rowId: item.rowId,
              rowKind: item.rowKind,
              documentId: getFileTreeRowDocumentId(item) || null,
              assetId: getFileTreeRowAssetId(item) || null,
            })),
          };
        };
        const readFileTreeRuntimeState = () => ({
          selection: {
            selectedRowIds: Array.from(selectedFileTreeRowIds),
            anchorRowId: fileTreeAnchorRowId,
            focusedRowId: fileTreeFocusedRowId,
          },
          dragRowIds: fileTreeRuntimeState.dragRowIds,
          dragEffect: fileTreeRuntimeState.dragEffect,
          dropTargetRowId: fileTreeRuntimeState.dropTargetRowId,
        });
        const normalizeFileTreeRuntimeResult = (runtimeResult, fallbackResult) => {
          if (!runtimeResult || runtimeResult.mode !== "fileTree") return null;
          const stateSnapshot =
            runtimeResult.state && runtimeResult.state.mode === "fileTree"
              ? runtimeResult.state.state
              : null;
          const fileTreePatch = Array.isArray(runtimeResult.domPatches)
            ? runtimeResult.domPatches.find((patch) => patch && patch.kind === "fileTreeState")
            : null;
          const selectionSource =
            fileTreePatch ||
            (stateSnapshot && typeof stateSnapshot === "object" ? stateSnapshot.selection : null);
          const dragRows = Array.isArray(fileTreePatch?.dragRowIds)
            ? normalizeStringArray(fileTreePatch.dragRowIds)
            : Array.isArray(stateSnapshot?.dragRowIds)
              ? normalizeStringArray(stateSnapshot.dragRowIds)
              : fallbackResult.dragRowIds;
          const dragEffect =
            normalizeFileTreeDragEffect(fileTreePatch?.dragEffect) ||
            normalizeFileTreeDragEffect(stateSnapshot?.dragEffect) ||
            fallbackResult.dragEffect ||
            null;
          const dropTargetRowId = hasOwn(fileTreePatch, "dropTargetRowId")
            ? normalizeText(fileTreePatch?.dropTargetRowId) || null
            : hasOwn(stateSnapshot, "dropTargetRowId")
              ? normalizeText(stateSnapshot?.dropTargetRowId) || null
              : fallbackResult.dropTargetRowId || null;
          const nextSelection = normalizeFileTreeSelectionState({
            selectedRowIds:
              Array.isArray(selectionSource?.selectedRowIds)
                ? selectionSource.selectedRowIds
                : fallbackResult.nextSelection.selectedRowIds,
            anchorRowId:
              typeof selectionSource?.anchorRowId === "string"
                ? selectionSource.anchorRowId
                : fallbackResult.nextSelection.anchorRowId,
            focusedRowId:
              typeof selectionSource?.focusedRowId === "string"
                ? selectionSource.focusedRowId
                : fallbackResult.nextSelection.focusedRowId,
          });
          return {
            nextSelection,
            dragRowIds: dragRows,
            dragEffect,
            dropTargetRowId,
            runtimeState: normalizeFileTreeRuntimeState({
              selection: nextSelection,
              dragRowIds: dragRows,
              dragEffect,
              dropTargetRowId,
            }),
            hostEvents: Array.isArray(runtimeResult.hostEvents) ? runtimeResult.hostEvents : [],
          };
        };
        const reduceFileTreeSelectionActionWithRuntime = async (
          action,
          fallbackResult,
          runtimeState = readFileTreeRuntimeState(),
          runtimeEnvironment = buildFileTreeRuntimeEnvironment(action),
        ) => {
          if (mode !== "filetree") {
            return null;
          }
          const runtimeAction = buildFileTreeRuntimeAction(action);
          if (!runtimeAction) return null;
          return reduceTreeShellRuntimeWithArtifact({
            mode: "fileTree",
            requestId: buildTreeShellRuntimeRequestId("filetree-selection"),
            environment: runtimeEnvironment,
            state: runtimeState,
            action: runtimeAction,
            fallbackResult,
            normalizeResult: normalizeFileTreeRuntimeResult,
          });
        };
        const computeFileTreeSelectionActionResult = (action) => {
          const currentRuntimeState = readFileTreeRuntimeState();
          const currentSelection = currentRuntimeState.selection;
          if (action?.kind === "update_drop_target") {
            return {
              nextSelection: currentSelection,
              dragRowIds: currentRuntimeState.dragRowIds,
              dragEffect: currentRuntimeState.dragEffect,
              dropTargetRowId: normalizeText(action.rowId) || null,
            };
          }
          if (action?.kind === "dispatch_internal_drop" || action?.kind === "dispatch_external_drop") {
            return {
              nextSelection: currentSelection,
              dragRowIds: [],
              dragEffect: null,
              dropTargetRowId: null,
            };
          }
          if (
            mode !== "filetree" ||
            filetreeSelectionReducerContractName !== "rust_filetree_selection_reducer_v1" ||
            !filetreeSelectionReducerActions.has(action?.kind || "")
          ) {
            return {
              nextSelection: currentSelection,
              dragRowIds:
                action?.kind === "resolve_drag_rows"
                  ? [normalizeText(action?.rowId)].filter(Boolean)
                  : currentRuntimeState.dragRowIds,
              dragEffect: currentRuntimeState.dragEffect,
              dropTargetRowId: currentRuntimeState.dropTargetRowId,
            };
          }
          if (action.kind === "select_row") {
            const rowId = normalizeText(action.rowId);
            if (!rowId) {
              return {
                nextSelection: currentSelection,
                dragRowIds: currentRuntimeState.dragRowIds,
                dragEffect: currentRuntimeState.dragEffect,
                dropTargetRowId: currentRuntimeState.dropTargetRowId,
              };
            }
            const shiftKey = action.modifiers?.shiftKey === true;
            const metaKey = action.modifiers?.metaKey === true;
            const ctrlKey = action.modifiers?.ctrlKey === true;
            const toggleSelection = metaKey || ctrlKey;
            if (shiftKey) {
              const anchor = currentSelection.anchorRowId || currentSelection.focusedRowId || rowId;
              const nextSelection = toggleSelection
                ? new Set(currentSelection.selectedRowIds)
                : new Set();
              getFileTreeRangeRowIds(anchor, rowId).forEach((id) => nextSelection.add(id));
              return {
                nextSelection: {
                  selectedRowIds: nextSelection,
                  anchorRowId: currentSelection.anchorRowId || anchor,
                  focusedRowId: rowId,
                },
                dragRowIds: currentRuntimeState.dragRowIds,
                dragEffect: currentRuntimeState.dragEffect,
                dropTargetRowId: currentRuntimeState.dropTargetRowId,
              };
            }
            if (toggleSelection) {
              const nextSelection = new Set(currentSelection.selectedRowIds);
              if (nextSelection.has(rowId)) nextSelection.delete(rowId);
              else nextSelection.add(rowId);
              return {
                nextSelection: {
                  selectedRowIds: nextSelection,
                  anchorRowId: rowId,
                  focusedRowId: rowId,
                },
                dragRowIds: currentRuntimeState.dragRowIds,
                dragEffect: currentRuntimeState.dragEffect,
                dropTargetRowId: currentRuntimeState.dropTargetRowId,
              };
            }
            return {
              nextSelection: {
                selectedRowIds: new Set([rowId]),
                anchorRowId: rowId,
                focusedRowId: rowId,
              },
              dragRowIds: currentRuntimeState.dragRowIds,
              dragEffect: currentRuntimeState.dragEffect,
              dropTargetRowId: currentRuntimeState.dropTargetRowId,
            };
          }
          if (action.kind === "select_context_row") {
            const rowId = normalizeText(action.rowId);
            if (!rowId) {
              return {
                nextSelection: currentSelection,
                dragRowIds: currentRuntimeState.dragRowIds,
                dragEffect: currentRuntimeState.dragEffect,
                dropTargetRowId: currentRuntimeState.dropTargetRowId,
              };
            }
            if (currentSelection.selectedRowIds.has(rowId)) {
              return {
                nextSelection: {
                  selectedRowIds: new Set(currentSelection.selectedRowIds),
                  anchorRowId: currentSelection.anchorRowId,
                  focusedRowId: rowId,
                },
                dragRowIds: currentRuntimeState.dragRowIds,
                dragEffect: currentRuntimeState.dragEffect,
                dropTargetRowId: currentRuntimeState.dropTargetRowId,
              };
            }
            return {
              nextSelection: {
                selectedRowIds: new Set([rowId]),
                anchorRowId: rowId,
                focusedRowId: rowId,
              },
              dragRowIds: currentRuntimeState.dragRowIds,
              dragEffect: currentRuntimeState.dragEffect,
              dropTargetRowId: currentRuntimeState.dropTargetRowId,
            };
          }
          if (action.kind === "normalize_visible_rows") {
            const visibleSet = new Set(normalizeStringArray(action.visibleRowIds));
            const nextSelectedRowIds = Array.from(currentSelection.selectedRowIds).filter((rowId) =>
              visibleSet.has(rowId),
            );
            return {
              nextSelection: {
                selectedRowIds: new Set(nextSelectedRowIds),
                anchorRowId:
                  currentSelection.anchorRowId && visibleSet.has(currentSelection.anchorRowId)
                    ? currentSelection.anchorRowId
                    : null,
                focusedRowId:
                  currentSelection.focusedRowId && visibleSet.has(currentSelection.focusedRowId)
                    ? currentSelection.focusedRowId
                    : null,
              },
              dragRowIds: currentRuntimeState.dragRowIds,
              dragEffect: currentRuntimeState.dragEffect,
              dropTargetRowId: currentRuntimeState.dropTargetRowId,
            };
          }
          if (action.kind === "clear") {
            return {
              nextSelection: {
                selectedRowIds: new Set(),
                anchorRowId: null,
                focusedRowId: null,
              },
              dragRowIds: currentRuntimeState.dragRowIds,
              dragEffect: currentRuntimeState.dragEffect,
              dropTargetRowId: currentRuntimeState.dropTargetRowId,
            };
          }
          if (action.kind === "resolve_drag_rows") {
            const rowId = normalizeText(action.rowId);
            if (!rowId) {
              return {
                nextSelection: currentSelection,
                dragRowIds: [],
                dragEffect: currentRuntimeState.dragEffect,
                dropTargetRowId: currentRuntimeState.dropTargetRowId,
              };
            }
            return {
              nextSelection: currentSelection,
              dragRowIds: currentSelection.selectedRowIds.has(rowId)
                ? Array.from(currentSelection.selectedRowIds)
                : [rowId],
              dragEffect: currentRuntimeState.dragEffect,
              dropTargetRowId: currentRuntimeState.dropTargetRowId,
            };
          }
          return {
            nextSelection: currentSelection,
            dragRowIds: currentRuntimeState.dragRowIds,
            dragEffect: currentRuntimeState.dragEffect,
            dropTargetRowId: currentRuntimeState.dropTargetRowId,
          };
        };
        const applyFileTreeSelectionAction = (action) => {
          const runtimeState = readFileTreeRuntimeState();
          const runtimeEnvironment = buildFileTreeRuntimeEnvironment(action);
          const fallbackResult = computeFileTreeSelectionActionResult(action);
          commitFileTreeRuntimeState({
            dragRowIds: fallbackResult.dragRowIds,
            dragEffect: fallbackResult.dragEffect,
            dropTargetRowId: fallbackResult.dropTargetRowId,
          });
          if (action?.kind !== "resolve_drag_rows") {
            commitFileTreeSelection(fallbackResult.nextSelection);
            void reduceFileTreeSelectionActionWithRuntime(
              action,
              fallbackResult,
              runtimeState,
              runtimeEnvironment,
            ).then(
              (runtimeResult) => {
                if (!runtimeResult) return;
                const shouldSyncSelection = !areFileTreeSelectionsEqual(
                  runtimeResult.nextSelection,
                  readFileTreeSelectionState(),
                );
                if (shouldSyncSelection) {
                  commitFileTreeSelection(runtimeResult.nextSelection);
                }
                commitFileTreeRuntimeState(runtimeResult.runtimeState);
                if (usedRustInitialRenderer) {
                  if (shouldSyncSelection) syncFileTreeSelectionDom();
                  syncFileTreeDropTargetDom();
                }
              },
            );
          } else {
            void reduceFileTreeSelectionActionWithRuntime(
              action,
              fallbackResult,
              runtimeState,
              runtimeEnvironment,
            ).then((runtimeResult) => {
              if (!runtimeResult) return;
              commitFileTreeRuntimeState(runtimeResult.runtimeState);
              if (usedRustInitialRenderer) {
                syncFileTreeDropTargetDom();
              }
            });
          }
          return fallbackResult;
        };
        const normalizeFileTreeSelectionForVisibleRows = () => {
          const currentSelection = readFileTreeSelectionState();
          const action = {
            kind: "normalize_visible_rows",
            visibleRowIds: visibleFileTreeRowIds,
          };
          const runtimeState = readFileTreeRuntimeState();
          const runtimeEnvironment = buildFileTreeRuntimeEnvironment(action);
          const fallbackResult = computeFileTreeSelectionActionResult(action);
          if (areFileTreeSelectionsEqual(fallbackResult.nextSelection, currentSelection)) {
            return;
          }
          commitFileTreeSelection(fallbackResult.nextSelection);
          void reduceFileTreeSelectionActionWithRuntime(
            action,
            fallbackResult,
            runtimeState,
            runtimeEnvironment,
          ).then(
            (runtimeResult) => {
              if (!runtimeResult || areFileTreeSelectionsEqual(runtimeResult.nextSelection, readFileTreeSelectionState())) {
                return;
              }
              commitFileTreeSelection(runtimeResult.nextSelection);
              commitFileTreeRuntimeState(runtimeResult.runtimeState);
              if (usedRustInitialRenderer) {
                syncFileTreeSelectionDom();
                syncFileTreeDropTargetDom();
              }
            },
          );
        };
        const selectFileTreeRow = (rowId, modifiers = {}) => {
          applyFileTreeSelectionAction({
            kind: "select_row",
            rowId,
            modifiers: {
              shiftKey: modifiers.shiftKey === true,
              ctrlKey: modifiers.ctrlKey === true,
              metaKey: modifiers.metaKey === true,
            },
          });
        };
        const selectFileTreeContextRow = (rowId) => {
          applyFileTreeSelectionAction({
            kind: "select_context_row",
            rowId,
          });
        };
        const reduceFileTreeIntentActionWithRuntime = (action, fallbackHostEvent = null) => {
          const runtimeState = readFileTreeRuntimeState();
          const fallbackResult = {
            nextSelection: readFileTreeSelectionState(),
            dragRowIds: runtimeState.dragRowIds,
            dragEffect: runtimeState.dragEffect,
            dropTargetRowId: runtimeState.dropTargetRowId,
          };
          void reduceFileTreeSelectionActionWithRuntime(
            action,
            fallbackResult,
            runtimeState,
            buildFileTreeRuntimeEnvironment(action),
          ).then((runtimeResult) => {
            replayFileTreeRuntimeHostEvents(runtimeResult, fallbackHostEvent);
          });
        };
        const openFileTreeContextMenu = ({ documentId, assetId, rowId, rowKind, clientX, clientY }) => {
          if (rowId) {
            reduceFileTreeIntentActionWithRuntime(
              { kind: "context_menu_row", rowId },
              {
                kind: "contextMenu",
                documentId: documentId || null,
                assetId: assetId || null,
                rowId: rowId || null,
                rowKind: rowKind || null,
                x: clientX,
                y: clientY,
              },
            );
          }
        };
        const openFileTreeItem = (item) => {
          const documentId = getFileTreeRowDocumentId(item);
          const assetId = getFileTreeRowAssetId(item);
          if (item?.rowId) {
            reduceFileTreeIntentActionWithRuntime(
              { kind: "open_row", rowId: item.rowId },
              item.rowKind === "document" || item.rowKind === "index"
                ? {
                    kind: "navigate",
                    documentId: documentId || item.nodeId,
                  }
                : {
                    kind: "assetOpen",
                    documentId: documentId || null,
                    assetId: assetId || null,
                  },
            );
          }
        };
        const getFileTreeDropTargetFromElement = (element) => {
          const row = element instanceof Element ? element.closest('.tree-row[data-shell-mode="filetree"]') : null;
          if (!(row instanceof HTMLElement)) {
            const firstDoc = visibleFileTreeRowIds.find((rowId) => rowId.startsWith("doc:"));
            const firstItem = normalizedItems.find((item) => item.rowId === firstDoc);
            const fallbackRowId = firstItem?.rowId || firstDoc || "";
            return {
              rowId: fallbackRowId,
              rowKind: firstItem?.rowKind || "document",
              documentId: firstItem ? getFileTreeRowDocumentId(firstItem) : currentActiveDocumentId,
              assetId: firstItem ? getFileTreeRowAssetId(firstItem) : "",
            };
          }
          return {
            rowId: row.dataset.rowId || "",
            rowKind: row.dataset.rowKind || "",
            documentId: row.dataset.documentId || "",
            assetId: row.dataset.assetId || "",
          };
        };
        const postFileTreeDropToHost = (type, target, extra = {}) => {
          postToHost(type, {
            workspaceId,
            rowId: target.rowId,
            rowKind: target.rowKind,
            targetRowId: target.rowId,
            targetRowKind: target.rowKind,
            documentId: target.documentId,
            assetId: target.assetId,
            ...extra,
          });
        };
        const buildFileTreeDropTargetFromRuntimeEvent = (event, fallbackTarget = {}) => {
          const target = event && typeof event.target === "object" ? event.target : null;
          const targetKind = normalizeText(target?.kind);
          const targetRowId = normalizeText(event?.targetRowId) || normalizeText(fallbackTarget.rowId);
          const documentId = normalizeText(target?.documentId) || normalizeText(fallbackTarget.documentId);
          const assetId = normalizeText(target?.assetId) || normalizeText(fallbackTarget.assetId);
          const rowKind = targetKind === "assetFolder"
            ? "asset_folder"
            : targetKind === "asset"
              ? "asset"
              : targetKind === "index"
                ? "index"
                : normalizeText(fallbackTarget.rowKind) || "document";
          return {
            rowId: targetRowId,
            rowKind,
            documentId,
            assetId,
          };
        };
        const replayFileTreeRuntimeHostEvents = (runtimeResult, fallbackHostEvent) => {
          const runtimeHostEvents = Array.isArray(runtimeResult?.hostEvents)
            ? runtimeResult.hostEvents
            : [];
          let replayed = false;
          runtimeHostEvents.forEach((event) => {
            if (!event || typeof event !== "object") return;
            if (event.kind === "fileTreeOpen") {
              const target = event.target;
              const targetKind = normalizeText(target?.kind);
              if (targetKind === "document" || targetKind === "index") {
                const documentId = normalizeText(target?.documentId);
                if (!documentId) return;
                handleNavigate(documentId);
                replayed = true;
                return;
              }
              if (targetKind === "assetFolder" || targetKind === "asset") {
                const documentId = normalizeText(target?.documentId) || null;
                const assetId = normalizeText(target?.assetId) || null;
                if (!assetId) return;
                postToHost("tree.asset.open", {
                  documentId,
                  assetId,
                  target: { documentId },
                });
                replayed = true;
              }
              return;
            }
            if (event.kind === "fileTreeContextMenu") {
              const target = event.target;
              const targetKind = normalizeText(target?.kind);
              const documentId = normalizeText(target?.documentId) || null;
              const assetId = normalizeText(target?.assetId) || null;
              const rowKind =
                targetKind === "assetFolder"
                  ? "asset_folder"
                  : targetKind === "asset"
                    ? "asset"
                    : targetKind === "index"
                      ? "index"
                      : "document";
              postToHost("tree.filetree.context-menu", {
                documentId,
                assetId,
                rowId: normalizeText(event.rowId) || null,
                rowKind,
                x: fallbackHostEvent?.x || 0,
                y: fallbackHostEvent?.y || 0,
              });
              replayed = true;
            }
            if (event.kind === "fileTreeInternalDrop") {
              postFileTreeDropToHost(
                "tree.filetree.internal-drop",
                buildFileTreeDropTargetFromRuntimeEvent(event, fallbackHostEvent?.target),
                {
                  rowIds: normalizeStringArray(event.rowIds),
                  copy: event.copy === true,
                },
              );
              replayed = true;
              return;
            }
            if (event.kind === "fileTreeExternalDrop") {
              postFileTreeDropToHost(
                "tree.filetree.external-drop",
                buildFileTreeDropTargetFromRuntimeEvent(event, fallbackHostEvent?.target),
                {
                  files: Array.isArray(fallbackHostEvent?.files) ? fallbackHostEvent.files : [],
                  fileCount: Number.isFinite(event.fileCount) ? event.fileCount : 0,
                },
              );
              replayed = true;
              return;
            }
          });
          if (replayed || !fallbackHostEvent) return replayed;
          if (runtimeResult && fallbackHostEvent.runtimeRequired === true) {
            return false;
          }
          if (fallbackHostEvent.kind === "navigate") {
            handleNavigate(fallbackHostEvent.documentId);
            return true;
          }
          if (fallbackHostEvent.kind === "assetOpen") {
            postToHost("tree.asset.open", {
              documentId: fallbackHostEvent.documentId,
              assetId: fallbackHostEvent.assetId,
              target: { documentId: fallbackHostEvent.documentId },
            });
            return true;
          }
          if (fallbackHostEvent.kind === "contextMenu") {
            postToHost("tree.filetree.context-menu", {
              documentId: fallbackHostEvent.documentId,
              assetId: fallbackHostEvent.assetId,
              rowId: fallbackHostEvent.rowId,
              rowKind: fallbackHostEvent.rowKind,
              x: fallbackHostEvent.x,
              y: fallbackHostEvent.y,
            });
            return true;
          }
          if (fallbackHostEvent.kind === "internalDrop") {
            postFileTreeDropToHost("tree.filetree.internal-drop", fallbackHostEvent.target, {
              rowIds: fallbackHostEvent.rowIds,
              copy: fallbackHostEvent.copy === true,
            });
            return true;
          }
          if (fallbackHostEvent.kind === "externalDrop") {
            postFileTreeDropToHost("tree.filetree.external-drop", fallbackHostEvent.target, {
              files: fallbackHostEvent.files,
            });
            return true;
          }
          return false;
        };
        const readFileTreeInternalDropPayload = (event) => {
          const raw =
            event.dataTransfer?.getData("application/x-mnote-filetree-row-ids") ||
            event.dataTransfer?.getData("application/x-mnote-file-tree") ||
            event.dataTransfer?.getData("text/plain") ||
            "";
          if (!raw) return null;
          try {
            const parsed = JSON.parse(raw);
            return Array.isArray(parsed.rowIds) ? parsed.rowIds.map((id) => normalizeText(id)).filter(Boolean) : null;
          } catch {
            return null;
          }
        };
        const syncFileTreeDropTargetDom = () => {
          if (mode !== "filetree") return;
          const dropTargetRowId = fileTreeRuntimeState.dropTargetRowId;
          appElement.querySelectorAll('.tree-row[data-shell-mode="filetree"]').forEach((row) => {
            if (!(row instanceof HTMLElement)) return;
            const rowId = normalizeText(row.dataset.rowId);
            row.dataset.dropTarget = String(Boolean(rowId && rowId === dropTargetRowId));
          });
          if (fileTreeRuntimeState.dragEffect) {
            appElement.dataset.dragEffect = fileTreeRuntimeState.dragEffect;
          } else {
            delete appElement.dataset.dragEffect;
          }
        };
        const applyFileTreeDropTargetAction = (rowId) => {
          const action = {
            kind: "update_drop_target",
            rowId: normalizeText(rowId) || null,
          };
          const runtimeState = readFileTreeRuntimeState();
          const runtimeEnvironment = buildFileTreeRuntimeEnvironment(action);
          const fallbackResult = computeFileTreeSelectionActionResult(action);
          const targetChanged = fallbackResult.dropTargetRowId !== fileTreeRuntimeState.dropTargetRowId;
          commitFileTreeRuntimeState({
            dragRowIds: fallbackResult.dragRowIds,
            dragEffect: fallbackResult.dragEffect,
            dropTargetRowId: fallbackResult.dropTargetRowId,
          });
          if (targetChanged) {
            syncFileTreeDropTargetDom();
          }
          if (!targetChanged) return fallbackResult;
          void reduceFileTreeSelectionActionWithRuntime(
            action,
            fallbackResult,
            runtimeState,
            runtimeEnvironment,
          ).then((runtimeResult) => {
            if (!runtimeResult) return;
            commitFileTreeRuntimeState(runtimeResult.runtimeState);
            syncFileTreeDropTargetDom();
          });
          return fallbackResult;
        };
        const clearFileTreeDropTarget = () => {
          applyFileTreeDropTargetAction(null);
        };
        const dispatchFileTreeDropWithRuntime = (target, dropPayload, event) => {
          const files = Array.isArray(dropPayload?.files) ? dropPayload.files : [];
          const internalRowIds = normalizeStringArray(dropPayload?.internalRowIds);
          const hasExternalFiles = files.length > 0;
          const action = hasExternalFiles
            ? {
                kind: "dispatch_external_drop",
                targetRowId: normalizeText(target?.rowId) || null,
                fileCount: files.length,
              }
            : {
                kind: "dispatch_internal_drop",
                targetRowId: normalizeText(target?.rowId) || null,
                rowIds: internalRowIds,
                copy: event?.altKey === true,
              };
          const fallbackHostEvent = hasExternalFiles
            ? {
                kind: "externalDrop",
                target,
                files,
                runtimeRequired: true,
              }
            : {
                kind: "internalDrop",
                target,
                rowIds: internalRowIds,
                copy: event?.altKey === true,
                runtimeRequired: true,
              };
          const runtimeState = readFileTreeRuntimeState();
          const runtimeEnvironment = buildFileTreeRuntimeEnvironment(action);
          const fallbackResult = computeFileTreeSelectionActionResult(action);
          commitFileTreeRuntimeState({
            dragRowIds: fallbackResult.dragRowIds,
            dragEffect: fallbackResult.dragEffect,
            dropTargetRowId: fallbackResult.dropTargetRowId,
          });
          syncFileTreeDropTargetDom();
          void reduceFileTreeSelectionActionWithRuntime(
            action,
            fallbackResult,
            runtimeState,
            runtimeEnvironment,
          ).then((runtimeResult) => {
            if (!runtimeResult) {
              replayFileTreeRuntimeHostEvents(null, fallbackHostEvent);
              return;
            }
            commitFileTreeRuntimeState(runtimeResult.runtimeState);
            syncFileTreeDropTargetDom();
            if (!replayFileTreeRuntimeHostEvents(runtimeResult, fallbackHostEvent)) {
              replayFileTreeRuntimeHostEvents(null, fallbackHostEvent);
            }
          });
          return fallbackResult;
        };
        const readFileTreeDropPayloadFromEvent = (event) => {
          const internalRowIds = readFileTreeInternalDropPayload(event);
          const files = Array.from(event.dataTransfer?.files || []);
          const transferTypes = Array.from(event.dataTransfer?.types || []);
          const hasExternalFiles = files.length > 0 || transferTypes.includes("Files");
          return {
            internalRowIds,
            files,
            hasExternalFiles,
            hasPayload: Boolean(internalRowIds || hasExternalFiles),
          };
        };
        const updateFileTreeDropTargetFromEvent = (event) => {
          const dropPayload = readFileTreeDropPayloadFromEvent(event);
          if (!dropPayload.hasPayload) return dropPayload;
          event.preventDefault();
          const target = getFileTreeDropTargetFromElement(event.target);
          applyFileTreeDropTargetAction(target.rowId);
          return dropPayload;
        };

        const syncFileTreeSelectionDom = () => {
          if (mode !== "filetree") return;
          appElement.querySelectorAll('[data-rust-rendered-row="filetree"]').forEach((row) => {
            if (!(row instanceof HTMLElement)) return;
            const rowId = normalizeText(row.dataset.rowId);
            row.dataset.selected = String(Boolean(rowId && selectedFileTreeRowIds.has(rowId)));
          });
        };
        const patchFileTreeActiveDom = () => {
          if (mode !== "filetree") return;
          appElement.querySelectorAll('[data-rust-rendered-row="filetree"]').forEach((row) => {
            if (!(row instanceof HTMLElement)) return;
            const rowDocumentId = normalizeText(row.dataset.documentId);
            const rowKind = normalizeText(row.dataset.rowKind);
            row.dataset.active = String(rowKind === "document" && rowDocumentId === currentActiveDocumentId);
          });
        };
        const bindFileTreeRootEvents = (root) => {
          root.addEventListener("dragover", (event) => {
            updateFileTreeDropTargetFromEvent(event);
          });
          root.addEventListener("dragleave", (event) => {
            if (event.currentTarget instanceof Node && event.relatedTarget instanceof Node) {
              if (event.currentTarget.contains(event.relatedTarget)) return;
            }
            clearFileTreeDropTarget();
          });
          root.addEventListener("drop", (event) => {
            const dropPayload = readFileTreeDropPayloadFromEvent(event);
            const { hasPayload } = dropPayload;
            if (!hasPayload) return;
            event.preventDefault();
            const target = getFileTreeDropTargetFromElement(event.target);
            dispatchFileTreeDropWithRuntime(target, dropPayload, event);
          });
        };
        const bindFileTreeRowEvents = (row, item) => {
          if (!(row instanceof HTMLElement) || !item) return;
          const documentId = getFileTreeRowDocumentId(item) || null;
          const assetId = getFileTreeRowAssetId(item) || null;
          row.dataset.active = String(item.rowKind === "document" && documentId === currentActiveDocumentId);
          row.dataset.nodeId = item.nodeId;
          row.dataset.rowId = item.rowId;
          row.dataset.rowKind = item.rowKind;
          row.dataset.documentId = documentId || "";
          row.dataset.assetId = assetId || "";
          row.dataset.shellMode = "filetree";
          row.dataset.selected = String(selectedFileTreeRowIds.has(item.rowId));
          row.dataset.dropTarget = String(item.rowId === fileTreeRuntimeState.dropTargetRowId);
          row.tabIndex = 0;
          row.setAttribute("role", "treeitem");
          row.addEventListener("click", (event) => {
            selectFileTreeRow(item.rowId, event);
            syncFileTreeSelectionDom();
          });
          row.addEventListener("dblclick", () => openFileTreeItem(item));
          row.addEventListener("contextmenu", (event) => {
            event.preventDefault();
            selectFileTreeContextRow(item.rowId);
            syncFileTreeSelectionDom();
            openFileTreeContextMenu({
              documentId,
              assetId,
              rowId: item.rowId,
              rowKind: item.rowKind,
              clientX: event.clientX,
              clientY: event.clientY,
            });
          });
          row.draggable = true;
          row.addEventListener("dragstart", (event) => {
            const rowIds =
              applyFileTreeSelectionAction({
                kind: "resolve_drag_rows",
                rowId: item.rowId,
                altKey: event.altKey === true,
              }).dragRowIds || [item.rowId];
            if (event.dataTransfer) {
              const payload = JSON.stringify({ type: "mnote-file-tree-dnd", version: 1, rowIds });
              event.dataTransfer.effectAllowed = "copyMove";
              event.dataTransfer.setData("application/x-mnote-filetree-row-ids", payload);
              event.dataTransfer.setData("application/x-mnote-file-tree", payload);
              event.dataTransfer.setData("text/plain", payload);
            }
          });
          row.addEventListener("dragend", () => {
            clearFileTreeDropTarget();
          });
          row.querySelectorAll("[data-rust-action]").forEach((element) => {
            if (!(element instanceof HTMLElement)) return;
            element.addEventListener("click", (event) => {
              const action = normalizeText(element.dataset.rustAction);
              if (action === "open") {
                openFileTreeItem(item);
                return;
              }
              if (action === "menu") {
                event.preventDefault();
                event.stopPropagation();
                openFileTreeContextMenu({
                  documentId,
                  assetId,
                  rowId: item.rowId,
                  rowKind: item.rowKind,
                  clientX: 0,
                  clientY: 0,
                });
              }
            });
          });
        };
        const hydrateInitialFileTree = () => {
          if (mode !== "filetree") return false;
          const root = appElement.querySelector('[data-rust-filetree-renderer="initial_v1"]');
          if (!(root instanceof HTMLElement)) return false;
          visibleFileTreeRowIds = [];
          bindFileTreeRootEvents(root);
          root.querySelectorAll('[data-rust-rendered-row="filetree"]').forEach((row) => {
            if (!(row instanceof HTMLElement)) return;
            const rowId = normalizeText(row.dataset.rowId);
            const nodeId = normalizeText(row.dataset.nodeId);
            const item = fileTreeRowById.get(rowId) || itemById.get(nodeId);
            if (!item) return;
            visibleFileTreeRowIds.push(item.rowId);
            bindFileTreeRowEvents(row, item);
          });
          normalizeFileTreeSelectionForVisibleRows();
          syncFileTreeSelectionDom();
          syncFileTreeDropTargetDom();
          return true;
        };
        const getPickablePickerEntries = () => {
          const visible = [];
          if (allowRootPick) {
            visible.push({ pickerItemKey: "__root__", documentId: null });
          }
          normalizedItems.forEach((item) => {
            visible.push({ pickerItemKey: item.nodeId, documentId: item.nodeId });
          });
          return visible.filter((entry) => {
            if (entry.pickerItemKey === "__root__") return true;
            return !excludedIds.has(entry.documentId || "");
          });
        };
        const normalizePickerItemKey = (pickerItemKey) => {
          const normalizedItemKey = normalizeText(pickerItemKey);
          if (normalizedItemKey === "__root__" && allowRootPick) {
            return "__root__";
          }
          if (
            normalizedItemKey &&
            itemById.has(normalizedItemKey) &&
            !excludedIds.has(normalizedItemKey)
          ) {
            return normalizedItemKey;
          }
          return "";
        };
        const resolveCurrentPickerItemKey = () => {
          const fromActive = normalizePickerItemKey(currentActivePickerItemKey);
          if (fromActive) return fromActive;
          const fromDocument = normalizePickerItemKey(currentActiveDocumentId);
          if (fromDocument) return fromDocument;
          return getPickablePickerEntries()[0]?.pickerItemKey || "";
        };
        const computePickerStateActionResult = (action) => {
          const currentPickerItemKey = resolveCurrentPickerItemKey();
          if (
            mode !== "picker" ||
            pickerStateReducerContractName !== "rust_picker_state_reducer_v1" ||
            !pickerStateReducerActions.has(action?.kind || "")
          ) {
            return {
              nextItemKey: currentPickerItemKey,
              pickedDocumentId:
                action?.kind === "pick" && currentPickerItemKey !== "__root__"
                  ? currentPickerItemKey || null
                  : null,
              pickedRoot: action?.kind === "pick" && currentPickerItemKey === "__root__",
            };
          }
          const pickable = getPickablePickerEntries();
          if (pickable.length === 0) {
            return {
              nextItemKey: "",
              pickedDocumentId: null,
              pickedRoot: false,
            };
          }
          const currentIndex = pickable.findIndex(
            (entry) => entry.pickerItemKey === currentPickerItemKey,
          );
          const baseIndex = currentIndex >= 0 ? currentIndex : 0;
          if (action.kind === "normalize") {
            return {
              nextItemKey: pickable[baseIndex]?.pickerItemKey || "",
              pickedDocumentId: null,
              pickedRoot: false,
            };
          }
          if (action.kind === "focus") {
            const nextItemKey = normalizePickerItemKey(action.itemKey);
            return {
              nextItemKey: nextItemKey || pickable[0]?.pickerItemKey || "",
              pickedDocumentId: null,
              pickedRoot: false,
            };
          }
          if (action.kind === "pick") {
            const target = pickable[baseIndex];
            const targetKey = target?.pickerItemKey || "";
            return {
              nextItemKey: targetKey,
              pickedDocumentId:
                targetKey && targetKey !== "__root__" ? targetKey : null,
              pickedRoot: targetKey === "__root__",
            };
          }
          const nextIndex =
            action.kind === "previous"
              ? Math.max(0, baseIndex - 1)
              : action.kind === "home"
                ? 0
                : action.kind === "end"
                  ? pickable.length - 1
                  : Math.min(pickable.length - 1, baseIndex + 1);
          const target = pickable[nextIndex];
          return {
            nextItemKey: target?.pickerItemKey || "",
            pickedDocumentId: null,
            pickedRoot: false,
          };
        };
        const buildPickerRuntimeAction = (action) => {
          const actionKind = normalizeText(action?.kind);
          if (actionKind === "focus") {
            const itemKey = normalizeText(action.itemKey);
            return itemKey
              ? {
                  kind: "focus",
                  itemKey,
                  focusDom: action.focusDom === true,
                }
              : null;
          }
          if (
            actionKind === "normalize" ||
            actionKind === "next" ||
            actionKind === "previous" ||
            actionKind === "home" ||
            actionKind === "end" ||
            actionKind === "pick"
          ) {
            return { kind: actionKind };
          }
          return null;
        };
        const buildPickerRuntimeEnvironment = () => {
          const items = [];
          if (allowRootPick) {
            items.push({ itemKey: "__root__", documentId: null, pickable: true });
          }
          normalizedItems.forEach((item) => {
            items.push({
              itemKey: item.nodeId,
              documentId: item.nodeId,
              pickable: !excludedIds.has(item.nodeId),
            });
          });
          return {
            items,
            excludedIds: Array.from(excludedIds),
            allowRootPick,
          };
        };
        const readPickerRuntimeState = () => {
          const activeItemKey = resolveCurrentPickerItemKey();
          return {
            activeItemKey: activeItemKey || null,
          };
        };
        const normalizePickerRuntimeResult = (runtimeResult, fallbackResult) => {
          if (!runtimeResult || runtimeResult.mode !== "picker") return null;
          const stateSnapshot =
            runtimeResult.state && runtimeResult.state.mode === "picker"
              ? runtimeResult.state.state
              : null;
          const pickerPatch = Array.isArray(runtimeResult.domPatches)
            ? runtimeResult.domPatches.find((patch) => patch && patch.kind === "pickerState")
            : null;
          const rawNextItemKey =
            typeof pickerPatch?.activeItemKey === "string"
              ? pickerPatch.activeItemKey
              : typeof stateSnapshot?.activeItemKey === "string"
                ? stateSnapshot.activeItemKey
                : fallbackResult.nextItemKey;
          const nextItemKey = normalizePickerItemKey(rawNextItemKey);
          const normalized = {
            nextItemKey: nextItemKey || fallbackResult.nextItemKey || "",
            focusDom: pickerPatch?.focusDom === true,
            pickedDocumentId: null,
            pickedRoot: false,
          };
          if (Array.isArray(runtimeResult.hostEvents)) {
            runtimeResult.hostEvents.forEach((event) => {
              if (!event || typeof event !== "object") return;
              if (event.kind === "pickerPickRoot") {
                normalized.pickedRoot = true;
                normalized.pickedDocumentId = null;
              }
              if (event.kind === "pickerPickDocument") {
                normalized.pickedRoot = false;
                normalized.pickedDocumentId = normalizeText(event.documentId) || null;
              }
            });
          }
          return normalized;
        };
        const reducePickerStateActionWithRuntime = async (
          action,
          fallbackResult,
          runtimeState = readPickerRuntimeState(),
          runtimeEnvironment = buildPickerRuntimeEnvironment(),
        ) => {
          if (mode !== "picker") {
            return null;
          }
          const runtimeAction = buildPickerRuntimeAction(action);
          if (!runtimeAction) return null;
          return reduceTreeShellRuntimeWithArtifact({
            mode: "picker",
            requestId: buildTreeShellRuntimeRequestId("picker-state"),
            environment: runtimeEnvironment,
            state: runtimeState,
            action: runtimeAction,
            fallbackResult,
            normalizeResult: normalizePickerRuntimeResult,
          });
        };
        const postPickerPickResultToHost = (result) => {
          if (mode !== "picker" || !result) return;
          if (result.pickedRoot) {
            postToHost("tree.pick.root", { documentId: null, target: { documentId: null } });
            return;
          }
          if (result.pickedDocumentId) {
            postToHost("tree.pick", {
              documentId: result.pickedDocumentId,
              itemKey: result.nextItemKey || result.pickedDocumentId,
              target: { documentId: result.pickedDocumentId },
            });
          }
        };
        const replayPickerRuntimePickResult = (runtimeResult, fallbackResult) => {
          if (runtimeResult) {
            if (runtimeResult.pickedRoot || runtimeResult.pickedDocumentId) {
              postPickerPickResultToHost(runtimeResult);
              return true;
            }
            return false;
          }
          if (fallbackResult && (fallbackResult.pickedRoot || fallbackResult.pickedDocumentId)) {
            postPickerPickResultToHost(fallbackResult);
            return true;
          }
          return false;
        };
        const commitPickerFocusResult = (result, options = {}) => {
          if (!result) return;
          const shouldFocusDom = options.focusDom === true;
          currentActivePickerItemKey = result.nextItemKey || "";
          currentActiveDocumentId =
            result.nextItemKey && result.nextItemKey !== "__root__"
              ? result.nextItemKey
              : "";
          postToHost("tree.picker.focus.changed", {
            documentId: currentActiveDocumentId || null,
            itemKey: currentActivePickerItemKey || null,
            pickerItemKey: currentActivePickerItemKey || null,
          });
          if (usedRustInitialRenderer) {
            patchPickerActiveDom();
            if (shouldFocusDom) focusPickerRowElement(currentActivePickerItemKey);
            return;
          }
          renderTree();
          if (shouldFocusDom) focusPickerRowElement(currentActivePickerItemKey);
        };
        const reconcilePickerRuntimeResult = (runtimeResult, fallbackResult) => {
          if (!runtimeResult) return;
          if (
            runtimeResult.nextItemKey &&
            runtimeResult.nextItemKey !== currentActivePickerItemKey
          ) {
            commitPickerFocusResult(runtimeResult, { focusDom: runtimeResult.focusDom });
            return;
          }
          if (runtimeResult.focusDom) {
            focusPickerRowElement(runtimeResult.nextItemKey || currentActivePickerItemKey);
          }
        };
        const applyPickerFocusByItemKey = (pickerItemKey, options = {}) => {
          const action = {
            kind: "focus",
            itemKey: pickerItemKey,
            focusDom: options.focusDom === true,
          };
          const runtimeState = readPickerRuntimeState();
          const runtimeEnvironment = buildPickerRuntimeEnvironment();
          const fallbackResult = computePickerStateActionResult(action);
          void reducePickerStateActionWithRuntime(
            action,
            fallbackResult,
            runtimeState,
            runtimeEnvironment,
          ).then((runtimeResult) => {
            if (runtimeResult) {
              reconcilePickerRuntimeResult(runtimeResult, fallbackResult);
              return;
            }
            commitPickerFocusResult(fallbackResult, { focusDom: action.focusDom });
          });
          return fallbackResult;
        };
        const dispatchPickerPickByItemKeyWithRuntime = (pickerItemKey, options = {}) => {
          const focusAction = {
            kind: "focus",
            itemKey: pickerItemKey,
            focusDom: options.focusDom === true,
          };
          const focusResult = computePickerStateActionResult(focusAction);
          const runtimeState = {
            activeItemKey: focusResult.nextItemKey || null,
          };
          const runtimeEnvironment = buildPickerRuntimeEnvironment();
          const fallbackResult = computePickerStateActionResult({ kind: "pick" });
          fallbackResult.nextItemKey = focusResult.nextItemKey || fallbackResult.nextItemKey;
          fallbackResult.pickedDocumentId =
            fallbackResult.nextItemKey && fallbackResult.nextItemKey !== "__root__"
              ? fallbackResult.nextItemKey
              : null;
          fallbackResult.pickedRoot = fallbackResult.nextItemKey === "__root__";
          void reducePickerStateActionWithRuntime(
            { kind: "pick" },
            fallbackResult,
            runtimeState,
            runtimeEnvironment,
          ).then((runtimeResult) => {
            if (runtimeResult) {
              reconcilePickerRuntimeResult(runtimeResult, fallbackResult);
            } else {
              commitPickerFocusResult(focusResult, { focusDom: focusAction.focusDom });
            }
            replayPickerRuntimePickResult(runtimeResult, fallbackResult);
          });
          return fallbackResult;
        };
        const applyPickerStateAction = (action) => {
          const runtimeState = readPickerRuntimeState();
          const runtimeEnvironment = buildPickerRuntimeEnvironment();
          const fallbackResult = computePickerStateActionResult(action);
          void reducePickerStateActionWithRuntime(
            action,
            fallbackResult,
            runtimeState,
            runtimeEnvironment,
          ).then((runtimeResult) => {
            if (runtimeResult) {
              reconcilePickerRuntimeResult(runtimeResult, fallbackResult);
            } else if (action?.kind !== "pick") {
              commitPickerFocusResult(fallbackResult);
            }
            if (action?.kind === "pick") {
              replayPickerRuntimePickResult(runtimeResult, fallbackResult);
            }
          });
          return fallbackResult;
        };
        const bindPickerRootEvents = (row) => {
          if (!(row instanceof HTMLElement)) return;
          row.dataset.focused = String(currentActivePickerItemKey === "__root__");
          row.addEventListener("click", () => {
            dispatchPickerPickByItemKeyWithRuntime("__root__", { focusDom: true });
          });
        };
        const bindPickerRowEvents = (row, item) => {
          if (!(row instanceof HTMLElement) || !item) return;
          row.dataset.nodeId = item.nodeId;
          row.dataset.shellMode = "picker";
          row.dataset.focused = String(
            currentActivePickerItemKey === item.nodeId ||
            (!currentActivePickerItemKey && currentActiveDocumentId === item.nodeId),
          );
          row.addEventListener("click", () => {
            dispatchPickerPickByItemKeyWithRuntime(item.nodeId, { focusDom: true });
          });
        };
        const focusPickerRowElement = (pickerItemKey) => {
          const normalizedItemKey = normalizeText(pickerItemKey);
          window.requestAnimationFrame(() => {
            const row =
              normalizedItemKey === "__root__"
                ? appElement.querySelector('[data-rust-rendered-row="picker-root"]')
                : appElement.querySelector(
                    \`.tree-row[data-node-id="\${CSS.escape(normalizedItemKey)}"]\`,
                  );
            if (!(row instanceof HTMLElement)) return;
            row.focus({ preventScroll: true });
            row.scrollIntoView({ block: "nearest" });
          });
        };
        const patchPickerActiveDom = () => {
          if (mode !== "picker") return;
          appElement
            .querySelectorAll('[data-rust-rendered-row="picker"], [data-rust-rendered-row="picker-root"]')
            .forEach((row) => {
              if (!(row instanceof HTMLElement)) return;
              const nodeId = normalizeText(row.dataset.nodeId);
              const isRoot = row.dataset.rustRenderedRow === "picker-root";
              const isFocused = isRoot
                ? currentActivePickerItemKey === "__root__"
                : currentActivePickerItemKey === nodeId ||
                  (!currentActivePickerItemKey && currentActiveDocumentId === nodeId);
              row.dataset.focused = String(isFocused);
              row.tabIndex = isFocused ? 0 : -1;
            });
        };
        const hydrateInitialPickerTree = () => {
          if (mode !== "picker") return false;
          const root = appElement.querySelector('[data-rust-picker-renderer="initial_v1"]');
          if (!(root instanceof HTMLElement)) return false;
          root.querySelectorAll('[data-rust-rendered-row="picker-root"]').forEach((row) => {
            bindPickerRootEvents(row);
          });
          root.querySelectorAll('[data-rust-rendered-row="picker"]').forEach((row) => {
            if (!(row instanceof HTMLElement)) return;
            const nodeId = normalizeText(row.dataset.nodeId);
            const item = itemById.get(nodeId);
            if (!item) return;
            bindPickerRowEvents(row, item);
          });
          return true;
        };
        const bindPageRowEvents = (row, item) => {
          if (!(row instanceof HTMLElement) || !item) return;
          row.dataset.active = String(item.nodeId === currentActiveDocumentId);
          row.dataset.focused = String(item.nodeId === focusedNodeId);
          row.dataset.nodeId = item.nodeId;
          row.dataset.shellMode = "page";
          row.dataset.draggable = "true";
          row.dataset.dropFeedback = String(Boolean(pageRuntimeState.dropFeedback && pageRuntimeState.dropTargetNodeId === item.nodeId));
          row.draggable = true;
          row.tabIndex = item.nodeId === focusedNodeId ? 0 : -1;
          row.addEventListener("focus", () => {
            if (focusedNodeId !== item.nodeId) {
              applyPageKeyboardAction({ kind: "focus", nodeId: item.nodeId }, item, row);
            }
          });
          row.addEventListener("keydown", (event) => {
            if (event.key === "Enter") {
              event.preventDefault();
              applyPageKeyboardAction({ kind: "open" }, item, event.currentTarget);
            }
            if (event.key === "ArrowDown") {
              event.preventDefault();
              applyPageKeyboardAction({ kind: "move_next" }, item, event.currentTarget);
            }
            if (event.key === "ArrowUp") {
              event.preventDefault();
              applyPageKeyboardAction({ kind: "move_previous" }, item, event.currentTarget);
            }
            if (event.key === "Home") {
              event.preventDefault();
              applyPageKeyboardAction({ kind: "move_home" }, item, event.currentTarget);
            }
            if (event.key === "End") {
              event.preventDefault();
              applyPageKeyboardAction({ kind: "move_end" }, item, event.currentTarget);
            }
          });
          row.addEventListener("contextmenu", (event) => {
            event.preventDefault();
            applyPageKeyboardAction({ kind: "context_menu" }, item, event.currentTarget);
          });
          row.addEventListener("dragstart", (event) => {
            draggingPageNodeId = item.nodeId;
            if (event.dataTransfer) {
              event.dataTransfer.effectAllowed = "move";
              event.dataTransfer.setData(PAGE_DRAG_MIME, item.nodeId);
              event.dataTransfer.setData("text/plain", item.nodeId);
            }
            setLastAction(\`开始拖拽页面 \${item.title}\`);
          });
          row.addEventListener("dragover", (event) => {
            const sourceNodeId = readPageDragNodeId(event);
            const targetNodeId = resolvePageDropTargetNodeId(event.target);
            if (!canSubmitPageDropCandidate(sourceNodeId, targetNodeId)) {
              clearPageDropFeedback();
              return;
            }
            event.preventDefault();
            if (event.dataTransfer) {
              event.dataTransfer.dropEffect = "move";
            }
            setPageDropFeedback(sourceNodeId, targetNodeId);
          });
          row.addEventListener("dragleave", (event) => {
            const relatedTarget = event.relatedTarget instanceof Node ? event.relatedTarget : null;
            if (relatedTarget && row.contains(relatedTarget)) {
              return;
            }
            if (pageRuntimeState.dropTargetNodeId === item.nodeId) {
              clearPageDropFeedback();
            }
          });
          row.addEventListener("drop", (event) => {
            const sourceNodeId = readPageDragNodeId(event);
            const targetNodeId = resolvePageDropTargetNodeId(event.target);
            clearPageDropFeedback();
            draggingPageNodeId = "";
            if (!canSubmitPageDropCandidate(sourceNodeId, targetNodeId)) {
              return;
            }
            event.preventDefault();
            dispatchPageMoveWithRuntime(sourceNodeId, targetNodeId);
          });
          row.addEventListener("dragend", () => {
            draggingPageNodeId = "";
            clearPageDropFeedback();
          });
          row.querySelectorAll("[data-rust-action]").forEach((element) => {
            if (!(element instanceof HTMLElement)) return;
            element.addEventListener("click", (event) => {
              const action = normalizeText(element.dataset.rustAction);
              if (action === "toggle") {
                event.preventDefault();
                event.stopPropagation();
                applyPageKeyboardAction({ kind: "toggle", nodeId: item.nodeId }, item, event.currentTarget);
                return;
              }
              if (action === "open") {
                applyPageKeyboardAction({ kind: "open" }, item, event.currentTarget);
                return;
              }
              if (action === "create") {
                event.preventDefault();
                event.stopPropagation();
                dispatchPageCreateWithRuntime(item.nodeId);
                return;
              }
              if (action === "rename") {
                event.preventDefault();
                event.stopPropagation();
                const nextTitle = window.prompt("重命名页面", item.title);
                const title = normalizeText(nextTitle);
                if (!title || title === item.title) return;
                dispatchPageRenameWithRuntime(
                  item.nodeId,
                  title,
                  row.querySelector(".tree-link-title"),
                );
                return;
              }
              if (action === "menu") {
                event.preventDefault();
                event.stopPropagation();
                applyPageKeyboardAction({ kind: "context_menu" }, item, event.currentTarget);
              }
            });
          });
        };
        const renderHydratedPageNode = (item) => {
          if (!item) return null;
          const children = getSiblings(item.nodeId);
          const hasChildren = item.childCount > 0 && children.length > 0;
          const nodeElement = document.createElement("li");
          nodeElement.className = "tree-node";
          nodeElement.dataset.nodeId = item.nodeId;

          const row = document.createElement("div");
          row.className = "tree-row";
          row.setAttribute("role", "treeitem");
          row.setAttribute("aria-level", String(item.depth + 1));
          row.setAttribute("aria-expanded", hasChildren ? String(expanded.has(item.nodeId)) : "false");
          row.setAttribute("data-testid", "tree-node-open");
          row.dataset.rustRenderedRow = "page";
          row.dataset.nodeId = item.nodeId;
          row.dataset.shellMode = "page";
          row.dataset.active = String(item.nodeId === currentActiveDocumentId);
          row.dataset.focused = String(item.nodeId === focusedNodeId);
          row.dataset.draggable = "true";
          row.dataset.dropFeedback = String(Boolean(pageRuntimeState.dropFeedback && pageRuntimeState.dropTargetNodeId === item.nodeId));
          row.draggable = true;

          if (hasChildren) {
            const toggleButton = document.createElement("button");
            toggleButton.type = "button";
            toggleButton.className = "tree-toggle";
            toggleButton.setAttribute("data-testid", "tree-node-toggle");
            toggleButton.dataset.rustAction = "toggle";
            toggleButton.dataset.nodeId = item.nodeId;
            toggleButton.setAttribute("aria-label", \`\${expanded.has(item.nodeId) ? "折叠" : "展开"} \${item.title}\`);
            toggleButton.textContent = expanded.has(item.nodeId) ? "▾" : "▸";
            row.appendChild(toggleButton);
          } else {
            const spacer = document.createElement("span");
            spacer.className = "tree-spacer";
            spacer.setAttribute("aria-hidden", "true");
            row.appendChild(spacer);
          }

          row.appendChild(createKindBadge("page"));

          const linkButton = document.createElement("button");
          linkButton.type = "button";
          linkButton.className = "tree-link";
          linkButton.setAttribute("data-testid", "tree-node-open");
          linkButton.dataset.rustAction = "open";
          linkButton.dataset.nodeId = item.nodeId;
          const titleElement = document.createElement("span");
          titleElement.className = "tree-link-title";
          titleElement.textContent = item.title;
          linkButton.appendChild(titleElement);
          row.appendChild(linkButton);

          const actions = document.createElement("div");
          actions.className = "tree-actions";
          [
            ["create", "tree-action-create", "+", "新建子页面"],
            ["rename", "tree-action-rename", "✎", "重命名"],
            ["menu", "tree-action-menu", "…", "更多操作"],
          ].forEach(([action, testId, label, ariaLabel]) => {
            const button = document.createElement("button");
            button.type = "button";
            button.className = "tree-action";
            button.setAttribute("data-testid", testId);
            button.dataset.rustAction = action;
            button.dataset.nodeId = item.nodeId;
            button.setAttribute("aria-label", ariaLabel);
            button.textContent = label;
            actions.appendChild(button);
          });
          row.appendChild(actions);
          nodeElement.appendChild(row);
          bindPageRowEvents(row, item);

          if (hasChildren && expanded.has(item.nodeId)) {
            const childrenList = document.createElement("ul");
            childrenList.className = "tree-children";
            children.forEach((child) => {
              const childNode = renderHydratedPageNode(child);
              if (childNode) childrenList.appendChild(childNode);
            });
            nodeElement.appendChild(childrenList);
          }
          return nodeElement;
        };
        const patchPageTreeExpansionDom = (nodeId) => {
          if (mode !== "page") return false;
          const normalizedNodeId = normalizeText(nodeId);
          if (!normalizedNodeId) return false;
          const item = itemById.get(normalizedNodeId);
          if (!item) return false;
          const nodeElement = appElement.querySelector(
            \`.tree-node[data-node-id="\${CSS.escape(normalizedNodeId)}"]\`,
          );
          if (!(nodeElement instanceof HTMLElement)) return false;
          const row = nodeElement.querySelector(
            \`:scope > .tree-row[data-node-id="\${CSS.escape(normalizedNodeId)}"]\`,
          );
          const children = getSiblings(normalizedNodeId);
          const hasChildren = item.childCount > 0 && children.length > 0;
          const isExpanded = hasChildren && expanded.has(normalizedNodeId);
          if (row instanceof HTMLElement) {
            row.setAttribute("aria-expanded", hasChildren ? String(isExpanded) : "false");
            const toggleButton = row.querySelector('[data-testid="tree-node-toggle"]');
            if (toggleButton instanceof HTMLButtonElement) {
              toggleButton.textContent = isExpanded ? "▾" : "▸";
              toggleButton.setAttribute("aria-label", \`\${isExpanded ? "折叠" : "展开"} \${item.title}\`);
            }
          }
          if (!hasChildren) {
            patchPageTreeActiveDom();
            return true;
          }
          let childrenList = Array.from(nodeElement.children).find(
            (child) => child instanceof HTMLElement && child.classList.contains("tree-children"),
          );
          if (isExpanded) {
            if (!(childrenList instanceof HTMLElement)) {
              childrenList = document.createElement("ul");
              childrenList.className = "tree-children";
              children.forEach((child) => {
                const childNode = renderHydratedPageNode(child);
                if (childNode) childrenList.appendChild(childNode);
              });
              nodeElement.appendChild(childrenList);
            }
            childrenList.hidden = false;
            childrenList.style.display = "";
          } else if (childrenList instanceof HTMLElement) {
            childrenList.hidden = true;
            childrenList.style.display = "none";
          }
          patchPageTreeActiveDom();
          return true;
        };
        const patchPageTreeActiveDom = () => {
          if (mode !== "page") return;
          appElement.querySelectorAll('[data-rust-rendered-row="page"]').forEach((row) => {
            if (!(row instanceof HTMLElement)) return;
            const nodeId = normalizeText(row.dataset.nodeId);
            const isFocused = nodeId === focusedNodeId;
            row.dataset.active = String(nodeId === currentActiveDocumentId);
            row.dataset.focused = String(isFocused);
            row.tabIndex = isFocused ? 0 : -1;
          });
        };
        const hydrateInitialPageTree = () => {
          if (mode !== "page") return false;
          const root = appElement.querySelector('[data-rust-page-renderer="initial_v1"]');
          if (!(root instanceof HTMLElement)) return false;
          root.querySelectorAll('[data-rust-rendered-row="page"]').forEach((row) => {
            if (!(row instanceof HTMLElement)) return;
            const nodeId = normalizeText(row.dataset.nodeId);
            const item = itemById.get(nodeId);
            if (!item) return;
            bindPageRowEvents(row, item);
          });
          syncPageDropFeedbackDom();
          if (focusedNodeId) focusRowElement(focusedNodeId);
          return true;
        };
        const hydrateInitialRenderer = () => {
          if (mode === "page") return hydrateInitialPageTree();
          if (mode === "filetree") return hydrateInitialFileTree();
          if (mode === "picker") return hydrateInitialPickerTree();
          return false;
        };

        const renderFileTree = () => {
          appElement.innerHTML = "";
          visibleFileTreeRowIds = [];
          const fileRoot = document.createElement("div");
          fileRoot.className = "tree-root";
          fileRoot.setAttribute("role", "tree");
          const appendFileTreeRow = (container, item) => {
            const children = getSiblings(item.nodeId);
            const hasBranches = item.childCount > 0 && children.length > 0;
            const documentId = getFileTreeRowDocumentId(item) || null;
            const assetId = getFileTreeRowAssetId(item) || null;
            const row = document.createElement("div");
            row.className = "tree-row";
            row.style.marginLeft = \`\${item.depth * 22}px\`;
            row.dataset.active = String(item.rowKind === "document" && documentId === currentActiveDocumentId);
            row.dataset.nodeId = item.nodeId;
            row.dataset.rowId = item.rowId;
            row.dataset.rowKind = item.rowKind;
            row.dataset.documentId = documentId || "";
            row.dataset.assetId = assetId || "";
            row.dataset.shellMode = "filetree";
            row.dataset.selected = String(selectedFileTreeRowIds.has(item.rowId));
            row.dataset.dropTarget = String(item.rowId === fileTreeRuntimeState.dropTargetRowId);
            row.setAttribute("data-testid", item.rowKind === "document" ? "filetree-doc-row" : item.rowKind === "index" ? "filetree-index-row" : "filetree-asset-row");
            row.tabIndex = 0;
            row.setAttribute("role", "treeitem");
            row.setAttribute("aria-level", String(item.depth + 1));
            row.setAttribute("aria-expanded", hasBranches ? String(expanded.has(item.nodeId)) : "false");
            visibleFileTreeRowIds.push(item.rowId);
            row.addEventListener("click", (event) => {
              selectFileTreeRow(item.rowId, event);
              renderTree();
            });
            row.addEventListener("dblclick", () => openFileTreeItem(item));
            row.addEventListener("contextmenu", (event) => {
              event.preventDefault();
              selectFileTreeContextRow(item.rowId);
              renderTree();
              openFileTreeContextMenu({
                documentId,
                assetId,
                rowId: item.rowId,
                rowKind: item.rowKind,
                clientX: event.clientX,
                clientY: event.clientY,
              });
            });
            row.draggable = true;
            row.addEventListener("dragstart", (event) => {
              const rowIds =
                applyFileTreeSelectionAction({
                  kind: "resolve_drag_rows",
                  rowId: item.rowId,
                  altKey: event.altKey === true,
                }).dragRowIds || [item.rowId];
              if (event.dataTransfer) {
                const payload = JSON.stringify({ type: "mnote-file-tree-dnd", version: 1, rowIds });
                event.dataTransfer.effectAllowed = "copyMove";
                event.dataTransfer.setData("application/x-mnote-filetree-row-ids", payload);
                event.dataTransfer.setData("application/x-mnote-file-tree", payload);
                event.dataTransfer.setData("text/plain", payload);
              }
            });
            row.addEventListener("dragend", () => {
              clearFileTreeDropTarget();
            });
            if (hasBranches) {
              const toggleButton = document.createElement("button");
              toggleButton.type = "button";
              toggleButton.className = "tree-toggle";
              toggleButton.textContent = expanded.has(item.nodeId) ? "▾" : "▸";
              toggleButton.addEventListener("click", (event) => {
                event.stopPropagation();
                toggleExpand(item.nodeId);
              });
              row.appendChild(toggleButton);
            } else {
              const spacer = document.createElement("div");
              spacer.className = "tree-spacer";
              row.appendChild(spacer);
            }
            row.appendChild(createKindBadge(getFileTreeRowIconKind(item)));
            const linkButton = document.createElement("button");
            linkButton.type = "button";
            linkButton.className = "tree-link";
            if (item.rowKind === "document") linkButton.setAttribute("data-testid", "filetree-doc-open");
            if (item.rowKind === "asset" || item.rowKind === "asset_folder") linkButton.setAttribute("data-testid", "filetree-asset-open");
            linkButton.addEventListener("click", () => openFileTreeItem(item));
            const title = document.createElement("span");
            title.className = "tree-link-title";
            title.textContent = item.title;
            linkButton.appendChild(title);
            row.appendChild(linkButton);
            const actions = document.createElement("div");
            actions.className = "tree-actions";
            const more = document.createElement("button");
            more.type = "button";
            more.className = "tree-action";
            more.setAttribute("data-testid", "filetree-action-menu");
            more.textContent = "⋯";
            more.addEventListener("click", () => openFileTreeContextMenu({
              documentId,
              assetId,
              rowId: item.rowId,
              rowKind: item.rowKind,
              clientX: 0,
              clientY: 0,
            }));
            actions.appendChild(more);
            row.appendChild(actions);
            container.appendChild(row);
            if (hasBranches && expanded.has(item.nodeId)) children.forEach((child) => appendFileTreeRow(container, child));
          };
          fileRoot.addEventListener("dragover", (event) => {
            updateFileTreeDropTargetFromEvent(event);
          });
          fileRoot.addEventListener("dragleave", (event) => {
            if (event.currentTarget instanceof Node && event.relatedTarget instanceof Node) {
              if (event.currentTarget.contains(event.relatedTarget)) return;
            }
            clearFileTreeDropTarget();
          });
          fileRoot.addEventListener("drop", (event) => {
            const dropPayload = readFileTreeDropPayloadFromEvent(event);
            const { hasPayload } = dropPayload;
            if (!hasPayload) return;
            event.preventDefault();
            const target = getFileTreeDropTargetFromElement(event.target);
            dispatchFileTreeDropWithRuntime(target, dropPayload, event);
          });
          if (roots.length === 0) {
            const empty = document.createElement("div");
            empty.className = "tree-empty";
            empty.textContent = "当前 file tree 没有可渲染的页面。";
            fileRoot.appendChild(empty);
          } else {
            roots.forEach((item) => appendFileTreeRow(fileRoot, item));
          }
          normalizeFileTreeSelectionForVisibleRows();
          appElement.appendChild(fileRoot);
          syncFileTreeDropTargetDom();
        };

        const renderNode = (item, container) => {
          const hasChildren = item.childCount > 0;
          const row = document.createElement("div");
          row.className = "tree-row";
          row.dataset.active = String(item.nodeId === currentActiveDocumentId);
          row.dataset.focused = String(item.nodeId === focusedNodeId);
          row.dataset.nodeId = item.nodeId;
          row.dataset.shellMode = mode;
          row.dataset.draggable = String(mode === "page");
          row.dataset.dropFeedback = String(Boolean(mode === "page" && pageRuntimeState.dropFeedback && pageRuntimeState.dropTargetNodeId === item.nodeId));
          row.draggable = mode === "page";
          row.tabIndex = item.nodeId === focusedNodeId ? 0 : -1;
          row.setAttribute("role", "treeitem");
          row.setAttribute("aria-level", String(item.depth + 1));
          row.setAttribute("aria-expanded", hasChildren ? String(expanded.has(item.nodeId)) : "false");
          row.addEventListener("focus", () => {
            if (focusedNodeId !== item.nodeId) {
              applyPageKeyboardAction({ kind: "focus", nodeId: item.nodeId }, item, row);
            }
          });
          row.addEventListener("keydown", (event) => {
            if (event.key === "Enter") {
              event.preventDefault();
              applyPageKeyboardAction({ kind: "open" }, item, event.currentTarget);
            }
            if (event.key === "ArrowDown") {
              event.preventDefault();
              applyPageKeyboardAction({ kind: "move_next" }, item, event.currentTarget);
            }
            if (event.key === "ArrowUp") {
              event.preventDefault();
              applyPageKeyboardAction({ kind: "move_previous" }, item, event.currentTarget);
            }
            if (event.key === "Home") {
              event.preventDefault();
              applyPageKeyboardAction({ kind: "move_home" }, item, event.currentTarget);
            }
            if (event.key === "End") {
              event.preventDefault();
              applyPageKeyboardAction({ kind: "move_end" }, item, event.currentTarget);
            }
          });
          row.addEventListener("contextmenu", (event) => {
            if (mode !== "page") return;
            event.preventDefault();
            applyPageKeyboardAction({ kind: "context_menu" }, item, event.currentTarget);
          });
          row.addEventListener("dragstart", (event) => {
            if (mode !== "page") return;
            draggingPageNodeId = item.nodeId;
            if (event.dataTransfer) {
              event.dataTransfer.effectAllowed = "move";
              event.dataTransfer.setData(PAGE_DRAG_MIME, item.nodeId);
              event.dataTransfer.setData("text/plain", item.nodeId);
            }
            setLastAction(\`开始拖拽页面 \${item.title}\`);
          });
          row.addEventListener("dragover", (event) => {
            if (mode !== "page") return;
            const sourceNodeId = readPageDragNodeId(event);
            const targetNodeId = resolvePageDropTargetNodeId(event.target);
            if (!canSubmitPageDropCandidate(sourceNodeId, targetNodeId)) {
              clearPageDropFeedback();
              return;
            }
            event.preventDefault();
            if (event.dataTransfer) {
              event.dataTransfer.dropEffect = "move";
            }
            setPageDropFeedback(sourceNodeId, targetNodeId);
          });
          row.addEventListener("dragleave", (event) => {
            if (mode !== "page") return;
            const relatedTarget = event.relatedTarget instanceof Node ? event.relatedTarget : null;
            if (relatedTarget && row.contains(relatedTarget)) {
              return;
            }
            if (pageRuntimeState.dropTargetNodeId === item.nodeId) {
              clearPageDropFeedback();
            }
          });
          row.addEventListener("drop", (event) => {
            if (mode !== "page") return;
            const sourceNodeId = readPageDragNodeId(event);
            const targetNodeId = resolvePageDropTargetNodeId(event.target);
            clearPageDropFeedback();
            draggingPageNodeId = "";
            if (!canSubmitPageDropCandidate(sourceNodeId, targetNodeId)) {
              return;
            }
            event.preventDefault();
            dispatchPageMoveWithRuntime(sourceNodeId, targetNodeId);
          });
          row.addEventListener("dragend", () => {
            if (mode !== "page") return;
            draggingPageNodeId = "";
            clearPageDropFeedback();
          });
          if (hasChildren) {
            const toggleButton = document.createElement("button");
            toggleButton.type = "button";
            toggleButton.className = "tree-toggle";
            toggleButton.setAttribute("data-testid", "tree-node-toggle");
            toggleButton.textContent = expanded.has(item.nodeId) ? "▾" : "▸";
            toggleButton.addEventListener("click", (event) => {
              event.stopPropagation();
              applyPageKeyboardAction({ kind: "toggle", nodeId: item.nodeId }, item, event.currentTarget);
            });
            row.appendChild(toggleButton);
          } else {
            const spacer = document.createElement("div");
            spacer.className = "tree-spacer";
            row.appendChild(spacer);
          }
          row.appendChild(createKindBadge("page"));
          const linkButton = document.createElement("button");
          linkButton.type = "button";
          linkButton.className = "tree-link";
          linkButton.setAttribute("data-testid", mode === "picker" ? "tree-picker-row" : "tree-node-open");
          linkButton.setAttribute("aria-label", \`打开 \${item.title}\`);
          linkButton.addEventListener("click", () => {
            if (mode === "picker") {
              dispatchPickerPickByItemKeyWithRuntime(item.nodeId, { focusDom: true });
              return;
            }
            applyPageKeyboardAction({ kind: "open" }, item, linkButton);
          });
          const titleElement = document.createElement("span");
          titleElement.className = "tree-link-title";
          titleElement.textContent = item.title;
          linkButton.appendChild(titleElement);
          row.appendChild(linkButton);
          const actions = document.createElement("div");
          actions.className = "tree-actions";
          if (mode === "page") {
            const createButton = document.createElement("button");
            createButton.type = "button";
            createButton.className = "tree-action";
            createButton.setAttribute("data-testid", "tree-action-create");
            createButton.setAttribute("aria-label", \`在 \${item.title} 下新建页面\`);
            createButton.textContent = "+";
            createButton.addEventListener("click", async (event) => {
              event.preventDefault();
              event.stopPropagation();
              dispatchPageCreateWithRuntime(item.nodeId);
            });
            actions.appendChild(createButton);

            const renameButton = document.createElement("button");
            renameButton.type = "button";
            renameButton.className = "tree-action";
            renameButton.setAttribute("data-testid", "tree-action-rename");
            renameButton.setAttribute("aria-label", \`重命名 \${item.title}\`);
            renameButton.textContent = "✎";
            renameButton.addEventListener("click", async (event) => {
              event.preventDefault();
              event.stopPropagation();
              const nextTitle = window.prompt("重命名页面", item.title);
              const title = normalizeText(nextTitle);
              if (!title || title === item.title) return;
              dispatchPageRenameWithRuntime(item.nodeId, title);
            });
            actions.appendChild(renameButton);

            const moreButton = document.createElement("button");
            moreButton.type = "button";
            moreButton.className = "tree-action";
            moreButton.setAttribute("data-testid", "tree-action-menu");
            moreButton.setAttribute("aria-label", \`更多操作 \${item.title}\`);
            moreButton.textContent = "⋯";
            moreButton.addEventListener("click", (event) => {
              event.preventDefault();
              event.stopPropagation();
              applyPageKeyboardAction({ kind: "context_menu" }, item, event.currentTarget);
            });
            actions.appendChild(moreButton);
          }
          row.appendChild(actions);
          container.appendChild(row);
          if (hasChildren && expanded.has(item.nodeId)) {
            const childContainer = document.createElement("div");
            childContainer.className = "tree-children";
            getSiblings(item.nodeId).forEach((child) => renderNode(child, childContainer));
            container.appendChild(childContainer);
          }
        };

        const renderTree = () => {
          if (mode === "filetree") {
            renderFileTree();
            emitFileTreeSelectionChange();
            return;
          }
          appElement.innerHTML = "";
          const root = document.createElement("div");
          root.className = "tree-root";
          root.setAttribute("role", "tree");
          if (mode === "picker" && allowRootPick) {
            const rootButton = document.createElement("button");
            rootButton.type = "button";
            rootButton.className = "tree-row";
            rootButton.setAttribute("data-testid", "tree-picker-root");
            rootButton.dataset.focused = String(currentActivePickerItemKey === "__root__");
            rootButton.tabIndex = currentActivePickerItemKey === "__root__" ? 0 : -1;
            rootButton.textContent = "根目录";
            rootButton.addEventListener("click", () => {
              dispatchPickerPickByItemKeyWithRuntime("__root__", { focusDom: true });
            });
            root.appendChild(rootButton);
          }
          if (roots.length === 0) {
            const empty = document.createElement("div");
            empty.className = "tree-empty";
            empty.textContent = mode === "picker" ? "没有可选择的页面。" : "当前页面树为空。";
            root.appendChild(empty);
          } else {
            roots.forEach((item) => renderNode(item, root));
          }
          appElement.appendChild(root);
        };
        const handlePickerCommand = (command) => {
          if (mode !== "picker") return;
          const normalizedCommand = normalizeText(command);
          if (!pickerStateReducerActions.has(normalizedCommand)) {
            return;
          }
          if (normalizedCommand === "pick") {
            applyPickerStateAction({ kind: "pick" });
            return;
          }
          applyPickerStateAction({ kind: normalizedCommand });
        };
        window.addEventListener("message", (event) => {
          const payload = event.data;
          if (!payload || typeof payload !== "object") return;
          if (normalizeText(payload.channel) !== channel) return;
          const messageType = normalizeText(payload.type);
          if (messageType === "tree.picker.command") {
            handlePickerCommand(payload.command);
            return;
          }
          if (messageType !== "tree.shell.state.patch") return;
          currentActiveDocumentId = normalizeText(payload.activeDocumentId);
          currentFocusedDocumentId = normalizeText(payload.focusedDocumentId);
          currentActivePickerItemKey = normalizeText(payload.activePickerItemKey);
          focusedNodeId = resolveFocusedNodeIdFromHostState();
          if (mode === "page" && usedRustInitialRenderer) {
            patchPageTreeActiveDom();
            syncPageDropFeedbackDom();
            if (focusedNodeId) focusRowElement(focusedNodeId);
            return;
          }
          if (mode === "filetree" && usedRustInitialRenderer) {
            patchFileTreeActiveDom();
            syncFileTreeSelectionDom();
            syncFileTreeDropTargetDom();
            return;
          }
          if (mode === "picker" && usedRustInitialRenderer) {
            patchPickerActiveDom();
            return;
          }
          renderTree();
          if (mode === "page" && focusedNodeId) focusRowElement(focusedNodeId);
        });
        createRootButton.addEventListener("click", () => {
          if (mode === "picker") return;
          setLastAction("请使用页面壳新建入口创建页面。");
        });

        const titleElement = document.getElementById("tree-shell-title");
        if (titleElement) {
          titleElement.textContent = mode === "picker" ? "页面选择" : mode === "filetree" ? "资源管理器" : "页面树";
        }
        if (mode === "picker") createRootButton.style.display = "none";
        const usedRustInitialRenderer = hydrateInitialRenderer();
        if (!usedRustInitialRenderer) renderTree();
        if (mode === "page" && focusedNodeId) postPageFocusChange(focusedNodeId);
        if (mode === "filetree") emitFileTreeSelectionChange();
        setStatus(mode === "filetree" ? "File tree shell 已就绪。" : "Tree shell 已就绪。");
        postToHost("tree.ready", { workspaceId, payload: { workspaceId } });
      })();
    </script>
  </body>
</html>`;

function compactTreeShellSrcDoc(html: string) {
  return html
    .split(/\r?\n/)
    .map((line) => line.trim())
    .join("");
}

const normalizeString = (value: unknown, fallback = "") => {
  if (typeof value !== "string") return fallback;
  const normalized = value.trim();
  return normalized || fallback;
};

const readNullableString = (value: unknown) => {
  const normalized = normalizeString(value);
  return normalized || null;
};

const readNumber = (value: unknown) => {
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
};

const readBoolean = (value: unknown) => value === true;

const readStringArray = (value: unknown) => {
  if (!Array.isArray(value)) {
    return [];
  }
  return value
    .map((item) => normalizeString(item))
    .filter(Boolean);
};

const readFiles = (value: unknown) => {
  if (typeof FileList !== "undefined" && value instanceof FileList) {
    return Array.from(value);
  }
  if (!Array.isArray(value)) {
    return [];
  }
  return value.filter((item): item is File => typeof File !== "undefined" && item instanceof File);
};

function parseTreeShellBridgeMessage(
  value: unknown,
  expectedChannel: string,
): TreeShellBridgeMessage | null {
  if (!value || typeof value !== "object") {
    return null;
  }
  const payload = value as Record<string, unknown>;
  const channel = normalizeString(payload.channel);
  const type = normalizeString(payload.type);
  if (!channel || !type || channel !== expectedChannel) {
    return null;
  }
  return {
    channel,
    type,
    documentId: readNullableString(payload.documentId ?? payload.docId),
    assetId: readNullableString(payload.assetId),
    rowId: readNullableString(payload.rowId ?? payload.targetRowId),
    rowKind: readNullableString(payload.rowKind ?? payload.targetRowKind),
    x: readNumber(payload.x),
    y: readNumber(payload.y),
    expanded: readBoolean(payload.expanded),
    selectedRowIds: readStringArray(payload.selectedRowIds),
    anchorRowId: readNullableString(payload.anchorRowId),
    focusedRowId: readNullableString(payload.focusedRowId),
    rowIds: readStringArray(payload.rowIds),
    copy: readBoolean(payload.copy),
    files: readFiles(payload.files),
    itemKey: readNullableString(payload.itemKey ?? payload.pickerItemKey),
  };
}

const escapeInlineScriptJson = (input: string) => {
  return input
    .replace(/&/g, "\\u0026")
    .replace(/</g, "\\u003c")
    .replace(/>/g, "\\u003e");
};

const escapeInlineHtml = (input: string) => {
  return input
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
};

export function buildTreeShellInlinePickerItems(
  items: TreeShellPickerItem[],
): TreeShellInlineProjectionItem[] {
  return buildTreeShellDomPickerItems(items);
}

export function buildTreeShellInlinePageItems(
  items: PageTreeProjectionItem[],
  expanded: ReadonlySet<string> = new Set(),
): TreeShellInlineProjectionItem[] {
  return buildTreeShellDomPageItems(items, expanded);
}

export function buildTreeShellInlineKernelFileTreeItems(
  items: KernelFileTreeProjectionItem[],
): TreeShellInlineProjectionItem[] {
  return buildTreeShellDomKernelFileTreeItems(items);
}

export function injectTreeShellInlineOverrides(
  html: string,
  overrides: { items?: TreeShellInlineProjectionItem[] },
) {
  const payloadJson = escapeInlineScriptJson(JSON.stringify(overrides));
  const scriptTag = `<script>window.__MNOTE_TREE_SHELL_OVERRIDE__ = ${payloadJson};</script>`;
  const anchor = '<script id="tree-shell-state"';
  if (html.includes(anchor)) {
    return html.replace(anchor, `${scriptTag}${anchor}`);
  }
  return `${scriptTag}${html}`;
}

function buildInlineChildrenByParent(items: TreeShellInlineProjectionItem[]) {
  return buildTreeShellDomChildrenByParent(items);
}

function buildInlineFiletreeSelection(activeDocumentId?: string | null) {
  return buildTreeShellDomFiletreeSelection(activeDocumentId);
}

function buildInlineRendererInput(input: {
  mode: TreeShellHostMode;
  items: TreeShellInlineProjectionItem[];
  activeDocumentId?: string | null;
  focusedDocumentId?: string | null;
  activePickerItemKey?: string | null;
  excludeIds?: string[];
  channel: string;
}): TreeShellInlineRendererInput {
  const projectionItemIds = input.items
    .map((item) => normalizeString(item.rowId ?? item.nodeId))
    .filter(Boolean);
  const expandedIds = input.items
    .filter((item) => item.childCount > 0 && item.expandedByDefault)
    .map((item) => normalizeString(item.nodeId))
    .filter(Boolean);
  const excludedPickerIds = (input.excludeIds ?? [])
    .map((item) => normalizeString(item))
    .filter(Boolean);
  const commandDispatcher = {
    channel: normalizeString(input.channel),
    commandNames: [...TREE_SHELL_COMMAND_NAMES],
  };
  const runtimeArtifact = {
    contractName: TREE_SHELL_RUNTIME_ARTIFACT.contractName,
    family: TREE_SHELL_RUNTIME_ARTIFACT.family,
    version: TREE_SHELL_RUNTIME_ARTIFACT.version,
    executionStrategy: TREE_SHELL_RUNTIME_ARTIFACT.executionStrategy,
    browserBridge: TREE_SHELL_RUNTIME_ARTIFACT.browserBridge,
    wasmModuleUrl: TREE_SHELL_RUNTIME_ARTIFACT.wasmModuleUrl,
    jsGlueUrl: TREE_SHELL_RUNTIME_ARTIFACT.jsGlueUrl,
    inputFields: [...TREE_SHELL_RUNTIME_ARTIFACT.inputFields],
    outputChannels: [...TREE_SHELL_RUNTIME_ARTIFACT.outputChannels],
    eventKinds: [...TREE_SHELL_RUNTIME_ARTIFACT.eventKinds],
    runtimeApi: {
      requestContract: TREE_SHELL_RUNTIME_ARTIFACT.runtimeApi.requestContract,
      resultContract: TREE_SHELL_RUNTIME_ARTIFACT.runtimeApi.resultContract,
      reduceEndpoint: TREE_SHELL_RUNTIME_ARTIFACT.runtimeApi.reduceEndpoint,
      stateSnapshots: [...TREE_SHELL_RUNTIME_ARTIFACT.runtimeApi.stateSnapshots],
      domPatchKinds: [...TREE_SHELL_RUNTIME_ARTIFACT.runtimeApi.domPatchKinds],
      hostEventKinds: [...TREE_SHELL_RUNTIME_ARTIFACT.runtimeApi.hostEventKinds],
      commandEventKinds: [...TREE_SHELL_RUNTIME_ARTIFACT.runtimeApi.commandEventKinds],
    },
  };
  if (input.mode === "filetree") {
    const filetreeSelection = buildInlineFiletreeSelection(input.activeDocumentId);
    return {
      mode: "fileTree",
      projectionItemIds,
      expandedIds,
      focusedId: filetreeSelection.focusedRowId,
      filetreeSelection,
      filetreeSelectionReducer: {
        contractName: "rust_filetree_selection_reducer_v1",
        actions: [...FILETREE_SELECTION_REDUCER_ACTIONS],
      },
      activePickerItem: null,
      excludedPickerIds: [],
      commandDispatcher,
      runtimeArtifact,
    };
  }
  if (input.mode === "picker") {
    const activePickerItem = readNullableString(input.activePickerItemKey);
    return {
      mode: "picker",
      projectionItemIds,
      expandedIds,
      focusedId: activePickerItem,
      filetreeSelection: buildInlineFiletreeSelection(null),
      activePickerItem,
      excludedPickerIds,
      pickerStateReducer: {
        contractName: "rust_picker_state_reducer_v1",
        actions: [...PICKER_STATE_REDUCER_ACTIONS],
      },
      commandDispatcher,
      runtimeArtifact,
    };
  }
  return {
    mode: "page",
    projectionItemIds,
    expandedIds,
    focusedId: readNullableString(input.focusedDocumentId ?? input.activeDocumentId),
    pageFocusKeyboardReducer: {
      contractName: "rust_page_focus_keyboard_reducer_v1",
      actions: [...PAGE_FOCUS_KEYBOARD_REDUCER_ACTIONS],
    },
    filetreeSelection: buildInlineFiletreeSelection(null),
    activePickerItem: null,
    excludedPickerIds: [],
    commandDispatcher,
    runtimeArtifact,
  };
}

function buildInlinePageTreeHtml(input: {
  items: TreeShellInlineProjectionItem[];
  activeDocumentId?: string | null;
  focusedDocumentId?: string | null;
}) {
  const childrenByParent = buildInlineChildrenByParent(input.items);
  const activeDocumentId = normalizeString(input.activeDocumentId);
  const focusedDocumentId = normalizeString(input.focusedDocumentId, activeDocumentId);
  const renderRow = (item: TreeShellInlineProjectionItem): string => {
    const children = childrenByParent.get(item.nodeId) ?? [];
    const expandable = item.childCount > 0 && children.length > 0;
    const expanded = expandable && item.expandedByDefault;
    const toggleHtml = expandable
      ? `<button type="button" class="tree-toggle" data-testid="tree-node-toggle" data-rust-action="toggle" data-node-id="${escapeInlineHtml(item.nodeId)}" aria-label="${expanded ? "折叠" : "展开"} ${escapeInlineHtml(item.title)}">${expanded ? "▾" : "▸"}</button>`
      : '<span class="tree-spacer" aria-hidden="true"></span>';
    const childHtml =
      expanded && children.length > 0
        ? `<ul class="tree-children">${children.map(renderRow).join("")}</ul>`
        : "";
    return `<li class="tree-node" data-node-id="${escapeInlineHtml(item.nodeId)}"><div class="tree-row" role="treeitem" aria-level="${item.depth + 1}" aria-expanded="${expanded}" data-rust-rendered-row="page" data-testid="tree-node-open" data-node-id="${escapeInlineHtml(item.nodeId)}" data-shell-mode="page" data-active="${item.nodeId === activeDocumentId}" data-focused="${item.nodeId === focusedDocumentId}" data-draggable="true" data-drop-feedback="false" draggable="true" tabindex="${item.nodeId === focusedDocumentId ? "0" : "-1"}">${toggleHtml}<span class="tree-kind-badge" data-kind="page" aria-hidden="true"></span><button type="button" class="tree-link" data-testid="tree-node-open" data-rust-action="open" data-node-id="${escapeInlineHtml(item.nodeId)}"><span class="tree-link-title">${escapeInlineHtml(item.title)}</span></button><div class="tree-actions"><button type="button" class="tree-action" data-testid="tree-action-create" data-rust-action="create" data-node-id="${escapeInlineHtml(item.nodeId)}" aria-label="新建子页面">+</button><button type="button" class="tree-action" data-testid="tree-action-rename" data-rust-action="rename" data-node-id="${escapeInlineHtml(item.nodeId)}" aria-label="重命名">✎</button><button type="button" class="tree-action" data-testid="tree-action-menu" data-rust-action="menu" data-node-id="${escapeInlineHtml(item.nodeId)}" aria-label="更多操作">…</button></div></div>${childHtml}</li>`;
  };
  const roots = childrenByParent.get("") ?? [];
  if (roots.length === 0) {
    return '<ul class="tree-root" role="tree" data-rust-page-renderer="initial_v1"><li class="tree-empty" data-rust-rendered-row="page-empty">当前 projection 没有可渲染的页面。</li></ul>';
  }
  return `<ul class="tree-root" role="tree" data-rust-page-renderer="initial_v1">${roots.map(renderRow).join("")}</ul>`;
}

function buildInlineFileTreeHtml(input: {
  items: TreeShellInlineProjectionItem[];
  activeDocumentId?: string | null;
}) {
  const childrenByParent = buildInlineChildrenByParent(input.items);
  const selectedRows = new Set(buildInlineFiletreeSelection(input.activeDocumentId).selectedRowIds);
  const renderRow = (item: TreeShellInlineProjectionItem): string => {
    const children = childrenByParent.get(item.nodeId) ?? [];
    const expandable = item.childCount > 0 && children.length > 0;
    const expanded = expandable && item.expandedByDefault;
    const rowKind = normalizeString(item.rowKind, "document");
    const documentId = normalizeString(item.resourceMeta?.documentId);
    const assetId = normalizeString(item.resourceMeta?.assetId);
    const testId =
      rowKind === "document"
        ? "filetree-doc-row"
        : rowKind === "index"
          ? "filetree-index-row"
          : "filetree-asset-row";
    const childHtml =
      expanded && children.length > 0
        ? `<ul class="tree-children">${children.map(renderRow).join("")}</ul>`
        : "";
    return `<li class="tree-node" data-node-id="${escapeInlineHtml(item.nodeId)}"><div class="tree-row" role="treeitem" aria-level="${item.depth + 1}" aria-expanded="${expanded}" data-rust-rendered-row="filetree" data-testid="${testId}" data-row-id="${escapeInlineHtml(item.rowId ?? item.nodeId)}" data-row-kind="${escapeInlineHtml(rowKind)}" data-node-id="${escapeInlineHtml(item.nodeId)}" data-document-id="${escapeInlineHtml(documentId)}" data-asset-id="${escapeInlineHtml(assetId)}" data-shell-mode="filetree" data-selected="${selectedRows.has(item.rowId ?? item.nodeId)}" data-active="false"><span class="tree-spacer" aria-hidden="true"></span><span class="tree-kind-badge" data-kind="${escapeInlineHtml(normalizeString(item.iconHint, "page"))}" aria-hidden="true"></span><button type="button" class="tree-link" data-rust-action="open" data-row-id="${escapeInlineHtml(item.rowId ?? item.nodeId)}"><span class="tree-link-title">${escapeInlineHtml(item.title)}</span></button><div class="tree-actions"><button type="button" class="tree-action" data-testid="filetree-action-menu" data-rust-action="menu" data-row-id="${escapeInlineHtml(item.rowId ?? item.nodeId)}" aria-label="更多操作">…</button></div></div>${childHtml}</li>`;
  };
  const roots = childrenByParent.get("") ?? [];
  if (roots.length === 0) {
    return '<ul class="tree-root" role="tree" data-rust-filetree-renderer="initial_v1"><li class="tree-empty" data-rust-rendered-row="filetree-empty">当前 file tree 没有可渲染的页面。</li></ul>';
  }
  return `<ul class="tree-root" role="tree" data-rust-filetree-renderer="initial_v1">${roots.map(renderRow).join("")}</ul>`;
}

function buildInlinePickerHtml(input: {
  items: TreeShellInlineProjectionItem[];
  allowRootPick?: boolean;
  activeDocumentId?: string | null;
  activePickerItemKey?: string | null;
}) {
  const childrenByParent = buildInlineChildrenByParent(input.items);
  const activeDocumentId = normalizeString(input.activeDocumentId);
  const activePickerItemKey = normalizeString(input.activePickerItemKey);
  const rootActive = activePickerItemKey === "__root__";
  const rootHtml = input.allowRootPick
    ? `<li class="tree-node" data-node-id="__root__"><button type="button" class="tree-row" data-testid="tree-picker-root" data-rust-rendered-row="picker-root" data-rust-action="pick-root" data-focused="${rootActive}" tabindex="${rootActive ? "0" : "-1"}"><span class="tree-spacer" aria-hidden="true"></span><span class="tree-link"><span class="tree-link-title">根目录</span></span></button></li>`
    : "";
  const renderRow = (item: TreeShellInlineProjectionItem): string => {
    const children = childrenByParent.get(item.nodeId) ?? [];
    const expandable = item.childCount > 0 && children.length > 0;
    const expanded = expandable && item.expandedByDefault;
    const active = activePickerItemKey
      ? activePickerItemKey === item.nodeId
      : activeDocumentId === item.nodeId;
    const childHtml =
      expanded && children.length > 0
        ? `<ul class="tree-children">${children.map(renderRow).join("")}</ul>`
        : "";
    return `<li class="tree-node" data-node-id="${escapeInlineHtml(item.nodeId)}"><button type="button" class="tree-row" role="treeitem" aria-level="${item.depth + 1}" aria-expanded="${expanded}" data-rust-rendered-row="picker" data-testid="tree-picker-row" data-node-id="${escapeInlineHtml(item.nodeId)}" data-shell-mode="picker" data-focused="${active}" data-rust-action="pick" tabindex="${active ? "0" : "-1"}"><span class="tree-spacer" aria-hidden="true"></span><span class="tree-kind-badge" data-kind="page" aria-hidden="true"></span><span class="tree-link"><span class="tree-link-title">${escapeInlineHtml(item.title)}</span></span></button>${childHtml}</li>`;
  };
  const roots = childrenByParent.get("") ?? [];
  return `<ul class="tree-root" role="tree" data-rust-picker-renderer="initial_v1">${rootHtml}${roots.map(renderRow).join("")}</ul>`;
}

function buildInlineInitialTreeHtml(input: {
  mode: TreeShellHostMode;
  items: TreeShellInlineProjectionItem[];
  activeDocumentId?: string | null;
  focusedDocumentId?: string | null;
  activePickerItemKey?: string | null;
  allowRootPick?: boolean;
}) {
  if (input.mode === "filetree") {
    return buildInlineFileTreeHtml({
      items: input.items,
      activeDocumentId: input.activeDocumentId,
    });
  }
  if (input.mode === "picker") {
    return buildInlinePickerHtml({
      items: input.items,
      allowRootPick: input.allowRootPick,
      activeDocumentId: input.activeDocumentId,
      activePickerItemKey: input.activePickerItemKey,
    });
  }
  return buildInlinePageTreeHtml({
    items: input.items,
    activeDocumentId: input.activeDocumentId,
    focusedDocumentId: input.focusedDocumentId,
  });
}

function buildTreeShellBootstrapHtml(input: {
  mode: TreeShellHostMode;
  workspaceId: string;
  items: TreeShellInlineProjectionItem[];
  rootNodeId?: string | null;
  activeDocumentId?: string | null;
  focusedDocumentId?: string | null;
  activePickerItemKey?: string | null;
  allowRootPick?: boolean;
  excludeIds?: string[];
  initialTreeHtml?: string;
  channel: string;
  host: string;
}) {
  const rendererInput = buildInlineRendererInput({
    mode: input.mode,
    items: input.items,
    activeDocumentId: input.activeDocumentId,
    focusedDocumentId: input.focusedDocumentId,
    activePickerItemKey: input.activePickerItemKey,
    excludeIds: input.excludeIds,
    channel: input.channel,
  });
  const appState = {
    workspaceId: input.workspaceId,
    rootNodeId: readNullableString(input.rootNodeId),
    activeDocumentId: readNullableString(input.activeDocumentId),
    focusedDocumentId: readNullableString(input.focusedDocumentId),
    activePickerItemKey: readNullableString(input.activePickerItemKey),
    actorId: "",
    channel: input.channel,
    host: input.host,
    mode: input.mode,
    allowRootPick: input.allowRootPick === true,
    excludeIds: (input.excludeIds ?? []).map((item) => normalizeString(item)).filter(Boolean),
    commandPath: "/api/tree/commands",
    items: input.items,
    mediaAssets: [],
    mindmapAssets: [],
    tableAssets: [],
    mindmapAssetChildren: {},
    rendererInput,
  };
  return compactTreeShellSrcDoc(
    LOCAL_TREE_SHELL_TEMPLATE.replace(
      "__INITIAL_TREE_HTML__",
      input.initialTreeHtml ?? "",
    ).replace(
      "__APP_STATE__",
      escapeInlineScriptJson(JSON.stringify(appState)),
    ),
  );
}

export function buildTreeShellIframeSrc(input: {
  mode: TreeShellHostMode;
  workspaceId: string;
  rootNodeId?: string | null;
  activeDocumentId?: string | null;
  focusedDocumentId?: string | null;
  activePickerItemKey?: string | null;
  allowRootPick?: boolean;
  excludeIds?: string[];
  channel: string;
  host: string;
}) {
  const params = new URLSearchParams();
  params.set("workspaceId", input.workspaceId);
  params.set("mode", input.mode);
  params.set("channel", input.channel);
  params.set("host", input.host);

  const rootNodeId = normalizeString(input.rootNodeId);
  if (rootNodeId) {
    params.set("rootNodeId", rootNodeId);
  }

  const activeDocumentId = normalizeString(input.activeDocumentId);
  if (activeDocumentId) {
    params.set("activeDocumentId", activeDocumentId);
  }

  const focusedDocumentId = normalizeString(input.focusedDocumentId);
  if (focusedDocumentId) {
    params.set("focusedDocumentId", focusedDocumentId);
  }

  const activePickerItemKey = normalizeString(input.activePickerItemKey);
  if (activePickerItemKey) {
    params.set("activePickerItemKey", activePickerItemKey);
  }

  if (input.allowRootPick) {
    params.set("allowRootPick", "1");
  }

  const excludeIds = (input.excludeIds ?? [])
    .map((item) => normalizeString(item))
    .filter(Boolean);
  if (excludeIds.length > 0) {
    params.set("excludeIds", excludeIds.join(","));
  }

  return `/api/tree/shell?${params.toString()}`;
}

export function TreeShellIframeHost({
  mode,
  surfaceTestId,
  workspaceId,
  rootNodeId = null,
  activeDocumentId = null,
  focusedDocumentId = null,
  activePickerItemKey = null,
  allowRootPick = false,
  excludeIds = [],
  pickerCommand = null,
  pickerItems,
  pageTreeItems,
  inlineFileTreeItems,
  channel,
  host,
  onNavigate,
  onPick,
  onPickerFocusChange,
  onPageContextMenu,
  onPageExpandChange,
  onPageFocusChange,
  onFileTreeContextMenu,
  onFileTreeSelectionChange,
  onInternalDrop,
  onDropFiles,
  onAssetOpen,
  onTreeMutation,
}: TreeShellIframeHostProps) {
  const iframeRef = useRef<HTMLIFrameElement | null>(null);
  const mounted = useSyncExternalStore(
    subscribeClientSnapshot,
    getClientSnapshot,
    getServerSnapshot,
  );
  const [iframeLoadVersion, setIframeLoadVersion] = useState(0);
  const resolvedChannel = channel?.trim() || surfaceTestId;
  const resolvedHost = host?.trim() || surfaceTestId;
  const hasInlinePageOverride = mode === "page" && Array.isArray(pageTreeItems);
  const expandedPageNodeIds = useMemo(
    () =>
      new Set(
        (pageTreeItems ?? [])
          .filter((item) => item.childCount > 0 && item.expandedByDefault)
          .map((item) => item.nodeId),
      ),
    [pageTreeItems],
  );
  const hasInlinePickerOverride = mode === "picker" && Array.isArray(pickerItems);
  const hasInlineFileTreeItemOverride = mode === "filetree" && Array.isArray(inlineFileTreeItems);
  const inlinePickerItems = useMemo(
    () => (hasInlinePickerOverride ? buildTreeShellInlinePickerItems(pickerItems ?? []) : []),
    [hasInlinePickerOverride, pickerItems],
  );
  const inlinePageItems = useMemo(
    () => (hasInlinePageOverride ? buildTreeShellInlinePageItems(pageTreeItems ?? [], expandedPageNodeIds) : []),
    [expandedPageNodeIds, hasInlinePageOverride, pageTreeItems],
  );
  const inlineFileTreeProjectionItems = useMemo(
    () =>
      hasInlineFileTreeItemOverride
        ? buildTreeShellInlineKernelFileTreeItems(inlineFileTreeItems ?? [])
        : [],
    [hasInlineFileTreeItemOverride, inlineFileTreeItems],
  );
  const inlineProjectionItems =
    mode === "picker"
      ? inlinePickerItems
      : mode === "page"
        ? inlinePageItems
        : inlineFileTreeProjectionItems;
  // 默认浏览器入口必须停留在 3000 本地 inline shell；/api/tree/shell -> 3104 仅保留显式 debug/internal 用途。
  const useInlineProjectionOverride = true;
  const inlineBootstrapActiveDocumentId = activeDocumentId;
  const inlineInitialTreeHtml = useMemo(
    () =>
      useInlineProjectionOverride
        ? buildInlineInitialTreeHtml({
            mode,
            items: inlineProjectionItems,
            activeDocumentId: inlineBootstrapActiveDocumentId,
            focusedDocumentId,
            activePickerItemKey,
            allowRootPick,
          })
        : "",
    [
      activePickerItemKey,
      allowRootPick,
      focusedDocumentId,
      inlineBootstrapActiveDocumentId,
      inlineProjectionItems,
      mode,
      useInlineProjectionOverride,
    ],
  );
  const inlineBootstrapHtml = useMemo(
    () =>
      buildTreeShellBootstrapHtml({
        mode,
        workspaceId,
        items: inlineProjectionItems,
        rootNodeId,
        activeDocumentId: inlineBootstrapActiveDocumentId,
        focusedDocumentId,
        activePickerItemKey,
        allowRootPick,
        excludeIds,
        initialTreeHtml: inlineInitialTreeHtml,
        channel: resolvedChannel,
        host: resolvedHost,
      }),
    [
      mode,
      workspaceId,
      rootNodeId,
      activePickerItemKey,
      focusedDocumentId,
      inlineBootstrapActiveDocumentId,
      inlineInitialTreeHtml,
      inlineProjectionItems,
      allowRootPick,
      excludeIds,
      resolvedChannel,
      resolvedHost,
    ],
  );
  const inlineSrcDoc = useMemo(
    () =>
      useInlineProjectionOverride
        ? inlineBootstrapHtml
        : null,
    [inlineBootstrapHtml, useInlineProjectionOverride],
  );
  const renderedInlineSrcDoc =
    mounted && inlineSrcDoc ? inlineSrcDoc : INLINE_TREE_SHELL_LOADING_HTML;

  useEffect(() => {
    const contentWindow = iframeRef.current?.contentWindow;
    if (!contentWindow) {
      return;
    }

    const message: TreeShellStatePatchMessage = {
      channel: resolvedChannel,
      type: "tree.shell.state.patch",
      activeDocumentId: readNullableString(activeDocumentId),
      focusedDocumentId: readNullableString(focusedDocumentId),
      activePickerItemKey: readNullableString(activePickerItemKey),
    };
    contentWindow.postMessage(message, "*");
  }, [
    activeDocumentId,
    activePickerItemKey,
    focusedDocumentId,
    iframeLoadVersion,
    resolvedChannel,
  ]);

  useEffect(() => {
    if (mode !== "picker" || !pickerCommand) {
      return;
    }

    const contentWindow = iframeRef.current?.contentWindow;
    if (!contentWindow) {
      return;
    }

    const message: TreeShellPickerCommandMessage = {
      channel: resolvedChannel,
      type: "tree.picker.command",
      command: pickerCommand.kind,
    };
    contentWindow.postMessage(message, "*");
  }, [
    iframeLoadVersion,
    mode,
    pickerCommand,
    resolvedChannel,
  ]);

  useEffect(() => {
    const onMessage = (event: MessageEvent) => {
      if (event.source !== iframeRef.current?.contentWindow) {
        return;
      }
      const message = parseTreeShellBridgeMessage(event.data, resolvedChannel);
      if (!message) {
        return;
      }

      switch (message.type) {
        case "tree.navigate":
          if (message.documentId) {
            onNavigate?.(message.documentId);
          }
          return;
        case "tree.pick":
          onPick?.(message.documentId);
          return;
        case "tree.pick.root":
          onPick?.(null);
          return;
        case "tree.picker.focus.changed":
          onPickerFocusChange?.({
            itemKey: message.itemKey,
            documentId: message.documentId,
          });
          return;
        case "tree.page.context-menu":
          if (message.documentId) {
            onPageContextMenu?.({
              documentId: message.documentId,
              x: message.x,
              y: message.y,
            });
          }
          return;
        case "tree.page.expand.changed":
          onPageExpandChange?.({
            documentId: message.documentId,
            expanded: message.expanded,
          });
          return;
        case "tree.page.focus.changed":
          onPageFocusChange?.({
            documentId: message.documentId,
          });
          return;
        case "tree.filetree.context-menu":
          onFileTreeContextMenu?.({
            documentId: message.documentId,
            assetId: message.assetId,
            rowId: message.rowId,
            rowKind: message.rowKind,
            x: message.x,
            y: message.y,
          });
          return;
        case "tree.filetree.selection.changed":
          onFileTreeSelectionChange?.({
            selectedRowIds: message.selectedRowIds,
            anchorRowId: message.anchorRowId,
            focusedRowId: message.focusedRowId,
          });
          return;
        case "tree.filetree.internal-drop":
          if (message.rowIds.length > 0) {
            onInternalDrop?.({
              targetDocumentId: message.documentId,
              targetRowId: message.rowId,
              targetRowKind: message.rowKind,
              targetAssetId: message.assetId,
              rowIds: message.rowIds,
              copy: message.copy,
            });
          }
          return;
        case "tree.filetree.external-drop":
        case "tree.filetree.drop-files":
          if (message.files.length > 0) {
            onDropFiles?.({
              targetDocumentId: message.documentId,
              targetRowId: message.rowId,
              targetRowKind: message.rowKind,
              targetAssetId: message.assetId,
              files: message.files,
            });
          }
          return;
        case "tree.asset.open":
          if (message.assetId) {
            onAssetOpen?.({
              assetId: message.assetId,
              documentId: message.documentId,
            });
          }
          return;
        case "tree.node.created":
        case "tree.node.renamed":
        case "tree.subtree.moved":
          onTreeMutation?.({
            type: message.type,
            documentId: message.documentId,
            ...(message.parentId !== undefined ? { parentId: message.parentId } : {}),
            ...(message.title !== undefined ? { title: message.title } : {}),
            ...(message.sortOrder !== undefined ? { sortOrder: message.sortOrder } : {}),
            ...(message.workspaceId !== undefined ? { workspaceId: message.workspaceId } : {}),
            ...(message.updatedAt !== undefined ? { updatedAt: message.updatedAt } : {}),
            ...(message.execution !== undefined ? { execution: message.execution } : {}),
          });
          return;
      }
    };

    window.addEventListener("message", onMessage);
    return () => {
      window.removeEventListener("message", onMessage);
    };
  }, [
    onAssetOpen,
    onFileTreeContextMenu,
    onFileTreeSelectionChange,
    onInternalDrop,
    onDropFiles,
    onNavigate,
    onPageContextMenu,
    onPageExpandChange,
    onPageFocusChange,
    onPick,
    onPickerFocusChange,
    onTreeMutation,
    resolvedChannel,
  ]);

  return (
    <iframe
      ref={iframeRef}
      src={undefined}
      srcDoc={renderedInlineSrcDoc}
      onLoad={() => setIframeLoadVersion((prev) => prev + 1)}
      title={`tree-shell-${mode}`}
      data-testid={`${surfaceTestId}-rust-iframe`}
      data-tree-shell-mode={mode}
      data-tree-shell-channel={resolvedChannel}
      data-tree-shell-inline="1"
      data-tree-runtime-artifact-host="rust_tree_shell_runtime_artifact_v1"
      data-tree-browser-bridge="iframe_srcdoc"
      className="h-full w-full border-0 bg-white"
    />
  );
}
