diff --git a/build-plugin.sh b/build-plugin.sh index 9e32399..2924131 100755 --- a/build-plugin.sh +++ b/build-plugin.sh @@ -2,7 +2,7 @@ set -eu PLUGIN="u-navigator" -VERSION="2026.06.23.r029" +VERSION="2026.06.23.r030" PKG_DIR="packages" WORK_DIR=".plugin-build" PKG_NAME="${PLUGIN}-${VERSION}.tgz" diff --git a/packages/u-navigator-2026.06.23.r029.tgz b/packages/u-navigator-2026.06.23.r029.tgz deleted file mode 100644 index e060eb6..0000000 Binary files a/packages/u-navigator-2026.06.23.r029.tgz and /dev/null differ diff --git a/packages/u-navigator-2026.06.23.r030.tgz b/packages/u-navigator-2026.06.23.r030.tgz new file mode 100644 index 0000000..5bbc173 Binary files /dev/null and b/packages/u-navigator-2026.06.23.r030.tgz differ diff --git a/server/public/app.js b/server/public/app.js index b2ee669..534beec 100644 --- a/server/public/app.js +++ b/server/public/app.js @@ -25,6 +25,11 @@ const state = { transferPanelClosing: false, transferAutoHideTimer: null, settingsPanelVisible: false, + typeAhead: { + key: '', + windowId: null, + at: 0 + }, nextWindowId: 1, zIndex: 20 }; @@ -53,6 +58,7 @@ window.addEventListener('error', (event) => { window.addEventListener('unhandledrejection', (event) => { recordDebug('window.unhandledrejection', { reason: serializeError(event.reason) }); }); +document.addEventListener('keydown', handleTypeAheadKey); init(); @@ -1015,6 +1021,59 @@ function updateSelectedRows() { } } +function handleTypeAheadKey(event) { + if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.altKey) return; + if (state.contextMenu || state.settingsPanelVisible) return; + if (isTypingTarget(document.activeElement)) return; + if (event.key.length !== 1) return; + + const key = event.key.toLocaleLowerCase(); + if (!/^[\p{L}\p{N}]$/u.test(key)) return; + + const win = state.windows.find((item) => item.id === state.focusedId && item.kind === 'explorer'); + if (!win || !win.data.entries?.length) return; + + const now = Date.now(); + const repeated = state.typeAhead.windowId === win.id && state.typeAhead.key === key && now - state.typeAhead.at < 1000; + const entries = win.data.entries; + const selectedIndex = entries.findIndex((entry) => entry.path === win.data.selectedPath); + const startIndex = repeated ? selectedIndex + 1 : Math.max(0, selectedIndex + 1); + const match = findTypeAheadMatch(entries, key, startIndex); + if (!match) return; + + event.preventDefault(); + state.typeAhead = { key, windowId: win.id, at: now }; + win.data.selectedPath = match.path; + state.selectedEntry = match; + focusWindow(win); + updateSelectedRows(); + updatePropertiesWindows(); + scrollEntryIntoView(win, match.path); + recordDebug('typeahead.match', { key, path: match.path }); +} + +function findTypeAheadMatch(entries, key, startIndex) { + for (let offset = 0; offset < entries.length; offset += 1) { + const index = (startIndex + offset) % entries.length; + const entry = entries[index]; + if (entry.name?.toLocaleLowerCase().startsWith(key)) { + return entry; + } + } + return null; +} + +function scrollEntryIntoView(win, path) { + const row = workspace.querySelector(`.window[data-window-id="${win.id}"] .file-row[data-path="${cssEscape(path)}"]`); + row?.scrollIntoView({ block: 'nearest', inline: 'nearest' }); +} + +function isTypingTarget(element) { + if (!element) return false; + const tag = element.tagName; + return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || element.isContentEditable; +} + function updatePropertiesWindows() { for (const win of state.windows.filter((item) => item.kind === 'properties')) { const body = workspace.querySelector(`.window[data-window-id="${win.id}"] .window-body`); @@ -1419,6 +1478,13 @@ function escapeAttr(value) { return escapeHtml(value); } +function cssEscape(value) { + if (window.CSS?.escape) { + return window.CSS.escape(String(value)); + } + return String(value).replace(/["\\]/g, '\\$&'); +} + function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } diff --git a/u-navigator.plg b/u-navigator.plg index f890578..895f04c 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; -2c27aa33b2a3be760a518625a0762cac +64307317988b7af11b76fce0993e76e6