feat: file viewer added

This commit is contained in:
kodi
2026-03-11 13:53:59 +01:00
parent 31a42d34c7
commit ba6a369f78
16 changed files with 550 additions and 2 deletions
+67
View File
@@ -58,6 +58,18 @@ function showActionSummary(action, successes, failures, firstError) {
setStatus(base);
}
function viewerElements() {
return {
overlay: document.getElementById("viewer-modal"),
title: document.getElementById("viewer-title"),
fileName: document.getElementById("viewer-file-name"),
filePath: document.getElementById("viewer-file-path"),
error: document.getElementById("viewer-error"),
content: document.getElementById("viewer-content"),
closeButton: document.getElementById("viewer-close-btn"),
};
}
async function apiRequest(method, url, body) {
const options = { method, headers: {} };
if (body !== undefined) {
@@ -145,6 +157,7 @@ function updateActionButtons() {
const hasSelection = count > 0;
const exactlyOne = count === 1;
const allFiles = hasSelection && selectedItems.every((item) => item.kind === "file");
document.getElementById("view-btn").disabled = !exactlyOne || !allFiles;
document.getElementById("rename-btn").disabled = !exactlyOne;
document.getElementById("delete-btn").disabled = !hasSelection;
document.getElementById("copy-btn").disabled = !allFiles;
@@ -633,6 +646,10 @@ function isWildcardPopupOpen() {
return !wildcardPopupElements().overlay.classList.contains("hidden");
}
function isViewerOpen() {
return !viewerElements().overlay.classList.contains("hidden");
}
function escapeRegExp(text) {
return text.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
}
@@ -715,6 +732,40 @@ function openWildcardPopup(mode) {
elements.input.focus();
}
function closeViewer() {
const viewer = viewerElements();
viewer.overlay.classList.add("hidden");
viewer.error.textContent = "";
viewer.content.textContent = "";
}
async function openViewer() {
const selectedItems = activePaneState().selectedItems;
if (selectedItems.length !== 1 || selectedItems[0].kind !== "file") {
return;
}
const selected = selectedItems[0];
const viewer = viewerElements();
viewer.overlay.classList.remove("hidden");
viewer.title.textContent = "View";
viewer.fileName.textContent = selected.name;
viewer.filePath.textContent = selected.path;
viewer.error.textContent = "";
viewer.content.textContent = "Loading...";
try {
const data = await apiRequest("GET", `/api/files/view?${new URLSearchParams({ path: selected.path }).toString()}`);
viewer.fileName.textContent = data.name;
viewer.filePath.textContent = data.path;
viewer.content.textContent = data.content;
if (data.truncated) {
viewer.error.textContent = "Preview truncated for safety";
}
} catch (err) {
viewer.content.textContent = "";
viewer.error.textContent = err.message;
}
}
function moveCurrentRow(delta) {
const pane = state.activePane;
const model = paneState(pane);
@@ -770,6 +821,13 @@ function clearSelectionForActivePane() {
}
function handleKeyboardShortcuts(event) {
if (isViewerOpen()) {
if (event.key === "Escape") {
event.preventDefault();
closeViewer();
}
return;
}
if (isWildcardPopupOpen()) {
return;
}
@@ -851,6 +909,7 @@ function setupEvents() {
setupPaneEvents("left");
setupPaneEvents("right");
document.addEventListener("keydown", handleKeyboardShortcuts);
document.getElementById("view-btn").onclick = openViewer;
document.getElementById("rename-btn").onclick = renameSelected;
document.getElementById("delete-btn").onclick = deleteSelected;
document.getElementById("copy-btn").onclick = startCopySelected;
@@ -876,6 +935,14 @@ function setupEvents() {
closeWildcardPopup();
}
};
const viewer = viewerElements();
viewer.closeButton.onclick = closeViewer;
viewer.overlay.onclick = (event) => {
if (event.target === viewer.overlay) {
closeViewer();
}
};
}
async function init() {