Keep Add Project method selection path-free (#3099)

* fix(app): remove search from project method selection

The fixed method list should not accept text because it can be mistaken for the directory path step that follows.

* fix(app): preserve native project picker keys

Keep hardware-keyboard delivery owned by the Add Project flow even when method selection has no visible text field.

* test(app): update add project overlay expectation
This commit is contained in:
Mohamed Boudra
2026-08-09 21:46:58 +02:00
committed by GitHub
parent ccb4e444ed
commit 02c4bc76a6
6 changed files with 89 additions and 56 deletions
@@ -69,7 +69,7 @@ import {
import { Shortcut } from "@/components/ui/shortcut";
import { useKeyboardShortcutsAvailable } from "@/keyboard/availability";
import { getIsElectronRuntime } from "@/constants/layout";
import { isWeb } from "@/constants/platform";
import { isNative, isWeb } from "@/constants/platform";
import { pickDirectory } from "@/desktop/pick-directory";
import { useFetchQuery } from "@/data/query";
import { getOpenProjectFailureReason, registerProjectDescriptor } from "@/hooks/open-project";
@@ -222,12 +222,12 @@ function pageTitle(page: AddProjectPage): string {
}
}
function pagePlaceholder(page: AddProjectPage): string {
type AddProjectInputPage = Exclude<AddProjectPage, { kind: "method" }>;
function pagePlaceholder(page: AddProjectInputPage): string {
switch (page.kind) {
case "host":
return "Search hosts...";
case "method":
return "Search methods...";
case "directory-search":
return "Search directories or enter a path...";
case "github-search":
@@ -240,7 +240,7 @@ function pagePlaceholder(page: AddProjectPage): string {
}
}
function pageInput(page: AddProjectPage): string {
function pageInput(page: AddProjectInputPage): string {
return page.kind === "new-directory-name" ? page.name : page.query;
}
@@ -374,7 +374,7 @@ export function AddProjectFlow({ request, onClose }: AddProjectFlowProps) {
const inputRef = useRef<TextInput>(null);
const submissionInFlightRef = useRef(false);
const browseInFlightRef = useRef(false);
const query = page.kind === "new-directory-name" ? "" : page.query;
const query = page.kind === "new-directory-name" || page.kind === "method" ? "" : page.query;
const [debouncedQuery, setDebouncedQuery] = useState(query);
useEffect(() => {
@@ -599,23 +599,15 @@ export function AddProjectFlow({ request, onClose }: AddProjectFlowProps) {
}
if (page.kind === "method") {
if (!host) return [];
const normalized = page.query.trim().toLowerCase();
return buildAddProjectMethods(host)
.filter(
(method) =>
!normalized ||
method.label.toLowerCase().includes(normalized) ||
method.description.toLowerCase().includes(normalized),
)
.map((method) => ({
id: method.id,
title: method.label,
subtitle: method.description,
icon: methodIcon(method.id),
disabled: method.disabled,
testID: `add-project-flow-method-${method.id}`,
select: () => selectMethod(method.id),
}));
return buildAddProjectMethods(host).map((method) => ({
id: method.id,
title: method.label,
subtitle: method.description,
icon: methodIcon(method.id),
disabled: method.disabled,
testID: `add-project-flow-method-${method.id}`,
select: () => selectMethod(method.id),
}));
}
if (page.kind === "directory-search") {
return pathOptions.map((option) => {
@@ -863,21 +855,41 @@ export function AddProjectFlow({ request, onClose }: AddProjectFlowProps) {
) : null}
</View>
</View>
<ThemedTextInput
key={page.kind}
ref={inputRef}
value={pageInput(page)}
onChangeText={handleInputChange}
onKeyPress={isWeb ? undefined : handleNativeKeyPress}
onSubmitEditing={isWeb ? undefined : submitActive}
placeholder={pagePlaceholder(page)}
style={styles.input}
autoCapitalize="none"
autoCorrect={false}
editable={!isSubmitting}
returnKeyType="go"
testID="add-project-flow-input"
/>
{page.kind === "method" && isNative ? (
// Native hardware-keyboard events need a focused responder even without a visible field.
<TextInput
key={page.kind}
ref={inputRef}
onKeyPress={handleNativeKeyPress}
onSubmitEditing={submitActive}
showSoftInputOnFocus={false}
caretHidden
contextMenuHidden
accessible={false}
accessibilityElementsHidden
importantForAccessibility="no-hide-descendants"
pointerEvents="none"
style={styles.keyboardCapture}
testID="add-project-flow-keyboard-capture"
/>
) : null}
{page.kind !== "method" ? (
<ThemedTextInput
key={page.kind}
ref={inputRef}
value={pageInput(page)}
onChangeText={handleInputChange}
onKeyPress={isWeb ? undefined : handleNativeKeyPress}
onSubmitEditing={isWeb ? undefined : submitActive}
placeholder={pagePlaceholder(page)}
style={styles.input}
autoCapitalize="none"
autoCorrect={false}
editable={!isSubmitting}
returnKeyType="go"
testID="add-project-flow-input"
/>
) : null}
</View>
<ScrollView
style={styles.results}
@@ -1012,6 +1024,12 @@ const styles = StyleSheet.create((theme) => ({
paddingVertical: theme.spacing[1],
outlineStyle: "none",
} as object,
keyboardCapture: {
position: "absolute",
width: 1,
height: 1,
opacity: 0,
},
results: { flexGrow: 0, flexShrink: 1, minHeight: 0 },
resultsContent: { paddingVertical: theme.spacing[2] },
row: {