feat: thumbnails added

This commit is contained in:
kodi
2026-03-12 12:27:47 +01:00
parent 76f5ed3e98
commit 3b376fa8ff
30 changed files with 955 additions and 3 deletions
+76 -2
View File
@@ -47,6 +47,7 @@ let batchMoveState = {
let settingsState = {
activeTab: "general",
logsLoaded: false,
showThumbnails: false,
};
let searchState = {
pane: "left",
@@ -200,6 +201,7 @@ function settingsElements() {
generalTab: document.getElementById("settings-general-tab"),
logsTab: document.getElementById("settings-logs-tab"),
generalPanel: document.getElementById("settings-general-panel"),
showThumbnailsInput: document.getElementById("settings-show-thumbnails"),
logsPanel: document.getElementById("settings-logs-panel"),
logsList: document.getElementById("settings-logs-list"),
logsError: document.getElementById("settings-logs-error"),
@@ -357,6 +359,58 @@ function currentRowItem(pane) {
return model.visibleItems[model.currentRowIndex];
}
function thumbnailsEnabled() {
return !!settingsState.showThumbnails;
}
function isThumbnailCandidate(entry) {
if (!entry || entry.kind !== "file") {
return false;
}
const lower = (entry.name || "").toLowerCase();
return lower.endsWith(".jpg") || lower.endsWith(".jpeg") || lower.endsWith(".png") || lower.endsWith(".webp");
}
function createMediaSlot(entry) {
const slot = document.createElement("span");
slot.className = "entry-media-slot";
if (thumbnailsEnabled() && isThumbnailCandidate(entry)) {
const image = document.createElement("img");
image.className = "entry-thumbnail";
image.loading = "lazy";
image.alt = "";
image.src = `/api/files/thumbnail?${new URLSearchParams({ path: entry.path }).toString()}`;
slot.append(image);
return slot;
}
const icon = document.createElement("span");
icon.className = `entry-media-icon ${entry.kind === "directory" ? "folder" : "file"}`;
icon.setAttribute("aria-hidden", "true");
slot.append(icon);
return slot;
}
async function loadSettings() {
const data = await apiRequest("GET", "/api/settings");
settingsState.showThumbnails = !!data.show_thumbnails;
const elements = settingsElements();
if (elements.showThumbnailsInput) {
elements.showThumbnailsInput.checked = settingsState.showThumbnails;
}
}
async function saveSettings(update) {
const data = await apiRequest("POST", "/api/settings", update);
settingsState.showThumbnails = !!data.show_thumbnails;
const elements = settingsElements();
if (elements.showThumbnailsInput) {
elements.showThumbnailsInput.checked = settingsState.showThumbnails;
}
renderPaneItems("left");
renderPaneItems("right");
}
function updateActionButtons() {
const selectedItems = activePaneState().selectedItems;
const count = selectedItems.length;
@@ -547,6 +601,7 @@ function createBrowseItem(pane, entry, kind) {
const name = document.createElement("span");
name.className = `entry-name ${kind === "directory" ? "entry-dir" : "entry-file"}`;
name.append(createMediaSlot({ ...entry, kind }));
if (kind === "directory") {
const open = document.createElement("button");
@@ -558,10 +613,12 @@ function createBrowseItem(pane, entry, kind) {
setActivePane(pane);
navigateTo(pane, entry.path);
};
open.classList.add("entry-label");
name.append(open);
} else {
const fileName = document.createElement("span");
fileName.textContent = entry.name;
fileName.className = "entry-label";
fileName.onclick = (ev) => {
ev.stopPropagation();
setActivePane(pane);
@@ -626,6 +683,11 @@ function renderPaneItems(pane) {
renderPaneItems(pane);
};
up.append(document.createElement("span"));
const upNameCell = document.createElement("span");
upNameCell.className = "entry-name entry-dir";
const upMedia = document.createElement("span");
upMedia.className = "entry-media-slot";
upNameCell.append(upMedia);
const upName = document.createElement("button");
upName.type = "button";
upName.className = "dir-link";
@@ -635,8 +697,7 @@ function renderPaneItems(pane) {
setActivePane(pane);
navigateTo(pane, entry.path);
};
const upNameCell = document.createElement("span");
upNameCell.className = "entry-name entry-dir";
upName.classList.add("entry-label");
upNameCell.append(upName);
up.append(upNameCell);
const upSize = document.createElement("span");
@@ -1630,6 +1691,17 @@ async function loadHistoryForSettings() {
}
}
async function handleShowThumbnailsChange(event) {
const input = event.target;
try {
await saveSettings({ show_thumbnails: !!input.checked });
} catch (err) {
input.checked = settingsState.showThumbnails;
settingsElements().logsError.textContent = "";
setError("actions-error", `Settings: ${err.message}`);
}
}
function closeSettings() {
settingsElements().overlay.classList.add("hidden");
}
@@ -2097,6 +2169,7 @@ function setupEvents() {
setSettingsTab("logs");
await loadHistoryForSettings();
};
settings.showThumbnailsInput.onchange = handleShowThumbnailsChange;
settings.overlay.onclick = (event) => {
if (event.target === settings.overlay) {
closeSettings();
@@ -2204,6 +2277,7 @@ async function init() {
applyTheme(preferredTheme());
setActivePane("left");
setupEvents();
await loadSettings();
await Promise.all([loadBrowsePane("left"), loadBrowsePane("right")]);
}