Fixed up project picker a bit
This commit is contained in:
@@ -16,6 +16,35 @@ function isFuzzyMatch(candidate: string, query: string) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCurrentPartial(input: string) {
|
||||||
|
const trimmed = input.trim();
|
||||||
|
if (!trimmed) return "";
|
||||||
|
if (trimmed.endsWith("/")) return "";
|
||||||
|
const idx = trimmed.lastIndexOf("/");
|
||||||
|
return idx >= 0 ? trimmed.slice(idx + 1) : trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderHighlightedMatch(text: string, query: string) {
|
||||||
|
if (!query) return text;
|
||||||
|
let qIndex = 0;
|
||||||
|
const lowerQuery = query.toLowerCase();
|
||||||
|
return text.split("").map((char, index) => {
|
||||||
|
const isMatch =
|
||||||
|
qIndex < lowerQuery.length && char.toLowerCase() === lowerQuery[qIndex];
|
||||||
|
if (isMatch) {
|
||||||
|
qIndex += 1;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
key={`${char}-${index}`}
|
||||||
|
style={isMatch ? { fontWeight: 600, color: "#222" } : undefined}
|
||||||
|
>
|
||||||
|
{char}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [projectPath, setProjectPath] = React.useState<string | null>(null);
|
const [projectPath, setProjectPath] = React.useState<string | null>(null);
|
||||||
const [errorMsg, setErrorMsg] = React.useState<string | null>(null);
|
const [errorMsg, setErrorMsg] = React.useState<string | null>(null);
|
||||||
@@ -125,18 +154,21 @@ function App() {
|
|||||||
setMatchList(list);
|
setMatchList(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
computeSuggestion().catch((error) => {
|
const debounceId = window.setTimeout(() => {
|
||||||
console.error(error);
|
computeSuggestion().catch((error) => {
|
||||||
if (!active) return;
|
console.error(error);
|
||||||
setCompletionError(
|
if (!active) return;
|
||||||
error instanceof Error
|
setCompletionError(
|
||||||
? error.message
|
error instanceof Error
|
||||||
: "Failed to compute suggestion.",
|
? error.message
|
||||||
);
|
: "Failed to compute suggestion.",
|
||||||
});
|
);
|
||||||
|
});
|
||||||
|
}, 60);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
active = false;
|
active = false;
|
||||||
|
window.clearTimeout(debounceId);
|
||||||
};
|
};
|
||||||
}, [pathInput, homeDir]);
|
}, [pathInput, homeDir]);
|
||||||
|
|
||||||
@@ -195,6 +227,8 @@ function App() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentPartial = getCurrentPartial(pathInput);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main
|
<main
|
||||||
className="container"
|
className="container"
|
||||||
@@ -288,6 +322,13 @@ function App() {
|
|||||||
setPathInput(next);
|
setPathInput(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (event.key === "Escape") {
|
||||||
|
event.preventDefault();
|
||||||
|
setMatchList([]);
|
||||||
|
setSelectedMatch(0);
|
||||||
|
setSuggestion(null);
|
||||||
|
setSuggestionTail("");
|
||||||
|
setCompletionError(null);
|
||||||
} else if (event.key === "Enter") {
|
} else if (event.key === "Enter") {
|
||||||
handleOpen();
|
handleOpen();
|
||||||
}
|
}
|
||||||
@@ -342,7 +383,7 @@ function App() {
|
|||||||
fontFamily: "inherit",
|
fontFamily: "inherit",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{match.name}/
|
{renderHighlightedMatch(match.name, currentPartial)}/
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
Reference in New Issue
Block a user