Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions src/browser/components/ProjectSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { TooltipWrapper, Tooltip } from "./Tooltip";
import SecretsModal from "./SecretsModal";
import type { Secret } from "@/common/types/secrets";
import { ForceDeleteModal } from "./ForceDeleteModal";
import { WorkspaceListItem } from "./WorkspaceListItem";
import { WorkspaceListItem, type WorkspaceSelection } from "./WorkspaceListItem";
import { RenameProvider } from "@/browser/contexts/WorkspaceRenameContext";
import { useProjectContext } from "@/browser/contexts/ProjectContext";
import { ChevronRight, KeyRound } from "lucide-react";
Expand Down Expand Up @@ -194,6 +194,31 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
updateSecrets: onUpdateSecrets,
} = useProjectContext();

// Mobile breakpoint for auto-closing sidebar
const MOBILE_BREAKPOINT = 768;

// Wrapper to close sidebar on mobile after workspace selection
const handleSelectWorkspace = useCallback(
(selection: WorkspaceSelection) => {
onSelectWorkspace(selection);
if (window.innerWidth <= MOBILE_BREAKPOINT && !collapsed) {
onToggleCollapsed();
}
},
[onSelectWorkspace, collapsed, onToggleCollapsed]
);

// Wrapper to close sidebar on mobile after adding workspace
const handleAddWorkspace = useCallback(
(projectPath: string) => {
onAddWorkspace(projectPath);
if (window.innerWidth <= MOBILE_BREAKPOINT && !collapsed) {
onToggleCollapsed();
}
},
[onAddWorkspace, collapsed, onToggleCollapsed]
);

// Workspace-specific subscriptions moved to WorkspaceListItem component

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

window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [selectedWorkspace, onAddWorkspace]);
}, [selectedWorkspace, handleAddWorkspace]);

return (
<RenameProvider onRenameWorkspace={onRenameWorkspace}>
Expand Down Expand Up @@ -575,7 +600,7 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
>
<div className="border-hover border-b px-3 py-2">
<button
onClick={() => onAddWorkspace(projectPath)}
onClick={() => handleAddWorkspace(projectPath)}
data-project-path={projectPath}
aria-label={`Add workspace to ${projectName}`}
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"
Expand All @@ -602,7 +627,7 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
isSelected={selectedWorkspace?.workspaceId === metadata.id}
isDeleting={deletingWorkspaceIds.has(metadata.id)}
lastReadTimestamp={lastReadTimestamps[metadata.id] ?? 0}
onSelectWorkspace={onSelectWorkspace}
onSelectWorkspace={handleSelectWorkspace}
onRemoveWorkspace={handleRemoveWorkspace}
onToggleUnread={_onToggleUnread}
/>
Expand Down
27 changes: 18 additions & 9 deletions src/browser/components/Settings/SettingsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,31 @@ export function SettingsModal() {
aria-modal="true"
aria-labelledby="settings-title"
onClick={(e) => e.stopPropagation()}
className="bg-dark border-border flex h-[70vh] max-h-[600px] w-[90%] max-w-[800px] overflow-hidden rounded-lg border shadow-lg"
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"
>
{/* Sidebar */}
<div className="border-border-medium flex w-48 shrink-0 flex-col border-r">
<div className="border-border-medium flex h-12 items-center border-b px-4">
{/* Sidebar - horizontal tabs on mobile, vertical on desktop */}
<div className="border-border-medium flex shrink-0 flex-col border-b md:w-48 md:border-r md:border-b-0">
<div className="border-border-medium flex h-12 items-center justify-between border-b px-4 md:justify-start">
<span id="settings-title" className="text-foreground text-sm font-semibold">
Settings
</span>
{/* Close button in header on mobile only */}
<button
type="button"
onClick={handleClose}
className="text-muted hover:text-foreground rounded p-1 transition-colors md:hidden"
aria-label="Close settings"
>
<X className="h-4 w-4" />
</button>
</div>
<nav className="flex-1 overflow-y-auto p-2">
<nav className="flex overflow-x-auto p-2 md:flex-1 md:flex-col md:overflow-y-auto">
{SECTIONS.map((section) => (
<button
key={section.id}
type="button"
onClick={() => setActiveSection(section.id)}
className={`flex w-full items-center gap-2 rounded-md px-3 py-2 text-left text-sm transition-colors ${
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 ${
activeSection === section.id
? "bg-accent/20 text-accent"
: "text-muted hover:bg-hover hover:text-foreground"
Expand All @@ -92,8 +101,8 @@ export function SettingsModal() {
</div>

{/* Content */}
<div className="flex flex-1 flex-col overflow-hidden">
<div className="border-border-medium flex h-12 items-center justify-between border-b px-6">
<div className="flex min-h-0 flex-1 flex-col overflow-hidden">
<div className="border-border-medium hidden h-12 items-center justify-between border-b px-6 md:flex">
<span className="text-foreground text-sm font-medium">{currentSection.label}</span>
<button
type="button"
Expand All @@ -104,7 +113,7 @@ export function SettingsModal() {
<X className="h-4 w-4" />
</button>
</div>
<div className="flex-1 overflow-y-auto p-6">
<div className="flex-1 overflow-y-auto p-4 md:p-6">
<SectionComponent />
</div>
</div>
Expand Down
6 changes: 3 additions & 3 deletions src/browser/components/Settings/sections/ModelRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ export interface ModelRowProps {

export function ModelRow(props: ModelRowProps) {
return (
<div className="border-border-medium bg-background-secondary flex items-center justify-between rounded-md border px-3 py-1.5">
<div className="flex min-w-0 flex-1 items-center gap-2">
<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">
<div className="flex min-w-0 flex-1 items-center gap-1.5 md:gap-2">
<ProviderWithIcon
provider={props.provider}
displayName
className="text-muted w-20 shrink-0 text-xs"
className="text-muted w-16 shrink-0 text-xs md:w-20"
/>
{props.isEditing ? (
<div className="flex min-w-0 flex-1 items-center gap-1">
Expand Down
8 changes: 4 additions & 4 deletions src/browser/components/Settings/sections/ModelsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,11 @@ export function ModelsSection() {

{/* Add new model form */}
<div className="border-border-medium bg-background-secondary rounded-md border p-2">
<div className="flex gap-1.5">
<div className="flex flex-wrap gap-1.5">
<select
value={newModel.provider}
onChange={(e) => setNewModel((prev) => ({ ...prev, provider: e.target.value }))}
className="bg-modal-bg border-border-medium focus:border-accent rounded border px-2 py-1 text-xs focus:outline-none"
className="bg-modal-bg border-border-medium focus:border-accent shrink-0 rounded border px-2 py-1 text-xs focus:outline-none"
>
<option value="">Provider</option>
{SUPPORTED_PROVIDERS.map((p) => (
Expand All @@ -178,7 +178,7 @@ export function ModelsSection() {
value={newModel.modelId}
onChange={(e) => setNewModel((prev) => ({ ...prev, modelId: e.target.value }))}
placeholder="model-id"
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"
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"
onKeyDown={(e) => {
if (e.key === "Enter") void handleAddModel();
}}
Expand All @@ -187,7 +187,7 @@ export function ModelsSection() {
type="button"
onClick={handleAddModel}
disabled={!newModel.provider || !newModel.modelId.trim()}
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"
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"
>
<Plus className="h-3.5 w-3.5" />
Add
Expand Down