Upload 2 files
Browse files- static/app.js +36 -4
- static/style.css +4 -0
static/app.js
CHANGED
|
@@ -6,6 +6,7 @@ const STATE = {
|
|
| 6 |
pageSize: 100,
|
| 7 |
total: 0,
|
| 8 |
results: [],
|
|
|
|
| 9 |
sources: [],
|
| 10 |
sourceMap: {},
|
| 11 |
folderTree: [],
|
|
@@ -372,6 +373,7 @@ function syncUrl(replace = true) {
|
|
| 372 |
if (!STATE.leftSidebarOpen) params.set("sidebar", "0");
|
| 373 |
if (DOM.leftSidebar.classList.contains("expanded-wide")) params.set("wide", "1");
|
| 374 |
if (STATE.rightSidebarOpen) params.set("filters", "1");
|
|
|
|
| 375 |
const basePath = STATE.source ? `/${encodeURIComponent(STATE.source)}` : "/";
|
| 376 |
const nextUrl = params.toString() ? `${basePath}?${params.toString()}` : basePath;
|
| 377 |
if (replace) history.replaceState(null, "", nextUrl);
|
|
@@ -398,6 +400,7 @@ function loadUrlState() {
|
|
| 398 |
DOM.sortSelect.value = sort;
|
| 399 |
STATE.leftSidebarOpen = !STATE.isMobile && params.get("sidebar") !== "0";
|
| 400 |
STATE.rightSidebarOpen = params.get("filters") === "1";
|
|
|
|
| 401 |
DOM.leftSidebar.classList.toggle("expanded-wide", params.get("wide") === "1" && !STATE.isMobile);
|
| 402 |
if (DOM.sidebarExpandBtn) DOM.sidebarExpandBtn.textContent = params.get("wide") === "1" && !STATE.isMobile ? "→" : "↔";
|
| 403 |
if (STATE.minSize !== null) DOM.filterMinSize.value = STATE.minSize;
|
|
@@ -574,7 +577,7 @@ function renderResults() {
|
|
| 574 |
DOM.multiActionBar.style.display = STATE.multiSelect ? "" : "none";
|
| 575 |
}
|
| 576 |
|
| 577 |
-
async function openPreview(docId, keyword) {
|
| 578 |
try {
|
| 579 |
const data = await API.preview(docId);
|
| 580 |
if (data.error) {
|
|
@@ -590,18 +593,39 @@ async function openPreview(docId, keyword) {
|
|
| 590 |
}
|
| 591 |
DOM.previewPanel.innerHTML = `
|
| 592 |
<div class="preview-header">
|
| 593 |
-
<div
|
| 594 |
-
|
|
|
|
|
|
|
|
|
|
| 595 |
</div>
|
| 596 |
<pre class="preview-body">${text}</pre>`;
|
| 597 |
DOM.previewPanel.style.display = "block";
|
| 598 |
-
|
|
|
|
|
|
|
| 599 |
} catch (error) {
|
| 600 |
console.error(error);
|
| 601 |
showToast("预览失败");
|
| 602 |
}
|
| 603 |
}
|
| 604 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 605 |
function renderCheckboxList(container, items, selected, onChange) {
|
| 606 |
container.innerHTML = items.map((item) => `
|
| 607 |
<label class="filter-checkbox-item">
|
|
@@ -1312,6 +1336,10 @@ function attachEvents() {
|
|
| 1312 |
doSearch();
|
| 1313 |
}
|
| 1314 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1315 |
document.addEventListener("keydown", (event) => {
|
| 1316 |
if (event.key === "/" && document.activeElement !== DOM.searchInput) {
|
| 1317 |
event.preventDefault();
|
|
@@ -1321,8 +1349,11 @@ function attachEvents() {
|
|
| 1321 |
});
|
| 1322 |
window.addEventListener("popstate", async () => {
|
| 1323 |
syncRoute();
|
|
|
|
| 1324 |
await renderSidebar();
|
| 1325 |
await renderFolderTree();
|
|
|
|
|
|
|
| 1326 |
doSearch();
|
| 1327 |
});
|
| 1328 |
}
|
|
@@ -1352,6 +1383,7 @@ async function init() {
|
|
| 1352 |
}
|
| 1353 |
updateSidebarVisibility();
|
| 1354 |
syncUrl();
|
|
|
|
| 1355 |
doSearch();
|
| 1356 |
}
|
| 1357 |
|
|
|
|
| 6 |
pageSize: 100,
|
| 7 |
total: 0,
|
| 8 |
results: [],
|
| 9 |
+
previewDocId: null,
|
| 10 |
sources: [],
|
| 11 |
sourceMap: {},
|
| 12 |
folderTree: [],
|
|
|
|
| 373 |
if (!STATE.leftSidebarOpen) params.set("sidebar", "0");
|
| 374 |
if (DOM.leftSidebar.classList.contains("expanded-wide")) params.set("wide", "1");
|
| 375 |
if (STATE.rightSidebarOpen) params.set("filters", "1");
|
| 376 |
+
if (STATE.previewDocId) params.set("preview", STATE.previewDocId);
|
| 377 |
const basePath = STATE.source ? `/${encodeURIComponent(STATE.source)}` : "/";
|
| 378 |
const nextUrl = params.toString() ? `${basePath}?${params.toString()}` : basePath;
|
| 379 |
if (replace) history.replaceState(null, "", nextUrl);
|
|
|
|
| 400 |
DOM.sortSelect.value = sort;
|
| 401 |
STATE.leftSidebarOpen = !STATE.isMobile && params.get("sidebar") !== "0";
|
| 402 |
STATE.rightSidebarOpen = params.get("filters") === "1";
|
| 403 |
+
STATE.previewDocId = params.get("preview") || null;
|
| 404 |
DOM.leftSidebar.classList.toggle("expanded-wide", params.get("wide") === "1" && !STATE.isMobile);
|
| 405 |
if (DOM.sidebarExpandBtn) DOM.sidebarExpandBtn.textContent = params.get("wide") === "1" && !STATE.isMobile ? "→" : "↔";
|
| 406 |
if (STATE.minSize !== null) DOM.filterMinSize.value = STATE.minSize;
|
|
|
|
| 577 |
DOM.multiActionBar.style.display = STATE.multiSelect ? "" : "none";
|
| 578 |
}
|
| 579 |
|
| 580 |
+
async function openPreview(docId, keyword, options = {}) {
|
| 581 |
try {
|
| 582 |
const data = await API.preview(docId);
|
| 583 |
if (data.error) {
|
|
|
|
| 593 |
}
|
| 594 |
DOM.previewPanel.innerHTML = `
|
| 595 |
<div class="preview-header">
|
| 596 |
+
<div>
|
| 597 |
+
<div class="preview-title">${escapeHTML(data.title)}</div>
|
| 598 |
+
<div class="preview-path">${escapeHTML(data.source)} / ${escapeHTML(data.path)}</div>
|
| 599 |
+
</div>
|
| 600 |
+
<button type="button" class="icon-btn-sm" data-action="close-preview" aria-label="关闭预览">×</button>
|
| 601 |
</div>
|
| 602 |
<pre class="preview-body">${text}</pre>`;
|
| 603 |
DOM.previewPanel.style.display = "block";
|
| 604 |
+
STATE.previewDocId = docId;
|
| 605 |
+
if (options.sync !== false) syncUrl();
|
| 606 |
+
if (options.scroll !== false) DOM.previewPanel.scrollIntoView({ behavior: "smooth", block: "start" });
|
| 607 |
} catch (error) {
|
| 608 |
console.error(error);
|
| 609 |
showToast("预览失败");
|
| 610 |
}
|
| 611 |
}
|
| 612 |
|
| 613 |
+
function closePreview() {
|
| 614 |
+
STATE.previewDocId = null;
|
| 615 |
+
DOM.previewPanel.innerHTML = "";
|
| 616 |
+
DOM.previewPanel.style.display = "none";
|
| 617 |
+
syncUrl();
|
| 618 |
+
}
|
| 619 |
+
|
| 620 |
+
function restorePreviewFromUrl(options = {}) {
|
| 621 |
+
if (!STATE.previewDocId) {
|
| 622 |
+
DOM.previewPanel.innerHTML = "";
|
| 623 |
+
DOM.previewPanel.style.display = "none";
|
| 624 |
+
return;
|
| 625 |
+
}
|
| 626 |
+
openPreview(STATE.previewDocId, STATE.query, { sync: false, scroll: options.scroll === true });
|
| 627 |
+
}
|
| 628 |
+
|
| 629 |
function renderCheckboxList(container, items, selected, onChange) {
|
| 630 |
container.innerHTML = items.map((item) => `
|
| 631 |
<label class="filter-checkbox-item">
|
|
|
|
| 1336 |
doSearch();
|
| 1337 |
}
|
| 1338 |
});
|
| 1339 |
+
DOM.previewPanel.addEventListener("click", (event) => {
|
| 1340 |
+
const closeBtn = event.target.closest("[data-action='close-preview']");
|
| 1341 |
+
if (closeBtn) closePreview();
|
| 1342 |
+
});
|
| 1343 |
document.addEventListener("keydown", (event) => {
|
| 1344 |
if (event.key === "/" && document.activeElement !== DOM.searchInput) {
|
| 1345 |
event.preventDefault();
|
|
|
|
| 1349 |
});
|
| 1350 |
window.addEventListener("popstate", async () => {
|
| 1351 |
syncRoute();
|
| 1352 |
+
loadUrlState();
|
| 1353 |
await renderSidebar();
|
| 1354 |
await renderFolderTree();
|
| 1355 |
+
updateSidebarVisibility();
|
| 1356 |
+
restorePreviewFromUrl({ scroll: false });
|
| 1357 |
doSearch();
|
| 1358 |
});
|
| 1359 |
}
|
|
|
|
| 1383 |
}
|
| 1384 |
updateSidebarVisibility();
|
| 1385 |
syncUrl();
|
| 1386 |
+
restorePreviewFromUrl({ scroll: false });
|
| 1387 |
doSearch();
|
| 1388 |
}
|
| 1389 |
|
static/style.css
CHANGED
|
@@ -448,6 +448,10 @@ ul, li {
|
|
| 448 |
}
|
| 449 |
|
| 450 |
.preview-header {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 451 |
padding: 12px 14px;
|
| 452 |
border-bottom: 1px solid var(--outline-variant);
|
| 453 |
}
|
|
|
|
| 448 |
}
|
| 449 |
|
| 450 |
.preview-header {
|
| 451 |
+
display: flex;
|
| 452 |
+
align-items: flex-start;
|
| 453 |
+
justify-content: space-between;
|
| 454 |
+
gap: 12px;
|
| 455 |
padding: 12px 14px;
|
| 456 |
border-bottom: 1px solid var(--outline-variant);
|
| 457 |
}
|