class MainPanel extends HTMLElement {
  connectedCallback() {
    if (this._initialized) return;
    this._initialized = true;
    this.className = 'main-panel';
    this.innerHTML = `
      <div id="panel-search" class="panel panel-search">
        <div class="search-container">
          <input type="text" id="query" class="search-input" placeholder="知識を検索..." autocomplete="off">
          <select id="search-category-filter" class="search-category-filter" title="カテゴリで絞り込み">
            <option value="">すべて</option>
          </select>
          <button onclick="search()" class="search-btn">検索</button>
        </div>

        <div id="result" class="results-panel">
          <div class="empty-state">クエリを入力して検索を開始してください</div>
        </div>

        <div class="sidebar-bottom" style="margin-top: auto; border-top: 1px solid var(--border-base);">
          <div class="accordion">
            <button class="accordion-header activity-toggle" aria-expanded="false" style="padding: 12px 16px;">
              MCP ACTIVITY <span id="activity-count" style="font-weight:normal; font-size: 0.65rem; margin-left: 8px;">LISTEN</span>
              <span class="accordion-toggle" style="margin-left: auto;">▸</span>
            </button>
            <div class="accordion-content activity-content" hidden style="padding: 0 16px 16px 16px;">
              <div id="connection-error-hint" class="connection-error-hint" style="display:none; margin-bottom:8px; padding:8px 10px; background: rgba(255,193,7,0.12); border: 1px solid var(--border-subtle); border-radius:6px; font-size:0.8rem; color: var(--text-secondary);">
                バックエンドの起動を待っています。しばらくお待ちください。
              </div>
              <div class="activity-log" style="height: 140px; margin-top: 0; background: rgba(255,255,255,0.02); border-color: var(--border-subtle);">
                <div id="activity-items"></div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <div id="panel-docs" class="panel panel-docs hidden">
        <h2 style="font-family:Outfit; margin-bottom:12px;">文書管理</h2>
        <div class="docs-toolbar">
          <button type="button" id="docs-add-btn" class="secondary-btn">新規登録</button>
          <button type="button" id="docs-refresh-btn" class="secondary-btn">一覧更新</button>
        </div>
        <div id="docs-list" class="docs-list">
          <div class="empty-state">一覧を読み込み中...</div>
        </div>
        <div id="docs-edit-modal" class="docs-edit-modal hidden">
          <div class="docs-edit-modal-backdrop"></div>
          <div class="docs-edit-modal-box">
            <div id="docs-form-area" class="docs-form-area">
              <fieldset class="settings-fieldset">
                <legend id="docs-form-legend">新規登録</legend>
                <div class="setting-row">
                  <label for="docs-path">パス (path)</label>
                  <input type="text" id="docs-path" placeholder="例: my-note.md" class="docs-input">
                </div>
                <div class="setting-row docs-import-row">
                  <label>ファイル</label>
                  <div class="docs-import-controls">
                    <input type="file" id="docs-file-input" accept=".txt,.md,.json,.csv,.html,.xml,.log,text/plain,text/markdown,text/html,application/json" aria-label="ファイルを選択">
                    <button type="button" id="docs-import-file-btn" class="secondary-btn">ファイルを取り込む</button>
                    <span id="docs-import-feedback" class="docs-import-feedback"></span>
                  </div>
                </div>
                <div class="setting-row">
                  <label for="docs-editor-container">本文</label>
                  <div id="docs-editor-container" class="docs-editor-container"></div>
                </div>
                <div class="setting-actions">
                  <button type="button" id="docs-save-btn" class="secondary-btn">保存</button>
                  <button type="button" id="docs-cancel-btn" class="secondary-btn">キャンセル</button>
                  <span id="docs-form-feedback" class="settings-feedback"></span>
                </div>
              </fieldset>
            </div>
          </div>
        </div>
      </div>

      <div id="panel-settings" class="panel panel-settings hidden">
        <h2 style="font-family:Outfit; margin-bottom:12px;">設定</h2>
        <div class="settings-form">
          <fieldset class="settings-fieldset">
            <legend>検索</legend>
            <div class="setting-row">
              <label for="setting-min-score">スコア足切り (0〜1)</label>
              <input type="number" id="setting-min-score" min="0" max="1" step="0.05" value="0.3" title="この値未満の類似度の結果は表示しません">
            </div>
            <div class="setting-row">
              <label for="setting-limit">取得件数</label>
              <input type="number" id="setting-limit" min="1" max="100" value="5" title="検索で返す最大件数">
            </div>
          </fieldset>
          <fieldset class="settings-fieldset">
            <legend>ログイン時自動起動</legend>
            <div class="setting-row">
              <label for="setting-run-on-login">有効にする</label>
              <input type="checkbox" id="setting-run-on-login" aria-describedby="setting-run-on-login-desc">
              <span id="setting-run-on-login-desc" class="setting-hint">OS にサインインしたときに TelosDB を自動で起動します</span>
            </div>
          </fieldset>
          <fieldset class="settings-fieldset">
            <legend id="label-monitor-paths">モニター先フォルダ</legend>
            <div class="setting-row">
              <div id="settings-monitor-paths-list" class="setting-monitor-paths-list" aria-labelledby="label-monitor-paths"></div>
              <button type="button" id="settings-monitor-path-add" class="secondary-btn">追加</button>
            </div>
            <div class="setting-row setting-row-watch-extensions">
              <label for="setting-watch-extensions">取込対象拡張子</label>
              <div class="setting-watch-extensions-cell">
                <input type="text" id="setting-watch-extensions" class="setting-watch-extensions" value="txt, md, json, html, css, js, mjs, ts, rs" placeholder="例: txt, md, json" aria-describedby="setting-watch-extensions-desc" title="カンマ区切り。モニター先フォルダ内のこの拡張子のファイルのみ取り込みます">
                <span id="setting-watch-extensions-desc" class="setting-hint setting-hint-block">カンマ区切り（例: txt, md, json）。空の場合は既定の拡張子を使用</span>
              </div>
            </div>
          </fieldset>
          <div class="setting-actions">
            <button type="button" id="settings-save-btn" class="secondary-btn">保存</button>
            <span id="settings-feedback" class="settings-feedback" aria-live="polite"></span>
          </div>
        </div>
      </div>
    `;

    // Handle navigation events from sidebar
    document.addEventListener('navigate-panel', (e) => {
      const target = String(e.detail || 'search');
      this.showPanel(target);
    });

    // Expose showPanel as method
    const SETTINGS_KEY = 'telosdb_settings';
    const DEFAULT_EXTENSIONS = ['txt', 'md', 'json', 'html', 'css', 'js', 'mjs', 'ts', 'rs'];
    const DEFAULTS = { min_score: 0.3, limit: 5, run_on_login: false, monitor_paths: [], watch_extensions: DEFAULT_EXTENSIONS };
    if (!this._removeFromIndexPaths) this._removeFromIndexPaths = new Set();

    const renderMonitorPathsList = (paths) => {
      const listEl = this.querySelector('#settings-monitor-paths-list');
      if (!listEl) return;
      const arr = Array.isArray(paths) ? paths : [];
      listEl.innerHTML = '';
      const addRow = (pathValue = '', categoryValue = '') => {
        const row = document.createElement('div');
        row.className = 'setting-monitor-path-row';
        row.innerHTML = `
          <input type="text" class="setting-monitor-path" placeholder="例: C:\\Documents\\Notes" aria-label="モニター先フォルダパス">
          <input type="text" class="setting-monitor-category" placeholder="カテゴリ名" aria-label="カテゴリ名" title="この監視フォルダのカテゴリ名（検索時の絞り込みに使用）">
          <button type="button" class="setting-monitor-path-remove secondary-btn" title="この行を削除">削除</button>
        `;
        row.querySelector('.setting-monitor-path').value = String(pathValue || '');
        row.querySelector('.setting-monitor-category').value = String(categoryValue || '');
        listEl.appendChild(row);
      };
      arr.forEach((p) => {
        if (typeof p === 'object' && p !== null) {
          addRow(p.path || '', p.category || '');
        } else {
          addRow(String(p || ''), '');
        }
      });
    };

    const loadSettingsIntoForm = (settingsOverrides) => {
      try {
        const s = settingsOverrides != null
          ? { ...DEFAULTS, ...settingsOverrides }
          : (() => {
              const raw = localStorage.getItem(SETTINGS_KEY);
              return raw ? { ...DEFAULTS, ...JSON.parse(raw) } : DEFAULTS;
            })();
        const minScoreEl = this.querySelector('#setting-min-score');
        const limitEl = this.querySelector('#setting-limit');
        const runOnLoginEl = this.querySelector('#setting-run-on-login');
        if (minScoreEl) minScoreEl.value = String(Number(s.min_score));
        if (limitEl) limitEl.value = String(Number(s.limit));
        if (runOnLoginEl) runOnLoginEl.checked = Boolean(s.run_on_login);
        renderMonitorPathsList(s.monitor_paths);
        const extEl = this.querySelector('#setting-watch-extensions');
        if (extEl) extEl.value = Array.isArray(s.watch_extensions) ? s.watch_extensions.join(', ') : (s.watch_extensions || '');
      } catch (e) {
        const minScoreEl = this.querySelector('#setting-min-score');
        const limitEl = this.querySelector('#setting-limit');
        const runOnLoginEl = this.querySelector('#setting-run-on-login');
        if (minScoreEl) minScoreEl.value = String(DEFAULTS.min_score);
        if (limitEl) limitEl.value = String(DEFAULTS.limit);
        if (runOnLoginEl) runOnLoginEl.checked = DEFAULTS.run_on_login;
        renderMonitorPathsList(DEFAULTS.monitor_paths);
        const extEl = this.querySelector('#setting-watch-extensions');
        if (extEl) extEl.value = DEFAULTS.watch_extensions.join(', ');
      }
    };

    const API_BASE = (typeof window !== 'undefined' && window.API_BASE) ? window.API_BASE : 'http://127.0.0.1:3001';

    // Tauri 内では invoke でアプリデータの settings.json を直接読む（MCP 起動待ち不要で永続化が確実）
    const loadSettingsFromFile = async () => {
      try {
        const { invoke } = await import('@tauri-apps/api/core');
        const fileSettings = await invoke('get_app_settings');
        if (fileSettings && typeof fileSettings === 'object') {
          return {
            min_score: fileSettings.min_score ?? DEFAULTS.min_score,
            limit: fileSettings.limit ?? DEFAULTS.limit,
            run_on_login: Boolean(fileSettings.run_on_login),
            monitor_paths: Array.isArray(fileSettings.monitor_paths) ? fileSettings.monitor_paths : DEFAULTS.monitor_paths,
            watch_extensions: Array.isArray(fileSettings.watch_extensions) ? fileSettings.watch_extensions : DEFAULTS.watch_extensions,
          };
        }
      } catch (_) {
        // Tauri 外（ブラウザ等）または invoke 失敗時は HTTP で取得
        try {
          const res = await fetch(`${API_BASE}/settings`);
          if (!res.ok) return null;
          const fileSettings = await res.json();
          if (fileSettings && typeof fileSettings === 'object') {
            return {
              min_score: fileSettings.min_score ?? DEFAULTS.min_score,
              limit: fileSettings.limit ?? DEFAULTS.limit,
              run_on_login: Boolean(fileSettings.run_on_login),
              monitor_paths: Array.isArray(fileSettings.monitor_paths) ? fileSettings.monitor_paths : DEFAULTS.monitor_paths,
              watch_extensions: Array.isArray(fileSettings.watch_extensions) ? fileSettings.watch_extensions : DEFAULTS.watch_extensions,
            };
          }
        } catch (_) {}
      }
      return null;
    };

    const saveSettingsToFile = async (payload) => {
      const toPersist = { min_score: payload.min_score, limit: payload.limit, run_on_login: payload.run_on_login, monitor_paths: payload.monitor_paths, watch_extensions: payload.watch_extensions };
      const hasRemovals = Array.isArray(payload.remove_from_index_paths) && payload.remove_from_index_paths.length > 0;
      if (hasRemovals) {
        try {
          const res = await fetch(`${API_BASE}/settings`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ ...toPersist, remove_from_index_paths: payload.remove_from_index_paths }),
          });
          return res.ok;
        } catch (_) {
          return false;
        }
      }
      try {
        const { invoke } = await import('@tauri-apps/api/core');
        await invoke('set_app_settings', { settings: toPersist });
        return true;
      } catch (_) {
        try {
          const res = await fetch(`${API_BASE}/settings`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(toPersist),
          });
          return res.ok;
        } catch (_) {
          return false;
        }
      }
    };

    this.showPanel = (panelId) => {
      const panels = this.querySelectorAll('.panel');
      panels.forEach((p) => p && p.classList.add('hidden'));
      const target =
        panelId === 'docs'
          ? this.querySelector('#panel-docs')
          : panelId === 'settings'
            ? this.querySelector('#panel-settings')
            : this.querySelector('#panel-search');
      if (target) target.classList.remove('hidden');
      if (panelId === 'settings') {
        (async () => {
          try {
            const fromFile = await loadSettingsFromFile();
            if (fromFile) {
              try {
                const invoke = window.__TAURI__?.core?.invoke;
                if (invoke) {
                  const osEnabled = await invoke('plugin:autostart|is_enabled');
                  if (fromFile.run_on_login !== osEnabled) {
                    console.warn('[TelosDB] autostart: 設定値=', fromFile.run_on_login, ' OS実状態=', osEnabled, ' → OS側に合わせて表示');
                    fromFile.run_on_login = osEnabled;
                  }
                }
              } catch (_) {}
              loadSettingsIntoForm(fromFile);
              localStorage.setItem(SETTINGS_KEY, JSON.stringify(fromFile));
            } else {
              loadSettingsIntoForm();
            }
          } catch (_) {
            loadSettingsIntoForm();
          }
        })();
      }
    };
    // 起動時: 再インストール後も継承するため Tauri の場合はアプリデータの settings.json から読む
    loadSettingsIntoForm();

    // default show search
    this.showPanel('search');

    (async () => {
      try {
        let s = (() => {
          const raw = localStorage.getItem(SETTINGS_KEY);
          return raw ? { ...DEFAULTS, ...JSON.parse(raw) } : DEFAULTS;
        })();
        await new Promise((r) => setTimeout(r, 0));
        const fromFile = await loadSettingsFromFile();
        if (fromFile) {
          s = fromFile;
          loadSettingsIntoForm(s);
          localStorage.setItem(SETTINGS_KEY, JSON.stringify(s));
        } else {
          console.warn('[TelosDB] 設定ファイル読み込み失敗 — autostart 同期をスキップ');
          return;
        }
        const invoke = window.__TAURI__?.core?.invoke;
        if (invoke) {
          const enabled = await invoke('plugin:autostart|is_enabled');
          if (s.run_on_login && !enabled) {
            await invoke('plugin:autostart|enable');
            console.log('[TelosDB] autostart: 設定に合わせて有効化');
          } else if (!s.run_on_login && enabled) {
            await invoke('plugin:autostart|disable');
            console.log('[TelosDB] autostart: 設定に合わせて無効化');
          }
        }
      } catch (e) {
        console.error('[TelosDB] autostart 同期エラー:', e);
      }
    })();

    const runOnLoginCheckbox = this.querySelector('#setting-run-on-login');
    if (runOnLoginCheckbox) {
      runOnLoginCheckbox.addEventListener('change', () => {
        console.log('[TelosDB] 自動起動:', runOnLoginCheckbox.checked ? 'オン' : 'オフ');
      });
    }

    const saveBtn = this.querySelector('#settings-save-btn');
    const feedbackEl = this.querySelector('#settings-feedback');
    if (saveBtn && feedbackEl) {
      saveBtn.addEventListener('click', async () => {
        const minScoreEl = this.querySelector('#setting-min-score');
        const limitEl = this.querySelector('#setting-limit');
        const runOnLoginEl = this.querySelector('#setting-run-on-login');
        const min_score = Math.max(0, Math.min(1, parseFloat(minScoreEl?.value) || DEFAULTS.min_score));
        const limit = Math.max(1, Math.min(100, parseInt(limitEl?.value, 10) || DEFAULTS.limit));
        const run_on_login = runOnLoginEl ? runOnLoginEl.checked : DEFAULTS.run_on_login;
        const pathRows = this.querySelectorAll('.setting-monitor-path-row');
        const monitor_paths = Array.from(pathRows)
          .map((row) => {
            const p = (row.querySelector('.setting-monitor-path')?.value || '').trim();
            const c = (row.querySelector('.setting-monitor-category')?.value || '').trim();
            return p ? { path: p, category: c } : null;
          })
          .filter(Boolean);
        const extInput = this.querySelector('#setting-watch-extensions');
        const watch_extensions = (extInput?.value || '')
          .split(/[\s,]+/)
          .map((s) => s.trim().toLowerCase())
          .filter(Boolean);
        try {
          let autostartError = null;
          try {
            const invoke = window.__TAURI__?.core?.invoke;
            if (!invoke) throw new Error('Tauri API が利用できません');
            if (run_on_login) {
              await invoke('plugin:autostart|enable');
              console.log('[TelosDB] autostart: 有効化成功');
            } else {
              await invoke('plugin:autostart|disable');
              console.log('[TelosDB] autostart: 無効化成功');
            }
          } catch (autoErr) {
            autostartError = String(autoErr?.message || autoErr);
            console.error('[TelosDB] autostart 設定失敗:', autoErr);
          }
          const remove_from_index_paths = Array.from(this._removeFromIndexPaths || []);
          const payload = { min_score, limit, run_on_login, monitor_paths, watch_extensions: watch_extensions.length ? watch_extensions : DEFAULTS.watch_extensions, remove_from_index_paths };
          console.log('[TelosDB] 保存: run_on_login =', run_on_login, 'monitor_paths =', monitor_paths.length, 'watch_extensions =', payload.watch_extensions.length, 'remove_from_index_paths =', remove_from_index_paths.length);
          const saved = await saveSettingsToFile(payload);
          if (saved) {
            this._removeFromIndexPaths?.clear();
            const toStore = { min_score, limit, run_on_login, monitor_paths, watch_extensions: payload.watch_extensions };
            localStorage.setItem(SETTINGS_KEY, JSON.stringify(toStore));
            if (minScoreEl) minScoreEl.value = String(min_score);
            if (limitEl) limitEl.value = String(limit);
            if (runOnLoginEl) runOnLoginEl.checked = run_on_login;
            renderMonitorPathsList(monitor_paths);
            if (autostartError) {
              feedbackEl.textContent = '保存しました（自動起動の登録に失敗: ' + autostartError + '）';
              feedbackEl.classList.add('error');
            } else {
              feedbackEl.textContent = '保存しました';
              feedbackEl.classList.remove('error');
            }
            setTimeout(() => { feedbackEl.textContent = ''; }, autostartError ? 5000 : 2000);
          } else {
            feedbackEl.textContent = '保存に失敗しました（ファイルへの書き込みに失敗しました）';
            feedbackEl.classList.add('error');
          }
        } catch (e) {
          feedbackEl.textContent = '保存に失敗しました';
          feedbackEl.classList.add('error');
        }
      });
    }

    this.querySelector('#settings-monitor-path-add')?.addEventListener('click', async (e) => {
      e.preventDefault();
      const listEl = this.querySelector('#settings-monitor-paths-list');
      if (!listEl) return;

      let selectedPath = null;
      try {
        const invoke = window.__TAURI__?.core?.invoke;
        if (invoke) {
          selectedPath = await invoke('plugin:dialog|open', {
            options: {
              directory: true,
              multiple: false,
              title: 'モニター先フォルダを選択',
            },
          });
        }
      } catch (err) {
        console.warn('[TelosDB] folder dialog failed, falling back to text input:', err);
      }

      if (selectedPath === null) return;

      const existing = [...listEl.querySelectorAll('.setting-monitor-path')].map(el => el.value.trim());
      if (existing.includes(selectedPath)) return;

      const row = document.createElement('div');
      row.className = 'setting-monitor-path-row';
      row.innerHTML = `
        <input type="text" class="setting-monitor-path" placeholder="例: C:\\Documents\\Notes" aria-label="モニター先フォルダパス">
        <input type="text" class="setting-monitor-category" placeholder="カテゴリ名" aria-label="カテゴリ名" title="この監視フォルダのカテゴリ名（検索時の絞り込みに使用）">
        <button type="button" class="setting-monitor-path-remove secondary-btn" title="この行を削除">削除</button>
      `;
      row.querySelector('.setting-monitor-path').value = selectedPath;
      listEl.appendChild(row);
    });

    this.querySelector('#settings-monitor-paths-list')?.addEventListener('click', (e) => {
      if (e.target.classList.contains('setting-monitor-path-remove')) {
        e.preventDefault();
        const row = e.target.closest('.setting-monitor-path-row');
        if (!row) return;
        const pathInput = row.querySelector('.setting-monitor-path');
        const path = (pathInput?.value || '').trim();
        if (path) {
          if (!this._removeFromIndexPaths) this._removeFromIndexPaths = new Set();
          this._removeFromIndexPaths.add(path);
        }
        row.remove();
      }
    });

    // Activity Log accordion behavior
    const activityToggle = this.querySelector('.activity-toggle');
    const activityContent = this.querySelector('.activity-content');
    if (activityToggle && activityContent) {
      activityToggle.addEventListener('click', () => {
        const expanded = activityToggle.getAttribute('aria-expanded') === 'true';
        activityToggle.setAttribute('aria-expanded', String(!expanded));
        if (expanded) {
          activityContent.hidden = true;
          activityToggle.querySelector('.accordion-toggle').textContent = '▸';
        } else {
          activityContent.hidden = false;
          activityToggle.querySelector('.accordion-toggle').textContent = '▾';
        }
      });
    }

    // --- 文書管理パネル ---
    const callMcp = async (method, params = {}) => {
      const res = await fetch(`${API_BASE}/messages`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          jsonrpc: '2.0',
          method,
          params,
          id: Date.now(),
        }),
      });
      const data = await res.json();
      if (data.error) throw new Error(data.error.message || JSON.stringify(data.error));
      return data.result;
    };

    const parseResultText = (result) => {
      const content = result?.content;
      if (!content || !Array.isArray(content) || content.length === 0) return null;
      if (content[0].type === 'text' && content[0].text) {
        try { return JSON.parse(content[0].text); } catch (_) { return content[0].text; }
      }
      return null;
    };

    let editingDocumentId = null;
    let docsEditorInstance = null;

    const docsListEl = this.querySelector('#docs-list');
    const DOCS_PAGE_SIZE = 20;
    let docsCurrentPage = 1;
    const docsEditModal = this.querySelector('#docs-edit-modal');
    const docsFormArea = this.querySelector('#docs-form-area');
    const docsPathEl = this.querySelector('#docs-path');
    const docsEditorContainer = this.querySelector('#docs-editor-container');
    const docsFormLegend = this.querySelector('#docs-form-legend');
    const docsFormFeedback = this.querySelector('#docs-form-feedback');
    const docsFileInput = this.querySelector('#docs-file-input');
    const docsImportFileBtn = this.querySelector('#docs-import-file-btn');
    const docsImportFeedback = this.querySelector('#docs-import-feedback');

    const loadDocsList = async () => {
      if (!docsListEl) return;
      docsListEl.innerHTML = '<div class="empty-state">一覧を読み込み中...</div>';
      try {
        const result = await callMcp('list_documents', { limit: DOCS_PAGE_SIZE, page: docsCurrentPage });
        const raw = parseResultText(result);
        const list = Array.isArray(raw) ? raw : (raw?.items ?? []);
        const totalPages = Math.max(0, raw?.total_pages ?? 1);
        const totalCount = raw?.total_count ?? list.length;
        const currentPage = raw?.page ?? docsCurrentPage;
        docsCurrentPage = currentPage;

        if (!Array.isArray(list)) {
          docsListEl.innerHTML = '<div class="empty-state">一覧の取得に失敗しました</div>';
          return;
        }
        if (list.length === 0) {
          docsListEl.innerHTML = totalCount > 0
            ? '<div class="empty-state">このページには文書がありません</div>'
            : '<div class="empty-state">登録された文書はありません</div>';
          return;
        }

        const prevDisabled = currentPage <= 1;
        const nextDisabled = currentPage >= totalPages;
        const pagerHtml = totalPages > 1
          ? `<div class="docs-pager">
              <button type="button" class="docs-pager-btn" data-action="prev" ${prevDisabled ? 'disabled' : ''}>前へ</button>
              <span class="docs-pager-info">${currentPage} / ${totalPages} ページ（全 ${totalCount} 件）</span>
              <button type="button" class="docs-pager-btn" data-action="next" ${nextDisabled ? 'disabled' : ''}>次へ</button>
            </div>`
          : (totalCount > 0 ? `<p class="docs-page-info">全 ${totalCount} 件</p>` : '');

        docsListEl.innerHTML = pagerHtml + `
          <table class="docs-table">
            <thead>
              <tr><th>パス</th><th>MIME</th><th>チャンク数</th><th>先頭（chunk0）</th><th>操作</th></tr>
            </thead>
            <tbody>
              ${list.map(d => `
                <tr>
                  <td class="docs-cell-path">${escapeHtml(d.path || '')}</td>
                  <td>${escapeHtml(d.mime || '')}</td>
                  <td>${Number(d.chunk_count) || 0}</td>
                  <td class="docs-cell-preview" title="${escapeHtml((d.chunk0_preview || '') + (d.chunk_count > 1 ? '…' : ''))}">${escapeHtml(d.chunk0_preview || '')}${Number(d.chunk_count) > 1 ? '…' : ''}</td>
                  <td class="docs-actions">
                    <button type="button" class="docs-btn docs-btn-edit" data-id="${escapeHtml(String(d.id))}">編集</button>
                    <button type="button" class="docs-btn docs-btn-delete" data-id="${escapeHtml(String(d.id))}">削除</button>
                  </td>
                </tr>
              `).join('')}
            </tbody>
          </table>
        `;

        docsListEl.querySelectorAll('[data-action="prev"]').forEach(btn => {
          btn.addEventListener('click', () => {
            if (docsCurrentPage > 1) { docsCurrentPage--; loadDocsList(); }
          });
        });
        docsListEl.querySelectorAll('[data-action="next"]').forEach(btn => {
          btn.addEventListener('click', () => {
            if (docsCurrentPage < totalPages) { docsCurrentPage++; loadDocsList(); }
          });
        });
        docsListEl.querySelectorAll('.docs-btn-edit').forEach(btn => {
          btn.addEventListener('click', () => openDocEdit(btn.dataset.id));
        });
        docsListEl.querySelectorAll('.docs-btn-delete').forEach(btn => {
          btn.addEventListener('click', () => deleteDoc(btn.dataset.id));
        });
      } catch (e) {
        docsListEl.innerHTML = `<div class="empty-state error-state">エラー: ${escapeHtml(e.message)}</div>`;
      }
    };

    function escapeHtml(s) {
      const div = document.createElement('div');
      div.textContent = s;
      return div.innerHTML;
    }

    const destroyDocsEditor = () => {
      if (docsEditorInstance) {
        try { docsEditorInstance.destroy(); } catch (_) {}
        docsEditorInstance = null;
      }
    };

    const createDocsEditor = (initialValue = '') => {
      if (!docsEditorContainer) return;
      destroyDocsEditor();
      if (typeof toastui === 'undefined' || !toastui.Editor) {
        docsEditorContainer.innerHTML = '<p class="docs-editor-fallback">Toast UI Editor を読み込んでください。npm run build-editor を実行し、vendor/toast-ui/ にバンドルを生成してください。</p>';
        return;
      }
      docsEditorInstance = new toastui.Editor({
        el: docsEditorContainer,
        initialValue: initialValue || '',
        height: '400px',
        initialEditType: 'wysiwyg',
        previewStyle: 'tab',
        usageStatistics: false,
        theme: 'dark',
      });
    };

    const getDocsEditorMarkdown = () => docsEditorInstance ? docsEditorInstance.getMarkdown() : '';
    const setDocsEditorMarkdown = (markdown) => { if (docsEditorInstance) docsEditorInstance.setMarkdown(markdown || ''); };

    const showDocsForm = (legend, path = '', content = '', documentId = null) => {
      editingDocumentId = documentId;
      if (docsFormLegend) docsFormLegend.textContent = legend;
      if (docsPathEl) docsPathEl.value = path;
      if (docsFormFeedback) { docsFormFeedback.textContent = ''; docsFormFeedback.classList.remove('error'); }
      if (docsEditModal) docsEditModal.classList.remove('hidden');
      createDocsEditor(content);
    };

    const hideDocsForm = () => {
      destroyDocsEditor();
      editingDocumentId = null;
      if (docsEditModal) docsEditModal.classList.add('hidden');
      loadDocsList();
    };

    const openDocEdit = async (id) => {
      const docId = typeof id === 'string' ? parseInt(id, 10) : id;
      if (!Number.isFinite(docId)) {
        if (docsFormFeedback) docsFormFeedback.textContent = '無効な文書IDです';
        return;
      }
      showDocsForm('編集', '', '読み込み中...', docId);
      try {
        const result = await callMcp('get_document', { document_id: docId });
        const doc = parseResultText(result);
        if (doc && typeof doc === 'object') {
          if (docsPathEl) docsPathEl.value = doc.path || '';
          setDocsEditorMarkdown(doc.content != null ? String(doc.content) : '');
          if (docsFormFeedback) { docsFormFeedback.textContent = ''; docsFormFeedback.classList.remove('error'); }
        } else {
          if (docsFormFeedback) docsFormFeedback.textContent = '文書の取得に失敗しました';
        }
      } catch (e) {
        if (docsFormFeedback) docsFormFeedback.textContent = 'エラー: ' + e.message;
        if (docsFormFeedback) docsFormFeedback.classList.add('error');
      }
    };

    const deleteDoc = async (id) => {
      const docId = typeof id === 'string' ? parseInt(id, 10) : id;
      if (!Number.isFinite(docId)) {
        alert('無効な文書IDです');
        return;
      }
      if (!confirm('この文書を削除しますか？')) return;
      try {
        await callMcp('delete_document', { document_id: docId });
        loadDocsList();
        if (typeof window !== 'undefined' && window.updateDocCount) window.updateDocCount();
      } catch (e) {
        alert('削除に失敗しました: ' + e.message);
      }
    };

    this.querySelector('#docs-add-btn')?.addEventListener('click', () => {
      showDocsForm('新規登録', '', '');
    });
    this.querySelector('#docs-refresh-btn')?.addEventListener('click', () => {
      loadDocsList();
    });
    this.querySelector('#docs-save-btn')?.addEventListener('click', async () => {
      const path = docsPathEl?.value?.trim();
      const content = getDocsEditorMarkdown();
      if (!path) {
        if (docsFormFeedback) { docsFormFeedback.textContent = 'パスを入力してください'; docsFormFeedback.classList.add('error'); }
        return;
      }
      if (docsFormFeedback) { docsFormFeedback.textContent = '保存中...'; docsFormFeedback.classList.remove('error'); }
      try {
        if (editingDocumentId) {
          await callMcp('delete_document', { document_id: editingDocumentId });
        }
        await callMcp('add_item_text', { path, content });
        if (docsFormFeedback) { docsFormFeedback.textContent = '保存しました'; docsFormFeedback.classList.remove('error'); }
        setTimeout(() => hideDocsForm(), 600);
        if (typeof window !== 'undefined' && window.updateDocCount) window.updateDocCount();
      } catch (e) {
        if (docsFormFeedback) { docsFormFeedback.textContent = 'エラー: ' + e.message; docsFormFeedback.classList.add('error'); }
      }
    });
    this.querySelector('#docs-cancel-btn')?.addEventListener('click', hideDocsForm);
    docsFileInput?.addEventListener('change', () => {
      const file = docsFileInput.files?.[0];
      if (!file) return;
      const reader = new FileReader();
      reader.onload = () => {
        const text = typeof reader.result === 'string' ? reader.result : '';
        if (!docsPathEl?.value?.trim()) docsPathEl.value = file.name;
        setDocsEditorMarkdown(text);
        if (docsImportFeedback) {
          docsImportFeedback.textContent = `「${escapeHtml(file.name)}」を読み込みました`;
          docsImportFeedback.classList.remove('error');
          setTimeout(() => { docsImportFeedback.textContent = ''; }, 3000);
        }
        docsFileInput.value = '';
      };
      reader.onerror = () => {
        if (docsImportFeedback) {
          docsImportFeedback.textContent = 'ファイルの読み込みに失敗しました';
          docsImportFeedback.classList.add('error');
        }
        docsFileInput.value = '';
      };
      reader.readAsText(file, 'UTF-8');
    });
    docsImportFileBtn?.addEventListener('click', () => docsFileInput?.click());

    const backdrop = this.querySelector('.docs-edit-modal-backdrop');
    backdrop?.addEventListener('click', hideDocsForm);

    const updateCategoryFilter = () => {
      const select = this.querySelector('#search-category-filter');
      if (!select) return;
      const raw = localStorage.getItem(SETTINGS_KEY);
      const s = raw ? JSON.parse(raw) : {};
      const cats = new Set();
      (s.monitor_paths || []).forEach((p) => {
        const c = typeof p === 'object' ? (p.category || '') : '';
        if (c) cats.add(c);
      });
      const current = select.value;
      select.innerHTML = '<option value="">すべて</option>';
      [...cats].sort().forEach((c) => {
        const opt = document.createElement('option');
        opt.value = c;
        opt.textContent = c;
        select.appendChild(opt);
      });
      select.value = current;
    };
    updateCategoryFilter();

    const originalShowPanel = this.showPanel;
    this.showPanel = (panelId) => {
      originalShowPanel.call(this, panelId);
      if (panelId === 'docs') loadDocsList();
      if (panelId === 'search') updateCategoryFilter();
    };
  }
}

customElements.define('main-panel', MainPanel);

export { };

