|
|
|
@ -18,6 +18,34 @@ export class CommandPalette { |
|
|
|
private filtered: RuntimeCommand[] = []; |
|
|
|
private selectedIndex = 0; |
|
|
|
private isOpen = false; |
|
|
|
private readonly onInput = () => this.refreshFiltered(); |
|
|
|
private readonly onInputKeydown = (event: KeyboardEvent) => { |
|
|
|
if (event.key === "ArrowDown") { |
|
|
|
event.preventDefault(); |
|
|
|
this.selectedIndex = Math.min(this.selectedIndex + 1, this.filtered.length - 1); |
|
|
|
this.renderList(); |
|
|
|
} else if (event.key === "ArrowUp") { |
|
|
|
event.preventDefault(); |
|
|
|
this.selectedIndex = Math.max(this.selectedIndex - 1, 0); |
|
|
|
this.renderList(); |
|
|
|
} else if (event.key === "Enter") { |
|
|
|
event.preventDefault(); |
|
|
|
this.executeSelected(); |
|
|
|
} else if (event.key === "Escape") { |
|
|
|
this.hide(); |
|
|
|
} |
|
|
|
}; |
|
|
|
private readonly onWindowKeydown = (event: KeyboardEvent) => { |
|
|
|
if (isEditableTarget(event.target)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
const isToggle = (event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "k"; |
|
|
|
if (!isToggle) { |
|
|
|
return; |
|
|
|
} |
|
|
|
event.preventDefault(); |
|
|
|
this.toggle(); |
|
|
|
}; |
|
|
|
|
|
|
|
constructor() { |
|
|
|
this.element = this.root.element; |
|
|
|
@ -40,23 +68,8 @@ export class CommandPalette { |
|
|
|
this.input.style.padding = "6px"; |
|
|
|
this.input.style.borderRadius = "4px"; |
|
|
|
|
|
|
|
this.input.addEventListener("input", () => this.refreshFiltered()); |
|
|
|
this.input.addEventListener("keydown", (event) => { |
|
|
|
if (event.key === "ArrowDown") { |
|
|
|
event.preventDefault(); |
|
|
|
this.selectedIndex = Math.min(this.selectedIndex + 1, this.filtered.length - 1); |
|
|
|
this.renderList(); |
|
|
|
} else if (event.key === "ArrowUp") { |
|
|
|
event.preventDefault(); |
|
|
|
this.selectedIndex = Math.max(this.selectedIndex - 1, 0); |
|
|
|
this.renderList(); |
|
|
|
} else if (event.key === "Enter") { |
|
|
|
event.preventDefault(); |
|
|
|
this.executeSelected(); |
|
|
|
} else if (event.key === "Escape") { |
|
|
|
this.hide(); |
|
|
|
} |
|
|
|
}); |
|
|
|
this.input.addEventListener("input", this.onInput); |
|
|
|
this.input.addEventListener("keydown", this.onInputKeydown); |
|
|
|
|
|
|
|
this.root.append(this.hintText.element, this.input, this.list.element); |
|
|
|
document.body.appendChild(this.element); |
|
|
|
@ -91,14 +104,7 @@ export class CommandPalette { |
|
|
|
} |
|
|
|
|
|
|
|
private bindKeyboard(): void { |
|
|
|
window.addEventListener("keydown", (event) => { |
|
|
|
const isToggle = (event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "k"; |
|
|
|
if (!isToggle) { |
|
|
|
return; |
|
|
|
} |
|
|
|
event.preventDefault(); |
|
|
|
this.toggle(); |
|
|
|
}); |
|
|
|
window.addEventListener("keydown", this.onWindowKeydown); |
|
|
|
} |
|
|
|
|
|
|
|
private refreshFiltered(): void { |
|
|
|
@ -125,4 +131,25 @@ export class CommandPalette { |
|
|
|
command.run(); |
|
|
|
this.hide(); |
|
|
|
} |
|
|
|
|
|
|
|
dispose(): void { |
|
|
|
window.removeEventListener("keydown", this.onWindowKeydown); |
|
|
|
this.input.removeEventListener("input", this.onInput); |
|
|
|
this.input.removeEventListener("keydown", this.onInputKeydown); |
|
|
|
this.element.remove(); |
|
|
|
this.commands.clear(); |
|
|
|
this.filtered = []; |
|
|
|
this.isOpen = false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function isEditableTarget(target: EventTarget | null): boolean { |
|
|
|
if (!(target instanceof HTMLElement)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
if (target.isContentEditable) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
const tagName = target.tagName.toLowerCase(); |
|
|
|
return tagName === "input" || tagName === "textarea"; |
|
|
|
} |
|
|
|
|