diff --git a/src/browser/components/LeftSidebar/LeftSidebar.tsx b/src/browser/components/LeftSidebar/LeftSidebar.tsx
index a0080b5910..debdae22cd 100644
--- a/src/browser/components/LeftSidebar/LeftSidebar.tsx
+++ b/src/browser/components/LeftSidebar/LeftSidebar.tsx
@@ -26,15 +26,15 @@ export function LeftSidebar(props: LeftSidebarProps) {
...projectSidebarProps
} = props;
const isDesktop = isDesktopMode();
- // Match the CSS gate for the mobile "overlay" sidebar; we don't show a drag handle in that mode.
- const isMobileTouch =
- typeof window !== "undefined" &&
- window.matchMedia("(max-width: 768px) and (pointer: coarse)").matches;
+ // Match the CSS gate for the mobile "overlay" sidebar (width-only, any pointer
+ // type); we don't show a drag handle in that mode since CSS pins the width.
+ const isMobileOverlay =
+ typeof window !== "undefined" && window.matchMedia("(max-width: 768px)").matches;
const handleBeforeOpenSettings = () => {
- // Keep settings navigation escapable on touch devices by dismissing the
+ // Keep settings navigation escapable on narrow viewports by dismissing the
// off-canvas sidebar as soon as the user opens settings from this sidebar.
- if (!collapsed && isMobileTouch) {
+ if (!collapsed && isMobileOverlay) {
onToggleCollapsed();
}
};
@@ -77,7 +77,7 @@ export function LeftSidebar(props: LeftSidebarProps) {
onToggleCollapsed={onToggleCollapsed}
/>
- {!collapsed && !isMobileTouch && onStartResize && (
+ {!collapsed && !isMobileOverlay && onStartResize && (
void;
}
export interface ProjectCreateFormHandle {
@@ -118,14 +119,23 @@ export const ProjectCreateForm = React.forwardRef
{
+ setErrorState(next);
+ onErrorChange?.(next.length > 0);
+ },
+ [onErrorChange]
+ );
+
useEffect(() => {
setPath(initialPath ?? "");
}, [initialPath]);
@@ -141,7 +151,7 @@ export const ProjectCreateForm = React.forwardRef {
setPath("");
setError("");
- }, []);
+ }, [setError]);
const handleCancel = useCallback(() => {
reset();
@@ -211,7 +221,7 @@ export const ProjectCreateForm = React.forwardRef {
@@ -297,6 +307,7 @@ interface ProjectCloneFormProps {
onIsCreatingChange?: (isCreating: boolean) => void;
hideFooter?: boolean;
autoFocus?: boolean;
+ onErrorChange?: (hasError: boolean) => void;
}
export interface ProjectCloneFormHandle {
@@ -375,7 +386,16 @@ const ProjectCloneForm = React.forwardRef {
+ setErrorState(next);
+ onErrorChange?.(next.length > 0);
+ },
+ [onErrorChange]
+ );
const [destinationExistsPath, setDestinationExistsPath] = useState(null);
const [isCreating, setIsCreating] = useState(false);
const [cloneOutput, setCloneOutput] = useState("");
@@ -402,7 +422,7 @@ const ProjectCloneForm = React.forwardRef {
if (!abortControllerRef.current) {
@@ -552,7 +572,7 @@ const ProjectCloneForm = React.forwardRef {
if (!api || !destinationExistsPath) {
@@ -593,13 +613,13 @@ const ProjectCloneForm = React.forwardRef {
setError("");
setCloneOutput("");
rawOutputRef.current = "";
- }, []);
+ }, [setError]);
const handleKeyDown = useCallback(
(e: React.KeyboardEvent) => {
@@ -794,6 +814,7 @@ interface ProjectAddFormProps {
autoFocus?: boolean;
hideFooter?: boolean;
showCancelButton?: boolean;
+ onErrorChange?: (hasError: boolean) => void;
}
export const ProjectAddForm = React.forwardRef(
@@ -864,6 +885,7 @@ export const ProjectAddForm = React.forwardRef {
if (nextMode !== "pick-folder" && nextMode !== "clone") {
@@ -871,11 +893,14 @@ export const ProjectAddForm = React.forwardRef
) : (
@@ -940,6 +966,7 @@ export const ProjectAddForm = React.forwardRef
diff --git a/src/browser/features/SplashScreens/OnboardingWizardSplash.tsx b/src/browser/features/SplashScreens/OnboardingWizardSplash.tsx
index 11baee93f8..396d9b1ab7 100644
--- a/src/browser/features/SplashScreens/OnboardingWizardSplash.tsx
+++ b/src/browser/features/SplashScreens/OnboardingWizardSplash.tsx
@@ -210,6 +210,7 @@ export function OnboardingWizardSplash(props: { onDismiss: () => void }) {
const projectAddFormRef = useRef(null);
const [isProjectCreating, setIsProjectCreating] = useState(false);
+ const [projectFormHasError, setProjectFormHasError] = useState(false);
const [direction, setDirection] = useState("forward");
@@ -843,6 +844,7 @@ export function OnboardingWizardSplash(props: { onDismiss: () => void }) {
autoFocus={userProjects.size === 0}
hideFooter
onIsCreatingChange={setIsProjectCreating}
+ onErrorChange={setProjectFormHasError}
onSuccess={(normalizedPath, projectConfig) => {
addProject(normalizedPath, projectConfig);
updatePersistedState(getAgentsInitNudgeKey(normalizedPath), true);
@@ -852,11 +854,14 @@ export function OnboardingWizardSplash(props: { onDismiss: () => void }) {
/>
-
- {userProjects.size > 0
- ? "Add another folder or repo, or leave this blank and click Next to continue."
- : "Click Next to add this project."}
-
+ {/* Hide the "click Next" nudge while the form shows an error; the two contradict. */}
+ {!projectFormHasError && (
+
+ {userProjects.size > 0
+ ? "Add another folder or repo, or leave this blank and click Next to continue."
+ : "Click Next to add this project."}
+
+ )}
>
),
});
@@ -1011,6 +1016,7 @@ export function OnboardingWizardSplash(props: { onDismiss: () => void }) {
muxGatewayLoginInProgress,
muxGatewayLoginStatus,
openProvidersSettings,
+ projectFormHasError,
userProjects.size,
providersConfig,
refreshMuxGatewayAccountStatus,
@@ -1048,6 +1054,9 @@ export function OnboardingWizardSplash(props: { onDismiss: () => void }) {
return;
}
setDirection("back");
+ // Changing steps remounts the project form without its inline error, so
+ // clear the stale flag or the "click Next" nudge stays hidden on return.
+ setProjectFormHasError(false);
setStepIndex((i) => Math.max(0, i - 1));
};
@@ -1056,6 +1065,7 @@ export function OnboardingWizardSplash(props: { onDismiss: () => void }) {
return;
}
setDirection("forward");
+ setProjectFormHasError(false);
setStepIndex((i) => Math.min(totalSteps - 1, i + 1));
};
diff --git a/src/browser/features/SplashScreens/SplashScreen.tsx b/src/browser/features/SplashScreens/SplashScreen.tsx
index 8b73b57491..0ba4827a4d 100644
--- a/src/browser/features/SplashScreens/SplashScreen.tsx
+++ b/src/browser/features/SplashScreens/SplashScreen.tsx
@@ -42,11 +42,18 @@ export function SplashScreen(props: SplashScreenProps) {
return (