function rebuildIndex() { openModal({ title: 'Rebuilding search index…', body: 'Walking the wiki tree.' }); fetch('/_reindex', { method: 'POST' }) .then(function (resp) { if (!resp.ok) throw new Error('rebuild failed: ' + resp.status); window.location.href = window.location.href; }) .catch(function (err) { closeModal(); openModal({ title: 'Rebuild failed', body: String(err), confirm: { label: 'OK' } }); }); } function encodeSearchPath(p) { if (p === '/' || p === '') return '/'; return '/' + p.replace(/^\/+/, '').split('/').map(encodeURIComponent).join('/'); } // createSearchPage runs the new-page flow (pick a parent folder, then confirm // the name) starting from the search query, so a query with no exact page can // be turned into that page in one step. The name field is pre-filled with the // query but stays editable. function createSearchPage(name) { var current = decodeURIComponent(window.location.pathname).replace(/\/+$/, '') || '/'; openTreePicker({ title: 'New page — where?', mode: 'folder', initialPath: current, preselect: current, hideFiles: true, confirmLabel: 'NEXT', onSelect: function (parentPath) { var input = document.createElement('input'); input.type = 'text'; input.className = 'input'; input.placeholder = 'Page name'; input.value = name || ''; openModal({ title: 'New page — name?', body: input, confirm: { label: 'CREATE', onConfirm: function () { var finalName = input.value.trim(); if (!finalName) return; var base = parentPath === '/' ? '/' : encodeSearchPath(parentPath) + '/'; window.location.href = base + encodeURIComponent(finalName) + '/?edit'; } } }); } }); } // The "create page" link on results pages; delegated, since boosted // navigation swaps the results in without a new DOMContentLoaded. document.addEventListener('click', function (e) { var createLink = e.target.closest && e.target.closest('[data-create-page]'); if (!createLink) return; e.preventDefault(); createSearchPage(createLink.getAttribute('data-create-page')); }); // Focus the search input on results pages so Tab steps directly into the // first match — the input sits immediately before the results in DOM order, // so the natural tab sequence is input → first result → next, … document.addEventListener('DOMContentLoaded', function () { var input = document.querySelector('.search-input'); if (input && input.value) { input.focus(); var end = input.value.length; try { input.setSelectionRange(end, end); } catch (e) {} } }); // The header (and its search box) survives boosted navigation: show the // current page's query, as a full load would. document.addEventListener('page:swapped', function () { var input = document.querySelector('.search-input'); if (input && document.activeElement !== input) { input.value = new URLSearchParams(location.search).get('q') || ''; } });