Skip to content

Commit 396c4e7

Browse files
committed
feat: tab autocomplete
1 parent 4f61a89 commit 396c4e7

File tree

2 files changed

+29
-4
lines changed

2 files changed

+29
-4
lines changed

src/components/terminal/TerminalPromptInput.tsx

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { FC, FormEvent, useState } from "react";
1+
import { FC, FormEvent, Fragment, KeyboardEvent, useState } from "react";
22
import { twMerge } from "tailwind-merge";
33
import { getColorfulPrompt } from "../../lib/utils";
44
import parse from "html-react-parser";
5+
import { COMMAND_NAMES } from "../../lib/commands";
56

67
interface TerminalPromptInputProps {
78
onEnter(prompt: string): void;
@@ -18,6 +19,20 @@ const TerminalPromptInput: FC<TerminalPromptInputProps> = ({ onEnter }) => {
1819
setInput("");
1920
}
2021

22+
const autocompleteCommand = input
23+
? COMMAND_NAMES.find((x) => x.startsWith(input))
24+
: undefined;
25+
26+
function handleKeyDown(e: KeyboardEvent<HTMLInputElement>) {
27+
setCaretPosition(e.currentTarget.selectionStart ?? 0);
28+
29+
if (autocompleteCommand && e.key === "Tab") {
30+
e.preventDefault();
31+
setInput(autocompleteCommand);
32+
setCaretPosition(autocompleteCommand.length);
33+
}
34+
}
35+
2136
return (
2237
<form onSubmit={handleSubmit} className="relative w-full">
2338
<input
@@ -30,13 +45,23 @@ const TerminalPromptInput: FC<TerminalPromptInputProps> = ({ onEnter }) => {
3045
}}
3146
value={input}
3247
onClick={(e) => setCaretPosition(e.currentTarget.selectionStart ?? 0)}
33-
onKeyDown={(e) => setCaretPosition(e.currentTarget.selectionStart ?? 0)}
48+
onKeyDown={handleKeyDown}
3449
onChange={(e) => {
3550
setInput(e.target.value);
3651
setCaretPosition(e.target.selectionStart ?? 0);
3752
}}
3853
className="w-full cursor-default whitespace-nowrap bg-transparent p-0 text-kali-white text-transparent focus:outline-none"
3954
/>
55+
56+
{autocompleteCommand && (
57+
<div className="absolute left-0 top-1/2 -translate-y-1/2 text-kali-text-muted">
58+
{[...input].map((_, idx) => (
59+
<Fragment key={idx}>&nbsp;</Fragment>
60+
))}
61+
{autocompleteCommand.substring(input.length)}
62+
</div>
63+
)}
64+
4065
<div
4166
className={twMerge(
4267
"pointer-events-none absolute left-0 top-1/2 h-[20px] max-w-full -translate-y-1/2 overflow-hidden overflow-ellipsis whitespace-nowrap pr-2"

src/lib/commands.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ const COMMANDS: Record<string, (username: string, args: string[]) => string> = {
105105
echo: (_, args) => args.join("&nbsp;"),
106106
};
107107

108-
const COMMAND_NAMES = [...Object.keys(COMMANDS), "clear", "help"].sort((a, z) =>
109-
a.localeCompare(z)
108+
export const COMMAND_NAMES = [...Object.keys(COMMANDS), "clear", "help"].sort(
109+
(a, z) => a.localeCompare(z)
110110
);
111111

112112
export function getCommandResponse(

0 commit comments

Comments
 (0)