diff --git a/build-plugin.sh b/build-plugin.sh index b7d6773..2a6d061 100755 --- a/build-plugin.sh +++ b/build-plugin.sh @@ -5,7 +5,7 @@ set -eu export COPYFILE_DISABLE=1 PLUGIN="u-navigator" -VERSION="2026.09.12.r001" +VERSION="2026.09.12.r002" PKG_DIR="packages" WORK_DIR=".plugin-build" PKG_NAME="${PLUGIN}-${VERSION}.tgz" diff --git a/packages/u-navigator-2026.09.12.r002.tgz b/packages/u-navigator-2026.09.12.r002.tgz new file mode 100644 index 0000000..daeb30d Binary files /dev/null and b/packages/u-navigator-2026.09.12.r002.tgz differ diff --git a/server/public/app.js b/server/public/app.js index 62e895c..a2e49f1 100644 --- a/server/public/app.js +++ b/server/public/app.js @@ -32,6 +32,7 @@ const state = { settingsPanelVisible: false, typeAhead: { key: '', + sequence: '', windowId: null, at: 0 }, @@ -2360,15 +2361,27 @@ function handleTypeAheadKey(event) { 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 sameWindow = state.typeAhead.windowId === win.id; + const withinWindow = now - state.typeAhead.at < 1000; + const sequence = sameWindow && withinWindow ? (state.typeAhead.sequence || '') : ''; + // Gleicher Buchstabe kurz nach einander = nächstes Vorkommen dieses Buchstabens + // (z. B. "AA" springt zum zweiten A-Eintrag), statt die Folge zu verdoppeln. + const repeated = sameWindow && withinWindow && state.typeAhead.key === key && sequence.length <= 1; + let next = repeated ? key : sequence + key; + if (!repeated && next.length > 16) next = key; + 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; + const startIndex = Math.max(0, selectedIndex + 1); + const match = findTypeAheadMatch(entries, next, startIndex, repeated); + if (!match) { + // Unvollständiger Pfad ohne Treffer: Auswahl bleibt, Folge wird verworfen + if (sequence) state.typeAhead = { key, sequence: '', windowId: win.id, at: now }; + return; + } event.preventDefault(); - state.typeAhead = { key, windowId: win.id, at: now }; + state.typeAhead = { key, sequence: next, windowId: win.id, at: now }; win.data.selectedPath = match.path; win.data.selectedPaths = [match.path]; win.data.selectionAnchorPath = match.path; @@ -2377,15 +2390,23 @@ function handleTypeAheadKey(event) { updateSelectedRows(); updatePropertiesWindows(); scrollEntryIntoView(win, match.path); - recordDebug('typeahead.match', { key, path: match.path }); + recordDebug('typeahead.match', { sequence: next, 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; +function findTypeAheadMatch(entries, sequence, startIndex, preferNextOccurrence = false) { + // Erst: Präfix-Treffer ab der aktuellen Position (mit Wraparound). + // Zweit: Teiltreffer (z. B. "amy" findet "Amy Spring"), damit Tippfolgen auch bei + // Präfix-Konkurrenz zum richtigen Eintrag springen. + for (let pass = 0; pass < 2; pass += 1) { + for (let offset = 0; offset < entries.length; offset += 1) { + const index = (startIndex + offset) % entries.length; + const entry = entries[index]; + const name = entry.name?.toLocaleLowerCase(); + if (!name) continue; + const isMatch = pass === 0 + ? name.startsWith(sequence) + : name.includes(sequence) && !(preferNextOccurrence && sequence.length <= 1); + if (isMatch) return entry; } } return null; diff --git a/u-navigator.plg b/u-navigator.plg index dbf2c2d..6b79a19 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; -9cee8533bb7f227236fb9ff4963e2d73 +97ee8d18a7eee6b3b1da1fa7451d5553