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