diff --git a/build-plugin.sh b/build-plugin.sh
index d52c928..373ce6a 100755
--- a/build-plugin.sh
+++ b/build-plugin.sh
@@ -2,7 +2,7 @@
set -eu
PLUGIN="u-navigator"
-VERSION="2026.07.14.r015"
+VERSION="2026.07.14.r016"
PKG_DIR="packages"
WORK_DIR=".plugin-build"
PKG_NAME="${PLUGIN}-${VERSION}.tgz"
diff --git a/packages/u-navigator-2026.07.14.r016.tgz b/packages/u-navigator-2026.07.14.r016.tgz
new file mode 100644
index 0000000..1a8bf81
Binary files /dev/null and b/packages/u-navigator-2026.07.14.r016.tgz differ
diff --git a/server/public/app.js b/server/public/app.js
index 9b5c62f..1643bfd 100644
--- a/server/public/app.js
+++ b/server/public/app.js
@@ -51,7 +51,10 @@ const uiIcons = {
chevronDown: '',
chevronRight: '',
close: '',
+ copy: '',
delete: '',
+ download: '',
+ edit: '',
enter: '',
file: '',
folder: '',
@@ -59,9 +62,13 @@ const uiIcons = {
grid: '',
list: '',
link: '',
+ info: '',
maximize: '',
minimize: '',
+ move: '',
+ preview: '',
refresh: '',
+ shield: '',
tree: '',
upload: ''
};
@@ -1995,33 +2002,40 @@ function renderContextMenu() {
if (entry) {
if (entry.type === 'directory') {
- actions.push(['Öffnen', () => loadExplorer(win, entry.path)]);
+ actions.push({ label: 'Öffnen', icon: 'folder', action: () => loadExplorer(win, entry.path) });
} else if (isPreviewable(entry)) {
- actions.push(['Vorschau', () => openPreview(entry, win.id)]);
+ actions.push({ label: 'Vorschau', icon: 'preview', action: () => openPreview(entry, win.id) });
}
- actions.push(['Eigenschaften', () => showPropertiesForEntry(win, entry)]);
- actions.push(['Herunterladen', () => downloadEntries(win, entry)]);
+ actions.push({ label: 'Eigenschaften', icon: 'info', action: () => showPropertiesForEntry(win, entry) });
+ actions.push({ label: 'Herunterladen', icon: 'download', action: () => downloadEntries(win, entry) });
if (!isReadOnly()) {
- actions.push(['Umbenennen', () => renameEntry(win, entry)]);
- actions.push(['Berechtigungen...', () => openPermissionsDialog(win, entry)]);
- actions.push(['Kopieren nach...', () => promptTransfer(win, entry, 'copy')]);
- actions.push(['Verschieben nach...', () => promptTransfer(win, entry, 'move')]);
- actions.push(['Löschen', () => deleteEntries(win, entry)]);
+ actions.push({ label: 'Umbenennen', icon: 'edit', separator: true, action: () => renameEntry(win, entry) });
+ actions.push({ label: 'Berechtigungen…', icon: 'shield', action: () => openPermissionsDialog(win, entry) });
+ actions.push({ label: 'Kopieren nach…', icon: 'copy', action: () => promptTransfer(win, entry, 'copy') });
+ actions.push({ label: 'Verschieben nach…', icon: 'move', action: () => promptTransfer(win, entry, 'move') });
+ actions.push({ label: 'Löschen', icon: 'delete', danger: true, separator: true, action: () => deleteEntries(win, entry) });
}
} else {
- actions.push(['Aktuellen Ordner herunterladen', () => downloadEntries(win, null)]);
- actions.push(['Neu laden', () => loadExplorer(win, win.data.path)]);
+ actions.push({ label: 'Aktuellen Ordner herunterladen', icon: 'download', action: () => downloadEntries(win, null) });
+ actions.push({ label: 'Neu laden', icon: 'refresh', action: () => loadExplorer(win, win.data.path) });
}
- for (const [label, action] of actions) {
+ for (const item of actions) {
+ if (item.separator) {
+ const separator = document.createElement('div');
+ separator.className = 'context-separator';
+ separator.setAttribute('role', 'separator');
+ menu.appendChild(separator);
+ }
const button = document.createElement('button');
button.type = 'button';
- button.textContent = label;
+ button.className = `context-action ${item.danger ? 'danger' : ''}`;
+ button.innerHTML = `${iconSvg(item.icon)}${escapeHtml(item.label)}`;
button.addEventListener('click', async () => {
state.contextMenu = null;
render();
try {
- await action();
+ await item.action();
} catch (error) {
showError(error);
}
diff --git a/server/public/styles.css b/server/public/styles.css
index b145d92..9603363 100644
--- a/server/public/styles.css
+++ b/server/public/styles.css
@@ -792,25 +792,62 @@
.u-nav .context-menu {
background: var(--surface);
border: 1px solid var(--line);
- border-radius: 8px;
+ border-radius: 10px;
box-shadow: var(--shadow);
display: grid;
- min-width: 170px;
- padding: 5px;
+ min-width: 205px;
+ padding: 4px;
position: absolute;
z-index: 100000;
}
-.u-nav .context-menu button {
- border-color: transparent;
- justify-content: flex-start;
- text-align: left;
+.u-nav .context-menu .context-action {
+ align-items: center;
+ background: transparent;
+ border: 0;
+ border-radius: 7px;
+ box-shadow: none;
+ display: grid;
+ font: 500 12px/1.2 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
+ gap: 7px;
+ grid-template-columns: 18px minmax(0, 1fr);
+ justify-content: stretch !important;
+ letter-spacing: normal !important;
+ margin: 0 !important;
+ min-height: 30px;
+ padding: 0 8px !important;
+ text-align: left !important;
+ text-transform: none !important;
+ white-space: nowrap;
width: 100%;
}
-.u-nav .context-menu button:hover {
- background: var(--surface-2);
- border-color: var(--line);
+.u-nav .context-menu .context-action:hover,
+.u-nav .context-menu .context-action:focus-visible {
+ background: var(--accent-soft);
+ color: var(--accent);
+}
+
+.u-nav .context-menu .context-action.danger {
+ color: #c4382d;
+}
+
+.u-nav .context-icon {
+ align-items: center;
+ display: inline-flex;
+ justify-content: center;
+}
+
+.u-nav .context-icon .unav-icon {
+ height: 15px;
+ stroke-width: 1.9;
+ width: 15px;
+}
+
+.u-nav .context-separator {
+ border-top: 1px solid var(--line);
+ height: 1px;
+ margin: 4px 5px;
}
.u-nav .modal-overlay {
diff --git a/u-navigator.plg b/u-navigator.plg
index 43a9c45..2f67859 100644
--- a/u-navigator.plg
+++ b/u-navigator.plg
@@ -1,7 +1,7 @@
-
+
@@ -24,7 +24,7 @@
https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/packages/&package;
-e7bd64a4dda7a5915d9ef73657dec517
+8c943cbf6a929bfb8e3249c901fb3869