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