VNC-8 Adding upload icon and fixing file paths
This commit is contained in:
parent
cba6e69af6
commit
250d5bf531
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="25"
|
||||||
|
height="25"
|
||||||
|
viewBox="0 0 25 25"
|
||||||
|
version="1.1">
|
||||||
|
<g transform="translate(0.5,0.5)">
|
||||||
|
<!-- Cloud/folder base -->
|
||||||
|
<path
|
||||||
|
style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 21,15 21,19 C 21,20.104569 20.104569,21 19,21 L 5,21 C 3.8954305,21 3,20.104569 3,19 L 3,15" />
|
||||||
|
<!-- Arrow shaft -->
|
||||||
|
<path
|
||||||
|
style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 12,3 L 12,15" />
|
||||||
|
<!-- Arrow head -->
|
||||||
|
<path
|
||||||
|
style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 17,8 L 12,3 L 7,8" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
169
app/ui.js
169
app/ui.js
|
|
@ -78,6 +78,8 @@ const UI = {
|
||||||
monitorStartX: 0,
|
monitorStartX: 0,
|
||||||
monitorStartY: 0,
|
monitorStartY: 0,
|
||||||
|
|
||||||
|
currentDownloadPath: '', // Track current folder path for downloads
|
||||||
|
|
||||||
supportsBroadcastChannel: (typeof BroadcastChannel !== "undefined"),
|
supportsBroadcastChannel: (typeof BroadcastChannel !== "undefined"),
|
||||||
|
|
||||||
prime() {
|
prime() {
|
||||||
|
|
@ -535,17 +537,25 @@ const UI = {
|
||||||
},
|
},
|
||||||
|
|
||||||
addUploadHandlers() {
|
addUploadHandlers() {
|
||||||
UI.addClickHandle('noVNC_upload_button', UI.toggleUploadPanel);
|
if (document.getElementById('noVNC_upload_button')) {
|
||||||
|
UI.addClickHandle('noVNC_upload_button', UI.toggleUploadPanel);
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById("noVNC_file_input")
|
const fileInput = document.getElementById("noVNC_file_input");
|
||||||
.addEventListener('change', UI.handleFileSelect);
|
if (fileInput) {
|
||||||
|
fileInput.addEventListener('change', UI.handleFileSelect);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addDownloadHandlers() {
|
addDownloadHandlers() {
|
||||||
UI.addClickHandle('noVNC_download_button', UI.toggleDownloadPanel);
|
if (document.getElementById('noVNC_download_button')) {
|
||||||
|
UI.addClickHandle('noVNC_download_button', UI.toggleDownloadPanel);
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById("noVNC_refresh_downloads_button")
|
const refreshButton = document.getElementById("noVNC_refresh_downloads_button");
|
||||||
.addEventListener('click', UI.refreshDownloadsList);
|
if (refreshButton) {
|
||||||
|
refreshButton.addEventListener('click', UI.refreshDownloadsList);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Add a call to save settings when the element changes,
|
// Add a call to save settings when the element changes,
|
||||||
|
|
@ -1549,20 +1559,33 @@ const UI = {
|
||||||
UI.closeAllPanels();
|
UI.closeAllPanels();
|
||||||
UI.openControlbar();
|
UI.openControlbar();
|
||||||
|
|
||||||
document.getElementById('noVNC_download_panel')
|
const panel = document.getElementById('noVNC_download_panel');
|
||||||
.classList.add("noVNC_open");
|
const button = document.getElementById('noVNC_download_button');
|
||||||
document.getElementById('noVNC_download_button')
|
|
||||||
.classList.add("noVNC_selected");
|
if (panel) {
|
||||||
|
panel.classList.add("noVNC_open");
|
||||||
|
}
|
||||||
|
if (button) {
|
||||||
|
button.classList.add("noVNC_selected");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset to root folder when opening
|
||||||
|
UI.currentDownloadPath = '';
|
||||||
|
|
||||||
// Refresh file list when opening
|
// Refresh file list when opening
|
||||||
UI.refreshDownloadsList();
|
UI.refreshDownloadsList();
|
||||||
},
|
},
|
||||||
|
|
||||||
closeDownloadPanel() {
|
closeDownloadPanel() {
|
||||||
document.getElementById('noVNC_download_panel')
|
const panel = document.getElementById('noVNC_download_panel');
|
||||||
.classList.remove("noVNC_open");
|
const button = document.getElementById('noVNC_download_button');
|
||||||
document.getElementById('noVNC_download_button')
|
|
||||||
.classList.remove("noVNC_selected");
|
if (panel) {
|
||||||
|
panel.classList.remove("noVNC_open");
|
||||||
|
}
|
||||||
|
if (button) {
|
||||||
|
button.classList.remove("noVNC_selected");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleDownloadPanel(e) {
|
toggleDownloadPanel(e) {
|
||||||
|
|
@ -1570,8 +1593,8 @@ const UI = {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.getElementById('noVNC_download_panel')
|
const panel = document.getElementById('noVNC_download_panel');
|
||||||
.classList.contains("noVNC_open")) {
|
if (panel && panel.classList.contains("noVNC_open")) {
|
||||||
UI.closeDownloadPanel();
|
UI.closeDownloadPanel();
|
||||||
} else {
|
} else {
|
||||||
UI.openDownloadPanel();
|
UI.openDownloadPanel();
|
||||||
|
|
@ -1581,24 +1604,77 @@ const UI = {
|
||||||
refreshDownloadsList() {
|
refreshDownloadsList() {
|
||||||
const downloadsList = document.getElementById('noVNC_download_files_list');
|
const downloadsList = document.getElementById('noVNC_download_files_list');
|
||||||
|
|
||||||
|
if (!downloadsList) {
|
||||||
|
console.log('Download files list element not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Show loading message
|
// Show loading message
|
||||||
downloadsList.innerHTML = '<div style="padding: 10px; text-align: center;">Loading files...</div>';
|
downloadsList.innerHTML = '<div style="padding: 10px; text-align: center;">Loading files...</div>';
|
||||||
|
|
||||||
|
// Build API URL with path parameter if we're in a subfolder
|
||||||
|
let apiUrl = '/api/downloads';
|
||||||
|
if (UI.currentDownloadPath) {
|
||||||
|
// Server expects path to start with /
|
||||||
|
apiUrl += '?path=' + encodeURIComponent('/' + UI.currentDownloadPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Fetching downloads from:', apiUrl, 'currentPath:', UI.currentDownloadPath);
|
||||||
|
|
||||||
// Fetch file list from API
|
// Fetch file list from API
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
xhr.open('GET', '/api/downloads', true);
|
xhr.open('GET', apiUrl, true);
|
||||||
|
|
||||||
xhr.addEventListener('load', () => {
|
xhr.addEventListener('load', () => {
|
||||||
if (xhr.status === 200) {
|
if (xhr.status === 200) {
|
||||||
try {
|
try {
|
||||||
const response = JSON.parse(xhr.responseText);
|
const response = JSON.parse(xhr.responseText);
|
||||||
|
|
||||||
if (response.success && response.downloads && response.downloads.length > 0) {
|
// Handle both old format (success/downloads) and new format (files)
|
||||||
// Clear loading message
|
const files = response.files || response.downloads || [];
|
||||||
downloadsList.innerHTML = '';
|
|
||||||
|
|
||||||
|
// Clear loading message
|
||||||
|
downloadsList.innerHTML = '';
|
||||||
|
|
||||||
|
// Add breadcrumb navigation
|
||||||
|
const breadcrumb = document.createElement('div');
|
||||||
|
breadcrumb.style.padding = '8px';
|
||||||
|
breadcrumb.style.marginBottom = '8px';
|
||||||
|
breadcrumb.style.borderBottom = '1px solid #444';
|
||||||
|
breadcrumb.style.display = 'flex';
|
||||||
|
breadcrumb.style.alignItems = 'center';
|
||||||
|
breadcrumb.style.gap = '8px';
|
||||||
|
|
||||||
|
// Add "up" button if not at root
|
||||||
|
if (UI.currentDownloadPath) {
|
||||||
|
const upBtn = document.createElement('button');
|
||||||
|
upBtn.textContent = '← Back';
|
||||||
|
upBtn.style.padding = '5px 10px';
|
||||||
|
upBtn.style.fontSize = '12px';
|
||||||
|
upBtn.style.cursor = 'pointer';
|
||||||
|
upBtn.addEventListener('click', () => {
|
||||||
|
// Go up one level
|
||||||
|
const pathParts = UI.currentDownloadPath.split('/').filter(p => p);
|
||||||
|
pathParts.pop();
|
||||||
|
UI.currentDownloadPath = pathParts.join('/');
|
||||||
|
UI.refreshDownloadsList();
|
||||||
|
});
|
||||||
|
breadcrumb.appendChild(upBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show current path
|
||||||
|
const pathLabel = document.createElement('span');
|
||||||
|
pathLabel.textContent = UI.currentDownloadPath ? '/' + UI.currentDownloadPath : '/Downloads';
|
||||||
|
pathLabel.style.color = '#cccccc';
|
||||||
|
pathLabel.style.fontSize = '12px';
|
||||||
|
pathLabel.style.fontWeight = 'bold';
|
||||||
|
breadcrumb.appendChild(pathLabel);
|
||||||
|
|
||||||
|
downloadsList.appendChild(breadcrumb);
|
||||||
|
|
||||||
|
if (files.length > 0) {
|
||||||
// Display each file
|
// Display each file
|
||||||
response.downloads.forEach(file => {
|
files.forEach(file => {
|
||||||
const fileItem = document.createElement('div');
|
const fileItem = document.createElement('div');
|
||||||
fileItem.className = 'noVNC_download_item';
|
fileItem.className = 'noVNC_download_item';
|
||||||
fileItem.style.marginBottom = '8px';
|
fileItem.style.marginBottom = '8px';
|
||||||
|
|
@ -1614,7 +1690,8 @@ const UI = {
|
||||||
fileInfo.style.minWidth = '0';
|
fileInfo.style.minWidth = '0';
|
||||||
|
|
||||||
const fileName = document.createElement('div');
|
const fileName = document.createElement('div');
|
||||||
fileName.textContent = file.filename;
|
// Add folder icon for directories
|
||||||
|
fileName.textContent = (file.is_dir ? '📁 ' : '') + file.filename;
|
||||||
fileName.style.fontSize = '13px';
|
fileName.style.fontSize = '13px';
|
||||||
fileName.style.fontWeight = 'bold';
|
fileName.style.fontWeight = 'bold';
|
||||||
fileName.style.color = '#ffffff';
|
fileName.style.color = '#ffffff';
|
||||||
|
|
@ -1626,7 +1703,23 @@ const UI = {
|
||||||
fileDetails.style.marginTop = '3px';
|
fileDetails.style.marginTop = '3px';
|
||||||
|
|
||||||
if (file.is_dir) {
|
if (file.is_dir) {
|
||||||
fileDetails.textContent = 'Directory';
|
fileDetails.textContent = 'Folder - Click to open';
|
||||||
|
// Make directories clickable
|
||||||
|
fileItem.style.cursor = 'pointer';
|
||||||
|
fileItem.style.transition = 'background-color 0.2s';
|
||||||
|
fileItem.addEventListener('mouseenter', () => {
|
||||||
|
fileItem.style.backgroundColor = 'rgba(255, 255, 255, 0.1)';
|
||||||
|
});
|
||||||
|
fileItem.addEventListener('mouseleave', () => {
|
||||||
|
fileItem.style.backgroundColor = '';
|
||||||
|
});
|
||||||
|
fileItem.addEventListener('click', () => {
|
||||||
|
// Navigate into directory
|
||||||
|
UI.currentDownloadPath = UI.currentDownloadPath
|
||||||
|
? UI.currentDownloadPath + '/' + file.filename
|
||||||
|
: file.filename;
|
||||||
|
UI.refreshDownloadsList();
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
fileDetails.textContent = UI.formatFileSize(file.size);
|
fileDetails.textContent = UI.formatFileSize(file.size);
|
||||||
}
|
}
|
||||||
|
|
@ -1642,7 +1735,8 @@ const UI = {
|
||||||
downloadBtn.style.padding = '5px 10px';
|
downloadBtn.style.padding = '5px 10px';
|
||||||
downloadBtn.style.fontSize = '12px';
|
downloadBtn.style.fontSize = '12px';
|
||||||
downloadBtn.style.cursor = 'pointer';
|
downloadBtn.style.cursor = 'pointer';
|
||||||
downloadBtn.addEventListener('click', () => {
|
downloadBtn.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation(); // Prevent any parent click handlers
|
||||||
UI.downloadFile(file.filename);
|
UI.downloadFile(file.filename);
|
||||||
});
|
});
|
||||||
fileItem.appendChild(fileInfo);
|
fileItem.appendChild(fileInfo);
|
||||||
|
|
@ -1657,14 +1751,27 @@ const UI = {
|
||||||
downloadsList.innerHTML = '<div style="padding: 10px; text-align: center; color: #999;">No files available</div>';
|
downloadsList.innerHTML = '<div style="padding: 10px; text-align: center; color: #999;">No files available</div>';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
downloadsList.innerHTML = '<div style="padding: 10px; text-align: center; color: #f44336;">Error parsing response</div>';
|
console.error('Error parsing downloads response:', e, xhr.responseText);
|
||||||
|
downloadsList.innerHTML = '<div style="padding: 10px; text-align: center; color: #f44336;">Error parsing response: ' + e.message + '</div>';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
downloadsList.innerHTML = '<div style="padding: 10px; text-align: center; color: #f44336;">Failed to load files</div>';
|
// Try to parse error message from server
|
||||||
|
let errorMsg = 'Failed to load files (HTTP ' + xhr.status + ')';
|
||||||
|
try {
|
||||||
|
const errorData = JSON.parse(xhr.responseText);
|
||||||
|
if (errorData.error) {
|
||||||
|
errorMsg = errorData.error;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Ignore parse errors, use default message
|
||||||
|
}
|
||||||
|
console.error('Downloads API error:', xhr.status, xhr.responseText);
|
||||||
|
downloadsList.innerHTML = '<div style="padding: 10px; text-align: center; color: #f44336;">' + errorMsg + '</div>';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
xhr.addEventListener('error', () => {
|
xhr.addEventListener('error', () => {
|
||||||
|
console.error('Network error loading downloads');
|
||||||
downloadsList.innerHTML = '<div style="padding: 10px; text-align: center; color: #f44336;">Network error</div>';
|
downloadsList.innerHTML = '<div style="padding: 10px; text-align: center; color: #f44336;">Network error</div>';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1674,7 +1781,13 @@ const UI = {
|
||||||
downloadFile(filename) {
|
downloadFile(filename) {
|
||||||
// Create a temporary anchor element and trigger download
|
// Create a temporary anchor element and trigger download
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = '/downloads/' + encodeURIComponent(filename);
|
|
||||||
|
// Build the full path including current directory
|
||||||
|
let fullPath = UI.currentDownloadPath
|
||||||
|
? UI.currentDownloadPath + '/' + filename
|
||||||
|
: filename;
|
||||||
|
|
||||||
|
a.href = '/Downloads/' + encodeURIComponent(fullPath);
|
||||||
a.download = filename;
|
a.download = filename;
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
|
|
@ -3409,4 +3522,4 @@ if (l10n.language === "en" || l10n.dictionary !== undefined) {
|
||||||
.then(UI.prime);
|
.then(UI.prime);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default UI;
|
export default UI;
|
||||||
Loading…
Reference in New Issue