Add explorer typeahead navigation
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
PLUGIN="u-navigator"
|
PLUGIN="u-navigator"
|
||||||
VERSION="2026.06.23.r029"
|
VERSION="2026.06.23.r030"
|
||||||
PKG_DIR="packages"
|
PKG_DIR="packages"
|
||||||
WORK_DIR=".plugin-build"
|
WORK_DIR=".plugin-build"
|
||||||
PKG_NAME="${PLUGIN}-${VERSION}.tgz"
|
PKG_NAME="${PLUGIN}-${VERSION}.tgz"
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -25,6 +25,11 @@ const state = {
|
|||||||
transferPanelClosing: false,
|
transferPanelClosing: false,
|
||||||
transferAutoHideTimer: null,
|
transferAutoHideTimer: null,
|
||||||
settingsPanelVisible: false,
|
settingsPanelVisible: false,
|
||||||
|
typeAhead: {
|
||||||
|
key: '',
|
||||||
|
windowId: null,
|
||||||
|
at: 0
|
||||||
|
},
|
||||||
nextWindowId: 1,
|
nextWindowId: 1,
|
||||||
zIndex: 20
|
zIndex: 20
|
||||||
};
|
};
|
||||||
@@ -53,6 +58,7 @@ window.addEventListener('error', (event) => {
|
|||||||
window.addEventListener('unhandledrejection', (event) => {
|
window.addEventListener('unhandledrejection', (event) => {
|
||||||
recordDebug('window.unhandledrejection', { reason: serializeError(event.reason) });
|
recordDebug('window.unhandledrejection', { reason: serializeError(event.reason) });
|
||||||
});
|
});
|
||||||
|
document.addEventListener('keydown', handleTypeAheadKey);
|
||||||
|
|
||||||
init();
|
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() {
|
function updatePropertiesWindows() {
|
||||||
for (const win of state.windows.filter((item) => item.kind === 'properties')) {
|
for (const win of state.windows.filter((item) => item.kind === 'properties')) {
|
||||||
const body = workspace.querySelector(`.window[data-window-id="${win.id}"] .window-body`);
|
const body = workspace.querySelector(`.window[data-window-id="${win.id}"] .window-body`);
|
||||||
@@ -1419,6 +1478,13 @@ function escapeAttr(value) {
|
|||||||
return escapeHtml(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) {
|
function clamp(value, min, max) {
|
||||||
return Math.max(min, Math.min(max, value));
|
return Math.max(min, Math.min(max, value));
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE PLUGIN [
|
<!DOCTYPE PLUGIN [
|
||||||
<!ENTITY name "u-navigator">
|
<!ENTITY name "u-navigator">
|
||||||
<!ENTITY author "Michael Roll">
|
<!ENTITY author "Michael Roll">
|
||||||
<!ENTITY version "2026.06.23.r029">
|
<!ENTITY version "2026.06.23.r030">
|
||||||
<!ENTITY package "&name;-&version;.tgz">
|
<!ENTITY package "&name;-&version;.tgz">
|
||||||
<!ENTITY pluginURL "https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/u-navigator.plg">
|
<!ENTITY pluginURL "https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/u-navigator.plg">
|
||||||
<!ENTITY support "https://git.casaderoll.de/michael/Unraid-Navigator">
|
<!ENTITY support "https://git.casaderoll.de/michael/Unraid-Navigator">
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<FILE Name="/boot/config/plugins/&name;/&package;">
|
<FILE Name="/boot/config/plugins/&name;/&package;">
|
||||||
<URL>https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/packages/&package;</URL>
|
<URL>https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/packages/&package;</URL>
|
||||||
<MD5>2c27aa33b2a3be760a518625a0762cac</MD5>
|
<MD5>64307317988b7af11b76fce0993e76e6</MD5>
|
||||||
</FILE>
|
</FILE>
|
||||||
|
|
||||||
<FILE Run="/bin/bash">
|
<FILE Run="/bin/bash">
|
||||||
|
|||||||
Reference in New Issue
Block a user