Adds a singleton, ephemeral 'settings' pane kind to the workspace. Opened via a new bottom-pinned button in ProjectSidebar (emits an open_settings_pane event when a session is mounted; navigates to /settings otherwise). Pane has three sections — Session, Project, Theme — and a maximize toggle that hides sibling pane columns via display:none on desktop only. Settings panes don't count toward MAX_PANES and are filtered out of the localStorage persistence layer so reload always restores a clean workspace. Schema (additive): - projects.default_system_prompt TEXT NOT NULL DEFAULT '' - projects.default_web_search_enabled BOOLEAN NOT NULL DEFAULT false - sessions.web_search_enabled BOOLEAN (nullable; null = inherit) Inference resolves user_prompt = session.system_prompt.trim() || project.default_system_prompt.trim() — empty/whitespace at either layer means "no override". Keeps the columns NOT NULL and matches the existing inherit semantics. Server routes: - GET /api/projects/:id (new; settings pane refetches on project_updated) - PATCH /api/projects/:id accepts default_system_prompt, default_web_search_enabled - PATCH /api/sessions/:id accepts web_search_enabled (tri-state) - POST /api/projects/:id/sessions/archive-all + GET /api/projects/:id/sessions/open-count - POST /api/sessions/:id/chats/archive-all + GET /api/sessions/:id/chats/open-count - PATCH /api/sessions/:id now broadcasts session_updated on every successful PATCH (was rename-only). Lets SettingsPane open in another tab pick up edits without a refetch. Bulk-archive publishes one session_archived / chat_archived frame per affected id so useSidebar's existing reducer cases handle them incrementally — no new frame type, no payload widening. ModelPicker refactored: shared ModelList inside a responsive shell. Desktop = labeled trigger + DropdownMenu, mobile = icon-only Cpu button + BottomSheet. Header in Session.tsx drops the pill wrap on mobile since the new trigger is the visual. ChatInput gains an icon-only '+' DropdownMenu next to AgentPicker when sessionId + webSearchEnabled props are provided. One item for now — Web search — with a checkmark reflecting the stored value (true), not the effective one. Click PATCHes the override; to restore inherit-from-project the user opens SettingsPane. ThemePicker lifted out of pages/Settings.tsx into a reusable component. The standalone /settings route is now a thin wrapper that mounts <ThemePicker /> with a Back button on top (navigate(-1) with fallback to '/'); the SettingsPane Theme tab renders the same picker bare. Project section delete-flow removed (button + confirm dialog + handler). Replaced with "Archive all sessions" using the same two-step count → confirm → fire pattern as "Archive all chats" in the Session section. api.projects.remove() stays in the client because useProjects.ts still uses it. Hand-rolled Switch primitive in SettingsPane (no shadcn switch in the project; spec said no new deps). Section nav is plain buttons (no shadcn Tabs). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
48 lines
1.6 KiB
TypeScript
48 lines
1.6 KiB
TypeScript
import { ArrowLeft } from 'lucide-react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { ThemePicker } from '@/components/ThemePicker';
|
|
|
|
// v1.9: thin wrapper around <ThemePicker />. The picker itself moved to a
|
|
// reusable component (also rendered in the workspace SettingsPane Theme tab).
|
|
// This page-level shell adds the back affordance + heading chrome that's
|
|
// appropriate when the picker is the entire route.
|
|
export function Settings() {
|
|
const navigate = useNavigate();
|
|
|
|
function handleBack() {
|
|
// History-aware: jump back to where the user came from when possible.
|
|
// Direct loads of /settings (no history) land on Home so the button
|
|
// always does *something* useful.
|
|
if (window.history.length > 1) {
|
|
navigate(-1);
|
|
} else {
|
|
navigate('/');
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="flex-1 overflow-y-auto">
|
|
<div className="max-w-[1000px] mx-auto w-full px-6 py-6 space-y-8">
|
|
<header className="space-y-2">
|
|
<button
|
|
type="button"
|
|
onClick={handleBack}
|
|
className="inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground -ml-1 px-1 py-0.5 rounded"
|
|
aria-label="Back"
|
|
>
|
|
<ArrowLeft className="size-4" />
|
|
<span>Back</span>
|
|
</button>
|
|
<div>
|
|
<h1 className="text-xl font-semibold">Settings</h1>
|
|
<p className="text-sm text-muted-foreground mt-1">
|
|
Theme appearance. Saved on change, applies immediately.
|
|
</p>
|
|
</div>
|
|
</header>
|
|
<ThemePicker />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|