Double root empty folder fix, side bar drag zone adjusted

This commit is contained in:
Ryan
2025-03-27 03:22:43 -04:00
committed by GitHub
parent 062cfc0dd4
commit a8f5a6d3bc
3 changed files with 364 additions and 347 deletions

View File

@@ -9,7 +9,7 @@ https://github.com/user-attachments/assets/179e6940-5798-4482-9a69-696f806c37de
changelogs available here: <https://github.com/error311/FileRise-docker/> changelogs available here: <https://github.com/error311/FileRise-docker/>
FileRise - Multi File Upload Editor is a lightweight, secure, self-hosted web application for uploading, syntax highlight editing, drag & drop and managing files. Built with an Apache/PHP backend and a modern JavaScript (ES6 modules) frontend, it offers a responsive, dynamic file management interface. It serves as an alternative to solutions like FileGator TinyFileManager or ProjectSend, providing an easy-to-setup experience ideal for document management, image galleries, firmware file hosting, and more. FileRise is a lightweight, secure, self-hosted web application for uploading, syntax-highlight editing, drag & drop file management, and more. Built with an Apache/PHP backend and a modern JavaScript (ES6 modules) frontend, it offers a responsive and dynamic interface designed to simplify file handling. As an alternative to solutions like FileGator, TinyFileManager, or ProjectSend, FileRise provides an easy-to-set-up experience ideal for document management, image galleries, firmware hosting, and other file-intensive applications.
--- ---
@@ -49,7 +49,7 @@ FileRise - Multi File Upload Editor is a lightweight, secure, self-hosted web ap
- **Move Files:** Move selected files to a different folder, automatically generating a unique filename if needed to avoid data loss. - **Move Files:** Move selected files to a different folder, automatically generating a unique filename if needed to avoid data loss.
- **Download Files as ZIP:** Download selected files as a ZIP archive. Users can specify a custom name for the ZIP file via a modal dialog. - **Download Files as ZIP:** Download selected files as a ZIP archive. Users can specify a custom name for the ZIP file via a modal dialog.
- **Extract Zip:** When one or more ZIP files are selected, users can extract the archive(s) directly into the current folder. - **Extract Zip:** When one or more ZIP files are selected, users can extract the archive(s) directly into the current folder.
- **Drag & Drop:** Easily move files by selecting them from the file list and simply dragging them onto your desired folder in the folder tree or breadcrumb. When you drop the files onto a folder, the system automatically moves them, updating your file organization in one seamless action. - **Drag & Drop (File Movement):** Easily move files by selecting them from the file list and dragging them onto your desired folder in the folder tree or breadcrumb. When you drop the files onto a folder, the system automatically moves them, updating your file organization in one seamless action.
- **Enhanced Context Menu & Keyboard Shortcuts:** - **Enhanced Context Menu & Keyboard Shortcuts:**
- **Right-Click Context Menu:** - **Right-Click Context Menu:**
- A custom context menu appears on right-clicking within the file list. - A custom context menu appears on right-clicking within the file list.
@@ -126,6 +126,16 @@ FileRise - Multi File Upload Editor is a lightweight, secure, self-hosted web ap
- The trash modal displays details such as file name, uploader/deleter, and the trashed date/time. - The trash modal displays details such as file name, uploader/deleter, and the trashed date/time.
- Material icons with tooltips visually represent the restore and delete actions. - Material icons with tooltips visually represent the restore and delete actions.
- **Drag & Drop Cards with Dedicated Drop Zones:**
- **Sidebar Drop Zone:**
- Cards (such as the upload card or folder management card) can be dragged into a dedicated sidebar drop zone for quick access to frequently used operations.
- The sidebar drop zone expands dynamically to accept drops anywhere within its visual area.
- **Top Bar Drop Zone:**
- A top drop zone is available for reordering or managing cards quickly.
- Dragging a card to the top drop zone provides immediate visual feedback, ensuring a fluid and customizable workflow.
- **Seamless Interaction:**
- Both drop zones support smooth drag and drop interactions with animations and pointer event adjustments to prevent interference, ensuring that cards can be dropped reliably regardless of screen position.
--- ---
## Screenshots ## Screenshots
@@ -214,6 +224,12 @@ For users who prefer containerization, a Docker image is available
docker pull error311/filerise-docker:latest docker pull error311/filerise-docker:latest
``` ```
macos M series:
```bash
docker pull --platform linux/x86_64 error311/filerise-docker:latest
```
2. **Run the Container:** 2. **Run the Container:**
```bash ```bash

View File

@@ -2,354 +2,363 @@
// Moves cards into the sidebar based on the saved order in localStorage. // Moves cards into the sidebar based on the saved order in localStorage.
export function loadSidebarOrder() { export function loadSidebarOrder() {
const sidebar = document.getElementById('sidebarDropArea'); const sidebar = document.getElementById('sidebarDropArea');
if (!sidebar) return; if (!sidebar) return;
const orderStr = localStorage.getItem('sidebarOrder'); const orderStr = localStorage.getItem('sidebarOrder');
if (orderStr) { if (orderStr) {
const order = JSON.parse(orderStr); const order = JSON.parse(orderStr);
if (order.length > 0) { if (order.length > 0) {
// Ensure main wrapper is visible. // Ensure main wrapper is visible.
const mainWrapper = document.querySelector('.main-wrapper'); const mainWrapper = document.querySelector('.main-wrapper');
if (mainWrapper) { if (mainWrapper) {
mainWrapper.style.display = 'flex'; mainWrapper.style.display = 'flex';
}
// For each saved ID, move the card into the sidebar.
order.forEach(id => {
const card = document.getElementById(id);
if (card && card.parentNode.id !== 'sidebarDropArea') {
sidebar.appendChild(card);
// Animate vertical slide for sidebar card
animateVerticalSlide(card);
}
});
} }
} // For each saved ID, move the card into the sidebar.
updateSidebarVisibility(); order.forEach(id => {
} const card = document.getElementById(id);
if (card && card.parentNode.id !== 'sidebarDropArea') {
// Internal helper: update sidebar visibility based on its content. sidebar.appendChild(card);
function updateSidebarVisibility() { // Animate vertical slide for sidebar card
const sidebar = document.getElementById('sidebarDropArea');
if (sidebar) {
const cards = sidebar.querySelectorAll('#uploadCard, #folderManagementCard');
if (cards.length > 0) {
sidebar.classList.add('active');
sidebar.style.display = 'block';
} else {
sidebar.classList.remove('active');
sidebar.style.display = 'none';
}
// Save the current order in localStorage.
saveSidebarOrder();
}
}
// Internal helper: update top zone layout (center a card if one column is empty).
function updateTopZoneLayout() {
const leftCol = document.getElementById('leftCol');
const rightCol = document.getElementById('rightCol');
const leftIsEmpty = !leftCol.querySelector('#uploadCard');
const rightIsEmpty = !rightCol.querySelector('#folderManagementCard');
if (leftIsEmpty && !rightIsEmpty) {
leftCol.style.display = 'none';
rightCol.style.margin = '0 auto';
} else if (rightIsEmpty && !leftIsEmpty) {
rightCol.style.display = 'none';
leftCol.style.margin = '0 auto';
} else {
leftCol.style.display = '';
rightCol.style.display = '';
leftCol.style.margin = '';
rightCol.style.margin = '';
}
}
// When a card is being dragged, if the top drop zone is empty, set its min-height.
function addTopZoneHighlight() {
const topZone = document.getElementById('uploadFolderRow');
if (topZone) {
topZone.classList.add('highlight');
if (topZone.querySelectorAll('#uploadCard, #folderManagementCard').length === 0) {
topZone.style.minHeight = '375px';
}
}
}
// When the drag ends, remove the extra min-height.
function removeTopZoneHighlight() {
const topZone = document.getElementById('uploadFolderRow');
if (topZone) {
topZone.classList.remove('highlight');
topZone.style.minHeight = '';
}
}
// Vertical slide/fade animation helper.
// It sets an initial vertical offset (30px down) and opacity 0, then animates to normal position and full opacity.
function animateVerticalSlide(card) {
card.style.transform = 'translateY(30px)';
card.style.opacity = '0';
// Force reflow.
card.offsetWidth;
requestAnimationFrame(() => {
card.style.transition = 'transform 0.3s ease, opacity 0.3s ease';
card.style.transform = 'translateY(0)';
card.style.opacity = '1';
});
setTimeout(() => {
card.style.transition = '';
card.style.transform = '';
card.style.opacity = '';
}, 310);
}
// Internal helper: insert card into sidebar at a proper position based on event.clientY.
function insertCardInSidebar(card, event) {
const sidebar = document.getElementById('sidebarDropArea');
if (!sidebar) return;
const existingCards = Array.from(sidebar.querySelectorAll('#uploadCard, #folderManagementCard'));
let inserted = false;
for (const currentCard of existingCards) {
const rect = currentCard.getBoundingClientRect();
const midY = rect.top + rect.height / 2;
if (event.clientY < midY) {
sidebar.insertBefore(card, currentCard);
inserted = true;
break;
}
}
if (!inserted) {
sidebar.appendChild(card);
}
// Ensure card fills the sidebar.
card.style.width = '100%';
animateVerticalSlide(card);
}
// Internal helper: save the current sidebar card order to localStorage.
function saveSidebarOrder() {
const sidebar = document.getElementById('sidebarDropArea');
if (sidebar) {
const cards = sidebar.querySelectorAll('#uploadCard, #folderManagementCard');
const order = Array.from(cards).map(card => card.id);
localStorage.setItem('sidebarOrder', JSON.stringify(order));
}
}
// Helper: move cards from sidebar back to the top drop area when on small screens.
function moveSidebarCardsToTop() {
if (window.innerWidth < 1205) {
const sidebar = document.getElementById('sidebarDropArea');
if (!sidebar) return;
const cards = Array.from(sidebar.querySelectorAll('#uploadCard, #folderManagementCard'));
cards.forEach(card => {
const orig = document.getElementById(card.dataset.originalContainerId);
if (orig) {
orig.appendChild(card);
animateVerticalSlide(card); animateVerticalSlide(card);
} }
}); });
updateSidebarVisibility();
updateTopZoneLayout();
} }
} }
updateSidebarVisibility();
}
// Listen for window resize to automatically move sidebar cards back to top on small screens. // Internal helper: update sidebar visibility based on its content.
window.addEventListener('resize', function () { function updateSidebarVisibility() {
if (window.innerWidth < 1205) { const sidebar = document.getElementById('sidebarDropArea');
moveSidebarCardsToTop(); if (sidebar) {
} const cards = sidebar.querySelectorAll('#uploadCard, #folderManagementCard');
}); if (cards.length > 0) {
sidebar.classList.add('active');
// This function ensures the top drop zone (#uploadFolderRow) has a stable width when empty. sidebar.style.display = 'block';
function ensureTopZonePlaceholder() {
const topZone = document.getElementById('uploadFolderRow');
if (!topZone) return;
if (topZone.querySelectorAll('#uploadCard, #folderManagementCard').length === 0) {
let placeholder = topZone.querySelector('.placeholder');
if (!placeholder) {
placeholder = document.createElement('div');
placeholder.className = 'placeholder';
placeholder.style.visibility = 'hidden';
placeholder.style.display = 'block';
placeholder.style.width = '100%';
placeholder.style.height = '375px';
topZone.appendChild(placeholder);
}
} else { } else {
const placeholder = topZone.querySelector('.placeholder'); sidebar.classList.remove('active');
if (placeholder) placeholder.remove(); sidebar.style.display = 'none';
}
// Save the current order in localStorage.
saveSidebarOrder();
}
}
// Internal helper: update top zone layout (center a card if one column is empty).
function updateTopZoneLayout() {
const leftCol = document.getElementById('leftCol');
const rightCol = document.getElementById('rightCol');
const leftIsEmpty = !leftCol.querySelector('#uploadCard');
const rightIsEmpty = !rightCol.querySelector('#folderManagementCard');
if (leftIsEmpty && !rightIsEmpty) {
leftCol.style.display = 'none';
rightCol.style.margin = '0 auto';
} else if (rightIsEmpty && !leftIsEmpty) {
rightCol.style.display = 'none';
leftCol.style.margin = '0 auto';
} else {
leftCol.style.display = '';
rightCol.style.display = '';
leftCol.style.margin = '';
rightCol.style.margin = '';
}
}
// When a card is being dragged, if the top drop zone is empty, set its min-height.
function addTopZoneHighlight() {
const topZone = document.getElementById('uploadFolderRow');
if (topZone) {
topZone.classList.add('highlight');
if (topZone.querySelectorAll('#uploadCard, #folderManagementCard').length === 0) {
topZone.style.minHeight = '375px';
} }
} }
}
// This sets up all drag-and-drop event listeners for cards. // When the drag ends, remove the extra min-height.
export function initDragAndDrop() { function removeTopZoneHighlight() {
function run() { const topZone = document.getElementById('uploadFolderRow');
const draggableCards = document.querySelectorAll('#uploadCard, #folderManagementCard'); if (topZone) {
draggableCards.forEach(card => { topZone.classList.remove('highlight');
if (!card.dataset.originalContainerId) { topZone.style.minHeight = '';
card.dataset.originalContainerId = card.parentNode.id; }
} }
const header = card.querySelector('.card-header');
if (header) {
header.classList.add('drag-header');
}
let isDragging = false; // Vertical slide/fade animation helper.
let dragTimer = null; function animateVerticalSlide(card) {
let offsetX = 0, offsetY = 0; card.style.transform = 'translateY(30px)';
let initialLeft, initialTop; card.style.opacity = '0';
// Force reflow.
card.offsetWidth;
requestAnimationFrame(() => {
card.style.transition = 'transform 0.3s ease, opacity 0.3s ease';
card.style.transform = 'translateY(0)';
card.style.opacity = '1';
});
setTimeout(() => {
card.style.transition = '';
card.style.transform = '';
card.style.opacity = '';
}, 310);
}
if (header) { // Internal helper: insert card into sidebar at a proper position based on event.clientY.
header.addEventListener('mousedown', function (e) { function insertCardInSidebar(card, event) {
e.preventDefault(); const sidebar = document.getElementById('sidebarDropArea');
const card = this.closest('.card'); if (!sidebar) return;
const rect = card.getBoundingClientRect(); const existingCards = Array.from(sidebar.querySelectorAll('#uploadCard, #folderManagementCard'));
const originX = ((e.clientX - rect.left) / rect.width) * 100; let inserted = false;
const originY = ((e.clientY - rect.top) / rect.height) * 100; for (const currentCard of existingCards) {
card.style.transformOrigin = `${originX}% ${originY}%`; const rect = currentCard.getBoundingClientRect();
dragTimer = setTimeout(() => { const midY = rect.top + rect.height / 2;
isDragging = true; if (event.clientY < midY) {
card.classList.add('dragging'); sidebar.insertBefore(card, currentCard);
addTopZoneHighlight(); inserted = true;
const rect = card.getBoundingClientRect(); break;
initialLeft = rect.left + window.pageXOffset; }
initialTop = rect.top + window.pageYOffset; }
offsetX = e.pageX - initialLeft; if (!inserted) {
offsetY = e.pageY - initialTop; sidebar.appendChild(card);
document.body.appendChild(card); }
card.style.position = 'absolute'; // Ensure card fills the sidebar.
card.style.left = initialLeft + 'px'; card.style.width = '100%';
card.style.top = initialTop + 'px'; animateVerticalSlide(card);
card.style.width = rect.width + 'px'; }
card.style.zIndex = '10000';
const sidebar = document.getElementById('sidebarDropArea');
if (sidebar) {
sidebar.classList.add('active');
sidebar.style.display = 'block';
sidebar.classList.add('highlight');
}
}, 500);
});
header.addEventListener('mouseup', function () {
clearTimeout(dragTimer);
});
}
document.addEventListener('mousemove', function (e) { // Internal helper: save the current sidebar card order to localStorage.
if (isDragging) { function saveSidebarOrder() {
card.style.left = (e.pageX - offsetX) + 'px'; const sidebar = document.getElementById('sidebarDropArea');
card.style.top = (e.pageY - offsetY) + 'px'; if (sidebar) {
} const cards = sidebar.querySelectorAll('#uploadCard, #folderManagementCard');
}); const order = Array.from(cards).map(card => card.id);
localStorage.setItem('sidebarOrder', JSON.stringify(order));
}
}
document.addEventListener('mouseup', function (e) { // Helper: move cards from sidebar back to the top drop area when on small screens.
if (isDragging) { function moveSidebarCardsToTop() {
isDragging = false; if (window.innerWidth < 1205) {
card.classList.remove('dragging'); const sidebar = document.getElementById('sidebarDropArea');
removeTopZoneHighlight(); if (!sidebar) return;
const cards = Array.from(sidebar.querySelectorAll('#uploadCard, #folderManagementCard'));
cards.forEach(card => {
const orig = document.getElementById(card.dataset.originalContainerId);
if (orig) {
orig.appendChild(card);
animateVerticalSlide(card);
}
});
updateSidebarVisibility();
updateTopZoneLayout();
}
}
// Listen for window resize to automatically move sidebar cards back to top on small screens.
window.addEventListener('resize', function () {
if (window.innerWidth < 1205) {
moveSidebarCardsToTop();
}
});
// This function ensures the top drop zone (#uploadFolderRow) has a stable width when empty.
function ensureTopZonePlaceholder() {
const topZone = document.getElementById('uploadFolderRow');
if (!topZone) return;
if (topZone.querySelectorAll('#uploadCard, #folderManagementCard').length === 0) {
let placeholder = topZone.querySelector('.placeholder');
if (!placeholder) {
placeholder = document.createElement('div');
placeholder.className = 'placeholder';
placeholder.style.visibility = 'hidden';
placeholder.style.display = 'block';
placeholder.style.width = '100%';
placeholder.style.height = '375px';
topZone.appendChild(placeholder);
}
} else {
const placeholder = topZone.querySelector('.placeholder');
if (placeholder) placeholder.remove();
}
}
// This sets up all drag-and-drop event listeners for cards.
export function initDragAndDrop() {
function run() {
const draggableCards = document.querySelectorAll('#uploadCard, #folderManagementCard');
draggableCards.forEach(card => {
if (!card.dataset.originalContainerId) {
card.dataset.originalContainerId = card.parentNode.id;
}
const header = card.querySelector('.card-header');
if (header) {
header.classList.add('drag-header');
}
let isDragging = false;
let dragTimer = null;
let offsetX = 0, offsetY = 0;
let initialLeft, initialTop;
if (header) {
header.addEventListener('mousedown', function (e) {
e.preventDefault();
const card = this.closest('.card');
const rect = card.getBoundingClientRect();
const originX = ((e.clientX - rect.left) / rect.width) * 100;
const originY = ((e.clientY - rect.top) / rect.height) * 100;
card.style.transformOrigin = `${originX}% ${originY}%`;
dragTimer = setTimeout(() => {
isDragging = true;
card.classList.add('dragging');
// Disable pointer events on the card so it doesn't block drop detection.
card.style.pointerEvents = 'none';
addTopZoneHighlight();
const sidebar = document.getElementById('sidebarDropArea'); const sidebar = document.getElementById('sidebarDropArea');
if (sidebar) { if (sidebar) {
sidebar.classList.remove('highlight'); sidebar.classList.add('active');
sidebar.style.display = 'block';
sidebar.classList.add('highlight');
// Force the sidebar to have a tall drop zone while dragging.
sidebar.style.height = '800px';
} }
const rect = card.getBoundingClientRect();
initialLeft = rect.left + window.pageXOffset;
initialTop = rect.top + window.pageYOffset;
offsetX = e.pageX - initialLeft;
offsetY = e.pageY - initialTop;
document.body.appendChild(card);
card.style.position = 'absolute';
card.style.left = initialLeft + 'px';
card.style.top = initialTop + 'px';
card.style.width = rect.width + 'px';
card.style.zIndex = '10000';
}, 500);
});
header.addEventListener('mouseup', function () {
clearTimeout(dragTimer);
});
}
const leftCol = document.getElementById('leftCol'); document.addEventListener('mousemove', function (e) {
const rightCol = document.getElementById('rightCol'); if (isDragging) {
let droppedInSidebar = false; card.style.left = (e.pageX - offsetX) + 'px';
let droppedInTop = false; card.style.top = (e.pageY - offsetY) + 'px';
}
});
const sidebarElem = document.getElementById('sidebarDropArea'); document.addEventListener('mouseup', function (e) {
if (sidebarElem) { if (isDragging) {
const rect = sidebarElem.getBoundingClientRect(); isDragging = false;
if ( // Re-enable pointer events on the card.
e.clientX >= rect.left && card.style.pointerEvents = '';
e.clientX <= rect.right && card.classList.remove('dragging');
e.clientY >= rect.top && removeTopZoneHighlight();
e.clientY <= rect.bottom const sidebar = document.getElementById('sidebarDropArea');
) { if (sidebar) {
insertCardInSidebar(card, e); sidebar.classList.remove('highlight');
droppedInSidebar = true; // Remove the forced height once the drag ends.
sidebarElem.blur(); sidebar.style.height = '';
}
const leftCol = document.getElementById('leftCol');
const rightCol = document.getElementById('rightCol');
let droppedInSidebar = false;
let droppedInTop = false;
const sidebarElem = document.getElementById('sidebarDropArea');
if (sidebarElem) {
// Instead of using elementsFromPoint, use a virtual drop zone.
const rect = sidebarElem.getBoundingClientRect();
// Define a drop zone from the top of the sidebar to 1000px below its top.
const dropZoneBottom = rect.top + 800;
if (
e.clientX >= rect.left &&
e.clientX <= rect.right &&
e.clientY >= rect.top &&
e.clientY <= dropZoneBottom
) {
insertCardInSidebar(card, e);
droppedInSidebar = true;
sidebarElem.blur();
}
}
const topRow = document.getElementById('uploadFolderRow');
if (!droppedInSidebar && topRow) {
const rect = topRow.getBoundingClientRect();
if (
e.clientX >= rect.left &&
e.clientX <= rect.right &&
e.clientY >= rect.top &&
e.clientY <= rect.bottom
) {
let container;
if (card.id === 'uploadCard') {
container = document.getElementById('leftCol');
} else if (card.id === 'folderManagementCard') {
container = document.getElementById('rightCol');
}
if (container) {
ensureTopZonePlaceholder();
container.appendChild(card);
droppedInTop = true;
container.style.position = 'relative';
card.style.position = 'absolute';
card.style.left = '0px';
// Animate vertical slide/fade.
card.style.transform = 'translateY(30px)';
card.style.opacity = '0';
card.offsetWidth; // Force reflow.
requestAnimationFrame(() => {
card.style.transition = 'transform 0.3s ease, opacity 0.3s ease';
card.style.transform = 'translateY(0)';
card.style.opacity = '1';
});
setTimeout(() => {
card.style.position = '';
container.style.position = '';
card.style.transition = '';
card.style.transform = '';
card.style.opacity = '';
card.style.width = '';
}, 310);
} }
} }
}
const topRow = document.getElementById('uploadFolderRow'); if (droppedInSidebar || droppedInTop) {
if (!droppedInSidebar && topRow) { card.style.position = '';
const rect = topRow.getBoundingClientRect(); card.style.left = '';
if ( card.style.top = '';
e.clientX >= rect.left && card.style.zIndex = '';
e.clientX <= rect.right && } else {
e.clientY >= rect.top && const orig = document.getElementById(card.dataset.originalContainerId);
e.clientY <= rect.bottom if (orig) {
) { orig.appendChild(card);
let container;
if (card.id === 'uploadCard') {
container = document.getElementById('leftCol');
} else if (card.id === 'folderManagementCard') {
container = document.getElementById('rightCol');
}
if (container) {
ensureTopZonePlaceholder();
container.appendChild(card);
droppedInTop = true;
container.style.position = 'relative';
card.style.position = 'absolute';
card.style.left = '0px';
// For top drop, animate vertical slide/fade.
card.style.transform = 'translateY(30px)';
card.style.opacity = '0';
card.offsetWidth; // Force reflow.
requestAnimationFrame(() => {
card.style.transition = 'transform 0.3s ease, opacity 0.3s ease';
card.style.transform = 'translateY(0)';
card.style.opacity = '1';
});
setTimeout(() => {
card.style.position = '';
container.style.position = '';
card.style.transition = '';
card.style.transform = '';
card.style.opacity = '';
// Ensure the card returns to full width (via CSS: width: 100%)
card.style.width = '';
}, 310);
}
}
}
if (droppedInSidebar || droppedInTop) {
card.style.position = ''; card.style.position = '';
card.style.left = ''; card.style.left = '';
card.style.top = ''; card.style.top = '';
card.style.zIndex = ''; card.style.zIndex = '';
} else { card.style.width = '';
const orig = document.getElementById(card.dataset.originalContainerId);
if (orig) {
orig.appendChild(card);
card.style.position = '';
card.style.left = '';
card.style.top = '';
card.style.zIndex = '';
card.style.width = '';
}
} }
updateTopZoneLayout();
updateSidebarVisibility();
} }
}); updateTopZoneLayout();
updateSidebarVisibility();
}
}); });
} });
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', run);
} else {
run();
}
} }
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', run);
} else {
run();
}
}

View File

@@ -63,8 +63,6 @@ function getParentFolder(folder) {
// Breadcrumb Functions // Breadcrumb Functions
// ---------------------- // ----------------------
// Render breadcrumb for a normalized folder path. // Render breadcrumb for a normalized folder path.
// For example, if window.currentFolder is "Folder1/Folder1SubFolder2",
// this will return: Root / Folder1 / Folder1SubFolder2.
function renderBreadcrumb(normalizedFolder) { function renderBreadcrumb(normalizedFolder) {
if (normalizedFolder === "root") { if (normalizedFolder === "root") {
return `<span class="breadcrumb-link" data-folder="root">Root</span>`; return `<span class="breadcrumb-link" data-folder="root">Root</span>`;
@@ -307,16 +305,10 @@ export async function loadFolderTree(selectedFolder) {
} }
let html = `<div id="rootRow" class="root-row"> let html = `<div id="rootRow" class="root-row">
<span class="folder-toggle" data-folder="root">[<span class="custom-dash">-</span>]</span> <span class="folder-toggle" data-folder="root">[<span class="custom-dash">-</span>]</span>
<span class="folder-option root-folder-option" data-folder="root">(Root)</span> <span class="folder-option root-folder-option" data-folder="root">(Root)</span>
</div>`; </div>`;
if (folders.length === 0) { if (folders.length > 0) {
html += `<ul class="folder-tree expanded">
<li class="folder-item">
<span class="folder-option" data-folder="root">(Root)</span>
</li>
</ul>`;
} else {
const tree = buildFolderTree(folders); const tree = buildFolderTree(folders);
html += renderFolderTree(tree, "", "block"); html += renderFolderTree(tree, "", "block");
} }
@@ -730,8 +722,8 @@ document.addEventListener("click", function () {
hideFolderManagerContextMenu(); hideFolderManagerContextMenu();
}); });
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function () {
document.addEventListener("keydown", function(e) { document.addEventListener("keydown", function (e) {
// Skip if the user is typing in an input, textarea, or contentEditable element. // Skip if the user is typing in an input, textarea, or contentEditable element.
const tag = e.target.tagName.toLowerCase(); const tag = e.target.tagName.toLowerCase();
if (tag === "input" || tag === "textarea" || e.target.isContentEditable) { if (tag === "input" || tag === "textarea" || e.target.isContentEditable) {