export function createSidebarPageSettingsRuntime(context) {
  const {
    currentDocumentId,
    currentPageAggregate,
    currentRootUri,
    currentSourceKind,
    currentWorkspaceSourcePayload,
    escapeHtml,
    pageUiState,
    resolveWorkspaceId,
    searchText,
  } = context;
  const MNOTE_GLOBAL_SHOW_HEADING_NUMBERS_KEY = context.globalShowHeadingNumbersKey || 'mnote.global.showHeadingNumbers';
  const PAGE_WIDTH_TYPES = ['default', 'markdown', 'word', 'pdf', 'excel', 'ppt', 'mindmap'];
  const PAGE_WIDTH_LABELS = {
    default: '默认',
    markdown: 'Markdown',
    word: 'Word',
    pdf: 'PDF',
    excel: 'Excel',
    ppt: 'PPT',
    mindmap: 'Mindmap'
  };
  const DEFAULT_PAGE_WIDTH_PREFERENCES = {
    default: { mode: 'comfortable', resolvedMode: 'comfortable', cssMaxWidth: '980px', source: 'system' },
    markdown: { mode: 'readable', resolvedMode: 'readable', cssMaxWidth: '760px', source: 'system' },
    word: { mode: 'wide', resolvedMode: 'wide', cssMaxWidth: '1180px', source: 'system' },
    pdf: { mode: 'wide', resolvedMode: 'wide', cssMaxWidth: '1180px', source: 'system' },
    excel: { mode: 'full', resolvedMode: 'full', cssMaxWidth: 'none', source: 'system' },
    ppt: { mode: 'wide', resolvedMode: 'wide', cssMaxWidth: '1180px', source: 'system' },
    mindmap: { mode: 'full', resolvedMode: 'full', cssMaxWidth: 'none', source: 'system' }
  };
  var knowledgeRagPipelineEventSource = null;
  var knowledgeRagPipelineWatcherScopeKey = '';
  var knowledgeRagPipelineWatcherRefreshPending = false;

  function defaultPageOptions() {
    return {
      wideLayout: false,
      smallText: false,
      layoutDensity: 'normal',
      showHeadingNumbers: false,
      showToc: false,
      showStructure: false,
      protectEditing: false,
      showWordCount: true,
      collapseBacklinks: false,
      pageFont: 'default',
      hideChildPages: false,
      showBlockRefCount: false,
      hideTitleHeader: false,
      embedDefaultBlockId: null
    };
  }

  function currentPageOptions() {
    if (pageUiState.pageOptions) return pageUiState.pageOptions;
    var aggregate = currentPageAggregate();
    var current = aggregate && aggregate.layout && aggregate.layout.pageOptions && typeof aggregate.layout.pageOptions === 'object'
      ? aggregate.layout.pageOptions
      : null;
    if (!current) {
      return defaultPageOptions();
    }
    pageUiState.pageOptions = Object.assign(defaultPageOptions(), current);
    return pageUiState.pageOptions;
  }

  function currentPageWidthPreferences() {
    return Object.assign({}, DEFAULT_PAGE_WIDTH_PREFERENCES, pageUiState.pageWidthPreferences || {});
  }

  function currentKnowledgeRagSummary() {
    return pageUiState.knowledgeRagSummary || {};
  }

  function pageWidthModeLabel(mode) {
    if (mode === 'inherit') return '继承默认';
    if (mode === 'readable') return '阅读';
    if (mode === 'comfortable') return '舒适';
    if (mode === 'wide') return '宽版';
    if (mode === 'full') return '全宽';
    return '舒适';
  }

  function normalizePageWidthPreferences(value) {
    var input = value && typeof value === 'object' ? value : {};
    var output = {};
    PAGE_WIDTH_TYPES.forEach(function(type) {
      var base = DEFAULT_PAGE_WIDTH_PREFERENCES[type];
      var current = input[type] && typeof input[type] === 'object' ? input[type] : {};
      var mode = String(current.mode || base.mode || 'comfortable');
      var resolvedMode = String(current.resolvedMode || current.resolved_mode || (mode === 'inherit' ? (input.default && input.default.resolvedMode) || 'comfortable' : mode));
      output[type] = {
        mode: mode,
        custom: current.custom || null,
        resolvedMode: resolvedMode,
        resolvedCustom: current.resolvedCustom || current.resolved_custom || null,
        cssMaxWidth: String(current.cssMaxWidth || current.css_max_width || pageWidthCssMaxWidth(resolvedMode)),
        source: String(current.source || base.source || 'system')
      };
    });
    return output;
  }

  function pageWidthCssMaxWidth(mode) {
    if (mode === 'readable') return '760px';
    if (mode === 'comfortable') return '980px';
    if (mode === 'wide') return '1180px';
    if (mode === 'full') return 'none';
    return '980px';
  }

  function markdownPageWidthPreference(options) {
    var preferences = currentPageWidthPreferences();
    var markdown = preferences.markdown || DEFAULT_PAGE_WIDTH_PREFERENCES.markdown;
    if (markdown.source === 'system' && options && options.wideLayout) {
      return {
        mode: 'comfortable',
        resolvedMode: 'comfortable',
        cssMaxWidth: '980px',
        source: 'wideLayout'
      };
    }
    return markdown;
  }

  function activeResourceWidthType() {
    var raw = document.documentElement.getAttribute('data-mnote-active-resource-width-type') || '';
    var type = String(raw).trim();
    if (type === 'sheet') return 'excel';
    if (PAGE_WIDTH_TYPES.indexOf(type) >= 0 && type !== 'default') return type;
    return '';
  }

  function activeResourceWidthPreference(options) {
    var type = activeResourceWidthType();
    if (!type || type === 'markdown') return markdownPageWidthPreference(options);
    var preferences = currentPageWidthPreferences();
    return preferences[type] || DEFAULT_PAGE_WIDTH_PREFERENCES[type] || DEFAULT_PAGE_WIDTH_PREFERENCES.default;
  }

  function notifyPageWidthPreferenceChanged(type) {
    var detail = {
      type: String(type || ''),
      preferences: currentPageWidthPreferences()
    };
    window.dispatchEvent(new CustomEvent('mnote:page-width-preference-changed', { detail: detail }));
    document.querySelectorAll('iframe.mnote-resource-tab-frame').forEach(function(frame) {
      if (!(frame instanceof HTMLIFrameElement) || !frame.contentWindow) return;
      try {
        frame.contentWindow.postMessage({
          type: 'mnote:page-width-preference-changed',
          contentType: detail.type
        }, window.location.origin);
      } catch (_) {}
    });
  }

  function readGlobalShowHeadingNumbers() {
    return Boolean(currentPageOptions().showHeadingNumbers);
  }

  function writeGlobalShowHeadingNumbers(value) {
    void persistPageOptionsPatch({ showHeadingNumbers: Boolean(value) });
  }

  function effectiveShowHeadingNumbers(options) {
    return Boolean(options && options.showHeadingNumbers);
  }

  function pageOptionIsSupported(name) {
    return name === 'wideLayout'
      || name === 'smallText'
      || name === 'layoutDensity'
      || name === 'pageFont'
      || name === 'showHeadingNumbers'
      || (name === 'hideTitleHeader' && currentSourceKind() === 'local_folder');
  }

  function pageOptionDescription(name) {
    if (name === 'wideLayout') return '自适应宽度';
    if (name === 'smallText') return '小字体';
    if (name === 'hideTitleHeader') return '隐藏本地 Markdown 文件标题';
    if (name === 'showToc') return '标题目录';
    if (name === 'showHeadingNumbers') return '标题自动编号';
    if (name === 'protectEditing') return '编辑保护';
    if (name === 'collapseBacklinks') return '折叠反向引用';
    if (name === 'hideChildPages') return '隐藏子页面';
    if (name === 'showBlockRefCount') return '显示块引用数字';
    return name;
  }

  function pageOptionHint(name) {
    if (name === 'wideLayout') return '已接通：主内容列宽度立即变化';
    if (name === 'smallText') return '已接通：正文排版会更紧凑';
    if (name === 'showHeadingNumbers') return '已接通：标题前显示顺序编号';
    if (name === 'hideTitleHeader') return '已接通：本地 Markdown 页头标题立即隐藏或显示';
    if (name === 'layoutDensity') return '已接通：段落与列表间距会变化';
    if (name === 'pageFont') return '已接通：当前页面字体会切换';
    if (name === 'showToc') return '待接线：当前 Rust 壳还没有正式目录面板';
    if (name === 'protectEditing') return '待接线：当前主编辑器只显示降级说明';
    if (name === 'collapseBacklinks') return '待接线：当前 Rust 壳未挂回链面板';
    if (name === 'hideChildPages') return '待接线：当前页面壳还没有子页面块显隐';
    if (name === 'showBlockRefCount') return '待接线：当前页面壳未显示块引用计数';
    return '待接线';
  }

  function ensureHistorySnapshotsSeeded() {
    if (pageUiState.historySnapshots.length > 0) return;
    var aggregate = currentPageAggregate();
    var stats = aggregate && aggregate.stats && typeof aggregate.stats === 'object' ? aggregate.stats : {};
    pageUiState.historySnapshots = [{
      id: 'snapshot-initial',
      timestamp: Date.now(),
      stats: {
        wordCount: Number(stats.wordCount || 0),
        characterCount: Number(stats.characterCount || 0),
        blockCount: Number(stats.blockCount || 0),
        todoTotal: Number(stats.todoTotal || 0),
        todoDone: Number(stats.todoDone || 0)
      }
    }];
  }

  function computeLivePageStats() {
    var aggregate = currentPageAggregate();
    var fallbackStats = aggregate && aggregate.stats && typeof aggregate.stats === 'object' ? aggregate.stats : {};
    var editor = document.querySelector('[data-testid="mnote-leptos-tiptap-island-editor-root"] .editor-surface .ProseMirror');
    if (!(editor instanceof HTMLElement)) {
      return {
        wordCount: Number(fallbackStats.wordCount || 0),
        characterCount: Number(fallbackStats.characterCount || 0),
        blockCount: Number(fallbackStats.blockCount || 0),
        todoTotal: Number(fallbackStats.todoTotal || 0),
        todoDone: Number(fallbackStats.todoDone || 0)
      };
    }
    var text = (editor.textContent || '').trim();
    var compact = text.replace(/\s+/g, ' ').trim();
    var wordCount = compact ? compact.split(' ').filter(Boolean).length : 0;
    var characterCount = text.replace(/\s/g, '').length;
    var blockCount = editor.querySelectorAll(':scope > *').length;
    var todos = Array.from(editor.querySelectorAll('input[type="checkbox"]'));
    return {
      wordCount: wordCount || Number(fallbackStats.wordCount || 0),
      characterCount: characterCount || Number(fallbackStats.characterCount || 0),
      blockCount: blockCount || Number(fallbackStats.blockCount || 0),
      todoTotal: todos.length || Number(fallbackStats.todoTotal || 0),
      todoDone: todos.filter(function(node){ return node.checked; }).length || Number(fallbackStats.todoDone || 0)
    };
  }

  function recordPageHistorySnapshot(reason, stats) {
    ensureHistorySnapshotsSeeded();
    pageUiState.historySnapshots = [{
      id: 'snapshot-' + Date.now(),
      timestamp: Date.now(),
      reason: reason || 'save',
      stats: stats || computeLivePageStats()
    }].concat(pageUiState.historySnapshots).slice(0, 15);
    renderPageHistoryDrawer();
  }

  function applyPageOptionsToShell() {
    var options = currentPageOptions();
    var globalShowHeadingNumbers = Boolean(options.showHeadingNumbers);
    var showHeadingNumbers = effectiveShowHeadingNumbers(options);
    var shell = document.querySelector('.document-shell');
    var editorRoot = document.querySelector('[data-testid="mnote-leptos-tiptap-island-editor-root"]');
    var editorSurface = editorRoot && editorRoot.querySelector('.editor-surface');
    var mindmapWidthPreference = currentPageWidthPreferences().mindmap || DEFAULT_PAGE_WIDTH_PREFERENCES.mindmap;
    var mindmapMaxWidth = String(mindmapWidthPreference.cssMaxWidth || pageWidthCssMaxWidth(mindmapWidthPreference.resolvedMode));
    var mindmapMaxWidthValue = mindmapMaxWidth === 'none' ? 'none' : mindmapMaxWidth;
    if (shell instanceof HTMLElement) {
      var widthPreference = activeResourceWidthPreference(options);
      var cssMaxWidth = String(widthPreference.cssMaxWidth || pageWidthCssMaxWidth(widthPreference.resolvedMode));
      shell.setAttribute('data-page-wide-layout', String(Boolean(options.wideLayout)));
      shell.setAttribute('data-page-width-content-type', activeResourceWidthType() || 'markdown');
      shell.setAttribute('data-page-width-mode', String(widthPreference.mode || 'readable'));
      shell.setAttribute('data-page-width-resolved-mode', String(widthPreference.resolvedMode || widthPreference.mode || 'readable'));
      shell.setAttribute('data-page-width-source', String(widthPreference.source || 'system'));
      shell.setAttribute('data-page-small-text', String(Boolean(options.smallText)));
      shell.setAttribute('data-layout-density', String(options.layoutDensity || 'normal'));
      shell.setAttribute('data-page-font', String(options.pageFont || 'default'));
      shell.setAttribute('data-page-show-heading-numbers', String(showHeadingNumbers));
      shell.style.width = '100%';
      shell.style.maxWidth = cssMaxWidth === 'none' ? 'none' : cssMaxWidth;
      shell.style.setProperty('--mnote-mindmap-block-max-width', mindmapMaxWidthValue);
    }
    if (editorRoot instanceof HTMLElement) {
      editorRoot.setAttribute('data-page-wide-layout', String(Boolean(options.wideLayout)));
      editorRoot.setAttribute('data-page-small-text', String(Boolean(options.smallText)));
      editorRoot.setAttribute('data-page-show-heading-numbers', String(showHeadingNumbers));
      editorRoot.setAttribute('data-page-embed-default-block-id', options.embedDefaultBlockId == null ? '' : String(options.embedDefaultBlockId));
      editorRoot.style.setProperty('--mnote-mindmap-block-max-width', mindmapMaxWidthValue);
    }
    if (editorSurface instanceof HTMLElement) {
      editorSurface.setAttribute('data-layout-density', String(options.layoutDensity || 'normal'));
      editorSurface.setAttribute('data-page-font', String(options.pageFont || 'default'));
      editorSurface.setAttribute('data-show-heading-numbers', String(showHeadingNumbers));
    }
    document.documentElement.setAttribute('data-page-wide-layout', String(Boolean(options.wideLayout)));
    var rootWidthPreference = activeResourceWidthPreference(options);
    document.documentElement.setAttribute('data-page-width-content-type', activeResourceWidthType() || 'markdown');
    document.documentElement.setAttribute('data-page-width-mode', String(rootWidthPreference.mode || 'readable'));
    document.documentElement.setAttribute('data-page-width-resolved-mode', String(rootWidthPreference.resolvedMode || rootWidthPreference.mode || 'readable'));
    document.documentElement.setAttribute('data-page-small-text', String(Boolean(options.smallText)));
    document.documentElement.setAttribute('data-layout-density', String(options.layoutDensity || 'normal'));
    document.documentElement.setAttribute('data-page-font', String(options.pageFont || 'default'));
    document.documentElement.setAttribute('data-global-show-heading-numbers', String(globalShowHeadingNumbers));
    document.documentElement.setAttribute('data-page-show-heading-numbers', String(showHeadingNumbers));
    document.documentElement.setAttribute('data-page-hide-title-header', String(Boolean(options.hideTitleHeader)));
    document.documentElement.style.setProperty('--mnote-mindmap-block-max-width', mindmapMaxWidthValue);
    var titleHeader = document.querySelector('.document-pane[data-pane-role="primary"] .document-shell-header') || document.querySelector('.document-shell-header');
    if (titleHeader instanceof HTMLElement) {
      var hidden = Boolean(options.hideTitleHeader);
      titleHeader.hidden = hidden;
      titleHeader.setAttribute('data-page-title-hidden', String(hidden));
    }
  }

  function updatePageSettingsTriggerState() {
    var trigger = document.querySelector('[data-testid="wolai-page-settings-trigger"]');
    if (!(trigger instanceof HTMLElement)) return;
    trigger.setAttribute('data-state', pageUiState.pageSettingsOpen ? 'open' : 'closed');
    trigger.setAttribute('aria-expanded', pageUiState.pageSettingsOpen ? 'true' : 'false');
  }

  function updateStandaloneSettingsTriggerState() {
    var indexTrigger = document.querySelector('[data-testid="mnote-local-index-settings-toggle"]');
    var indexOpen = isLocalIndexSettingsOpen();
    if (indexTrigger instanceof HTMLElement) {
      indexTrigger.setAttribute('data-state', indexOpen ? 'open' : 'closed');
      indexTrigger.setAttribute('aria-expanded', indexOpen ? 'true' : 'false');
    }
    var ragTrigger = document.querySelector('[data-testid="mnote-knowledge-rag-settings-toggle"]');
    var ragOpen = isKnowledgeRagSettingsOpen();
    if (ragTrigger instanceof HTMLElement) {
      ragTrigger.setAttribute('data-state', ragOpen ? 'open' : 'closed');
      ragTrigger.setAttribute('aria-expanded', ragOpen ? 'true' : 'false');
    }
  }

  function createPageOptionRow(key, type) {
    var inputType = type || 'checkbox';
    var supported = pageOptionIsSupported(key);
    if (inputType === 'checkbox') {
      return '' +
        '<label class="wolai-page-setting-row' + (supported ? '' : ' is-pending') + '" data-page-setting-row="' + key + '">' +
          '<span class="wolai-page-setting-copy">' +
            '<span class="wolai-page-setting-label">' + escapeHtml(pageOptionDescription(key)) + '</span>' +
            '<span class="wolai-page-setting-hint">' + escapeHtml(pageOptionHint(key)) + '</span>' +
          '</span>' +
          '<input type="checkbox" class="wolai-page-setting-checkbox" data-page-option-checkbox="' + key + '"' + (supported ? '' : ' data-setting-pending="true"') + ' />' +
        '</label>';
    }
    return '' +
      '<label class="wolai-page-setting-row" data-page-setting-row="' + key + '">' +
        '<span class="wolai-page-setting-copy">' +
          '<span class="wolai-page-setting-label">' + escapeHtml(pageOptionDescription(key)) + '</span>' +
          '<span class="wolai-page-setting-hint">' + escapeHtml(pageOptionHint(key)) + '</span>' +
        '</span>' +
        '<select class="wolai-page-setting-select" data-page-option-select="' + key + '">' +
          '<option value="compact">紧凑</option>' +
          '<option value="normal">默认</option>' +
          '<option value="spacious">宽松</option>' +
        '</select>' +
      '</label>';
  }

  function createGlobalHeadingNumbersRow() {
    return '' +
      '<label class="wolai-page-setting-row" data-page-setting-row="globalShowHeadingNumbers">' +
        '<span class="wolai-page-setting-copy">' +
          '<span class="wolai-page-setting-label">标题自动编号</span>' +
        '</span>' +
        '<input type="checkbox" class="wolai-page-setting-checkbox" data-global-option-checkbox="showHeadingNumbers" />' +
      '</label>';
  }

  function createPageWidthSelectRow(type) {
    var options = type === 'default'
      ? [
          ['readable', '阅读'],
          ['comfortable', '舒适'],
          ['wide', '宽版'],
          ['full', '全宽']
        ]
      : [
          ['inherit', '继承默认'],
          ['readable', '阅读'],
          ['comfortable', '舒适'],
          ['wide', '宽版'],
          ['full', '全宽']
        ];
    return '' +
      '<label class="wolai-page-setting-row" data-page-width-row="' + type + '">' +
        '<span class="wolai-page-setting-copy">' +
          '<span class="wolai-page-setting-label">' + escapeHtml(PAGE_WIDTH_LABELS[type] || type) + '</span>' +
          '<span class="wolai-page-setting-hint" data-page-width-hint="' + type + '"></span>' +
        '</span>' +
        '<select class="wolai-page-setting-select" data-page-width-select="' + type + '">' +
          options.map(function(option) {
            return '<option value="' + option[0] + '">' + option[1] + '</option>';
          }).join('') +
        '</select>' +
      '</label>';
  }

  function createPageWidthRows() {
    return '' +
      '<div class="wolai-page-settings-global-note">页面宽度</div>' +
      PAGE_WIDTH_TYPES.map(createPageWidthSelectRow).join('');
  }

  function renderGlobalOptions(popover) {
    var globalHeadingNumbers = readGlobalShowHeadingNumbers();
    popover.querySelectorAll('[data-global-option-checkbox="showHeadingNumbers"]').forEach(function(input) {
      input.checked = globalHeadingNumbers;
    });
    var preferences = currentPageWidthPreferences();
    popover.querySelectorAll('[data-page-width-select]').forEach(function(select) {
      var type = select.getAttribute('data-page-width-select') || '';
      var preference = preferences[type] || DEFAULT_PAGE_WIDTH_PREFERENCES[type] || DEFAULT_PAGE_WIDTH_PREFERENCES.default;
      select.value = String(preference.mode || 'comfortable');
    });
    popover.querySelectorAll('[data-page-width-hint]').forEach(function(node) {
      var type = node.getAttribute('data-page-width-hint') || '';
      var preference = preferences[type] || DEFAULT_PAGE_WIDTH_PREFERENCES[type] || DEFAULT_PAGE_WIDTH_PREFERENCES.default;
      var resolvedMode = String(preference.resolvedMode || preference.mode || 'comfortable');
      node.textContent = pageWidthModeLabel(resolvedMode) + ' · ' + String(preference.cssMaxWidth || pageWidthCssMaxWidth(resolvedMode));
    });
  }

  function markRetiredLocalOcrPreferenceSurface() {
    document.documentElement.setAttribute('data-mnote-local-ocr-auto-retired', 'true');
  }

  function createPageFontRow() {
    return '' +
      '<label class="wolai-page-setting-row" data-page-setting-row="pageFont">' +
        '<span class="wolai-page-setting-copy">' +
          '<span class="wolai-page-setting-label">页面字体</span>' +
          '<span class="wolai-page-setting-hint">已接通：仅对当前页面生效</span>' +
        '</span>' +
        '<select class="wolai-page-setting-select" data-page-option-select="pageFont">' +
          '<option value="default">默认</option>' +
          '<option value="song">宋体</option>' +
          '<option value="kai">楷体</option>' +
        '</select>' +
      '</label>';
  }

  function ensurePageHistoryDrawer() {
    var existing = document.querySelector('[data-testid="wolai-page-history-drawer"]');
    if (existing instanceof HTMLElement) return existing;
    var drawer = document.createElement('aside');
    drawer.className = 'wolai-page-history-drawer';
    drawer.setAttribute('data-testid', 'wolai-page-history-drawer');
    drawer.setAttribute('data-mnote-surface', 'page-history');
    drawer.hidden = true;
    drawer.innerHTML = '' +
      '<div class="wolai-page-history-panel">' +
        '<div class="wolai-page-history-header">' +
          '<div><div class="wolai-page-history-title">页面历史</div><div class="wolai-page-history-subtitle">当前会话内最近保存的 15 个快照。</div></div>' +
          '<button type="button" class="wolai-surface-close" data-page-history-action="close" aria-label="关闭页面历史">×</button>' +
        '</div>' +
        '<div class="wolai-page-history-list" data-page-history-list></div>' +
      '</div>';
    drawer.addEventListener('click', function(event) {
      if (event.target === drawer) closePageHistoryDrawer();
    });
    document.body.appendChild(drawer);
    return drawer;
  }

  function renderPageHistoryDrawer() {
    var drawer = ensurePageHistoryDrawer();
    var list = drawer.querySelector('[data-page-history-list]');
    if (!(list instanceof HTMLElement)) return;
    ensureHistorySnapshotsSeeded();
    list.innerHTML = pageUiState.historySnapshots.length
      ? pageUiState.historySnapshots.map(function(snapshot) {
          var stats = snapshot.stats || {};
          var label = new Date(snapshot.timestamp).toLocaleString('zh-CN', {
            year: 'numeric',
            month: '2-digit',
            day: '2-digit',
            hour: '2-digit',
            minute: '2-digit'
          });
          return '' +
            '<div class="wolai-page-history-item">' +
              '<div class="wolai-page-history-item-copy">' +
                '<div class="wolai-page-history-item-title">' + escapeHtml(label) + '</div>' +
                '<div class="wolai-page-history-item-meta">字数 ' + Number(stats.wordCount || 0) + ' · 字符 ' + Number(stats.characterCount || 0) + ' · 块数 ' + Number(stats.blockCount || 0) + '</div>' +
              '</div>' +
              '<button type="button" class="wolai-page-history-item-ghost" data-page-history-action="noop">仅查看</button>' +
            '</div>';
        }).join('')
      : '<div class="wolai-page-history-empty">尚未产生历史快照，编辑后会自动生成。</div>';
  }

  function openPageHistoryDrawer() {
    renderPageHistoryDrawer();
    var drawer = ensurePageHistoryDrawer();
    drawer.hidden = false;
    document.documentElement.setAttribute('data-mnote-page-history-open', 'true');
  }

  function closePageHistoryDrawer() {
    var drawer = document.querySelector('[data-testid="wolai-page-history-drawer"]');
    if (drawer instanceof HTMLElement) drawer.hidden = true;
    document.documentElement.removeAttribute('data-mnote-page-history-open');
  }

  function ensurePageShareDialog() {
    var existing = document.querySelector('[data-testid="wolai-page-share-dialog"]');
    if (existing instanceof HTMLElement) return existing;
    var dialog = document.createElement('div');
    dialog.className = 'wolai-page-share-dialog';
    dialog.setAttribute('data-testid', 'wolai-page-share-dialog');
    dialog.setAttribute('data-mnote-surface', 'page-share');
    dialog.hidden = true;
    dialog.innerHTML = '' +
      '<div class="wolai-page-share-card" role="dialog" aria-modal="true">' +
        '<div class="wolai-page-share-header">' +
          '<div><div class="wolai-page-share-title">公开分享页面</div><div class="wolai-page-share-subtitle">当前 3000 公开入口由 mnote-web 持有。</div></div>' +
          '<button type="button" class="wolai-surface-close" data-page-share-action="close" aria-label="关闭公开分享页面">×</button>' +
        '</div>' +
        '<div class="wolai-page-share-state">' +
          '<span class="wolai-public-pill wolai-public-pill--inline">全网公开</span>' +
          '<span class="wolai-page-share-copy">任何拥有链接的人都可以访问当前页面。</span>' +
        '</div>' +
        '<div class="wolai-page-share-url-row">' +
          '<input class="wolai-page-share-url" type="text" readonly data-page-share-url value="" />' +
          '<button type="button" class="wolai-page-share-copy-button" data-page-share-action="copy-link">复制链接</button>' +
        '</div>' +
        '<div class="wolai-page-share-footer">更多共享者、群组公开和权限策略仍待接线。</div>' +
      '</div>';
    dialog.addEventListener('click', function(event) {
      if (event.target === dialog) closePageShareDialog();
    });
    document.body.appendChild(dialog);
    return dialog;
  }

  function openPageShareDialog() {
    var dialog = ensurePageShareDialog();
    var input = dialog.querySelector('[data-page-share-url]');
    if (input instanceof HTMLInputElement) input.value = window.location.href;
    dialog.hidden = false;
    document.documentElement.setAttribute('data-mnote-page-share-open', 'true');
  }

  function closePageShareDialog() {
    var dialog = document.querySelector('[data-testid="wolai-page-share-dialog"]');
    if (dialog instanceof HTMLElement) dialog.hidden = true;
    document.documentElement.removeAttribute('data-mnote-page-share-open');
  }

  function ensurePageSettingsPopover() {
    var existing = document.querySelector('[data-testid="wolai-page-settings-popover"]');
    if (existing instanceof HTMLElement) return existing;
    var popover = document.createElement('div');
    popover.className = 'wolai-page-settings-popover';
    popover.setAttribute('data-testid', 'wolai-page-settings-popover');
    popover.setAttribute('data-mnote-surface', 'page-settings');
    popover.hidden = true;
    popover.innerHTML = '' +
      '<div class="wolai-page-settings-panel" role="dialog" aria-modal="false">' +
        '<div class="wolai-page-settings-tabs" data-testid="wolai-page-settings-tabs" role="tablist" aria-label="页面设置分组">' +
          '<button type="button" class="wolai-page-settings-tab is-active" role="tab" aria-selected="true" data-page-settings-tab="page">页面选项</button>' +
          '<button type="button" class="wolai-page-settings-tab" role="tab" aria-selected="false" data-page-settings-tab="custom">自定义页面</button>' +
          '<button type="button" class="wolai-page-settings-tab" role="tab" aria-selected="false" data-page-settings-tab="global">全局选项</button>' +
        '</div>' +
        '<div class="wolai-page-settings-section" data-page-settings-panel="page">' +
          createPageOptionRow('wideLayout', 'checkbox') +
          createPageOptionRow('smallText', 'checkbox') +
          createPageOptionRow('showToc', 'checkbox') +
          createPageOptionRow('protectEditing', 'checkbox') +
          createPageOptionRow('hideTitleHeader', 'checkbox') +
        '</div>' +
        '<div class="wolai-page-settings-section" data-page-settings-panel="custom" hidden>' +
          createPageFontRow() +
          createPageOptionRow('layoutDensity', 'select') +
          createPageOptionRow('collapseBacklinks', 'checkbox') +
          createPageOptionRow('hideChildPages', 'checkbox') +
          createPageOptionRow('showBlockRefCount', 'checkbox') +
        '</div>' +
        '<div class="wolai-page-settings-section" data-page-settings-panel="global" hidden>' +
          createGlobalHeadingNumbersRow() +
          createPageWidthRows() +
        '</div>' +
        '<div class="wolai-page-settings-actions">' +
          '<button type="button" class="wolai-page-settings-action" data-page-settings-action="history">页面历史...</button>' +
          '<button type="button" class="wolai-page-settings-action" data-page-settings-action="share">公开分享页面...</button>' +
          '<button type="button" class="wolai-page-settings-action is-disabled" data-page-settings-action="move" disabled>移动到...</button>' +
          '<button type="button" class="wolai-page-settings-action is-disabled" data-page-settings-action="embed" disabled>嵌入到...</button>' +
          '<button type="button" class="wolai-page-settings-action is-danger is-disabled" data-page-settings-action="delete" disabled>删除页面</button>' +
        '</div>' +
        '<div class="wolai-page-settings-stats" data-testid="wolai-page-settings-stats"></div>' +
      '</div>';
    document.body.appendChild(popover);
    return popover;
  }

  function createLocalIndexSettingsPanelHtml() {
    return '' +
      '<div class="wolai-page-settings-panel mnote-settings-panel mnote-local-index-settings-panel" role="dialog" aria-modal="false" aria-label="索引设置">' +
        '<div class="mnote-settings-panel-head">' +
          '<strong>索引设置</strong>' +
          '<button type="button" class="mnote-settings-panel-close" data-settings-action="close" aria-label="关闭索引设置">×</button>' +
        '</div>' +
        '<div class="wolai-page-settings-index-panel" data-testid="wolai-page-settings-local-index-panel">' +
          '<div class="wolai-page-settings-index-status" data-testid="wolai-page-settings-local-index-status"></div>' +
          '<section class="wolai-page-settings-index-group">' +
            '<div class="wolai-page-settings-index-title">索引范围</div>' +
            '<div class="wolai-page-settings-index-range-list" data-testid="wolai-page-settings-local-index-ranges"></div>' +
            '<button type="button" class="wolai-page-settings-index-add" data-local-index-action="add-path">新增范围</button>' +
            '<div class="wolai-page-settings-index-schedule">' +
              '<label><span>索引时间</span><select data-testid="wolai-page-settings-local-index-schedule-mode"><option value="daily">每日</option><option value="once">指定日期</option><option value="manual">手动</option></select></label>' +
              '<label><span>时间</span><input type="time" data-testid="wolai-page-settings-local-index-schedule-time" value="02:00"></label>' +
              '<label><span>日期</span><input type="date" data-testid="wolai-page-settings-local-index-schedule-date"></label>' +
              '<label class="wolai-page-settings-index-inline"><input type="checkbox" data-testid="wolai-page-settings-local-index-run-on-change"><span>文档变化时立即索引</span></label>' +
            '</div>' +
            '<div class="wolai-page-settings-index-actions">' +
              '<button type="button" data-local-index-action="save">保存设置</button>' +
              '<button type="button" data-local-index-action="refresh">重建索引</button>' +
            '</div>' +
          '</section>' +
        '</div>' +
      '</div>';
  }

  function ensureLocalIndexSettingsPopover() {
    var existing = document.querySelector('[data-testid="mnote-local-index-settings-popover"]');
    if (existing instanceof HTMLElement) return existing;
    var popover = document.createElement('div');
    popover.className = 'wolai-page-settings-popover mnote-local-index-settings-popover';
    popover.setAttribute('data-testid', 'mnote-local-index-settings-popover');
    popover.setAttribute('data-mnote-surface', 'local-index-settings');
    popover.hidden = true;
    popover.innerHTML = createLocalIndexSettingsPanelHtml();
    document.body.appendChild(popover);
    return popover;
  }

  function createKnowledgeRagSettingsPanelHtml() {
    return '' +
      '<div class="wolai-page-settings-panel mnote-settings-panel mnote-knowledge-rag-settings-panel" role="dialog" aria-modal="false" aria-label="资料库问答设置">' +
        '<div class="mnote-settings-panel-head">' +
          '<strong>资料库问答</strong>' +
          '<button type="button" class="mnote-settings-panel-close" data-settings-action="close" aria-label="关闭资料库问答设置">×</button>' +
        '</div>' +
        '<div class="wolai-page-settings-index-panel" data-testid="mnote-knowledge-rag-settings-panel">' +
          '<div class="wolai-page-settings-index-status" data-testid="mnote-knowledge-rag-status"></div>' +
          '<div class="mnote-knowledge-rag-meta" data-testid="mnote-knowledge-rag-meta"></div>' +
          '<section class="wolai-page-settings-index-group mnote-knowledge-rag-control-group">' +
            '<div class="wolai-page-settings-index-title">资料操作</div>' +
            '<div class="mnote-knowledge-rag-source-inputs" data-testid="mnote-knowledge-rag-source-inputs">' +
              knowledgeRagSourceInputRow('') +
            '</div>' +
            '<div class="mnote-knowledge-rag-source-tools">' +
              '<button type="button" class="wolai-page-settings-index-add" data-knowledge-rag-action="add-source">新增来源</button>' +
              '<button type="button" class="wolai-page-settings-index-add" data-knowledge-rag-action="use-filetree-selection">使用选中文件树项</button>' +
            '</div>' +
            '<div class="wolai-page-settings-index-actions">' +
              '<button type="button" data-knowledge-rag-action="refresh">刷新状态</button>' +
              '<button type="button" data-knowledge-rag-action="ingest">索引/重建来源</button>' +
              '<button type="button" data-knowledge-rag-action="prune">清除失效记录</button>' +
              '<button type="button" data-knowledge-rag-action="open-dashboard">打开 LightRAG</button>' +
            '</div>' +
          '</section>' +
          '<section class="wolai-page-settings-index-group">' +
            '<div class="wolai-page-settings-index-title">资料来源</div>' +
            '<div class="mnote-knowledge-rag-source-filters" data-testid="mnote-knowledge-rag-source-filters"></div>' +
            '<div class="mnote-knowledge-rag-source-list" data-testid="mnote-knowledge-rag-sources"></div>' +
          '</section>' +
        '</div>' +
      '</div>';
  }

  function ensureKnowledgeRagSettingsPopover() {
    var existing = document.querySelector('[data-testid="mnote-knowledge-rag-settings-popover"]');
    if (existing instanceof HTMLElement) return existing;
    var popover = document.createElement('div');
    popover.className = 'wolai-page-settings-popover mnote-knowledge-rag-settings-popover';
    popover.setAttribute('data-testid', 'mnote-knowledge-rag-settings-popover');
    popover.setAttribute('data-mnote-surface', 'knowledge-rag-settings');
    popover.hidden = true;
    popover.innerHTML = createKnowledgeRagSettingsPanelHtml();
    document.body.appendChild(popover);
    return popover;
  }

  function pageSettingsLocalIndexScopeKey() {
    return [
      currentSourceKind(),
      currentRootUri(),
      resolveWorkspaceId(document.body),
      currentDocumentId()
    ].join('|');
  }

  function pageSettingsLocalIndexIsAvailable() {
    return currentSourceKind() === 'local_folder' && Boolean(currentRootUri());
  }

  function pageSettingsLocalIndexEmpty(message) {
    return '<div class="wolai-page-settings-index-empty">' + escapeHtml(message) + '</div>';
  }

  function renderPageSettingsLocalIndex(popover) {
    popover = popover || ensureLocalIndexSettingsPopover();
    var statusNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-status"]');
    var rangesNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-ranges"]');
    var scheduleModeNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-schedule-mode"]');
    var scheduleTimeNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-schedule-time"]');
    var scheduleDateNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-schedule-date"]');
    var runOnChangeNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-run-on-change"]');
    if (!(statusNode instanceof HTMLElement)) return;

    if (!pageSettingsLocalIndexIsAvailable()) {
      statusNode.textContent = '本地索引仅在本地工作区可用';
      renderLocalIndexRangeRows(popover, [], 'fault', true, {});
      setLocalIndexScheduleControlsDisabled(true);
      return;
    }

    var summary = pageUiState.localIndexSummary || {};
    if (summary.loading) {
      statusNode.textContent = '正在读取本地索引...';
      renderLocalIndexRangeRows(popover, currentLocalIndexRangeValues(popover), 'indexing', true, summary.status || {});
      setLocalIndexScheduleControlsDisabled(true);
      return;
    }
    if (summary.error) {
      statusNode.textContent = '本地索引读取失败';
      renderLocalIndexRangeRows(popover, currentLocalIndexRangeValues(popover), 'fault', false, summary.status || {});
      setLocalIndexScheduleControlsDisabled(false);
      return;
    }
    if (summary.scopeKey !== pageSettingsLocalIndexScopeKey()) {
      statusNode.textContent = '打开索引设置后读取本地索引';
      renderLocalIndexRangeRows(popover, [], 'indexing', true, {});
      setLocalIndexScheduleControlsDisabled(true);
      return;
    }

    var status = summary.status || {};
    var settings = status.settings && typeof status.settings === 'object' ? status.settings : {};
    var savedIncludePaths = Array.isArray(settings.includePaths) ? settings.includePaths : [];
    var indexedPaths = Array.isArray(status.indexedPaths) ? status.indexedPaths : [];
    var includePaths = savedIncludePaths.length ? savedIncludePaths : indexedPaths;
    var hasIndexCache = status.indexExists === true || status.evidenceIndexExists === true;
    if (!savedIncludePaths.length && !hasIndexCache) {
      statusNode.textContent = '未设置索引范围';
    } else {
      statusNode.textContent = '索引 ' + Number(status.documentCount || 0) + ' 个页面 / ' + Number(status.resourceCount || 0) + ' 个资源 / ' + Number(status.evidenceBlockCount || 0) + ' 个证据块' + (status.scheduledDue ? '，已到计划时间' : '');
    }
    if (!(rangesNode instanceof HTMLElement) || !rangesNode.contains(document.activeElement)) {
      renderLocalIndexRangeRows(popover, includePaths, localIndexStatusKind(status), false, status);
    }
    if (scheduleModeNode instanceof HTMLSelectElement && document.activeElement !== scheduleModeNode) {
      scheduleModeNode.value = String(settings.scheduleMode || 'daily');
    }
    if (scheduleTimeNode instanceof HTMLInputElement && document.activeElement !== scheduleTimeNode) {
      scheduleTimeNode.value = String(settings.scheduleTime || '02:00');
    }
    if (scheduleDateNode instanceof HTMLInputElement && document.activeElement !== scheduleDateNode) {
      scheduleDateNode.value = String(settings.scheduleDate || '');
    }
    if (runOnChangeNode instanceof HTMLInputElement) {
      runOnChangeNode.checked = settings.runOnChange === true;
    }
    setLocalIndexScheduleControlsDisabled(false);
  }

  function localIndexStatusKind(status) {
    if (!status || typeof status !== 'object') return 'fault';
    var settings = status.settings && typeof status.settings === 'object' ? status.settings : {};
    var includePaths = Array.isArray(settings.includePaths) ? settings.includePaths : [];
    if (!includePaths.length && status.indexExists !== true && status.evidenceIndexExists !== true) return 'indexed';
    if (status.indexExists !== true) return 'fault';
    if (status.cacheMatchesSettings === true && status.scheduledDue !== true) return 'indexed';
    return 'indexing';
  }

  function localIndexStatusLabel(kind) {
    if (kind === 'indexed') return '已索引';
    if (kind === 'indexing') return '索引中';
    return '索引故障';
  }

  function localIndexPathStatusKind(path, status, fallbackKind) {
    var indexedPaths = Array.isArray(status && status.indexedPaths) ? status.indexedPaths : [];
    var normalizedPath = String(path || '').trim() || '.';
    if (indexedPaths.indexOf(normalizedPath) >= 0 && status.indexExists === true) return 'indexed';
    if (fallbackKind === 'fault' || fallbackKind === 'indexing') return fallbackKind;
    if (status.indexExists === true) return 'indexing';
    return 'fault';
  }

  function renderLocalIndexRangeRows(popover, includePaths, fallbackKind, disabled, status) {
    var rangesNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-ranges"]');
    if (!(rangesNode instanceof HTMLElement)) return;
    var rawPaths = Array.isArray(includePaths) ? includePaths.map(function(value) {
      return String(value || '').trim();
    }) : [];
    var paths = disabled ? rawPaths.filter(Boolean) : rawPaths;
    var savedPaths = Array.isArray(status && status.settings && status.settings.includePaths)
      ? status.settings.includePaths.map(function(value) { return String(value || '').trim(); }).filter(Boolean)
      : [];
    var indexedPaths = Array.isArray(status && status.indexedPaths)
      ? status.indexedPaths.map(function(value) { return String(value || '').trim(); }).filter(Boolean)
      : [];
    rangesNode.innerHTML = paths.map(function(path, index) {
      var isPersisted = savedPaths.indexOf(path) >= 0 || indexedPaths.indexOf(path) >= 0;
      var inputDisabled = disabled || isPersisted;
      var kind = localIndexPathStatusKind(path, status || {}, fallbackKind || 'fault');
      var normalizedPath = String(path || '').trim() || '.';
      var displayPath = inputDisabled && normalizedPath === '.' ? '工作区根目录（全部）' : path;
      var rootScopeHint = normalizedPath === '.'
        ? '<span class="wolai-page-settings-index-root-hint">当前工作区全部</span>'
        : '';
      return '' +
        '<div class="wolai-page-settings-index-range-row" data-local-index-range-row="true">' +
          '<span class="wolai-page-settings-index-status-dot" data-index-status="' + kind + '" title="' + escapeHtml(localIndexStatusLabel(kind)) + '"></span>' +
          '<input type="text" class="wolai-page-settings-index-path" data-local-index-range-input="true" data-local-index-value="' + escapeHtml(normalizedPath) + '" value="' + escapeHtml(displayPath) + '" spellcheck="false"' + (inputDisabled ? ' disabled' : '') + (isPersisted ? ' data-local-index-persisted="true" title="已保存的索引目录不能直接修改；删除后重新新增范围"' : '') + ' />' +
          rootScopeHint +
          '<button type="button" class="wolai-page-settings-index-remove" data-local-index-action="remove-path" data-local-index-path-index="' + String(index) + '"' + (disabled ? ' disabled' : '') + ' aria-label="删除索引范围">×</button>' +
        '</div>';
    }).join('');
  }

  function setLocalIndexScheduleControlsDisabled(disabled) {
    var popover = ensureLocalIndexSettingsPopover();
    [
      '[data-testid="wolai-page-settings-local-index-schedule-mode"]',
      '[data-testid="wolai-page-settings-local-index-schedule-time"]',
      '[data-testid="wolai-page-settings-local-index-schedule-date"]',
      '[data-testid="wolai-page-settings-local-index-run-on-change"]'
    ].forEach(function(selector) {
      var node = popover.querySelector(selector);
      if (node instanceof HTMLInputElement || node instanceof HTMLSelectElement) node.disabled = disabled;
    });
  }

  async function loadPageSettingsLocalIndex(force) {
    if (!pageSettingsLocalIndexIsAvailable()) {
      renderPageSettingsLocalIndex();
      return;
    }
    var scopeKey = pageSettingsLocalIndexScopeKey();
    var current = pageUiState.localIndexSummary || {};
    if (!force && current.scopeKey === scopeKey && !current.error && !current.loading) {
      renderPageSettingsLocalIndex();
      return;
    }
    pageUiState.localIndexSummary = {
      scopeKey: scopeKey,
      loading: true,
      error: ''
    };
    renderPageSettingsLocalIndex();
    try {
      var baseParams = new URLSearchParams();
      baseParams.set('workspaceId', resolveWorkspaceId(document.body));
      baseParams.set('rootUri', currentRootUri());
      var statusUrl = '/api/search/local-index/status?' + baseParams.toString();
      var response = await fetch(statusUrl, { headers: { accept: 'application/json' } });
      var statusPayload = await response.json().catch(function(){ return null; });
      if (!response.ok || !statusPayload || statusPayload.ok !== true) {
        throw new Error('status_' + response.status);
      }
      pageUiState.localIndexSummary = {
        scopeKey: scopeKey,
        loading: false,
        error: '',
        status: statusPayload.result || {}
      };
    } catch (error) {
      pageUiState.localIndexSummary = {
        scopeKey: scopeKey,
        loading: false,
        error: error instanceof Error ? error.message : String(error),
        status: {}
      };
    }
    renderPageSettingsLocalIndex();
  }

  function localIndexIncludePathsFromForm() {
    var popover = ensureLocalIndexSettingsPopover();
    return currentLocalIndexRangeValues(popover).map(function(value) {
      return value.trim();
    }).filter(Boolean);
  }

  function currentLocalIndexRangeValues(popover) {
    var root = popover || ensureLocalIndexSettingsPopover();
    return Array.from(root.querySelectorAll('[data-local-index-range-input]')).map(function(input) {
      if (!(input instanceof HTMLInputElement)) return '';
      if (input.disabled && input.dataset.localIndexValue) return input.dataset.localIndexValue;
      return input.value;
    });
  }

  function localIndexSchedulePayloadFromForm() {
    var popover = ensureLocalIndexSettingsPopover();
    var modeNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-schedule-mode"]');
    var timeNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-schedule-time"]');
    var dateNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-schedule-date"]');
    var runOnChangeNode = popover.querySelector('[data-testid="wolai-page-settings-local-index-run-on-change"]');
    return {
      scheduleMode: modeNode instanceof HTMLSelectElement ? modeNode.value : 'daily',
      scheduleTime: timeNode instanceof HTMLInputElement ? (timeNode.value || '02:00') : '02:00',
      scheduleDate: dateNode instanceof HTMLInputElement ? dateNode.value : '',
      runOnChange: runOnChangeNode instanceof HTMLInputElement ? runOnChangeNode.checked : false
    };
  }

  function addLocalIndexRange() {
    var popover = ensureLocalIndexSettingsPopover();
    var summary = pageUiState.localIndexSummary || {};
    renderLocalIndexRangeRows(popover, currentLocalIndexRangeValues(popover).concat(['']), localIndexStatusKind(summary.status || {}), false, summary.status || {});
    var inputs = Array.from(popover.querySelectorAll('[data-local-index-range-input]'));
    var last = inputs[inputs.length - 1];
    if (last instanceof HTMLInputElement) last.focus();
  }

  function removeLocalIndexRange(index) {
    var popover = ensureLocalIndexSettingsPopover();
    var summary = pageUiState.localIndexSummary || {};
    var values = currentLocalIndexRangeValues(popover);
    values.splice(Number(index || 0), 1);
    renderLocalIndexRangeRows(popover, values, localIndexStatusKind(summary.status || {}), false, summary.status || {});
  }

  async function persistLocalIndexSettings() {
    if (!pageSettingsLocalIndexIsAvailable()) return;
    pageUiState.localIndexSummary = Object.assign({}, pageUiState.localIndexSummary || {}, {
      scopeKey: pageSettingsLocalIndexScopeKey(),
      loading: true,
      error: ''
    });
    renderPageSettingsLocalIndex();
    try {
      var response = await fetch('/api/search/local-index/settings', {
        method: 'PUT',
        headers: { 'content-type': 'application/json', accept: 'application/json' },
        body: JSON.stringify(Object.assign({
          workspaceId: resolveWorkspaceId(document.body),
          rootUri: currentRootUri(),
          includePaths: localIndexIncludePathsFromForm()
        }, localIndexSchedulePayloadFromForm()))
      });
      var payload = await response.json().catch(function(){ return null; });
      if (!response.ok || !payload || payload.ok !== true) {
        throw new Error(payload && payload.error && payload.error.message ? payload.error.message : 'local_index_settings_' + response.status);
      }
      await loadPageSettingsLocalIndex(true);
    } catch (error) {
      pageUiState.localIndexSummary = Object.assign({}, pageUiState.localIndexSummary || {}, {
        scopeKey: pageSettingsLocalIndexScopeKey(),
        loading: false,
        error: error instanceof Error ? error.message : String(error)
      });
      renderPageSettingsLocalIndex();
    }
  }

  function emitLocalIndexBackgroundTask(status, label, error) {
    var now = Date.now();
    window.dispatchEvent(new CustomEvent('mnote:local-background-task-updated', {
      detail: {
        task: {
          taskKind: 'local_index',
          taskId: 'local-index',
          sourceRootRelativePath: '本地索引',
          rootUri: currentRootUri(),
          status: status,
          stageLabel: label,
          updatedAtMs: now,
          finishedAtMs: status === 'running' ? null : now,
          error: error || null
        }
      }
    }));
  }

  async function refreshLocalIndex() {
    if (!pageSettingsLocalIndexIsAvailable()) return;
    emitLocalIndexBackgroundTask('running', '本地索引重建中', null);
    pageUiState.localIndexSummary = Object.assign({}, pageUiState.localIndexSummary || {}, {
      scopeKey: pageSettingsLocalIndexScopeKey(),
      loading: true,
      error: ''
    });
    renderPageSettingsLocalIndex();
    try {
      var response = await fetch('/api/search/local-index/refresh', {
        method: 'POST',
        headers: { 'content-type': 'application/json', accept: 'application/json' },
        body: JSON.stringify({
          workspaceId: resolveWorkspaceId(document.body),
          rootUri: currentRootUri()
        })
      });
      var payload = await response.json().catch(function(){ return null; });
      if (!response.ok || !payload || payload.ok !== true) {
        throw new Error(payload && payload.error && payload.error.message ? payload.error.message : 'local_index_refresh_' + response.status);
      }
      emitLocalIndexBackgroundTask('done', '本地索引已完成', null);
      await loadPageSettingsLocalIndex(true);
    } catch (error) {
      emitLocalIndexBackgroundTask('failed', '本地索引失败', error instanceof Error ? error.message : String(error));
      pageUiState.localIndexSummary = Object.assign({}, pageUiState.localIndexSummary || {}, {
        scopeKey: pageSettingsLocalIndexScopeKey(),
        loading: false,
        error: error instanceof Error ? error.message : String(error)
      });
      renderPageSettingsLocalIndex();
    }
  }

  function knowledgeRagScopeKey() {
    return [
      currentSourceKind(),
      currentRootUri(),
      resolveWorkspaceId(document.body)
    ].join('|');
  }

  function knowledgeRagIsAvailable() {
    return currentSourceKind() === 'local_folder' && Boolean(currentRootUri());
  }

  function knowledgeRagSourceInputRow(value) {
    return '' +
      '<div class="mnote-knowledge-rag-source-input-row" data-knowledge-rag-source-input-row="true">' +
        '<input type="text" class="wolai-page-settings-index-path" data-knowledge-rag-source-input="true" value="' + escapeHtml(value || '') + '" placeholder="文件或目录，例如 docs/book.pdf / docs" spellcheck="false" />' +
        '<span class="mnote-knowledge-rag-input-status" data-knowledge-rag-input-status-label="true" hidden></span>' +
        '<button type="button" class="wolai-page-settings-index-remove" data-knowledge-rag-action="remove-source" aria-label="删除资料来源">×</button>' +
      '</div>';
  }

  function knowledgeRagSourceInputValues() {
    var popover = ensureKnowledgeRagSettingsPopover();
    return Array.from(popover.querySelectorAll('[data-knowledge-rag-source-input]')).map(function(input) {
      return input instanceof HTMLInputElement ? input.value.trim() : '';
    }).filter(Boolean).filter(function(value, index, list) {
      return list.indexOf(value) === index;
    });
  }

  function knowledgeRagIndexedRootValues() {
    var status = currentKnowledgeRagSummary().status || {};
    var registry = status.registry && typeof status.registry === 'object' ? status.registry : {};
    var roots = Array.isArray(registry.indexedRoots) ? registry.indexedRoots : [];
    return roots.map(function(root) {
      return String(root && (root.rootRelativePath || root.root_relative_path) || '').trim();
    }).filter(Boolean).filter(function(value, index, list) {
      return list.indexOf(value) === index;
    });
  }

  function knowledgeRagCurrentFilter() {
    return pageUiState.knowledgeRagSourceFilter || 'attention';
  }

  function setKnowledgeRagSourceFilter(filter) {
    pageUiState.knowledgeRagSourceFilter = String(filter || 'attention').trim() || 'attention';
    renderKnowledgeRagSettings();
  }

  function knowledgeRagRegistryEntryCategory(entry) {
    var deleted = entry && entry.deletedAtMs;
    var stale = entry && entry.stale;
    var providerStatus = String(entry && entry.lightRagStatus || '').trim();
    var failed = providerStatus === 'failed';
    var deleting = providerStatus === 'delete_submitted';
    var deletedDone = providerStatus === 'delete_completed';
    var docId = String(entry && entry.lightRagDocId || '');
    var indexed = entry && entry.indexedAtMs && docId && !deleted && !stale && !failed && !deleting && !deletedDone;
    if (deletedDone) return 'removed';
    if (failed || stale || deleted) return 'failed';
    if (indexed) return 'indexed';
    return 'indexing';
  }

  function knowledgeRagProviderDocuments(status) {
    var documents = status && status.documents && typeof status.documents === 'object' ? status.documents : {};
    return Array.isArray(documents.documents) ? documents.documents : [];
  }

  function knowledgeRagStatusHasInFlight(status) {
    var pipeline = status && status.pipeline && typeof status.pipeline === 'object' ? status.pipeline : {};
    if (pipeline.busy === true || pipeline.scanning === true || pipeline.destructiveBusy === true || pipeline.requestPending === true || pipeline.pendingRequests === true || Number(pipeline.pendingEnqueues || 0) > 0) return true;
    var registry = status && status.registry && typeof status.registry === 'object' ? status.registry : {};
    var entries = Array.isArray(registry.entries) ? registry.entries : [];
    if (entries.some(function(entry) {
      var providerStatus = String(entry && entry.lightRagStatus || '').trim();
      return !entry?.deletedAtMs && !entry?.stale && !(entry?.indexedAtMs && entry?.lightRagDocId)
        && ['submitted', 'pending', 'scanning', 'parsing', 'parse', 'parsed', 'analyzing', 'analyze', 'processing', 'process', ''].includes(providerStatus);
    })) return true;
    var groups = status && status.documents && status.documents.rawStatusGroups && typeof status.documents.rawStatusGroups === 'object'
      ? status.documents.rawStatusGroups
      : {};
    return ['pending', 'scanning', 'parsing', 'parse', 'parsed', 'analyzing', 'analyze', 'processing', 'process'].some(function(key) {
      return Number(groups[key] || 0) > 0;
    });
  }

  function knowledgeRagPipelineHasInFlight(pipeline) {
    pipeline = pipeline && typeof pipeline === 'object' ? pipeline : {};
    return pipeline.busy === true
      || pipeline.scanning === true
      || pipeline.destructiveBusy === true
      || pipeline.requestPending === true
      || pipeline.pendingRequests === true
      || Number(pipeline.pendingEnqueues || 0) > 0;
  }

  function parseKnowledgeRagChunkProgress(message) {
    var text = String(message || '');
    var match = text.match(/Chunk\s+(\d+)\s+of\s+(\d+).*?\b(doc-[A-Za-z0-9_-]+?)(?=-(?:chunk|mm)-|\s|$)/i);
    if (!match) return null;
    var current = Number(match[1]);
    var total = Number(match[2]);
    if (!Number.isFinite(current) || !Number.isFinite(total) || current <= 0 || total <= 0) return null;
    return {
      current: Math.min(current, total),
      total: total,
      docId: String(match[3] || ''),
      latestMessage: text
    };
  }

  function normalizeKnowledgeRagPipelineEvent(payload) {
    var input = payload && typeof payload === 'object' ? payload : {};
    var latestMessage = String(input.latestMessage || input.latest_message || '');
    var historyMessages = Array.isArray(input.historyMessages)
      ? input.historyMessages
      : (Array.isArray(input.history_messages) ? input.history_messages : []);
    var progress = input.progress && typeof input.progress === 'object'
      ? input.progress
      : parseKnowledgeRagChunkProgress(latestMessage);
    if (!progress) {
      for (var index = historyMessages.length - 1; index >= 0; index -= 1) {
        progress = parseKnowledgeRagChunkProgress(historyMessages[index]);
        if (progress) break;
      }
    }
    return {
      ok: input.ok !== false,
      busy: input.busy === true,
      destructiveBusy: input.destructiveBusy === true || input.destructive_busy === true,
      scanning: input.scanning === true,
      scanningExclusive: input.scanningExclusive === true || input.scanning_exclusive === true,
      requestPending: input.requestPending === true || input.request_pending === true,
      pendingRequests: input.pendingRequests === true || input.pending_requests === true,
      pendingEnqueues: Number(input.pendingEnqueues ?? input.pending_enqueues ?? 0) || 0,
      docs: Number(input.docs || 0) || 0,
      batches: Number(input.batches ?? input.batchs ?? 0) || 0,
      currentBatch: Number(input.currentBatch ?? input.cur_batch ?? 0) || 0,
      jobName: String(input.jobName || input.job_name || ''),
      jobStart: input.jobStart || input.job_start || null,
      latestMessage: latestMessage,
      historyMessages: historyMessages.map(function(message) { return String(message || ''); }),
      cancellationRequested: input.cancellationRequested === true || input.cancellation_requested === true,
      cancellationReason: input.cancellationReason || input.cancellation_reason || null,
      progress: progress || null
    };
  }

  function knowledgeRagPipelineProgress(status) {
    var pipeline = status && status.pipeline && typeof status.pipeline === 'object' ? status.pipeline : {};
    var progress = pipeline.progress && typeof pipeline.progress === 'object' ? pipeline.progress : null;
    if (!progress) return null;
    var current = Number(progress.current || 0);
    var total = Number(progress.total || 0);
    if (!Number.isFinite(current) || !Number.isFinite(total) || current <= 0 || total <= 0) return null;
    return {
      current: Math.min(current, total),
      total: total,
      docId: String(progress.docId || ''),
      latestMessage: String(pipeline.latestMessage || '')
    };
  }

  function knowledgeRagSourceProgressHtml(progress) {
    if (!progress) return '';
    var current = Number(progress.current || 0);
    var total = Number(progress.total || 0);
    var determinate = Number.isFinite(current) && Number.isFinite(total) && current > 0 && total > 0;
    var percent = determinate ? Math.max(0, Math.min(100, Math.round((current / total) * 100))) : 0;
    var label = determinate ? current + ' / ' + total + ' · ' + percent + '%' : String(progress.label || '处理中');
    var mode = determinate ? 'determinate' : 'indeterminate';
    var ariaValues = determinate
      ? ' aria-valuemin="0" aria-valuemax="' + String(total) + '" aria-valuenow="' + String(current) + '"'
      : ' aria-valuetext="' + escapeHtml(label) + '"';
    return '' +
      '<div class="mnote-knowledge-rag-source-progress" data-progress-mode="' + mode + '" role="progressbar"' + ariaValues + ' aria-label="' + escapeHtml('索引进度 ' + label) + '">' +
        '<div><i' + (determinate ? ' style="width:' + String(percent) + '%"' : '') + '></i></div>' +
        '<span>' + escapeHtml(label) + '</span>' +
      '</div>';
  }

  function knowledgeRagDocumentIsInFlight(doc) {
    var status = String(doc && (doc.statusGroup || doc.status) || '').toLowerCase();
    return ['pending', 'scanning', 'parsing', 'parse', 'parsed', 'analyzing', 'analyze', 'processing', 'process'].includes(status);
  }

  function knowledgeRagProgressLabel(status, doc, fallback) {
    var pipeline = status && status.pipeline && typeof status.pipeline === 'object' ? status.pipeline : {};
    var latest = String(pipeline.latestMessage || '').trim();
    if (latest) return '处理中';
    var providerStatus = String(doc && (doc.statusGroup || doc.status) || fallback || '').toLowerCase();
    if (providerStatus === 'pending') return '排队中';
    if (providerStatus === 'scanning') return '扫描中';
    if (providerStatus === 'parsing' || providerStatus === 'parse' || providerStatus === 'parsed') return '解析中';
    if (providerStatus === 'analyzing' || providerStatus === 'analyze') return '分析中';
    return '索引中';
  }

  function knowledgeRagIndeterminateProgress(status, doc, fallback) {
    return { mode: 'indeterminate', label: knowledgeRagProgressLabel(status, doc, fallback) };
  }

  function stopKnowledgeRagPipelineWatcher() {
    if (knowledgeRagPipelineEventSource && typeof knowledgeRagPipelineEventSource.close === 'function') {
      knowledgeRagPipelineEventSource.close();
    }
    knowledgeRagPipelineEventSource = null;
    knowledgeRagPipelineWatcherScopeKey = '';
  }

  function handleKnowledgeRagPipelineEvent(payload) {
    var scopeKey = knowledgeRagScopeKey();
    if (knowledgeRagPipelineWatcherScopeKey && knowledgeRagPipelineWatcherScopeKey !== scopeKey) return;
    var current = currentKnowledgeRagSummary();
    var currentStatus = current.status && typeof current.status === 'object' ? current.status : {};
    var previousPipeline = currentStatus.pipeline && typeof currentStatus.pipeline === 'object' ? currentStatus.pipeline : {};
    var previousInFlight = knowledgeRagPipelineHasInFlight(previousPipeline);
    var pipeline = normalizeKnowledgeRagPipelineEvent(payload);
    var nextStatus = Object.assign({}, currentStatus, { pipeline: pipeline });
    pageUiState.knowledgeRagSummary = Object.assign({}, current, {
      scopeKey: scopeKey,
      loading: false,
      error: '',
      status: nextStatus
    });
    if (isKnowledgeRagSettingsOpen()) renderKnowledgeRagSettings();
    var currentInFlight = knowledgeRagPipelineHasInFlight(pipeline);
    if (currentInFlight) return;
    var latest = String(pipeline.latestMessage || '').toLowerCase();
    var finished = previousInFlight || latest.indexOf('completed') !== -1 || latest.indexOf('complete') !== -1;
    if (!finished || knowledgeRagPipelineWatcherRefreshPending) return;
    knowledgeRagPipelineWatcherRefreshPending = true;
    void loadKnowledgeRagStatus(true, {
      silentLoading: true,
      notifyUpdated: true,
      reason: 'pipeline-event-complete'
    }).finally(function() {
      knowledgeRagPipelineWatcherRefreshPending = false;
      if (!knowledgeRagStatusHasInFlight(currentKnowledgeRagSummary().status || {})) {
        stopKnowledgeRagPipelineWatcher();
      }
    });
  }

  function startKnowledgeRagPipelineWatcher(reason) {
    document.documentElement.setAttribute('data-mnote-knowledge-rag-watcher-required', String(reason || 'source-updated'));
    if (!knowledgeRagIsAvailable() || typeof window.EventSource !== 'function') return;
    var scopeKey = knowledgeRagScopeKey();
    if (knowledgeRagPipelineEventSource && knowledgeRagPipelineWatcherScopeKey === scopeKey) return;
    stopKnowledgeRagPipelineWatcher();
    var url = new URL('/api/knowledge-rag/pipeline-events', window.location.origin);
    url.searchParams.set('workspaceId', resolveWorkspaceId(document.body));
    url.searchParams.set('rootUri', currentRootUri());
    var source = new EventSource(url.toString());
    knowledgeRagPipelineEventSource = source;
    knowledgeRagPipelineWatcherScopeKey = scopeKey;
    source.addEventListener('pipeline_status', function(event) {
      var payload = null;
      try {
        payload = JSON.parse(event.data || '{}');
      } catch (_) {
        return;
      }
      handleKnowledgeRagPipelineEvent(payload);
    });
    source.onerror = function() {
      document.documentElement.setAttribute('data-mnote-knowledge-rag-watcher-error', 'true');
    };
  }

  function scheduleKnowledgeRagStatusBridge(reason) {
    startKnowledgeRagPipelineWatcher(reason);
  }

  function knowledgeRagUnmappedProviderRows(status, entries) {
    var docs = knowledgeRagProviderDocuments(status);
    if (!docs.length) return [];
    var mappedDocIds = new Set();
    var mappedFilePaths = new Set();
    (Array.isArray(entries) ? entries : []).forEach(function(entry) {
      var docId = String(entry && entry.lightRagDocId || '').trim();
      var filePath = String(entry && entry.lightRagFilePath || '').trim();
      if (docId) mappedDocIds.add(docId);
      if (filePath) mappedFilePaths.add(filePath);
    });
    return docs.filter(function(doc) {
      var docId = String(doc && doc.id || '').trim();
      var filePath = String(doc && doc.filePath || '').trim();
      return !(docId && mappedDocIds.has(docId)) && !(filePath && mappedFilePaths.has(filePath));
    }).map(function(doc) {
      return {
        kind: 'provider',
        category: String(doc.status || doc.statusGroup || '').toLowerCase().includes('fail') ? 'unmapped_failed' : 'unmapped',
        doc: doc
      };
    });
  }

  function knowledgeRagVisibleForFilter(row, filter) {
    if (filter === 'all') return true;
    if (filter === 'attention') return row.category !== 'indexed';
    if (filter === 'unmapped') return row.category === 'unmapped' || row.category === 'unmapped_failed';
    if (filter === 'failed') return row.category === 'failed' || row.category === 'unmapped_failed';
    return row.category === filter;
  }

  function renderKnowledgeRagSourceFilters(popover, rows) {
    var filtersNode = popover.querySelector('[data-testid="mnote-knowledge-rag-source-filters"]');
    if (!(filtersNode instanceof HTMLElement)) return;
    var counts = rows.reduce(function(acc, row) {
      acc.all += 1;
      if (row.category !== 'indexed') acc.attention += 1;
      if (row.category === 'indexing') acc.indexing += 1;
      if (row.category === 'failed' || row.category === 'unmapped_failed') acc.failed += 1;
      if (row.category === 'indexed') acc.indexed += 1;
      if (row.category === 'removed') acc.removed += 1;
      if (row.category === 'unmapped' || row.category === 'unmapped_failed') acc.unmapped += 1;
      return acc;
    }, { all: 0, attention: 0, indexing: 0, failed: 0, indexed: 0, removed: 0, unmapped: 0 });
    var current = knowledgeRagCurrentFilter();
    var options = [
      ['attention', '需处理'],
      ['indexing', '索引中'],
      ['failed', '失败/过期'],
      ['indexed', '已索引'],
      ['removed', '已移除'],
      ['unmapped', 'LightRAG 未映射'],
      ['all', '全部']
    ];
    filtersNode.innerHTML = options.map(function(item) {
      var key = item[0];
      var label = item[1];
      return '<button type="button" data-knowledge-rag-action="filter-sources" data-knowledge-rag-filter="' + key + '" data-active="' + (current === key ? 'true' : 'false') + '">' + escapeHtml(label) + ' <span>' + String(counts[key] || 0) + '</span></button>';
    }).join('');
  }

  function renderKnowledgeRagSources(popover, entries, status) {
    var sourcesNode = popover.querySelector('[data-testid="mnote-knowledge-rag-sources"]');
    if (!(sourcesNode instanceof HTMLElement)) return;
    var registryRows = (Array.isArray(entries) ? entries : []).map(function(entry) {
      return { kind: 'registry', category: knowledgeRagRegistryEntryCategory(entry), entry: entry };
    });
    var rows = registryRows.concat(knowledgeRagUnmappedProviderRows(status || {}, entries));
    renderKnowledgeRagSourceFilters(popover, rows);
    var filter = knowledgeRagCurrentFilter();
    var items = rows.filter(function(row) {
      return knowledgeRagVisibleForFilter(row, filter);
    });
    if (!rows.length) {
      sourcesNode.innerHTML = '<div class="wolai-page-settings-index-empty">还没有登记到 LightRAG 的资料来源</div>';
      return;
    }
    if (!items.length) {
      sourcesNode.innerHTML = '<div class="wolai-page-settings-index-empty">当前分类没有资料来源</div>';
      return;
    }
    var pipelineProgress = knowledgeRagPipelineProgress(status || {});
    var providerDocs = knowledgeRagProviderDocuments(status || {});
    var providerDocsById = new Map();
    var providerDocsByPath = new Map();
    providerDocs.forEach(function(doc) {
      var docId = String(doc && doc.id || '').trim();
      var filePath = String(doc && doc.filePath || '').trim();
      if (docId) providerDocsById.set(docId, doc);
      if (filePath) providerDocsByPath.set(filePath, doc);
    });
    sourcesNode.innerHTML = items.map(function(row) {
      if (row.kind === 'provider') {
        var doc = row.doc || {};
        var providerPath = String(doc.filePath || doc.id || '');
        var docStatus = String(doc.status || doc.statusGroup || '');
        var failedDoc = row.category === 'unmapped_failed';
        var providerProgress = pipelineProgress && pipelineProgress.docId && pipelineProgress.docId === String(doc.id || '') ? pipelineProgress : null;
        if (!providerProgress && !failedDoc && knowledgeRagDocumentIsInFlight(doc)) {
          providerProgress = knowledgeRagIndeterminateProgress(status || {}, doc, docStatus);
        }
        return '' +
          '<div class="mnote-knowledge-rag-source-row" data-knowledge-rag-source-row="true" data-knowledge-rag-source-kind="provider" data-knowledge-rag-source-path="' + escapeHtml(providerPath) + '">' +
            '<span class="wolai-page-settings-index-status-dot" data-index-status="' + (failedDoc ? 'fault' : 'indexed') + '" title="' + escapeHtml(docStatus || 'LightRAG 文档') + '"></span>' +
            '<div class="mnote-knowledge-rag-source-main">' +
              '<strong title="' + escapeHtml(providerPath) + '">' + escapeHtml(providerPath || 'LightRAG 文档') + '</strong>' +
              '<span>' + escapeHtml('LightRAG 未映射 · ' + (docStatus || 'unknown') + (doc.id ? ' · ' + doc.id : '')) + '</span>' +
              knowledgeRagSourceProgressHtml(providerProgress) +
            '</div>' +
            '<div class="mnote-knowledge-rag-source-actions"><button type="button" disabled>只读</button></div>' +
          '</div>';
      }
      var entry = row.entry || {};
      var deleted = entry && entry.deletedAtMs;
      var stale = entry && entry.stale;
      var providerStatus = String(entry && entry.lightRagStatus || '').trim();
      var failed = providerStatus === 'failed';
      var deleting = providerStatus === 'delete_submitted';
      var deletedDone = providerStatus === 'delete_completed';
      var docId = String(entry && entry.lightRagDocId || '');
      var indexed = entry && entry.indexedAtMs && docId && !deleted && !stale && !failed && !deleting && !deletedDone;
      var kind = deletedDone ? '' : (deleting ? 'indexing' : (failed || stale || deleted ? 'fault' : (indexed ? 'indexed' : 'indexing')));
      var title = String(entry && entry.sourceRootRelativePath || entry && entry.lightRagFilePath || '');
      var statusLabel = deletedDone ? 'LightRAG 已移除'
        : deleting ? '删除已提交'
          : deleted ? '已删除'
            : stale ? '已过期'
              : failed ? '索引失败'
                : indexed ? '已索引'
                  : (docId || providerStatus ? '正在索引' : '待提交');
      var meta = docId ? statusLabel + ' · ' + docId : statusLabel;
      var sourcePath = String(entry && entry.sourceRootRelativePath || '');
      var canReindex = Boolean(sourcePath && !deleting);
      var canDelete = Boolean(sourcePath && !deleted && !deletedDone && !deleting);
      var entryProgress = pipelineProgress && pipelineProgress.docId && pipelineProgress.docId === docId && !indexed && !failed && !stale && !deleted && !deletedDone ? pipelineProgress : null;
      var providerDoc = (docId && providerDocsById.get(docId)) || providerDocsByPath.get(String(entry && entry.lightRagFilePath || '').trim()) || null;
      if (!entryProgress && !indexed && !failed && !stale && !deleted && !deletedDone && !deleting && (knowledgeRagDocumentIsInFlight(providerDoc) || docId || providerStatus)) {
        entryProgress = knowledgeRagIndeterminateProgress(status || {}, providerDoc, providerStatus);
      }
      return '' +
        '<div class="mnote-knowledge-rag-source-row" data-knowledge-rag-source-row="true" data-knowledge-rag-source-path="' + escapeHtml(sourcePath) + '">' +
          '<span class="wolai-page-settings-index-status-dot" data-index-status="' + kind + '" title="' + escapeHtml(statusLabel) + '"></span>' +
          '<div class="mnote-knowledge-rag-source-main">' +
            '<strong title="' + escapeHtml(title) + '">' + escapeHtml(title || '未命名来源') + '</strong>' +
            '<span>' + escapeHtml(meta) + '</span>' +
            knowledgeRagSourceProgressHtml(entryProgress) +
          '</div>' +
          '<div class="mnote-knowledge-rag-source-actions">' +
            '<button type="button" data-knowledge-rag-action="reindex-source" data-knowledge-rag-source-path="' + escapeHtml(sourcePath) + '"' + (canReindex ? '' : ' disabled') + '>重索引</button>' +
            '<button type="button" data-knowledge-rag-action="delete-source" data-knowledge-rag-source-path="' + escapeHtml(sourcePath) + '"' + (canDelete ? '' : ' disabled') + '>删除索引</button>' +
          '</div>' +
        '</div>';
    }).join('');
  }

  function renderKnowledgeRagSettings(popover) {
    popover = popover || ensureKnowledgeRagSettingsPopover();
    var statusNode = popover.querySelector('[data-testid="mnote-knowledge-rag-status"]');
    var metaNode = popover.querySelector('[data-testid="mnote-knowledge-rag-meta"]');
    var dashboardButton = popover.querySelector('[data-knowledge-rag-action="open-dashboard"]');
    if (!(statusNode instanceof HTMLElement)) return;

    if (!knowledgeRagIsAvailable()) {
      statusNode.textContent = '资料库问答仅在本地工作区可用';
      if (metaNode instanceof HTMLElement) metaNode.innerHTML = '';
      renderKnowledgeRagSourceFilters(popover, []);
      renderKnowledgeRagSources(popover, [], {});
      if (dashboardButton instanceof HTMLButtonElement) dashboardButton.disabled = true;
      return;
    }

    var summary = currentKnowledgeRagSummary();
    if (summary.loading) {
      statusNode.textContent = '正在读取 LightRAG 状态...';
      if (metaNode instanceof HTMLElement) metaNode.innerHTML = '';
      renderKnowledgeRagSourceFilters(popover, []);
      renderKnowledgeRagSources(popover, [], {});
      if (dashboardButton instanceof HTMLButtonElement) dashboardButton.disabled = true;
      return;
    }
    if (summary.error) {
      statusNode.textContent = 'LightRAG 状态读取失败：' + summary.error;
      if (metaNode instanceof HTMLElement) metaNode.innerHTML = '';
      renderKnowledgeRagSourceFilters(popover, []);
      renderKnowledgeRagSources(popover, [], {});
      if (dashboardButton instanceof HTMLButtonElement) dashboardButton.disabled = true;
      return;
    }

    var status = summary.status || {};
    var health = status.health && typeof status.health === 'object' ? status.health : {};
    var healthBody = health.health && typeof health.health === 'object' ? health.health : {};
    var healthy = health.ok === true && String(healthBody.status || '') === 'healthy';
    var registry = status.registry && typeof status.registry === 'object' ? status.registry : {};
    var entries = Array.isArray(registry.entries) ? registry.entries : [];
    var providerDocs = knowledgeRagProviderDocuments(status);
    statusNode.textContent = healthy
      ? 'LightRAG healthy，MNote 登记 ' + entries.length + ' 个来源，LightRAG 文档 ' + providerDocs.length + ' 个'
      : 'LightRAG 未就绪';
    if (metaNode instanceof HTMLElement) {
      var dashboardUrl = publicKnowledgeRagDashboardUrl(status);
      var inputDir = String(status.inputDir || '');
      var workingDir = String(healthBody.working_directory || '');
      var config = healthBody.configuration && typeof healthBody.configuration === 'object' ? healthBody.configuration : {};
      var llmModel = String(config.llm_model || config.llmModel || '');
      var vlmModel = String(config.vlm_model || config.vlmModel || '');
      var rerank = status.rerank && typeof status.rerank === 'object' ? status.rerank : {};
      var rerankStatus = String(rerank.status || 'unknown');
      var rerankBinding = searchText(rerank.binding || '');
      var rerankModel = searchText(rerank.model || '');
      var rerankLabel = rerankStatus === 'disabled'
        ? 'disabled'
        : rerankStatus === 'available'
          ? 'available'
          : rerankStatus === 'unavailable'
            ? 'unavailable'
            : 'unknown';
      metaNode.innerHTML = '' +
        '<div><span>Dashboard</span><code>' + escapeHtml(dashboardUrl || '未配置') + '</code></div>' +
        '<div><span>Model</span><code>' + escapeHtml(llmModel || '未上报') + (vlmModel ? ' / VLM ' + escapeHtml(vlmModel) : '') + '</code></div>' +
        '<div><span>Rerank</span><code>' + escapeHtml(rerankLabel + (rerankBinding ? ' · ' + rerankBinding : '') + (rerankModel ? ' · ' + rerankModel : '')) + '</code></div>' +
        '<div><span>Input</span><code>' + escapeHtml(inputDir || '未配置') + '</code></div>' +
        '<div><span>Storage</span><code>' + escapeHtml(workingDir || '未上报') + '</code></div>';
    }
    renderKnowledgeRagSources(popover, entries, status);
    if (dashboardButton instanceof HTMLButtonElement) dashboardButton.disabled = !publicKnowledgeRagDashboardUrl(status);
  }

  function publicKnowledgeRagDashboardUrl(status) {
    var url = String(status && (status.dashboardUrl || status.endpoint) || '').trim();
    if (!url) return '';
    try {
      var parsed = new URL(url, window.location.href);
      if (['127.0.0.1', 'localhost', '::1'].includes(parsed.hostname) && window.location.hostname && !['127.0.0.1', 'localhost', '::1'].includes(window.location.hostname)) {
        parsed.hostname = window.location.hostname;
        return parsed.toString();
      }
    } catch (_) {}
    return url;
  }

  async function loadKnowledgeRagStatus(force, options) {
    options = options || {};
    if (!knowledgeRagIsAvailable()) {
      renderKnowledgeRagSettings();
      return;
    }
    var scopeKey = knowledgeRagScopeKey();
    var current = currentKnowledgeRagSummary();
    var previousStatus = current.status || {};
    var previousDigest = knowledgeRagRegistryStatusDigest(previousStatus);
    if (!force && current.scopeKey === scopeKey && !current.error && !current.loading) {
      renderKnowledgeRagSettings();
      return;
    }
    if (!options.silentLoading) {
      pageUiState.knowledgeRagSummary = {
        scopeKey: scopeKey,
        loading: true,
        error: ''
      };
      renderKnowledgeRagSettings();
    }
    try {
      var params = new URLSearchParams();
      params.set('workspaceId', resolveWorkspaceId(document.body));
      params.set('rootUri', currentRootUri());
      var response = await fetch('/api/knowledge-rag/status?' + params.toString(), {
        headers: { accept: 'application/json' }
      });
      var payload = await response.json().catch(function(){ return null; });
      if (!response.ok || !payload || payload.ok !== true) {
        throw new Error(payload && payload.message ? payload.message : 'knowledge_rag_status_' + response.status);
      }
      var nextDigest = knowledgeRagRegistryStatusDigest(payload);
      pageUiState.knowledgeRagSummary = {
        scopeKey: scopeKey,
        loading: false,
        error: '',
        status: payload
      };
      if (knowledgeRagStatusHasInFlight(payload)) {
        startKnowledgeRagPipelineWatcher(options.reason || 'status-in-flight');
      } else if (!knowledgeRagPipelineWatcherRefreshPending) {
        stopKnowledgeRagPipelineWatcher();
      }
      if (options.notifyUpdated || previousDigest !== nextDigest) {
        notifyKnowledgeRagSourceUpdated({ action: 'status-sync', reason: options.reason || 'registry-status-changed', status: payload });
      }
    } catch (error) {
      pageUiState.knowledgeRagSummary = {
        scopeKey: scopeKey,
        loading: false,
        error: error instanceof Error ? error.message : String(error),
        status: {}
      };
    }
    renderKnowledgeRagSettings();
  }

  function addKnowledgeRagSourceInput() {
    var popover = ensureKnowledgeRagSettingsPopover();
    var node = popover.querySelector('[data-testid="mnote-knowledge-rag-source-inputs"]');
    if (!(node instanceof HTMLElement)) return;
    node.insertAdjacentHTML('beforeend', knowledgeRagSourceInputRow(''));
    var inputs = Array.from(node.querySelectorAll('[data-knowledge-rag-source-input]'));
    var last = inputs[inputs.length - 1];
    if (last instanceof HTMLInputElement) last.focus();
  }

  function knowledgeRagFileTreeSelectionPaths() {
    var rows = Array.from(document.querySelectorAll(
      '#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-selected="true"][data-local-relative-path]'
    ));
    if (!rows.length) {
      rows = Array.from(document.querySelectorAll(
        '#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-active="true"][data-local-relative-path]'
      ));
    }
    return rows.map(function(row) {
      return String(row.getAttribute('data-local-relative-path') || '').trim();
    }).filter(Boolean).filter(function(value, index, list) {
      return list.indexOf(value) === index;
    });
  }

  function useKnowledgeRagFileTreeSelection() {
    var selectedPaths = knowledgeRagFileTreeSelectionPaths();
    if (!selectedPaths.length) {
      pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
        scopeKey: knowledgeRagScopeKey(),
        loading: false,
        error: '请先在文件树中选中要索引的文件或目录'
      });
      renderKnowledgeRagSettings();
      return;
    }
    var popover = ensureKnowledgeRagSettingsPopover();
    var node = popover.querySelector('[data-testid="mnote-knowledge-rag-source-inputs"]');
    if (!(node instanceof HTMLElement)) return;
    var existing = knowledgeRagSourceInputValues();
    var next = existing.concat(selectedPaths).filter(function(value, index, list) {
      return list.indexOf(value) === index;
    });
    node.innerHTML = next.map(knowledgeRagSourceInputRow).join('');
  }

  function notifyKnowledgeRagSourceUpdated(detail) {
    window.dispatchEvent(new CustomEvent('mnote:knowledge-rag-source-updated', {
      detail: Object.assign({
        rootUri: currentRootUri(),
        workspaceId: resolveWorkspaceId(document.body)
      }, detail || {})
    }));
  }

  function knowledgeRagRegistryStatusDigest(status) {
    var registry = status && status.registry && typeof status.registry === 'object' ? status.registry : {};
    var entries = Array.isArray(registry.entries) ? registry.entries : [];
    return entries.map(function(entry) {
      return [
        String(entry && entry.sourceRootRelativePath || ''),
        String(entry && entry.lightRagDocId || ''),
        String(entry && entry.lightRagStatus || ''),
        String(entry && entry.indexedAtMs || ''),
        String(entry && entry.deletedAtMs || ''),
        entry && entry.stale ? 'stale' : 'fresh'
      ].join('|');
    }).sort().join('\n');
  }

  function removeKnowledgeRagSourceInput(button) {
    var row = button && button.closest ? button.closest('[data-knowledge-rag-source-input-row]') : null;
    if (row instanceof HTMLElement) row.remove();
    var popover = ensureKnowledgeRagSettingsPopover();
    var node = popover.querySelector('[data-testid="mnote-knowledge-rag-source-inputs"]');
    if (node instanceof HTMLElement && !node.querySelector('[data-knowledge-rag-source-input]')) {
      node.innerHTML = knowledgeRagSourceInputRow('');
    }
  }

  function resetKnowledgeRagSourceInputs() {
    var popover = ensureKnowledgeRagSettingsPopover();
    var node = popover.querySelector('[data-testid="mnote-knowledge-rag-source-inputs"]');
    if (node instanceof HTMLElement) node.innerHTML = knowledgeRagSourceInputRow('');
  }

  async function ingestKnowledgeRagSources() {
    if (!knowledgeRagIsAvailable()) return;
    var sources = knowledgeRagSourceInputValues();
    if (!sources.length) sources = knowledgeRagIndexedRootValues();
    if (!sources.length) {
      pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
        scopeKey: knowledgeRagScopeKey(),
        loading: false,
        error: '请先填写要索引的资料路径，或保留至少一个已保存的索引范围'
      });
      renderKnowledgeRagSettings();
      return;
    }
    setKnowledgeRagInputStatuses(Object.fromEntries(sources.map(function(path) {
      return [path, 'running'];
    })));
    pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
      scopeKey: knowledgeRagScopeKey(),
      loading: true,
      error: ''
    });
    renderKnowledgeRagSettings();
    try {
      var response = await fetch('/api/knowledge-rag/ingest', {
        method: 'POST',
        headers: { 'content-type': 'application/json', accept: 'application/json' },
        body: JSON.stringify({
          workspaceId: resolveWorkspaceId(document.body),
          rootUri: currentRootUri(),
          sources: sources.map(function(path) { return { sourcePath: path }; })
        })
      });
      var payload = await response.json().catch(function(){ return null; });
      if (!response.ok || !payload) {
        throw new Error(payload && payload.message ? payload.message : 'knowledge_rag_ingest_' + response.status);
      }
      if (payload.retryRequired) {
        setKnowledgeRagInputStatuses(Object.fromEntries(sources.map(function(path) {
          return [path, 'retry'];
        })));
        await loadKnowledgeRagStatus(true);
        notifyKnowledgeRagSourceUpdated({ action: 'ingest', sources: sources, result: payload, retryRequired: true });
        scheduleKnowledgeRagStatusBridge('ingest-retry');
        pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
          scopeKey: knowledgeRagScopeKey(),
          loading: false,
          error: 'LightRAG 正在处理其他任务，请稍后刷新或重试'
        });
        renderKnowledgeRagSettings();
        return;
      }
      var configured = Array.isArray(payload.configuredSources) ? payload.configuredSources : [];
      var done = {};
      configured.forEach(function(item) {
        var requested = String(item && item.requestedSourcePath || item && item.sourceRootRelativePath || '').trim();
        if (requested) done[requested] = 'done';
      });
      setKnowledgeRagInputStatuses(done, 'done');
      await loadKnowledgeRagStatus(true);
      resetKnowledgeRagSourceInputs();
      notifyKnowledgeRagSourceUpdated({ action: 'ingest', sources: sources, result: payload });
      scheduleKnowledgeRagStatusBridge('ingest');
    } catch (error) {
      setKnowledgeRagInputStatuses(Object.fromEntries(sources.map(function(path) {
        return [path, 'failed'];
      })));
      pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
        scopeKey: knowledgeRagScopeKey(),
        loading: false,
        error: error instanceof Error ? error.message : String(error)
      });
      renderKnowledgeRagSettings();
    }
  }

  async function ingestSingleKnowledgeRagSource(sourcePath) {
    sourcePath = String(sourcePath || '').trim();
    if (!sourcePath || !knowledgeRagIsAvailable()) return;
    setKnowledgeRagInputStatuses({ [sourcePath]: 'running' });
    pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
      scopeKey: knowledgeRagScopeKey(),
      loading: true,
      error: ''
    });
    renderKnowledgeRagSettings();
    try {
      var response = await fetch('/api/knowledge-rag/ingest', {
        method: 'POST',
        headers: { 'content-type': 'application/json', accept: 'application/json' },
        body: JSON.stringify({
          workspaceId: resolveWorkspaceId(document.body),
          rootUri: currentRootUri(),
          sources: [{ sourcePath: sourcePath }],
          force: true
        })
      });
      var payload = await response.json().catch(function(){ return null; });
      if (!response.ok || !payload) {
        throw new Error(payload && payload.message ? payload.message : 'knowledge_rag_reindex_' + response.status);
      }
      if (payload.retryRequired) {
        await loadKnowledgeRagStatus(true);
        notifyKnowledgeRagSourceUpdated({ action: 'reindex', sourceRootRelativePath: sourcePath, result: payload, retryRequired: true });
        scheduleKnowledgeRagStatusBridge('reindex-retry');
        pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
          scopeKey: knowledgeRagScopeKey(),
          loading: false,
          error: 'LightRAG 正在处理其他任务，请稍后重试'
        });
        renderKnowledgeRagSettings();
        return;
      }
      await loadKnowledgeRagStatus(true);
      notifyKnowledgeRagSourceUpdated({ action: 'reindex', sourceRootRelativePath: sourcePath, result: payload });
      scheduleKnowledgeRagStatusBridge('reindex');
    } catch (error) {
      pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
        scopeKey: knowledgeRagScopeKey(),
        loading: false,
        error: error instanceof Error ? error.message : String(error)
      });
      renderKnowledgeRagSettings();
    }
  }

  async function deleteKnowledgeRagSource(sourcePath) {
    sourcePath = String(sourcePath || '').trim();
    if (!sourcePath || !knowledgeRagIsAvailable()) return;
    pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
      scopeKey: knowledgeRagScopeKey(),
      loading: true,
      error: ''
    });
    renderKnowledgeRagSettings();
    try {
      var response = await fetch('/api/knowledge-rag/delete-source', {
        method: 'POST',
        headers: { 'content-type': 'application/json', accept: 'application/json' },
        body: JSON.stringify({
          workspaceId: resolveWorkspaceId(document.body),
          rootUri: currentRootUri(),
          sourcePath: sourcePath
        })
      });
      var payload = await response.json().catch(function(){ return null; });
      if (!response.ok || !payload || payload.ok !== true) {
        throw new Error(payload && payload.message ? payload.message : 'knowledge_rag_delete_' + response.status);
      }
      await loadKnowledgeRagStatus(true);
      notifyKnowledgeRagSourceUpdated({ action: 'delete', sourceRootRelativePath: sourcePath, result: payload });
    } catch (error) {
      pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
        scopeKey: knowledgeRagScopeKey(),
        loading: false,
        error: error instanceof Error ? error.message : String(error)
      });
      renderKnowledgeRagSettings();
    }
  }

  async function pruneKnowledgeRagRegistry() {
    if (!knowledgeRagIsAvailable()) return;
    pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
      scopeKey: knowledgeRagScopeKey(),
      loading: true,
      error: ''
    });
    renderKnowledgeRagSettings();
    try {
      var response = await fetch('/api/knowledge-rag/prune-registry', {
        method: 'POST',
        headers: { 'content-type': 'application/json', accept: 'application/json' },
        body: JSON.stringify({
          workspaceId: resolveWorkspaceId(document.body),
          rootUri: currentRootUri()
        })
      });
      var payload = await response.json().catch(function(){ return null; });
      if (!response.ok || !payload || payload.ok !== true) {
        throw new Error(payload && payload.message ? payload.message : 'knowledge_rag_prune_' + response.status);
      }
      await loadKnowledgeRagStatus(true);
      notifyKnowledgeRagSourceUpdated({ action: 'prune', result: payload });
    } catch (error) {
      pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
        scopeKey: knowledgeRagScopeKey(),
        loading: false,
        error: error instanceof Error ? error.message : String(error)
      });
      renderKnowledgeRagSettings();
    }
  }

  function openKnowledgeRagDashboard() {
    var status = currentKnowledgeRagSummary().status || {};
    var url = publicKnowledgeRagDashboardUrl(status);
    if (!url) return;
    window.open(url, '_blank', 'noopener');
  }

  function knowledgeRagInputStatusText(status) {
    if (status === 'running') return '索引中';
    if (status === 'done') return '已提交';
    if (status === 'retry') return '需重试';
    if (status === 'failed') return '失败';
    return '待索引';
  }

  function setKnowledgeRagInputStatuses(statusByPath, fallbackStatus) {
    var popover = ensureKnowledgeRagSettingsPopover();
    Array.from(popover.querySelectorAll('[data-knowledge-rag-source-input-row]')).forEach(function(row) {
      var input = row.querySelector('[data-knowledge-rag-source-input]');
      var label = row.querySelector('[data-knowledge-rag-input-status-label]');
      if (!(input instanceof HTMLInputElement) || !(label instanceof HTMLElement)) return;
      var value = input.value.trim();
      var status = statusByPath && statusByPath[value] || fallbackStatus || 'idle';
      label.textContent = knowledgeRagInputStatusText(status);
      label.setAttribute('data-status', status);
      label.hidden = status === 'idle';
    });
  }

  function renderPageSettingsPopover() {
    var popover = ensurePageSettingsPopover();
    var options = currentPageOptions();
    popover.querySelectorAll('[data-page-option-checkbox]').forEach(function(input) {
      var key = input.getAttribute('data-page-option-checkbox');
      input.checked = Boolean(options[key]);
      input.disabled = !pageOptionIsSupported(key);
    });
    popover.querySelectorAll('[data-page-option-select]').forEach(function(select) {
      var key = select.getAttribute('data-page-option-select');
      var value = key === 'layoutDensity' ? String(options.layoutDensity || 'normal') : String(options.pageFont || 'default');
      select.value = value;
    });
    renderGlobalOptions(popover);
    var statsNode = popover.querySelector('[data-testid="wolai-page-settings-stats"]');
    if (statsNode instanceof HTMLElement) {
      var stats = computeLivePageStats();
      statsNode.innerHTML = '' +
        '<span>字数 ' + Number(stats.wordCount || 0) + '</span>' +
        '<span>字符 ' + Number(stats.characterCount || 0) + '</span>' +
        '<span>块数 ' + Number(stats.blockCount || 0) + '</span>' +
        '<span>待办 ' + Number(stats.todoDone || 0) + '/' + Number(stats.todoTotal || 0) + '</span>';
    }
  }

  function setActivePageSettingsTab(tabName) {
    var popover = ensurePageSettingsPopover();
    popover.querySelectorAll('[data-page-settings-tab]').forEach(function(tab) {
      var active = tab.getAttribute('data-page-settings-tab') === tabName;
      tab.classList.toggle('is-active', active);
      tab.setAttribute('aria-selected', active ? 'true' : 'false');
    });
    popover.querySelectorAll('[data-page-settings-panel]').forEach(function(panel) {
      panel.hidden = panel.getAttribute('data-page-settings-panel') !== tabName;
    });
  }

  async function persistPageOptionsPatch(patch) {
    var previous = Object.assign({}, currentPageOptions());
    pageUiState.pageOptions = Object.assign({}, previous, patch);
    var nextOptions = Object.assign({}, pageUiState.pageOptions);
    applyPageOptionsToShell();
    renderPageSettingsPopover();
    try {
      var response = await fetch('/api/ui/preferences', {
        method: 'PUT',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({
          documentId: currentDocumentId(),
          workspaceId: resolveWorkspaceId(document.body),
          ...currentWorkspaceSourcePayload(),
          updates: nextOptions
        })
      });
      var payload = await response.json().catch(function(){ return null; });
      if (!response.ok || !payload || payload.ok !== true) {
        throw new Error(payload && payload.error && payload.error.message ? payload.error.message : 'page_settings_save_failed_' + response.status);
      }
      var savedOptions = payload && payload.result && payload.result.pageOptions && typeof payload.result.pageOptions === 'object'
        ? Object.assign(defaultPageOptions(), payload.result.pageOptions)
        : nextOptions;
      pageUiState.pageOptions = Object.assign({}, savedOptions);
      nextOptions = Object.assign({}, pageUiState.pageOptions);
      applyPageOptionsToShell();
      renderPageSettingsPopover();
      var script = document.getElementById('__MNOTE_PAGE_AGGREGATE__');
      if (script) {
        try {
          var aggregate = JSON.parse(script.textContent || 'null');
          if (aggregate && typeof aggregate === 'object') {
            aggregate.layout = aggregate.layout && typeof aggregate.layout === 'object' ? aggregate.layout : {};
            aggregate.layout.pageOptions = Object.assign({}, nextOptions);
            aggregate.layout_options = Object.assign({}, nextOptions);
            script.textContent = JSON.stringify(aggregate);
            script.setAttribute('data-mnote-page-options-synced-at', String(Date.now()));
            script.setAttribute('data-mnote-page-options-local-write-at', String(Date.now()));
          }
        } catch (_) {}
      }
      document.documentElement.setAttribute('data-mnote-page-options-saved', 'true');
    } catch (error) {
      pageUiState.pageOptions = previous;
      applyPageOptionsToShell();
      renderPageSettingsPopover();
      document.documentElement.setAttribute('data-mnote-page-options-error', error instanceof Error ? error.message : String(error));
    }
  }

  async function loadPageWidthPreferences() {
    var params = new URLSearchParams();
    if (currentDocumentId()) params.set('documentId', currentDocumentId());
    params.set('workspaceId', resolveWorkspaceId(document.body));
    var sourcePayload = currentWorkspaceSourcePayload();
    Object.keys(sourcePayload || {}).forEach(function(key) {
      if (sourcePayload[key] != null && sourcePayload[key] !== '') params.set(key, sourcePayload[key]);
    });
    try {
      var response = await fetch('/api/ui/preferences/effective?' + params.toString(), {
        headers: { accept: 'application/json' }
      });
      var payload = await response.json().catch(function(){ return null; });
      if (!response.ok || !payload || payload.ok !== true) return;
      pageUiState.pageWidthPreferences = normalizePageWidthPreferences(payload.result && payload.result.pageWidthPreferences);
      markRetiredLocalOcrPreferenceSurface();
      applyPageOptionsToShell();
      if (isPageSettingsOpen()) renderPageSettingsPopover();
    } catch (_) {}
  }

  async function persistPageWidthPreference(type, mode) {
    if (PAGE_WIDTH_TYPES.indexOf(type) < 0) return;
    var previous = pageUiState.pageWidthPreferences;
    var next = normalizePageWidthPreferences(previous);
    next[type] = Object.assign({}, next[type], {
      mode: mode,
      resolvedMode: mode === 'inherit' ? next.default.resolvedMode : mode,
      cssMaxWidth: pageWidthCssMaxWidth(mode === 'inherit' ? next.default.resolvedMode : mode),
      source: 'optimistic'
    });
    pageUiState.pageWidthPreferences = next;
    applyPageOptionsToShell();
    renderPageSettingsPopover();
    notifyPageWidthPreferenceChanged(type);
    try {
      var updates = {};
      updates['pageWidth.' + type] = { mode: mode, custom: null };
      var response = await fetch('/api/ui/preferences', {
        method: 'PUT',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({
          documentId: currentDocumentId(),
          workspaceId: resolveWorkspaceId(document.body),
          ...currentWorkspaceSourcePayload(),
          updates: updates
        })
      });
      var payload = await response.json().catch(function(){ return null; });
      if (!response.ok || !payload || payload.ok !== true) {
        throw new Error(payload && payload.error && payload.error.message ? payload.error.message : 'page_width_save_failed_' + response.status);
      }
      pageUiState.pageWidthPreferences = normalizePageWidthPreferences(payload.result && payload.result.pageWidthPreferences);
      applyPageOptionsToShell();
      renderPageSettingsPopover();
      notifyPageWidthPreferenceChanged(type);
      document.documentElement.setAttribute('data-mnote-page-width-saved', 'true');
    } catch (error) {
      pageUiState.pageWidthPreferences = previous;
      applyPageOptionsToShell();
      renderPageSettingsPopover();
      document.documentElement.setAttribute('data-mnote-page-width-error', error instanceof Error ? error.message : String(error));
    }
  }

  function isPageSettingsOpen() {
    var popover = document.querySelector('[data-testid="wolai-page-settings-popover"]');
    return popover instanceof HTMLElement && !popover.hidden;
  }

  function isLocalIndexSettingsOpen() {
    var popover = document.querySelector('[data-testid="mnote-local-index-settings-popover"]');
    return popover instanceof HTMLElement && !popover.hidden;
  }

  function isKnowledgeRagSettingsOpen() {
    var popover = document.querySelector('[data-testid="mnote-knowledge-rag-settings-popover"]');
    return popover instanceof HTMLElement && !popover.hidden;
  }

  function isAnySettingsOpen() {
    return isPageSettingsOpen() || isLocalIndexSettingsOpen() || isKnowledgeRagSettingsOpen();
  }

  function openPageSettingsPopover(initialTab) {
    if (!currentDocumentId()) return;
    closeLocalIndexSettingsPopover();
    closeKnowledgeRagSettingsPopover();
    var popover = ensurePageSettingsPopover();
    renderPageSettingsPopover();
    setActivePageSettingsTab(initialTab || 'page');
    popover.hidden = false;
    pageUiState.pageSettingsOpen = true;
    updatePageSettingsTriggerState();
  }

  function openPageIndexSettingsPopover() {
    closePageSettingsPopover();
    closeKnowledgeRagSettingsPopover();
    var popover = ensureLocalIndexSettingsPopover();
    renderPageSettingsLocalIndex(popover);
    popover.hidden = false;
    void loadPageSettingsLocalIndex(false);
    updateStandaloneSettingsTriggerState();
  }

  function openKnowledgeRagSettingsPopover() {
    closePageSettingsPopover();
    closeLocalIndexSettingsPopover();
    var popover = ensureKnowledgeRagSettingsPopover();
    renderKnowledgeRagSettings(popover);
    popover.hidden = false;
    void loadKnowledgeRagStatus(false).then(function() {
      if (knowledgeRagStatusHasInFlight(currentKnowledgeRagSummary().status || {})) {
        scheduleKnowledgeRagStatusBridge('open-panel');
      }
    });
    updateStandaloneSettingsTriggerState();
  }

  function closePageSettingsPopover() {
    var popover = document.querySelector('[data-testid="wolai-page-settings-popover"]');
    if (popover instanceof HTMLElement) popover.hidden = true;
    pageUiState.pageSettingsOpen = false;
    updatePageSettingsTriggerState();
  }

  function closeLocalIndexSettingsPopover() {
    var popover = document.querySelector('[data-testid="mnote-local-index-settings-popover"]');
    if (popover instanceof HTMLElement) popover.hidden = true;
    updateStandaloneSettingsTriggerState();
  }

  function closeKnowledgeRagSettingsPopover() {
    var popover = document.querySelector('[data-testid="mnote-knowledge-rag-settings-popover"]');
    if (popover instanceof HTMLElement) popover.hidden = true;
    updateStandaloneSettingsTriggerState();
  }

  function closeAllSettingsPopovers() {
    closePageSettingsPopover();
    closeLocalIndexSettingsPopover();
    closeKnowledgeRagSettingsPopover();
  }

  function togglePageSettingsPopover() {
    if (isPageSettingsOpen()) closePageSettingsPopover();
    else openPageSettingsPopover();
  }

  window.addEventListener('mnote:active-resource-tab-changed', function() {
    applyPageOptionsToShell();
  });

  window.addEventListener('mnote:knowledge-rag-source-updated', function() {
    if (isKnowledgeRagSettingsOpen()) void loadKnowledgeRagStatus(true);
  });

  return {
    addLocalIndexRange,
    applyPageOptionsToShell,
    closeAllSettingsPopovers,
    closeKnowledgeRagSettingsPopover,
    closePageHistoryDrawer,
    closeLocalIndexSettingsPopover,
    closePageSettingsPopover,
    closePageShareDialog,
    currentKnowledgeRagSummary,
    currentPageOptions,
    ensureHistorySnapshotsSeeded,
    ensureKnowledgeRagSettingsPopover,
    ensureLocalIndexSettingsPopover,
    ensurePageHistoryDrawer,
    ensurePageSettingsPopover,
    ensurePageShareDialog,
    isAnySettingsOpen,
    isKnowledgeRagSettingsOpen,
    isLocalIndexSettingsOpen,
    isPageSettingsOpen,
    openKnowledgeRagSettingsPopover,
    openPageHistoryDrawer,
    openPageSettingsPopover,
    openPageIndexSettingsPopover,
    openPageShareDialog,
    pageOptionIsSupported,
    persistLocalIndexSettings,
    persistPageOptionsPatch,
    persistPageWidthPreference,
    recordPageHistorySnapshot,
    removeLocalIndexRange,
    renderPageSettingsPopover,
    setActivePageSettingsTab,
    refreshLocalIndex,
    addKnowledgeRagSourceInput,
    deleteKnowledgeRagSource,
    ingestKnowledgeRagSources,
    ingestSingleKnowledgeRagSource,
    loadKnowledgeRagStatus,
    openKnowledgeRagDashboard,
    pruneKnowledgeRagRegistry,
    removeKnowledgeRagSourceInput,
    setKnowledgeRagSourceFilter,
    useKnowledgeRagFileTreeSelection,
    togglePageSettingsPopover,
    updatePageSettingsTriggerState,
    writeGlobalShowHeadingNumbers,
    loadPageWidthPreferences,
  };
}
