import {
  conflictDetectionKeyFromBody,
  editorDocumentFromTiptapDocument,
  flattenText,
  hydrateMindmapAttrsFromDom,
  legacyBlocksFromEditorDocument,
  pageBodyTiptapDocument,
  pageBodyTiptapDocumentSource,
  revisionFromConflictKey,
  textToTiptapDocument,
  toTiptapDocument,
} from './document-tiptap-conversion-runtime.js';

export const createDocumentSessionRuntime = (dependencies = {}) => {
  const {
    bridgeProtocol,
    commandEvent,
    normalizeEnvelopePayload,
    pageAggregateUrl,
    setStatus,
    syncPageAggregateScript,
    syncResourceSessionTabGuards,
  } = dependencies;
  const documentSessionRegistry = new Map();
  const localFolderEventRegistry = new Map();
  const externalConflictMessage = '本地 Markdown 文件已在外部更新，请刷新或保存前先处理冲突';
  const treeExternalConflictMessage = '当前页面已在其它窗口更新，请刷新或保存前先处理冲突';
  const SESSION_RELEASE_DELAY_MS = 1200;
  const LOCAL_FOLDER_SELF_CHANGE_SUPPRESSIONS_KEY = 'mnote.localFolder.selfChangeSuppressions.v1';

  const ensureLocalFolderSelfChangeSuppressions = () => {
    const now = Date.now();
    const map = window.__mnoteLocalFolderSelfChangeSuppressions instanceof Map
      ? window.__mnoteLocalFolderSelfChangeSuppressions
      : new Map();
    window.__mnoteLocalFolderSelfChangeSuppressions = map;
    try {
      const raw = window.sessionStorage ? window.sessionStorage.getItem(LOCAL_FOLDER_SELF_CHANGE_SUPPRESSIONS_KEY) : '';
      const parsed = raw ? JSON.parse(raw) : null;
      if (parsed && typeof parsed === 'object') {
        Object.entries(parsed).forEach(([documentId, expiresAt]) => {
          const doc = String(documentId || '').trim();
          const expiry = Number(expiresAt || 0);
          if (doc && Number.isFinite(expiry) && expiry > now) {
            map.set(doc, expiry);
          } else if (doc) {
            map.delete(doc);
            delete parsed[documentId];
          }
        });
        if (window.sessionStorage) window.sessionStorage.setItem(LOCAL_FOLDER_SELF_CHANGE_SUPPRESSIONS_KEY, JSON.stringify(parsed));
      }
    } catch (_) {}
    return map;
  };

  const parseLocalFolderEventPayload = (event) => {
    try {
      return JSON.parse(String(event?.data || '{}'));
    } catch (_) {
      return null;
    }
  };

  const shouldSuppressLocalFolderSelfChange = (documentId, eventKind) => {
    const doc = String(documentId || '').trim();
    if (!doc) return false;
    const kind = String(eventKind || '');
    const suppressibleSelfWrite = kind.includes('Create')
      || kind.includes('Metadata')
      || kind.includes('Modify(Data')
      || kind.includes('Modify(Any')
      || kind.includes('Modify(Name');
    if (!suppressibleSelfWrite) return false;
    const suppressions = ensureLocalFolderSelfChangeSuppressions();
    if (!suppressions || typeof suppressions.get !== 'function') return false;
    const expiresAt = Number(suppressions.get(doc) || 0);
    if (!Number.isFinite(expiresAt) || expiresAt <= 0) return false;
    if (Date.now() > expiresAt) {
      if (typeof suppressions.delete === 'function') suppressions.delete(doc);
      return false;
    }
    return true;
  };

  const markLocalFolderSelfChangeSuppression = (session, ttlMs = 5000) => {
    if (!session || session.sourceKind !== 'local_folder') return;
    const doc = String(session.documentId || '').trim();
    if (!doc) return;
    const suppressions = ensureLocalFolderSelfChangeSuppressions();
    if (!suppressions || typeof suppressions.set !== 'function') return;
    const expiresAt = Date.now() + Math.max(1000, Number(ttlMs) || 5000);
    suppressions.set(doc, expiresAt);
    try {
      if (!window.sessionStorage) return;
      const raw = window.sessionStorage.getItem(LOCAL_FOLDER_SELF_CHANGE_SUPPRESSIONS_KEY);
      const parsed = raw ? JSON.parse(raw) : {};
      const next = parsed && typeof parsed === 'object' ? parsed : {};
      next[doc] = expiresAt;
      window.sessionStorage.setItem(LOCAL_FOLDER_SELF_CHANGE_SUPPRESSIONS_KEY, JSON.stringify(next));
    } catch (_) {}
  };

  const normalizeSessionSourceKind = (bootstrap) => {
    const value = typeof bootstrap?.sourceKind === 'string' ? bootstrap.sourceKind.trim() : '';
    return value || 'local_folder';
  };

  const buildDocumentSessionKey = (bootstrap) => {
    const sourceKind = normalizeSessionSourceKind(bootstrap);
    const scope = sourceKind === 'local_folder'
      ? String(bootstrap?.rootUri || '').trim()
      : String(bootstrap?.workspaceId || '').trim();
    return `${sourceKind}:${scope}:${String(bootstrap?.documentId || '').trim()}`;
  };

  const sessionViews = (session) => Array.from(session.views.values());

  const resourceWatchPath = (session) => String(session?.resourcePath || session?.relativePath || '').trim();

  const localFolderEventChannelKey = (session) => {
    const scope = session?.sessionKind === 'resource'
      ? `resource:${resourceWatchPath(session)}`
      : String(session?.documentId || '').trim();
    return `${String(session?.rootUri || '').trim()}#${scope}`;
  };

  const localFolderEventBusChannelKey = (session) => `${String(session?.rootUri || '').trim()}#event-bus`;

  const localMarkdownRelativePathFromDocumentId = (documentId) => {
    const value = String(documentId || '').trim();
    if (!value.startsWith('local-md:')) return '';
    return value.slice('local-md:'.length).replace(/~2F/g, '/');
  };

  const sessionRelativePath = (session) => (
    String(session?.relativePath || '').trim()
      || localMarkdownRelativePathFromDocumentId(session?.documentId)
  );

  const localFolderEventItems = (payload) => {
    if (!payload || typeof payload !== 'object') return [];
    const changedPaths = Array.isArray(payload.changedPaths)
      ? payload.changedPaths
      : (Array.isArray(payload.changed_paths) ? payload.changed_paths : []);
    if (changedPaths.length > 0) {
      return changedPaths.map((item) => {
        if (typeof item === 'string') return { relativePath: item };
        if (!item || typeof item !== 'object') return null;
        return {
          relativePath: String(item.relativePath || item.relative_path || item.path || '').trim(),
          documentId: String(item.documentId || item.document_id || '').trim(),
          eventKind: String(item.eventKind || item.event_kind || item.changeType || item.change_type || '').trim(),
        };
      }).filter(Boolean);
    }
    return [payload];
  };

  const sessionBufferStateUrl = (session) => {
    if (!session || session.sourceKind !== 'local_folder' || !session.rootUri || !session.documentId) return null;
    const url = new URL('/api/documents/buffer-state', window.location.origin);
    url.searchParams.set('documentId', session.documentId);
    url.searchParams.set('sourceKind', session.sourceKind);
    url.searchParams.set('rootUri', session.rootUri);
    if (session.workspaceId) url.searchParams.set('workspaceId', session.workspaceId);
    const relativePath = sessionRelativePath(session);
    if (relativePath) url.searchParams.set('relativePath', relativePath);
    return url;
  };

  const applyBufferStateToSession = (session, bufferState) => {
    if (!session || !bufferState || typeof bufferState !== 'object') return;
    const dirtyState = String(bufferState.dirtyState || '').trim();
    if (dirtyState) session.bufferDirtyState = dirtyState;
    if (typeof bufferState.fileVersion === 'string' && bufferState.fileVersion.trim()) {
      session.fileVersion = bufferState.fileVersion.trim();
      if (!session.conflictDetectionKey) session.conflictDetectionKey = session.fileVersion;
    }
    if (typeof bufferState.externalActor === 'string' && bufferState.externalActor.trim()) {
      session.lastExternalWriteSource = bufferState.externalActor.trim();
    }
  };

  const fetchSessionBufferState = async (session) => {
    const url = sessionBufferStateUrl(session);
    if (!url) return null;
    try {
      const response = await fetch(url.toString(), {
        cache: 'no-store',
        headers: { accept: 'application/json' },
      });
      const payload = await response.json().catch(() => null);
      if (!response.ok || !payload || payload.ok !== true) return null;
      const bufferState = payload.result || null;
      applyBufferStateToSession(session, bufferState);
      return bufferState;
    } catch (_error) {
      return null;
    }
  };

  const markSessionBufferDirty = (session) => {
    if (!session || session.sourceKind !== 'local_folder' || session.sessionKind === 'resource') return;
    const relativePath = sessionRelativePath(session);
    if (!relativePath) return;
    const serialized = String(session.currentSerialized || '');
    const contentHash = `browser-dirty:${serialized.length}:${serialized.charCodeAt(0) || 0}:${serialized.charCodeAt(serialized.length - 1) || 0}`;
    void fetch('/api/documents/buffer-state/dirty', {
      method: 'POST',
      headers: { 'content-type': 'application/json', accept: 'application/json' },
      body: JSON.stringify({
        documentId: session.documentId,
        workspaceId: session.workspaceId,
        sourceKind: session.sourceKind,
        rootUri: session.rootUri,
        relativePath,
        contentHash,
      }),
    }).then((response) => response.json().catch(() => null)).then((payload) => {
      if (payload && payload.ok === true) applyBufferStateToSession(session, payload.result);
    }).catch(() => undefined);
  };

  const detachSessionFromLocalFolderChannel = (session) => {
    const channel = session.localFolderChannel;
    if (!channel) return;
    channel.sessions.delete(session.key);
    if (channel.sessions.size === 0) {
      if (typeof channel.unsubscribe === 'function') {
        channel.unsubscribe();
      } else if (channel.eventSource && typeof channel.eventSource.close === 'function') {
        try {
          channel.eventSource.close();
        } catch (_) {
          // noop
        }
      }
      localFolderEventRegistry.delete(channel.key);
    }
    session.localFolderChannel = null;
  };

  const releaseDocumentSession = (session) => {
    if (session.saveTimer) {
      window.clearTimeout(session.saveTimer);
      session.saveTimer = 0;
    }
    if (session.externalRefreshTimer) {
      window.clearTimeout(session.externalRefreshTimer);
      session.externalRefreshTimer = 0;
    }
    if (session.releaseTimer) {
      window.clearTimeout(session.releaseTimer);
      session.releaseTimer = 0;
    }
    detachSessionFromLocalFolderChannel(session);
    if (documentSessionRegistry.get(session.key) === session) {
      documentSessionRegistry.delete(session.key);
    }
  };

  const scheduleDocumentSessionRelease = (session) => {
    if (session.views.size > 0) return;
    if (session.releaseTimer) window.clearTimeout(session.releaseTimer);
    session.releaseTimer = window.setTimeout(() => {
      session.releaseTimer = 0;
      if (session.views.size === 0) {
        releaseDocumentSession(session);
      }
    }, SESSION_RELEASE_DELAY_MS);
  };

  const cancelDocumentSessionRelease = (session) => {
    if (!session.releaseTimer) return;
    window.clearTimeout(session.releaseTimer);
    session.releaseTimer = 0;
  };

  window.__mnoteDebugDocumentSessions = {
    snapshot: () => ({
      sessionCount: documentSessionRegistry.size,
      sessions: Array.from(documentSessionRegistry.entries()).map(([key, session]) => ({
        key,
        documentId: session.documentId,
        sourceKind: session.sourceKind,
        rootUri: session.rootUri,
        workspaceId: session.workspaceId,
        viewCount: session.views.size,
        status: session.status,
        conflictDetectionKey: session.conflictDetectionKey || '',
        lastExternalConflictDetectionKey: session.lastExternalConflictDetectionKey || '',
        lastExternalConflictEnvelope: session.lastExternalConflictEnvelope || null,
        dirtyState: session.dirty ? 'Dirty' : (session.hasExternalConflict ? 'ExternalModified' : 'Clean'),
      })),
      localFolderChannelCount: localFolderEventRegistry.size,
      localFolderRoots: Array.from(localFolderEventRegistry.keys()),
    }),
  };

  const currentEditorText = (view) => {
    const editor = view.runtimeDescriptor.root.querySelector('.editor-surface .ProseMirror');
    return editor?.textContent || '';
  };

  const findScrollableEditorContainer = (view) => {
    let current = view.runtimeDescriptor.root.querySelector('.editor-surface .ProseMirror');
    while (current instanceof HTMLElement) {
      if (current.scrollHeight > current.clientHeight + 8) {
        return current;
      }
      current = current.parentElement;
    }
    return null;
  };

  const preserveViewScrollPosition = (view, operation) => {
    const scrollable = findScrollableEditorContainer(view);
    const scrollTop = scrollable instanceof HTMLElement ? scrollable.scrollTop : null;
    const viewportTop = window.scrollY;
    const viewportLeft = window.scrollX;
    operation();
    const restore = () => {
      const current = findScrollableEditorContainer(view);
      if (current instanceof HTMLElement && scrollTop != null) {
        current.scrollTop = scrollTop;
      }
      if (Number.isFinite(viewportTop) || Number.isFinite(viewportLeft)) {
        window.scrollTo({
          top: Number.isFinite(viewportTop) ? viewportTop : window.scrollY,
          left: Number.isFinite(viewportLeft) ? viewportLeft : window.scrollX,
          behavior: 'auto',
        });
      }
    };
    window.requestAnimationFrame(() => {
      restore();
      window.setTimeout(restore, 0);
      window.setTimeout(restore, 80);
      window.setTimeout(restore, 180);
      window.setTimeout(restore, 320);
    });
  };

  const dispatchRuntimeCommand = (view, payload, source) => {
    view.runtimeDescriptor.root.dispatchEvent(new CustomEvent(commandEvent, {
      bubbles: true,
      detail: {
        protocol: bridgeProtocol,
        runtime: 'mnote-leptos-tiptap-spike',
        version: '1.1.0',
        source: source || 'mnote-web-document-session',
        event: commandEvent,
        payload,
      },
    }));
  };

  const clearEmbeddedLocalDraft = (runtimeDescriptor) => {
    if (runtimeDescriptor.bootstrap.sourceKind !== 'local_folder') return;
    try {
      const storage = window.localStorage;
      const base = 'mnote.leptos-tiptap-spike.document';
      const keys = [
        `${base}:${runtimeDescriptor.bootstrap.workspaceId}:${runtimeDescriptor.bootstrap.documentId}`,
        `${base}:${runtimeDescriptor.bootstrap.documentId}`,
      ];
      for (const key of keys) storage.removeItem(key);
    } catch (error) {
      console.warn('mnote local folder 草稿清理失败', error);
    }
  };

  const setSessionStatus = (session, status, message) => {
    session.status = status;
    session.error = message || null;
    sessionViews(session).forEach((view) => {
      setStatus(view.runtimeDescriptor, status, message);
    });
  };

  const dispatchSessionContentToView = (session, view, source) => {
    view.suppressedSerialized = session.currentSerialized;
    view.lastKnownSerialized = session.currentSerialized;
    preserveViewScrollPosition(view, () => {
      dispatchRuntimeCommand(view, {
        command: 'replaceContent',
        documentId: session.documentId,
        workspaceId: session.workspaceId,
        title: session.title,
        content: session.currentTiptapDocument,
        revision: session.revision,
        conflictDetectionKey: session.conflictDetectionKey,
        readOnly: session.readOnly,
        editable: !session.readOnly,
      }, source || 'mnote-web-document-session-sync');
    });
  };

  const dispatchSessionMetaToView = (session, view, source) => {
    dispatchRuntimeCommand(view, {
      command: 'replaceContent',
      documentId: session.documentId,
      workspaceId: session.workspaceId,
      title: session.title,
      revision: session.revision,
      conflictDetectionKey: session.conflictDetectionKey,
      readOnly: session.readOnly,
      editable: !session.readOnly,
    }, source || 'mnote-web-document-session-meta');
  };

  const pageBodySourceMeta = (sourceKind, pageBody) => {
    const pageBodySource = pageBodyTiptapDocumentSource(pageBody || {}, '');
    const projectionSource = String(pageBody?.projectionSource || pageBody?.projection_source || '').trim();
    const blockProjectionVersion = String(pageBody?.blockProjectionVersion || pageBody?.block_projection_version || '').trim();
    const localCompatFallback = sourceKind === 'local_folder' && pageBodySource === 'compat.legacy_content';
    return {
      pageBodySource,
      projectionSource,
      blockProjectionVersion,
      localCompatFallback,
      hardGuard: localCompatFallback
        ? 'local_compat_fallback'
        : (sourceKind === 'local_folder' ? 'local_ok' : 'compat_allowed'),
    };
  };

  const applyPageBodySourceMetaToSession = (session, pageBody) => {
    const meta = pageBodySourceMeta(session.sourceKind, pageBody || {});
    session.pageBodySource = meta.pageBodySource;
    session.projectionSource = meta.projectionSource;
    session.blockProjectionVersion = meta.blockProjectionVersion;
    session.pageBodyLocalCompatFallback = meta.localCompatFallback;
    session.pageBodyHardGuard = meta.hardGuard;
  };

  const syncPageBodySourceDiagnosticsToViews = (session) => {
    sessionViews(session).forEach((view) => {
      const root = view?.runtimeDescriptor?.root;
      if (!(root instanceof HTMLElement)) return;
      root.setAttribute('data-mnote-page-body-source', session.pageBodySource || '');
      root.setAttribute('data-mnote-page-body-local-compat-fallback', session.pageBodyLocalCompatFallback ? 'true' : 'false');
      root.setAttribute('data-mnote-page-body-hard-guard', session.pageBodyHardGuard || '');
      if (session.projectionSource) {
        root.setAttribute('data-mnote-projection-source', session.projectionSource);
      } else {
        root.removeAttribute('data-mnote-projection-source');
      }
      if (session.blockProjectionVersion) {
        root.setAttribute('data-mnote-block-projection-version', session.blockProjectionVersion);
      } else {
        root.removeAttribute('data-mnote-block-projection-version');
      }
    });
  };

  const syncSessionMetaToViews = (session) => {
    syncPageBodySourceDiagnosticsToViews(session);
    sessionViews(session).forEach((view) => {
      if (view.mountId != null) dispatchSessionMetaToView(session, view);
    });
  };

  const broadcastSessionContent = (session, sourceView) => {
    sessionViews(session).forEach((view) => {
      if (sourceView && view.id === sourceView.id) return;
      if (view.mountId == null) return;
      dispatchSessionContentToView(session, view);
    });
  };

  const normalizePlainText = (value) => String(value || '').replace(/\s+/g, ' ').trim();

  const sessionPlainText = (session) => {
    const liveText = sessionViews(session)
      .map((view) => normalizePlainText(currentEditorText(view)))
      .find((text) => text);
    if (liveText) return liveText;
    return normalizePlainText(flattenText(session.currentTiptapDocument));
  };

  const sessionHasRecentExternalSignal = (session) => (
    session.sourceKind === 'local_folder'
      && Number.isFinite(session.lastExternalChangeSignalAt)
      && session.lastExternalChangeSignalAt > 0
      && (Date.now() - session.lastExternalChangeSignalAt) < 1500
  );

  const sessionHasRecentLocalInput = (session) => (
    Number.isFinite(session.lastUserInputAt)
      && session.lastUserInputAt > 0
      && (Date.now() - session.lastUserInputAt) < 1500
  );

  const fetchLatestSessionAggregate = async (session) => {
    const response = await fetch(pageAggregateUrl({
      documentId: session.documentId,
      sourceKind: session.sourceKind,
      workspaceId: session.workspaceId,
      rootUri: session.rootUri,
    }).toString(), {
      cache: 'no-store',
      headers: { accept: 'application/json' },
    });
    if (!response.ok) throw new Error('conflict_latest_fetch_failed_' + response.status);
    const payload = await response.json();
    const nextAggregate = payload?.result;
    if (!nextAggregate || typeof nextAggregate !== 'object') {
      throw new Error('conflict_latest_missing_aggregate');
    }
    return nextAggregate;
  };

  const fetchLatestResourceSnapshot = async (session) => {
    const url = new URL('/api/local-folder/resource/read', window.location.origin);
    url.searchParams.set('rootUri', session.rootUri || '');
    url.searchParams.set('path', session.resourcePath || '');
    const response = await fetch(url.toString(), {
      cache: 'no-store',
      headers: { accept: 'application/json' },
    });
    const payload = await response.json().catch(() => null);
    if (!response.ok || !payload || payload.ok !== true) throw new Error('resource_conflict_latest_fetch_failed_' + response.status);
    const nextResource = payload.result;
    if (!nextResource || typeof nextResource !== 'object') throw new Error('resource_conflict_latest_missing_snapshot');
    return nextResource;
  };

  const aggregatePlainText = (aggregate) => {
    const body = aggregate?.body || {};
    return flattenText(pageBodyTiptapDocument(body)).replace(/\n{3,}/g, '\n\n').trim();
  };

  const resourceSnapshotPlainText = (snapshot) => (
    String(snapshot?.text || flattenText(toTiptapDocument(snapshot?.content, '')) || '')
      .replace(/\n{3,}/g, '\n\n')
      .trim()
  );

  const shouldRetryTransientEmptyLocalAggregate = (session, nextAggregate) => {
    if (!session || session.sourceKind !== 'local_folder') return false;
    if (!sessionHasRecentExternalSignal(session)) return false;
    if (!sessionPlainText(session)) return false;
    return !aggregatePlainText(nextAggregate);
  };

  const delay = (ms) => new Promise((resolve) => window.setTimeout(resolve, ms));

  const conflictEnvelopeFromResponse = (payload) => (
    payload?.error?.details?.conflict
      || payload?.details?.conflict
      || null
  );

  const conflictSourceLabel = (session) => {
    if (!session) return '';
    if (session.lastExternalWriteSource === 'mnote-hermes-tool') {
      const runId = String(session.lastExternalWriteRunId || '').trim();
      return runId ? `agent run ${runId}` : 'agent run';
    }
    return '本地文件变更';
  };

  const conflictMessageFromEnvelope = (session, envelope) => {
    const baseMessage = envelope?.message || externalConflictMessage;
    const sourceLabel = conflictSourceLabel(session);
    return sourceLabel ? `${baseMessage}（来源：${sourceLabel}）` : baseMessage;
  };

  const ensureSessionConflictEnvelope = (session, message) => {
    if (!session) return null;
    if (session.lastExternalConflictEnvelope) return session.lastExternalConflictEnvelope;
    const isResourceSession = session.sessionKind === 'resource';
    const defaultMessage = isResourceSession
      ? '当前本地资源文件已在外部更新，请刷新或保存前先处理冲突'
      : externalConflictMessage;
    const envelope = {
      code: isResourceSession ? 'local_resource_external_change' : 'local_markdown_external_change',
      documentId: session.documentId,
      rootUri: session.rootUri || null,
      path: isResourceSession ? (session.resourcePath || '') : undefined,
      currentDiskVersion: session.conflictDetectionKey || session.fileVersion || null,
      editorBaseVersion: session.lastExternalConflictDetectionKey || session.conflictDetectionKey || session.fileVersion || null,
      externalActor: session.lastExternalWriteSource || null,
      dirtyState: session.dirty ? 'Dirty' : (session.hasExternalConflict ? 'ExternalModified' : 'Clean'),
      bufferFileVersion: session.fileVersion || session.conflictDetectionKey || null,
      message: isResourceSession && (!message || message === externalConflictMessage) ? defaultMessage : (message || defaultMessage),
      suggestedActions: ['accept_disk', 'keep_editor', 'open_diff', 'merge'],
    };
    session.lastExternalConflictEnvelope = envelope;
    return envelope;
  };

  const clearSessionConflictSurface = (session) => {
    var _rt_ = window.__mnoteDocumentConflictPanelRuntime;
    if (_rt_ && typeof _rt_.clearSessionConflictSurface === 'function') {
      _rt_.clearSessionConflictSurface(session, { sessionViews: sessionViews });
      return;
    }
    sessionViews(session).forEach((view) => {
      const host = view.runtimeDescriptor.root.closest('.document-pane') || view.runtimeDescriptor.root;
      if (!(host instanceof HTMLElement)) return;
      host.querySelectorAll('[data-testid="mnote-editor-conflict-panel"]').forEach((node) => node.remove());
    });
  };

  const applyAggregateSnapshotToSession = (session, nextAggregate, source) => {
    const nextBody = nextAggregate?.body || {};
    const nextPermissions = nextAggregate?.head?.permissions || {};
    const nextConflictKey = conflictDetectionKeyFromBody(nextBody);
    const nextTiptapDocument = pageBodyTiptapDocument(nextBody, '', session);
    const nextSerialized = JSON.stringify(nextTiptapDocument);
    const nextRevision = Number.isInteger(nextBody.revision) ? nextBody.revision : revisionFromConflictKey(nextConflictKey);
    session.latestAggregate = nextAggregate;
    syncPageAggregateScript(session, nextAggregate);
    applyPageBodySourceMetaToSession(session, nextBody);
    session.title = nextAggregate?.head?.title || session.title;
    session.currentTiptapDocument = nextTiptapDocument;
    session.currentSerialized = nextSerialized;
    session.lastPersistedSerialized = nextSerialized;
    session.revision = nextRevision;
    session.conflictDetectionKey = nextConflictKey;
    session.lastExternalConflictDetectionKey = nextConflictKey || '';
    session.readOnly = Boolean(nextPermissions.readOnly);
    session.dirty = false;
    session.hasExternalConflict = false;
    session.externalChangePending = false;
    session.lastUserInputAt = 0;
    clearSessionConflictSurface(session);
    sessionViews(session).forEach((view) => {
      if (view.mountId != null) dispatchSessionContentToView(session, view, source || 'mnote-web-conflict-resolved');
    });
    setSessionStatus(session, 'synced-external-change');
  };

  const applyResourceSnapshotToSession = (session, nextResource, source) => {
    const nextConflictKey = String(nextResource?.fileVersion || nextResource?.conflictDetectionKey || '').trim();
    const nextTiptapDocument = toTiptapDocument(nextResource?.content, nextResource?.text || '');
    const nextSerialized = JSON.stringify(nextTiptapDocument);
    session.currentTiptapDocument = nextTiptapDocument;
    session.currentSerialized = nextSerialized;
    session.lastPersistedSerialized = nextSerialized;
    if (nextConflictKey) {
      session.conflictDetectionKey = nextConflictKey;
      session.lastExternalConflictDetectionKey = nextConflictKey;
      session.fileVersion = nextConflictKey;
    }
    session.dirty = false;
    session.hasExternalConflict = false;
    session.externalChangePending = false;
    session.lastUserInputAt = 0;
    clearSessionConflictSurface(session);
    sessionViews(session).forEach((view) => {
      if (view.mountId != null) dispatchSessionContentToView(session, view, source || 'mnote-web-resource-conflict-resolved');
    });
    setSessionStatus(session, 'synced-external-change');
  };

  const openConflictDiffPanel = async (session, panel) => {
    const diffPanel = panel.querySelector('[data-testid="mnote-conflict-diff-panel"]');
    if (!(diffPanel instanceof HTMLElement)) return;
    diffPanel.hidden = false;
    diffPanel.replaceChildren();
    const loading = document.createElement('div');
    loading.className = 'mnote-conflict-diff-status';
    loading.textContent = '正在读取磁盘版本...';
    diffPanel.appendChild(loading);
    try {
      const latest = session.sessionKind === 'resource'
        ? await fetchLatestResourceSnapshot(session)
        : await fetchLatestSessionAggregate(session);
      const latestText = session.sessionKind === 'resource'
        ? resourceSnapshotPlainText(latest)
        : aggregatePlainText(latest);
      diffPanel.replaceChildren();
      var _dpRT_ = window.__mnoteDocumentConflictPanelRuntime;
      if (_dpRT_ && typeof _dpRT_.populateSessionConflictDiffPanel === 'function') {
        _dpRT_.populateSessionConflictDiffPanel(diffPanel, {
          currentText: sessionPlainText(session) || '(当前编辑器为空)',
          diskText: latestText || '(磁盘版本为空)',
          currentMergeText: sessionPlainText(session) || '',
          diskMergeText: latestText || '',
          mergeDefaultText: sessionPlainText(session) || latestText || '',
        }, {
          onUseCurrent: function() {
            return sessionPlainText(session) || '';
          },
          onUseDisk: function() {
            return latestText || '';
          },
          onSaveMerge: function(mergeText) {
            writeMergedConflictResult(session, panel, mergeText).catch((error) => {
              setSessionStatus(session, 'external-change-conflict', error instanceof Error ? error.message : String(error));
              renderSessionConflictSurface(session, error instanceof Error ? error.message : String(error));
            });
          },
        });
      } else {
        const current = document.createElement('pre');
        current.setAttribute('data-testid', 'mnote-conflict-current-text');
        current.textContent = sessionPlainText(session) || '(当前编辑器为空)';
        const disk = document.createElement('pre');
        disk.setAttribute('data-testid', 'mnote-conflict-disk-text');
        disk.textContent = latestText || '(磁盘版本为空)';
        const currentTitle = document.createElement('h3');
        currentTitle.textContent = '当前编辑器版本';
        const diskTitle = document.createElement('h3');
        diskTitle.textContent = '磁盘版本';
        const currentBox = document.createElement('section');
        currentBox.append(currentTitle, current);
        const diskBox = document.createElement('section');
        diskBox.append(diskTitle, disk);
        const mergeTitle = document.createElement('h3');
        mergeTitle.textContent = '合并结果';
        const mergeText = document.createElement('textarea');
        mergeText.setAttribute('data-testid', 'mnote-conflict-merge-text');
        mergeText.value = sessionPlainText(session) || latestText || '';
        const mergeActions = document.createElement('div');
        mergeActions.className = 'mnote-conflict-actions';
        const useCurrent = document.createElement('button');
        useCurrent.type = 'button';
        useCurrent.textContent = '使用当前版本';
        useCurrent.setAttribute('data-testid', 'mnote-conflict-merge-use-current');
        const useDisk = document.createElement('button');
        useDisk.type = 'button';
        useDisk.textContent = '使用磁盘版本';
        useDisk.setAttribute('data-testid', 'mnote-conflict-merge-use-disk');
        const saveMerge = document.createElement('button');
        saveMerge.type = 'button';
        saveMerge.textContent = '写回合并结果';
        saveMerge.setAttribute('data-testid', 'mnote-conflict-merge-save');
        mergeActions.append(useCurrent, useDisk, saveMerge);
        const mergeBox = document.createElement('section');
        mergeBox.className = 'mnote-conflict-merge-box';
        mergeBox.append(mergeTitle, mergeText, mergeActions);
        diffPanel.append(currentBox, diskBox, mergeBox);
        useCurrent.addEventListener('click', () => {
          mergeText.value = sessionPlainText(session) || '';
        });
        useDisk.addEventListener('click', () => {
          mergeText.value = latestText || '';
        });
        saveMerge.addEventListener('click', () => {
          writeMergedConflictResult(session, panel, mergeText.value).catch((error) => {
            setSessionStatus(session, 'external-change-conflict', error instanceof Error ? error.message : String(error));
            renderSessionConflictSurface(session, error instanceof Error ? error.message : String(error));
          });
        });
      }
    } catch (error) {
      loading.textContent = error instanceof Error ? error.message : String(error);
      diffPanel.replaceChildren(loading);
    }
  };

  const acceptDiskVersion = async (session) => {
    var _RT_ = window.__mnoteDocumentConflictPanelRuntime;
    if (_RT_ && typeof _RT_.acceptDiskVersion === 'function') {
      return _RT_.acceptDiskVersion(session, {
        setSessionStatus: setSessionStatus,
        fetchLatestResourceSnapshot: fetchLatestResourceSnapshot,
        applyResourceSnapshotToSession: applyResourceSnapshotToSession,
        fetchLatestSessionAggregate: fetchLatestSessionAggregate,
        applyAggregateSnapshotToSession: applyAggregateSnapshotToSession,
      });
    }
    // inline fallback
    setSessionStatus(session, 'conflict-resolving', '正在接受磁盘版本...');
    if (session.sessionKind === 'resource') {
      const latestResource = await fetchLatestResourceSnapshot(session);
      applyResourceSnapshotToSession(session, latestResource, 'mnote-web-resource-conflict-accept-disk');
      return;
    }
    const latest = await fetchLatestSessionAggregate(session);
    applyAggregateSnapshotToSession(session, latest, 'mnote-web-conflict-accept-disk');
  };

  const keepCurrentEditorVersion = async (session) => {
    var _RT_ = window.__mnoteDocumentConflictPanelRuntime;
    if (_RT_ && typeof _RT_.keepCurrentEditorVersion === 'function') {
      return _RT_.keepCurrentEditorVersion(session, {
        setSessionStatus: setSessionStatus,
        fetchLatestResourceSnapshot: fetchLatestResourceSnapshot,
        fetchLatestSessionAggregate: fetchLatestSessionAggregate,
        sessionViews: sessionViews,
        currentEditorText: currentEditorText,
        normalizePlainText: normalizePlainText,
        hydrateMindmapAttrsFromDom: hydrateMindmapAttrsFromDom,
        textToTiptapDocument: textToTiptapDocument,
        conflictDetectionKeyFromBody: conflictDetectionKeyFromBody,
        clearSessionConflictSurface: clearSessionConflictSurface,
        persistSession: persistSession,
      });
    }
    // inline fallback
    setSessionStatus(session, 'conflict-resolving', '正在保留当前编辑器版本...');
    const latest = session.sessionKind === 'resource'
      ? await fetchLatestResourceSnapshot(session)
      : await fetchLatestSessionAggregate(session);
    const hydrateView = sessionViews(session).find((item) => item.mountId != null) || sessionViews(session)[0];
    if (hydrateView) {
      const liveText = normalizePlainText(currentEditorText(hydrateView));
      if (liveText) {
        session.currentTiptapDocument = hydrateMindmapAttrsFromDom(
          textToTiptapDocument(liveText),
          hydrateView.runtimeDescriptor.root,
        );
      }
      session.currentSerialized = JSON.stringify(session.currentTiptapDocument);
    }
    const nextKey = session.sessionKind === 'resource'
      ? String(latest?.fileVersion || latest?.conflictDetectionKey || '').trim()
      : conflictDetectionKeyFromBody(latest.body || {});
    if (nextKey) {
      session.conflictDetectionKey = nextKey;
      session.lastExternalConflictDetectionKey = nextKey;
    }
    session.hasExternalConflict = false;
    session.externalChangePending = false;
    session.saving = false;
    session.dirty = true;
    clearSessionConflictSurface(session);
    await persistSession(session);
  };

  const writeMergedConflictResult = async (session, panel, mergedText) => {
    var _RT_ = window.__mnoteDocumentConflictPanelRuntime;
    if (_RT_ && typeof _RT_.writeMergedConflictResult === 'function') {
      return _RT_.writeMergedConflictResult(session, panel, mergedText, {
        setSessionStatus: setSessionStatus,
        fetchLatestResourceSnapshot: fetchLatestResourceSnapshot,
        fetchLatestSessionAggregate: fetchLatestSessionAggregate,
        conflictDetectionKeyFromBody: conflictDetectionKeyFromBody,
        textToTiptapDocument: textToTiptapDocument,
        persistSession: persistSession,
      });
    }
    // inline fallback
    setSessionStatus(session, 'conflict-resolving', '正在写回合并结果...');
    const latest = session.sessionKind === 'resource'
      ? await fetchLatestResourceSnapshot(session)
      : await fetchLatestSessionAggregate(session);
    const nextKey = session.sessionKind === 'resource'
      ? String(latest?.fileVersion || latest?.conflictDetectionKey || '').trim()
      : conflictDetectionKeyFromBody(latest.body || {});
    if (nextKey) {
      session.conflictDetectionKey = nextKey;
      session.lastExternalConflictDetectionKey = nextKey;
    }
    session.currentTiptapDocument = textToTiptapDocument(String(mergedText || ''));
    session.currentSerialized = JSON.stringify(session.currentTiptapDocument);
    session.hasExternalConflict = false;
    session.externalChangePending = false;
    session.saving = false;
    session.dirty = true;
    if (panel && typeof panel.remove === 'function') panel.remove();
    await persistSession(session);
  };

  const runSessionConflictAction = (action, onError) => {
    const runtime = window.__mnoteDocumentConflictPanelRuntime;
    if (runtime && typeof runtime.runSessionConflictAction === 'function') {
      runtime.runSessionConflictAction(action, { onError });
      return;
    }
    try {
      const result = action();
      if (result && typeof result.then === 'function') result.catch(onError);
    } catch (error) {
      onError(error);
    }
  };

  const renderSessionConflictSurface = (session, message) => {
    clearSessionConflictSurface(session);
    sessionViews(session).forEach((view) => {
      const runtime = window.__mnoteDocumentConflictPanelRuntime;
      const handleConflictActionError = (error) => {
        setSessionStatus(session, 'external-change-conflict', error instanceof Error ? error.message : String(error));
        renderSessionConflictSurface(session, error instanceof Error ? error.message : String(error));
      };
      let panel = null;
      if (runtime && typeof runtime.createSessionConflictPanel === 'function') {
        panel = runtime.createSessionConflictPanel(session, message, {
          externalConflictMessage,
          conflictSourceLabel,
          onAcceptDisk: () => {
            runSessionConflictAction(() => acceptDiskVersion(session), handleConflictActionError);
          },
          onKeepCurrent: () => {
            runSessionConflictAction(() => keepCurrentEditorVersion(session), handleConflictActionError);
          },
          onOpenDiff: (_session, createdPanel) => {
            openConflictDiffPanel(session, createdPanel);
          },
        });
      }
      if (!(panel instanceof HTMLElement)) {
        panel = document.createElement('section');
        panel.className = 'mnote-editor-conflict-panel';
        panel.setAttribute('data-testid', 'mnote-editor-conflict-panel');
        panel.setAttribute('role', 'status');
        panel.setAttribute('aria-live', 'polite');

        const heading = document.createElement('h2');
        heading.textContent = '文件冲突';
        const text = document.createElement('p');
        text.textContent = message || externalConflictMessage;
        const meta = document.createElement('div');
        meta.className = 'mnote-conflict-meta';
        const fileLabel = session.sessionKind === 'resource'
          ? `${session.rootUri || ''}/${session.resourcePath || session.documentId}`
          : (session.rootUri || session.documentId);
        meta.textContent = `文件：${fileLabel} · 来源：${conflictSourceLabel(session) || '本地文件变更'}`;
        const actions = document.createElement('div');
        actions.className = 'mnote-conflict-actions';
        const acceptDisk = document.createElement('button');
        acceptDisk.type = 'button';
        acceptDisk.textContent = '接受磁盘版本';
        acceptDisk.setAttribute('data-testid', 'mnote-conflict-accept-disk');
        const keepCurrent = document.createElement('button');
        keepCurrent.type = 'button';
        keepCurrent.textContent = '保留当前编辑器版本';
        keepCurrent.setAttribute('data-testid', 'mnote-conflict-keep-current');
        const openDiff = document.createElement('button');
        openDiff.type = 'button';
        openDiff.textContent = '打开 diff';
        openDiff.setAttribute('data-testid', 'mnote-conflict-open-diff');
        actions.append(acceptDisk, keepCurrent, openDiff);
        const diffPanel = document.createElement('div');
        diffPanel.className = 'mnote-conflict-diff-panel';
        diffPanel.setAttribute('data-testid', 'mnote-conflict-diff-panel');
        diffPanel.hidden = true;
        panel.append(heading, text, meta, actions, diffPanel);

        acceptDisk.addEventListener('click', () => {
          runSessionConflictAction(() => acceptDiskVersion(session), handleConflictActionError);
        });
        keepCurrent.addEventListener('click', () => {
          runSessionConflictAction(() => keepCurrentEditorVersion(session), handleConflictActionError);
        });
        openDiff.addEventListener('click', () => {
          openConflictDiffPanel(session, panel);
        });
      }

      if (runtime && typeof runtime.mountSessionConflictPanel === 'function') {
        if (runtime.mountSessionConflictPanel(view.runtimeDescriptor.root, panel)) {
          return;
        }
      }
      const host = view.runtimeDescriptor.root.closest('.document-pane') || view.runtimeDescriptor.root;
      if (!(host instanceof HTMLElement)) return;
      const header = host.querySelector('.document-shell-header');
      if (header && header.parentNode) {
        header.parentNode.insertBefore(panel, header.nextSibling);
      } else {
        host.prepend(panel);
      }
    });
  };

  const markSessionExternalConflict = (session, message) => {
    session.externalChangePending = false;
    const envelope = ensureSessionConflictEnvelope(session, message);
    session.hasExternalConflict = true;
    if (session.saveTimer) {
      window.clearTimeout(session.saveTimer);
      session.saveTimer = 0;
    }
    const nextMessage = envelope?.message || message || externalConflictMessage;
    setSessionStatus(session, 'external-change-conflict', nextMessage);
    renderSessionConflictSurface(session, nextMessage);
  };

  const queueSessionSave = (session) => {
    if (session.readOnly || session.hasExternalConflict) return;
    if (session.saveTimer) window.clearTimeout(session.saveTimer);
    setSessionStatus(session, 'dirty');
    markSessionBufferDirty(session);
    session.saveTimer = window.setTimeout(() => {
      session.saveTimer = 0;
      void persistSession(session);
    }, 650);
  };

  const makeSaveOperationToken = (prefix, session) => {
    const doc = String(session?.documentId || session?.resourcePath || 'unknown')
      .replace(/[^a-zA-Z0-9._:-]+/g, '-')
      .slice(0, 80);
    const random = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
      ? crypto.randomUUID()
      : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
    return `${prefix}:${doc}:${random}`;
  };

  const persistSession = async (session) => {
    if (session.readOnly || session.saving || session.hasExternalConflict) return;
    const hydrateView = sessionViews(session).find((item) => item.mountId != null) || sessionViews(session)[0];
    if (hydrateView) {
      hydrateMindmapAttrsFromDom(session.currentTiptapDocument, hydrateView.runtimeDescriptor.root);
      session.currentSerialized = JSON.stringify(session.currentTiptapDocument);
    }
    const serialized = session.currentSerialized;
    if (!session.dirty && serialized === session.lastPersistedSerialized) {
      setSessionStatus(session, 'saved');
      return;
    }
    session.saving = true;
    setSessionStatus(session, 'saving');
    try {
      const editorDocument = editorDocumentFromTiptapDocument({ documentId: session.documentId }, session.currentTiptapDocument);
      const content = legacyBlocksFromEditorDocument(editorDocument);
      const saveEndpoint = session.saveEndpoint || '/api/documents/save';
      const expectedFileVersion = (
        session.sourceKind === 'local_folder'
        && !session.lastExternalConflictEnvelope
        && session.lastExternalConflictDetectionKey
      )
        ? session.lastExternalConflictDetectionKey
        : session.conflictDetectionKey;
      if (session.sourceKind === 'local_folder' && expectedFileVersion && expectedFileVersion !== session.conflictDetectionKey) {
        session.conflictDetectionKey = expectedFileVersion;
        session.fileVersion = expectedFileVersion;
      }
      const writeIntentId = session.sourceKind === 'local_folder'
        ? makeSaveOperationToken('intent:tiptap', session)
        : '';
      const saveOperationId = session.sourceKind === 'local_folder'
        ? makeSaveOperationToken('save:tiptap', session)
        : '';
      const savePayload = session.sessionKind === 'resource'
        ? {
          rootUri: session.rootUri,
          path: session.resourcePath,
          expectedFileVersion: expectedFileVersion,
          writeIntentId,
          saveOperationId,
          contentFormat: 'editorBlocks',
          editorSource: 'tiptap-resource-tab',
          editorDocument,
          content,
          tiptapDocument: session.currentTiptapDocument,
          blockCount: editorDocument.blocks.length,
        }
        : {
          documentId: session.documentId,
          workspaceId: session.workspaceId,
          sourceKind: session.sourceKind,
          rootUri: session.rootUri,
          revision: session.revision,
          expectedFileVersion: expectedFileVersion,
          writeIntentId,
          saveOperationId,
          contentFormat: 'editorBlocks',
          editorSource: 'tiptap',
          editorDocument,
          content,
          tiptapDocument: session.currentTiptapDocument,
          blockCount: editorDocument.blocks.length,
        };
      if (session.sourceKind !== 'local_folder' && session.sessionKind !== 'resource') {
        savePayload.conflictDetectionKey = session.conflictDetectionKey;
      }
      markLocalFolderSelfChangeSuppression(session);
      const response = await fetch(saveEndpoint, {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify(savePayload),
      });
      const result = await response.json().catch(() => null);
      if (!response.ok || !result || result.ok !== true) {
        const message = result?.error?.message || result?.message || `save_failed_${response.status}`;
        const conflictEnvelope = conflictEnvelopeFromResponse(result);
        if (response.status === 409 && conflictEnvelope && session.sourceKind === 'local_folder') {
          session.saving = false;
          session.lastExternalConflictEnvelope = conflictEnvelope;
          markSessionExternalConflict(session, conflictMessageFromEnvelope(session, conflictEnvelope));
          return;
        }
        throw new Error(message);
      }
      const saved = result.result || {};
      if (typeof saved.writeIntentId === 'string' && saved.writeIntentId.trim()) {
        session.lastWriteIntentId = saved.writeIntentId.trim();
      } else if (writeIntentId) {
        session.lastWriteIntentId = writeIntentId;
      }
      if (typeof saved.saveOperationId === 'string' && saved.saveOperationId.trim()) {
        session.lastSaveOperationId = saved.saveOperationId.trim();
      } else if (saveOperationId) {
        session.lastSaveOperationId = saveOperationId;
      }
      if (Number.isInteger(saved.revision)) session.revision = saved.revision;
      if (typeof saved.conflict_detection_key === 'string' && saved.conflict_detection_key.trim()) {
        session.conflictDetectionKey = saved.conflict_detection_key.trim();
      }
      if (typeof saved.conflictDetectionKey === 'string' && saved.conflictDetectionKey.trim()) {
        session.conflictDetectionKey = saved.conflictDetectionKey.trim();
      }
      if (typeof saved.fileVersion === 'string' && saved.fileVersion.trim()) {
        session.conflictDetectionKey = saved.fileVersion.trim();
      }
      if (session.conflictDetectionKey) session.lastExternalConflictDetectionKey = session.conflictDetectionKey;
      session.hasExternalConflict = false;
      session.externalChangePending = false;
      session.bufferDirtyState = 'Clean';
      session.lastUserInputAt = 0;
      syncSessionMetaToViews(session);
      session.lastPersistedSerialized = serialized;
      session.saving = false;
      if (session.sessionKind !== 'resource' && typeof window.__mnoteRecordPageHistorySnapshot === 'function') {
        const primaryView = sessionViews(session).find((view) => view.runtimeDescriptor.paneRole === 'primary') || sessionViews(session)[0];
        if (primaryView) {
          const plainText = sessionPlainText(session);
          window.__mnoteRecordPageHistorySnapshot('save', {
            wordCount: content.filter((block) => typeof block?.content === 'string' && block.content.trim()).length,
            characterCount: plainText.replace(/\s/g, '').length,
            blockCount: editorDocument.blocks.length,
            todoTotal: editorDocument.blocks.filter((block) => block.blockType === 'todo').length,
            todoDone: editorDocument.blocks.filter((block) => block.blockType === 'todo' && block.props?.checked).length,
          });
        }
      }
      if (session.currentSerialized === serialized) {
        session.dirty = false;
        setSessionStatus(session, 'saved');
      } else {
        session.dirty = true;
        setSessionStatus(session, 'dirty');
        queueSessionSave(session);
      }
      syncResourceSessionTabGuards(session);
    } catch (error) {
      session.saving = false;
      setSessionStatus(session, 'error', error instanceof Error ? error.message : String(error));
      syncResourceSessionTabGuards(session);
    }
  };

  const scheduleSessionExternalRefresh = (session, source) => {
    if (!session || session.views.size === 0) return;
    if (session.externalRefreshTimer) return;
    session.externalRefreshSource = source || session.externalRefreshSource || 'mnote-web-external-change';
    session.externalRefreshTimer = window.setTimeout(() => {
      const refreshSource = session.externalRefreshSource || 'mnote-web-external-change';
      session.externalRefreshSource = '';
      session.externalRefreshTimer = 0;
      void refreshSessionFromExternalChange(session, refreshSource);
    }, 120);
  };

  const refreshSessionFromExternalChange = async (session, source) => {
    if (document.hidden) return;
    if (!session || session.views.size === 0) return;
    if (session.sourceKind === 'local_folder' && !session.rootUri) return;
    if (session.sessionKind === 'resource') {
      await refreshResourceSessionFromExternalChange(session, source);
      return;
    }
    try {
      const bufferState = await fetchSessionBufferState(session);
      const dirtyState = String(bufferState?.dirtyState || session.bufferDirtyState || '').trim();
      if (dirtyState === 'Deleted') {
        session.lastExternalConflictEnvelope = {
          code: 'local_markdown_deleted',
          documentId: session.documentId,
          rootUri: session.rootUri || null,
          currentDiskVersion: bufferState?.fileVersion || session.conflictDetectionKey || session.fileVersion || null,
          editorBaseVersion: session.conflictDetectionKey || session.fileVersion || null,
          externalActor: bufferState?.externalActor || null,
          dirtyState,
          bufferFileVersion: bufferState?.fileVersion || session.fileVersion || null,
          message: '当前本地 Markdown 文件已被删除，请恢复文件或关闭当前标签',
          suggestedActions: ['keep_editor', 'open_diff'],
        };
        markSessionExternalConflict(session, session.lastExternalConflictEnvelope.message);
        return;
      }
      if (session.hasExternalConflict) {
        markSessionExternalConflict(session, conflictMessageFromEnvelope(session, {
          message: externalConflictMessage,
          dirtyState,
          fileVersion: bufferState?.fileVersion || null,
          externalActor: bufferState?.externalActor || null,
        }));
        return;
      }
      if (sessionHasRecentLocalInput(session) || session.dirty || session.saveTimer || session.saving) {
        session.externalChangePending = true;
        if (session.dirty && !session.saveTimer && !session.saving) {
          queueSessionSave(session);
        } else if (!session.saving) {
          setSessionStatus(session, 'dirty');
        }
        return;
      }
      const response = await fetch(pageAggregateUrl({
        documentId: session.documentId,
        sourceKind: session.sourceKind,
        workspaceId: session.workspaceId,
        rootUri: session.rootUri,
      }).toString(), {
        cache: 'no-store',
        headers: { accept: 'application/json' },
      });
      if (!response.ok) {
        if (session.sourceKind === 'local_folder') {
          markSessionExternalConflict(session, externalConflictMessage);
        }
        return;
      }
      const payload = await response.json();
      const conflictEnvelope = conflictEnvelopeFromResponse(payload);
      if (response.status === 409 && conflictEnvelope) {
        session.lastExternalConflictEnvelope = conflictEnvelope;
        markSessionExternalConflict(session, conflictMessageFromEnvelope(session, conflictEnvelope));
        return;
      }
      let nextAggregate = payload?.result;
      if (shouldRetryTransientEmptyLocalAggregate(session, nextAggregate)) {
        await delay(500);
        try {
          const retryAggregate = await fetchLatestSessionAggregate(session);
          if (aggregatePlainText(retryAggregate)) {
            nextAggregate = retryAggregate;
          } else {
            markSessionExternalConflict(session, '检测到外部编辑器正在写入空内容，已暂停自动刷新以保护当前编辑区。');
            return;
          }
        } catch (_retryError) {
          markSessionExternalConflict(session, externalConflictMessage);
          return;
        }
      }
      session.latestAggregate = nextAggregate;
      syncPageAggregateScript(session, nextAggregate);
      const nextBody = nextAggregate?.body || {};
      const nextPermissions = nextAggregate?.head?.permissions || {};
      const nextConflictKey = conflictDetectionKeyFromBody(nextBody);
      const nextTiptapDocument = pageBodyTiptapDocument(nextBody, '', session);
      const nextPageBodyMeta = pageBodySourceMeta(session.sourceKind, nextBody);
      const nextSerialized = JSON.stringify(nextTiptapDocument);
      const contentChanged = nextSerialized !== session.currentSerialized;
      session.externalChangePending = false;
      session.bufferDirtyState = 'Clean';
      if (!nextConflictKey || !session.lastExternalConflictDetectionKey) {
        session.lastExternalConflictDetectionKey = nextConflictKey || session.lastExternalConflictDetectionKey;
        if (!contentChanged) {
          applyPageBodySourceMetaToSession(session, nextBody);
          syncPageBodySourceDiagnosticsToViews(session);
          return;
        }
      }
      if (nextConflictKey === session.lastExternalConflictDetectionKey) {
        applyPageBodySourceMetaToSession(session, nextBody);
        syncPageBodySourceDiagnosticsToViews(session);
        if (nextConflictKey && session.conflictDetectionKey !== nextConflictKey) {
          session.conflictDetectionKey = nextConflictKey;
          session.fileVersion = nextConflictKey;
          syncSessionMetaToViews(session);
        }
        return;
      }
      const nextRevision = Number.isInteger(nextBody.revision) ? nextBody.revision : revisionFromConflictKey(nextConflictKey);
      session.latestAggregate = nextAggregate;
      syncPageAggregateScript(session, nextAggregate);
      session.pageBodySource = nextPageBodyMeta.pageBodySource;
      session.projectionSource = nextPageBodyMeta.projectionSource;
      session.blockProjectionVersion = nextPageBodyMeta.blockProjectionVersion;
      session.pageBodyLocalCompatFallback = nextPageBodyMeta.localCompatFallback;
      session.pageBodyHardGuard = nextPageBodyMeta.hardGuard;
      session.title = nextAggregate?.head?.title || session.title;
      session.currentTiptapDocument = nextTiptapDocument;
      session.currentSerialized = nextSerialized;
      session.lastPersistedSerialized = session.currentSerialized;
      session.revision = nextRevision;
      session.conflictDetectionKey = nextConflictKey;
      session.lastExternalConflictDetectionKey = nextConflictKey || '';
      session.readOnly = Boolean(nextPermissions.readOnly);
      session.dirty = false;
      session.hasExternalConflict = false;
      session.lastUserInputAt = 0;
      sessionViews(session).forEach((view) => {
        if (view.mountId != null) dispatchSessionContentToView(session, view, source || 'mnote-web-external-change');
      });
      setSessionStatus(session, 'synced-external-change');
    } catch (error) {
      console.warn('mnote 页面外部更新检测失败', error);
    }
  };

  const refreshResourceSessionFromExternalChange = async (session, source) => {
    if (document.hidden) return;
    if (!session || session.views.size === 0) return;
    if (session.sourceKind !== 'local_folder' || !session.rootUri || !session.resourcePath) return;
    let nextResource = null;
    try {
      nextResource = await fetchLatestResourceSnapshot(session);
    } catch (error) {
      session.lastExternalConflictEnvelope = {
        code: 'local_resource_deleted',
        documentId: session.documentId,
        rootUri: session.rootUri || null,
        path: session.resourcePath || '',
        currentDiskVersion: null,
        editorBaseVersion: session.conflictDetectionKey || session.fileVersion || null,
        externalActor: 'external-editor',
        dirtyState: 'Deleted',
        message: '当前本地资源文件已被删除或移动，请恢复文件或关闭当前标签',
        suggestedActions: ['keep_editor', 'open_diff'],
      };
      markSessionExternalConflict(session, session.lastExternalConflictEnvelope.message);
      return;
    }
    const nextConflictKey = String(nextResource?.fileVersion || nextResource?.conflictDetectionKey || '').trim();
    const nextTiptapDocument = toTiptapDocument(nextResource?.content, nextResource?.text || '');
    const nextSerialized = JSON.stringify(nextTiptapDocument);
    const contentChanged = nextSerialized !== session.currentSerialized;
    session.externalChangePending = false;
    if (!nextConflictKey || !session.lastExternalConflictDetectionKey) {
      session.lastExternalConflictDetectionKey = nextConflictKey || session.lastExternalConflictDetectionKey;
      if (!contentChanged) return;
    }
    if (nextConflictKey === session.lastExternalConflictDetectionKey) {
      if (nextConflictKey && session.conflictDetectionKey !== nextConflictKey) {
        session.conflictDetectionKey = nextConflictKey;
        session.fileVersion = nextConflictKey;
        syncSessionMetaToViews(session);
      }
      return;
    }
    if (session.hasExternalConflict || sessionHasRecentLocalInput(session) || session.dirty || session.saveTimer || session.saving) {
      session.lastExternalConflictEnvelope = {
        code: 'local_resource_external_change',
        documentId: session.documentId,
        rootUri: session.rootUri || null,
        path: session.resourcePath || '',
        currentDiskVersion: nextConflictKey || null,
        editorBaseVersion: session.conflictDetectionKey || session.fileVersion || null,
        externalActor: 'external-editor',
        dirtyState: session.dirty ? 'Dirty' : 'Clean',
        message: '当前本地资源文件已在外部更新，请刷新或保存前先处理冲突',
        suggestedActions: ['accept_disk', 'keep_editor', 'open_diff'],
      };
      markSessionExternalConflict(session, session.lastExternalConflictEnvelope.message);
      return;
    }
    session.currentTiptapDocument = nextTiptapDocument;
    session.currentSerialized = nextSerialized;
    session.lastPersistedSerialized = nextSerialized;
    session.conflictDetectionKey = nextConflictKey;
    session.fileVersion = nextConflictKey;
    session.lastExternalConflictDetectionKey = nextConflictKey || '';
    session.dirty = false;
    session.hasExternalConflict = false;
    session.lastUserInputAt = 0;
    sessionViews(session).forEach((view) => {
      if (view.mountId != null) dispatchSessionContentToView(session, view, source || 'mnote-web-resource-watch');
    });
    setSessionStatus(session, 'synced-external-change');
    syncResourceSessionTabGuards(session);
  };

  const refreshSessionFromExternalFileChange = async (session) => {
    if (session.sourceKind !== 'local_folder') return;
    await refreshSessionFromExternalChange(session, 'mnote-web-local-folder-watch');
  };

  const handleLocalFolderEventPayloadForSessions = (channel, payload) => {
    if (!payload) return;
    localFolderEventItems(payload).forEach((item) => {
      Array.from(channel.sessions.values()).forEach((targetSession) => {
        if (!targetSession || targetSession.views.size === 0) return;
        const documentId = typeof item.documentId === 'string' ? item.documentId.trim() : '';
        const relativePath = typeof item.relativePath === 'string' ? item.relativePath.trim() : '';
        if (targetSession.sessionKind === 'resource') {
          if (!relativePath || relativePath !== resourceWatchPath(targetSession)) return;
          if (targetSession.saving) {
            targetSession.externalChangePending = false;
            targetSession.lastSelfSaveSignalAt = Date.now();
            return;
          }
          targetSession.lastExternalChangeSignalAt = Date.now();
          targetSession.externalChangePending = true;
          scheduleSessionExternalRefresh(targetSession, 'mnote-web-local-resource-watch');
          return;
        }
        const eventKind = String(item.eventKind || '');
        const relativeTarget = sessionRelativePath(targetSession);
        const targetsCurrentDocument = Boolean(
          (documentId && documentId === targetSession.documentId)
          || (!documentId && relativePath && relativeTarget && relativePath === relativeTarget)
        );
        if (!targetsCurrentDocument) return;
        if (shouldSuppressLocalFolderSelfChange(targetSession.documentId, eventKind)) {
          targetSession.externalChangePending = false;
          targetSession.lastSelfSaveSignalAt = Date.now();
          return;
        }
        if (targetSession.saving) {
          targetSession.externalChangePending = false;
          targetSession.lastSelfSaveSignalAt = Date.now();
          return;
        }
        targetSession.lastExternalChangeSignalAt = Date.now();
        targetSession.externalChangePending = true;
        scheduleSessionExternalRefresh(targetSession, 'mnote-web-local-folder-watch');
      });
    });
  };

  const ensureLocalFolderEventChannel = (session) => {
    if (session.sourceKind !== 'local_folder' || !session.rootUri) {
      return;
    }

    const eventBus = window.__mnoteLocalFolderEventBus;
    if (eventBus && typeof eventBus.startLocalFolderWatcher === 'function') {
      const channelKey = localFolderEventBusChannelKey(session);
      let channel = localFolderEventRegistry.get(channelKey);
      if (!channel) {
        const rootUri = String(session.rootUri || '').trim();
        const workspaceId = String(session.workspaceId || '').trim();
        const handler = (event) => {
          const detail = event?.detail || {};
          const eventRootUri = String(detail.rootUri || detail.payload?.rootUri || '').trim();
          if (eventRootUri && eventRootUri !== rootUri) return;
          handleLocalFolderEventPayloadForSessions(channel, detail.payload || detail);
        };
        const handle = eventBus.startLocalFolderWatcher({
          rootUri,
          workspaceId,
          bootstrap: {
            schema: 'mnote.document_session.local_folder_event_bus.v1',
            transport: 'local-folder-events',
            workspaceId,
          },
        });
        channel = {
          key: channelKey,
          rootUri,
          documentId: '',
          resourcePath: '',
          eventSource: handle,
          sessions: new Map(),
          unsubscribe: () => window.removeEventListener('mnote:local-folder:document-changed', handler),
        };
        window.addEventListener('mnote:local-folder:document-changed', handler);
        localFolderEventRegistry.set(channelKey, channel);
      }
      channel.sessions.set(session.key, session);
      session.localFolderChannel = channel;
      return;
    }

    if (typeof window.EventSource !== 'function') return;
    const channelKey = localFolderEventChannelKey(session);
    let channel = localFolderEventRegistry.get(channelKey);
    if (!channel) {
      const url = new URL('/api/local-folder/events', window.location.origin);
      url.searchParams.set('rootUri', session.rootUri);
      if (session.sessionKind === 'resource') {
        url.searchParams.set('resourcePath', resourceWatchPath(session));
      } else {
        url.searchParams.set('documentId', session.documentId);
      }
      const eventSource = new EventSource(url.toString());
      channel = {
        key: channelKey,
        rootUri: session.rootUri,
        documentId: session.documentId,
        resourcePath: session.sessionKind === 'resource' ? resourceWatchPath(session) : '',
        eventSource,
        sessions: new Map(),
      };
      eventSource.addEventListener('change', (event) => {
        handleLocalFolderEventPayloadForSessions(channel, parseLocalFolderEventPayload(event));
      });
      eventSource.onerror = () => {
        console.warn('mnote local folder 外部更新事件流中断，将等待浏览器自动重连');
      };
      localFolderEventRegistry.set(channelKey, channel);
    }
    channel.sessions.set(session.key, session);
    session.localFolderChannel = channel;
  };

  const readTreePayloadData = (payload) => (
    payload && typeof payload === 'object'
      ? (payload.data || payload.delta || payload)
      : null
  );

  const readTreePayloadOverview = (payload) => (
    payload && typeof payload === 'object' && payload.overview && typeof payload.overview === 'object'
      ? payload.overview
      : null
  );

  const readTreePayloadCursor = (payload) => {
    const raw = String(payload?.cursor || payload?.revision || '').trim();
    if (!raw) return { id: '', createdAt: '', raw: '' };
    try {
      const parsed = JSON.parse(raw);
      if (parsed && typeof parsed === 'object') {
        return {
          id: String(parsed.id || parsed.commandId || parsed.command_id || '').trim(),
          createdAt: String(parsed.createdAt || parsed.created_at || '').trim(),
          raw,
        };
      }
    } catch (_) {}
    return { id: raw, createdAt: '', raw };
  };

  const treeRecordMatchesPayloadCursor = (record, payload) => {
    if (!record || typeof record !== 'object') return false;
    const cursor = readTreePayloadCursor(payload);
    if (!cursor.id && !cursor.createdAt && !cursor.raw) return false;
    const ids = [
      record.id,
      record._id,
      record.command_log_id,
      record.commandLogId,
      record.domain_event_id,
      record.domainEventId,
      record.command_id,
      record.commandId,
    ].map((value) => String(value || '').trim()).filter(Boolean);
    if (cursor.id && ids.includes(cursor.id)) return true;
    const createdAt = String(record.created_at || record.createdAt || '').trim();
    return Boolean(cursor.createdAt && createdAt && cursor.createdAt === createdAt);
  };

  const treeRecordTargetsDocument = (record, documentId) => {
    if (!record || typeof record !== 'object' || !documentId) return false;
    const targetPageId = String(record.target_page_id || record.targetPageId || '').trim();
    const aggregateId = String(record.aggregate_id || record.aggregateId || '').trim();
    if (targetPageId === documentId || aggregateId === documentId) return true;
    const payload = record.payload && typeof record.payload === 'object' ? record.payload : null;
    if (!payload) return false;
    const streamDelta = payload.streamDelta || payload.stream_delta || null;
    const deltaDocumentId = streamDelta && typeof streamDelta === 'object'
      ? String(streamDelta.documentId || streamDelta.pageId || streamDelta.document_id || streamDelta.page_id || '').trim()
      : '';
    return deltaDocumentId === documentId;
  };

  const collectMindmapIdsFromTreeRecord = (record, documentId, out) => {
    if (!record || typeof record !== 'object' || !documentId) return;
    if (!treeRecordTargetsDocument(record, documentId)) return;
    const targetBlockId = String(record.target_block_id || record.targetBlockId || '').trim();
    if (targetBlockId) out.add(targetBlockId);
    const aggregateType = String(record.aggregate_type || record.aggregateType || '').trim();
    const aggregateId = String(record.aggregate_id || record.aggregateId || '').trim();
    if (aggregateType === 'block' && aggregateId) out.add(aggregateId);
    const payload = record.payload && typeof record.payload === 'object' ? record.payload : null;
    const streamDelta = payload && typeof payload === 'object' ? (payload.streamDelta || payload.stream_delta || null) : null;
    const blockId = streamDelta && typeof streamDelta === 'object'
      ? String(streamDelta.blockId || streamDelta.block_id || '').trim()
      : '';
    if (blockId) out.add(blockId);
  };

  const collectMindmapIdsFromTreePayload = (payload, session) => {
    const ids = new Set();
    if (!payload || typeof payload !== 'object' || !session?.documentId) return [];
    const kind = String(payload.kind || '').trim();
    const data = readTreePayloadData(payload);
    if (kind === 'delta' && data && typeof data === 'object') {
      const documentId = String(data.documentId || data.pageId || data.document_id || data.page_id || '').trim();
      if (!documentId || documentId === session.documentId) {
        const blockId = String(data.blockId || data.block_id || '').trim();
        if (blockId) ids.add(blockId);
        const streamDelta = data.streamDelta || data.stream_delta || null;
        const streamBlockId = streamDelta && typeof streamDelta === 'object'
          ? String(streamDelta.blockId || streamDelta.block_id || '').trim()
          : '';
        if (streamBlockId) ids.add(streamBlockId);
      }
    }
    if (kind === 'resync') {
      const overview = readTreePayloadOverview(payload);
      if (overview) {
        const commandLogs = Array.isArray(overview.command_logs) ? overview.command_logs : Array.isArray(overview.commandLogs) ? overview.commandLogs : [];
        const domainEvents = Array.isArray(overview.domain_events) ? overview.domain_events : Array.isArray(overview.domainEvents) ? overview.domainEvents : [];
        commandLogs
          .filter((record) => treeRecordMatchesPayloadCursor(record, payload))
          .forEach((record) => collectMindmapIdsFromTreeRecord(record, session.documentId, ids));
        domainEvents
          .filter((record) => treeRecordMatchesPayloadCursor(record, payload))
          .forEach((record) => collectMindmapIdsFromTreeRecord(record, session.documentId, ids));
      }
    }
    return Array.from(ids);
  };

  const refreshMindmapRuntimesFromTreePayload = (payload, session) => {
    const registry = window.__MNOTE_LEPTOS_MINDMAP_BRIDGES__ || {};
    collectMindmapIdsFromTreePayload(payload, session).forEach((mindmapId) => {
      const bridge = registry[mindmapId];
      if (bridge && typeof bridge.refreshProjection === 'function') {
        void bridge.refreshProjection('mnote-web-tree-live');
      }
    });
  };

  const treePayloadTargetsDocument = (payload, session) => {
    if (!payload || typeof payload !== 'object' || !session?.documentId) return false;
    if (payload.workspaceId && session.workspaceId && String(payload.workspaceId) !== String(session.workspaceId)) return false;
    const data = readTreePayloadData(payload);
    if (data && typeof data === 'object') {
      const documentId = String(data.documentId || data.pageId || data.document_id || data.page_id || '').trim();
      if (documentId === session.documentId) return true;
      const documents = Array.isArray(data.upsertDocuments) ? data.upsertDocuments : Array.isArray(data.upsert_documents) ? data.upsert_documents : [];
      if (documents.some((item) => String(item?.id || item?.documentId || '').trim() === session.documentId)) return true;
    }
    const overview = readTreePayloadOverview(payload);
    if (!overview) return false;
    const commandLogs = Array.isArray(overview.command_logs) ? overview.command_logs : Array.isArray(overview.commandLogs) ? overview.commandLogs : [];
    const domainEvents = Array.isArray(overview.domain_events) ? overview.domain_events : Array.isArray(overview.domainEvents) ? overview.domainEvents : [];
    return commandLogs.some((record) => treeRecordTargetsDocument(record, session.documentId))
      || domainEvents.some((record) => treeRecordTargetsDocument(record, session.documentId));
  };

  const handleTreeExternalChange = (event) => {
    const payload = event?.detail?.payload || event?.detail || null;
      if (!payload) return;
    Array.from(documentSessionRegistry.values()).forEach((session) => {
      if (session.sourceKind === 'local_folder') return;
      if (!treePayloadTargetsDocument(payload, session)) return;
      refreshMindmapRuntimesFromTreePayload(payload, session);
      session.lastExternalChangeSignalAt = Date.now();
      session.externalChangePending = true;
      if (session.hasExternalConflict || sessionHasRecentLocalInput(session) || session.dirty || session.saveTimer || session.saving) {
        markSessionExternalConflict(session, treeExternalConflictMessage);
        return;
      }
      scheduleSessionExternalRefresh(session, 'mnote-web-tree-live');
    });
  };

  const sessionMatchesDocumentWorkspace = (session, documentId, workspaceId) => {
    if (!session) return false;
    const doc = String(documentId || '').trim();
    const workspace = String(workspaceId || '').trim();
    if (doc && session.documentId !== doc) return false;
    if (workspace && session.workspaceId && session.workspaceId !== workspace) return false;
    return true;
  };

  const refreshDocumentSessionsFromExternalWrite = (detail, source) => {
    const documentId = String(detail?.documentId || '').trim();
    const workspaceId = String(detail?.workspaceId || '').trim();
    let scheduled = 0;
    Array.from(documentSessionRegistry.values()).forEach((session) => {
      if (!sessionMatchesDocumentWorkspace(session, documentId, workspaceId)) return;
      session.lastExternalChangeSignalAt = Date.now();
      session.externalChangePending = true;
      session.lastExternalWriteSource = source || session.lastExternalWriteSource || '';
      session.lastExternalWriteRunId = String(detail?.runId || detail?.traceId || detail?.toolCallId || '').trim();
      session.lastExternalWriteDocumentId = documentId;
      if (session.hasExternalConflict || sessionHasRecentLocalInput(session) || session.dirty || session.saveTimer || session.saving) {
        markSessionExternalConflict(session, conflictMessageFromEnvelope(session, session.lastExternalConflictEnvelope) || treeExternalConflictMessage);
        return;
      }
      scheduled += 1;
      scheduleSessionExternalRefresh(session, source || 'mnote-web-external-write');
    });
    return scheduled;
  };

  const applyLocalUploadEditorSave = (detail) => {
    const documentId = String(detail?.documentId || '').trim();
    const rootUri = String(detail?.rootUri || '').trim();
    const fileVersion = String(detail?.fileVersion || '').trim();
    const writeIntentId = String(detail?.writeIntentId || '').trim();
    const saveOperationId = String(detail?.saveOperationId || '').trim();
    const serialized = String(detail?.serialized || '');
    if (!documentId || !rootUri || !fileVersion) return 0;
    let applied = 0;
    Array.from(documentSessionRegistry.values()).forEach((session) => {
      if (session.sourceKind !== 'local_folder') return;
      if (session.documentId !== documentId) return;
      if (String(session.rootUri || '').trim() !== rootUri) return;
      if (session.saveTimer) {
        window.clearTimeout(session.saveTimer);
        session.saveTimer = 0;
      }
      session.conflictDetectionKey = fileVersion;
      session.fileVersion = fileVersion;
      session.lastExternalConflictDetectionKey = fileVersion;
      if (writeIntentId) session.lastWriteIntentId = writeIntentId;
      if (saveOperationId) session.lastSaveOperationId = saveOperationId;
      if (session.latestAggregate && typeof session.latestAggregate === 'object') {
        const nextAggregate = { ...session.latestAggregate };
        const nextBody = nextAggregate.body && typeof nextAggregate.body === 'object'
          ? { ...nextAggregate.body }
          : {};
        nextBody.fileVersion = fileVersion;
        nextBody.conflictDetectionKey = fileVersion;
        nextBody.conflict_detection_key = fileVersion;
        nextAggregate.body = nextBody;
        session.latestAggregate = nextAggregate;
        syncPageAggregateScript(session, nextAggregate);
      }
      session.externalChangePending = false;
      session.hasExternalConflict = false;
      session.lastExternalConflictEnvelope = null;
      session.bufferDirtyState = 'Clean';
      if (!serialized || serialized === session.currentSerialized) {
        session.lastPersistedSerialized = session.currentSerialized;
        session.dirty = false;
        setSessionStatus(session, 'saved');
      } else {
        session.dirty = true;
        setSessionStatus(session, 'dirty');
        queueSessionSave(session);
      }
      syncResourceSessionTabGuards(session);
      applied += 1;
    });
    return applied;
  };

  window.addEventListener('tree:delta', handleTreeExternalChange);
  window.addEventListener('tree:resync', handleTreeExternalChange);
  window.addEventListener('mnote:page-ai-tool-write-completed', (event) => {
    refreshDocumentSessionsFromExternalWrite(event?.detail || {}, 'mnote-hermes-tool');
  });
  window.addEventListener('mnote:local-upload-editor-save-completed', (event) => {
    applyLocalUploadEditorSave(event?.detail || {});
  });

  const createDocumentSession = (runtimeDescriptor) => {
    const pageBody = runtimeDescriptor.aggregate.body || {};
    const permissions = runtimeDescriptor.aggregate.head?.permissions || {};
    const conflictDetectionKey = conflictDetectionKeyFromBody(pageBody);
    const pageBodyRevision = Number.isInteger(pageBody.revision) ? pageBody.revision : null;
    const keyRevision = revisionFromConflictKey(conflictDetectionKey);
    const sourceKind = normalizeSessionSourceKind(runtimeDescriptor.bootstrap);
    const tiptapDocument = pageBodyTiptapDocument(pageBody, '', runtimeDescriptor.bootstrap);
    const pageBodyMeta = pageBodySourceMeta(sourceKind, pageBody);
    const session = {
      key: buildDocumentSessionKey(runtimeDescriptor.bootstrap),
      documentId: runtimeDescriptor.bootstrap.documentId,
      workspaceId: runtimeDescriptor.bootstrap.workspaceId,
      sourceKind,
      rootUri: runtimeDescriptor.bootstrap.rootUri,
      saveEndpoint: runtimeDescriptor.bootstrap.saveEndpoint || '/api/documents/save',
      pageAggregateScriptId: runtimeDescriptor.bootstrap.pageAggregateScriptId || '__MNOTE_PAGE_AGGREGATE__',
      latestAggregate: runtimeDescriptor.aggregate,
      title: runtimeDescriptor.aggregate.head?.title || '无标题',
      pageBodySource: pageBodyMeta.pageBodySource,
      projectionSource: pageBodyMeta.projectionSource,
      blockProjectionVersion: pageBodyMeta.blockProjectionVersion,
      pageBodyLocalCompatFallback: pageBodyMeta.localCompatFallback,
      pageBodyHardGuard: pageBodyMeta.hardGuard,
      currentTiptapDocument: tiptapDocument,
      currentSerialized: JSON.stringify(tiptapDocument),
      lastPersistedSerialized: JSON.stringify(tiptapDocument),
      revision: pageBodyRevision && pageBodyRevision > 0 ? pageBodyRevision : keyRevision,
      conflictDetectionKey,
      fileVersion: typeof pageBody.fileVersion === 'string' ? pageBody.fileVersion : conflictDetectionKey,
      lastExternalConflictDetectionKey: conflictDetectionKey || '',
      relativePath: localMarkdownRelativePathFromDocumentId(runtimeDescriptor.bootstrap.documentId),
      bufferDirtyState: 'Clean',
      readOnly: Boolean(permissions.readOnly),
      dirty: false,
      saving: false,
      hasExternalConflict: false,
      externalChangePending: false,
      externalRefreshSource: '',
      lastExternalChangeSignalAt: 0,
      lastSelfSaveSignalAt: 0,
      lastUserInputAt: 0,
      status: 'booting',
      error: null,
      saveTimer: 0,
      externalRefreshTimer: 0,
      releaseTimer: 0,
      views: new Map(),
      localFolderChannel: null,
    };
    ensureLocalFolderEventChannel(session);
    return session;
  };

  const getOrCreateDocumentSession = (runtimeDescriptor) => {
    const key = buildDocumentSessionKey(runtimeDescriptor.bootstrap);
    const existing = documentSessionRegistry.get(key);
    if (existing) {
      cancelDocumentSessionRelease(existing);
      return existing;
    }
    const session = createDocumentSession(runtimeDescriptor);
    documentSessionRegistry.set(key, session);
    return session;
  };


  return {
    cancelDocumentSessionRelease,
    broadcastSessionContent,
    clearEmbeddedLocalDraft,
    clearSessionConflictSurface,
    currentEditorText,
    dispatchSessionContentToView,
    documentSessionRegistry,
    externalConflictMessage,
    getOrCreateDocumentSession,
    markSessionExternalConflict,
    queueSessionSave,
    refreshSessionFromExternalChange,
    ensureLocalFolderEventChannel,
    releaseDocumentSession,
    scheduleDocumentSessionRelease,
    sessionHasRecentExternalSignal,
    sessionHasRecentLocalInput,
    sessionMatchesDocumentWorkspace,
    sessionViews,
    setSessionStatus,
  };
};
