Skip to content

Commit e729c9a

Browse files
authored
🤖 fix: make settings modal responsive for mobile (#889)
On mobile screens, the settings modal now: - Uses horizontal scrollable tabs instead of a sidebar - Stacks content vertically for full width usage - Has compact padding and gaps for better space efficiency - Places close button in the header **Before:** Two-panel layout cramped on mobile with truncated content **After:** Vertical layout with horizontal tab navigation _Generated with `mux`_
1 parent a1ba71d commit e729c9a

File tree

4 files changed

+55
-21
lines changed

4 files changed

+55
-21
lines changed

src/browser/components/ProjectSidebar.tsx

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { TooltipWrapper, Tooltip } from "./Tooltip";
2323
import SecretsModal from "./SecretsModal";
2424
import type { Secret } from "@/common/types/secrets";
2525
import { ForceDeleteModal } from "./ForceDeleteModal";
26-
import { WorkspaceListItem } from "./WorkspaceListItem";
26+
import { WorkspaceListItem, type WorkspaceSelection } from "./WorkspaceListItem";
2727
import { RenameProvider } from "@/browser/contexts/WorkspaceRenameContext";
2828
import { useProjectContext } from "@/browser/contexts/ProjectContext";
2929
import { ChevronRight, KeyRound } from "lucide-react";
@@ -194,6 +194,31 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
194194
updateSecrets: onUpdateSecrets,
195195
} = useProjectContext();
196196

197+
// Mobile breakpoint for auto-closing sidebar
198+
const MOBILE_BREAKPOINT = 768;
199+
200+
// Wrapper to close sidebar on mobile after workspace selection
201+
const handleSelectWorkspace = useCallback(
202+
(selection: WorkspaceSelection) => {
203+
onSelectWorkspace(selection);
204+
if (window.innerWidth <= MOBILE_BREAKPOINT && !collapsed) {
205+
onToggleCollapsed();
206+
}
207+
},
208+
[onSelectWorkspace, collapsed, onToggleCollapsed]
209+
);
210+
211+
// Wrapper to close sidebar on mobile after adding workspace
212+
const handleAddWorkspace = useCallback(
213+
(projectPath: string) => {
214+
onAddWorkspace(projectPath);
215+
if (window.innerWidth <= MOBILE_BREAKPOINT && !collapsed) {
216+
onToggleCollapsed();
217+
}
218+
},
219+
[onAddWorkspace, collapsed, onToggleCollapsed]
220+
);
221+
197222
// Workspace-specific subscriptions moved to WorkspaceListItem component
198223

199224
// Store as array in localStorage, convert to Set for usage
@@ -427,13 +452,13 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
427452
// Create new workspace for the project of the selected workspace
428453
if (matchesKeybind(e, KEYBINDS.NEW_WORKSPACE) && selectedWorkspace) {
429454
e.preventDefault();
430-
onAddWorkspace(selectedWorkspace.projectPath);
455+
handleAddWorkspace(selectedWorkspace.projectPath);
431456
}
432457
};
433458

434459
window.addEventListener("keydown", handleKeyDown);
435460
return () => window.removeEventListener("keydown", handleKeyDown);
436-
}, [selectedWorkspace, onAddWorkspace]);
461+
}, [selectedWorkspace, handleAddWorkspace]);
437462

438463
return (
439464
<RenameProvider onRenameWorkspace={onRenameWorkspace}>
@@ -575,7 +600,7 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
575600
>
576601
<div className="border-hover border-b px-3 py-2">
577602
<button
578-
onClick={() => onAddWorkspace(projectPath)}
603+
onClick={() => handleAddWorkspace(projectPath)}
579604
data-project-path={projectPath}
580605
aria-label={`Add workspace to ${projectName}`}
581606
className="text-muted border-border-medium hover:bg-hover hover:border-border-darker hover:text-foreground w-full cursor-pointer rounded border border-dashed bg-transparent px-3 py-1.5 text-left text-[13px] transition-all duration-200"
@@ -602,7 +627,7 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
602627
isSelected={selectedWorkspace?.workspaceId === metadata.id}
603628
isDeleting={deletingWorkspaceIds.has(metadata.id)}
604629
lastReadTimestamp={lastReadTimestamps[metadata.id] ?? 0}
605-
onSelectWorkspace={onSelectWorkspace}
630+
onSelectWorkspace={handleSelectWorkspace}
606631
onRemoveWorkspace={handleRemoveWorkspace}
607632
onToggleUnread={_onToggleUnread}
608633
/>

src/browser/components/Settings/SettingsModal.tsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,22 +63,31 @@ export function SettingsModal() {
6363
aria-modal="true"
6464
aria-labelledby="settings-title"
6565
onClick={(e) => e.stopPropagation()}
66-
className="bg-dark border-border flex h-[70vh] max-h-[600px] w-[90%] max-w-[800px] overflow-hidden rounded-lg border shadow-lg"
66+
className="bg-dark border-border flex h-[80vh] max-h-[600px] w-[95%] max-w-[800px] flex-col overflow-hidden rounded-lg border shadow-lg md:h-[70vh] md:flex-row"
6767
>
68-
{/* Sidebar */}
69-
<div className="border-border-medium flex w-48 shrink-0 flex-col border-r">
70-
<div className="border-border-medium flex h-12 items-center border-b px-4">
68+
{/* Sidebar - horizontal tabs on mobile, vertical on desktop */}
69+
<div className="border-border-medium flex shrink-0 flex-col border-b md:w-48 md:border-r md:border-b-0">
70+
<div className="border-border-medium flex h-12 items-center justify-between border-b px-4 md:justify-start">
7171
<span id="settings-title" className="text-foreground text-sm font-semibold">
7272
Settings
7373
</span>
74+
{/* Close button in header on mobile only */}
75+
<button
76+
type="button"
77+
onClick={handleClose}
78+
className="text-muted hover:text-foreground rounded p-1 transition-colors md:hidden"
79+
aria-label="Close settings"
80+
>
81+
<X className="h-4 w-4" />
82+
</button>
7483
</div>
75-
<nav className="flex-1 overflow-y-auto p-2">
84+
<nav className="flex overflow-x-auto p-2 md:flex-1 md:flex-col md:overflow-y-auto">
7685
{SECTIONS.map((section) => (
7786
<button
7887
key={section.id}
7988
type="button"
8089
onClick={() => setActiveSection(section.id)}
81-
className={`flex w-full items-center gap-2 rounded-md px-3 py-2 text-left text-sm transition-colors ${
90+
className={`flex shrink-0 items-center gap-2 rounded-md px-3 py-2 text-left text-sm whitespace-nowrap transition-colors md:w-full ${
8291
activeSection === section.id
8392
? "bg-accent/20 text-accent"
8493
: "text-muted hover:bg-hover hover:text-foreground"
@@ -92,8 +101,8 @@ export function SettingsModal() {
92101
</div>
93102

94103
{/* Content */}
95-
<div className="flex flex-1 flex-col overflow-hidden">
96-
<div className="border-border-medium flex h-12 items-center justify-between border-b px-6">
104+
<div className="flex min-h-0 flex-1 flex-col overflow-hidden">
105+
<div className="border-border-medium hidden h-12 items-center justify-between border-b px-6 md:flex">
97106
<span className="text-foreground text-sm font-medium">{currentSection.label}</span>
98107
<button
99108
type="button"
@@ -104,7 +113,7 @@ export function SettingsModal() {
104113
<X className="h-4 w-4" />
105114
</button>
106115
</div>
107-
<div className="flex-1 overflow-y-auto p-6">
116+
<div className="flex-1 overflow-y-auto p-4 md:p-6">
108117
<SectionComponent />
109118
</div>
110119
</div>

src/browser/components/Settings/sections/ModelRow.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ export interface ModelRowProps {
2626

2727
export function ModelRow(props: ModelRowProps) {
2828
return (
29-
<div className="border-border-medium bg-background-secondary flex items-center justify-between rounded-md border px-3 py-1.5">
30-
<div className="flex min-w-0 flex-1 items-center gap-2">
29+
<div className="border-border-medium bg-background-secondary flex items-center justify-between gap-2 rounded-md border px-2 py-1.5 md:px-3">
30+
<div className="flex min-w-0 flex-1 items-center gap-1.5 md:gap-2">
3131
<ProviderWithIcon
3232
provider={props.provider}
3333
displayName
34-
className="text-muted w-20 shrink-0 text-xs"
34+
className="text-muted w-16 shrink-0 text-xs md:w-20"
3535
/>
3636
{props.isEditing ? (
3737
<div className="flex min-w-0 flex-1 items-center gap-1">

src/browser/components/Settings/sections/ModelsSection.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,11 @@ export function ModelsSection() {
160160

161161
{/* Add new model form */}
162162
<div className="border-border-medium bg-background-secondary rounded-md border p-2">
163-
<div className="flex gap-1.5">
163+
<div className="flex flex-wrap gap-1.5">
164164
<select
165165
value={newModel.provider}
166166
onChange={(e) => setNewModel((prev) => ({ ...prev, provider: e.target.value }))}
167-
className="bg-modal-bg border-border-medium focus:border-accent rounded border px-2 py-1 text-xs focus:outline-none"
167+
className="bg-modal-bg border-border-medium focus:border-accent shrink-0 rounded border px-2 py-1 text-xs focus:outline-none"
168168
>
169169
<option value="">Provider</option>
170170
{SUPPORTED_PROVIDERS.map((p) => (
@@ -178,7 +178,7 @@ export function ModelsSection() {
178178
value={newModel.modelId}
179179
onChange={(e) => setNewModel((prev) => ({ ...prev, modelId: e.target.value }))}
180180
placeholder="model-id"
181-
className="bg-modal-bg border-border-medium focus:border-accent flex-1 rounded border px-2 py-1 font-mono text-xs focus:outline-none"
181+
className="bg-modal-bg border-border-medium focus:border-accent min-w-0 flex-1 rounded border px-2 py-1 font-mono text-xs focus:outline-none"
182182
onKeyDown={(e) => {
183183
if (e.key === "Enter") void handleAddModel();
184184
}}
@@ -187,7 +187,7 @@ export function ModelsSection() {
187187
type="button"
188188
onClick={handleAddModel}
189189
disabled={!newModel.provider || !newModel.modelId.trim()}
190-
className="bg-accent hover:bg-accent-dark disabled:bg-border-medium flex items-center gap-1 rounded px-2 py-1 text-xs text-white transition-colors disabled:cursor-not-allowed"
190+
className="bg-accent hover:bg-accent-dark disabled:bg-border-medium flex shrink-0 items-center gap-1 rounded px-2 py-1 text-xs text-white transition-colors disabled:cursor-not-allowed"
191191
>
192192
<Plus className="h-3.5 w-3.5" />
193193
Add

0 commit comments

Comments
 (0)