Workspaces in Workspaces all around

This commit is contained in:
2026-02-19 01:49:15 +00:00
parent 72bca6cd42
commit dd8ab84df5
84 changed files with 3047 additions and 749 deletions
@@ -1,6 +1,6 @@
import { useState, useEffect, useMemo } from 'react';
import { toast } from 'sonner';
import { Terminal, Eye, Trash2 } from 'lucide-react';
import { Terminal, Eye, Trash2, Bot } from 'lucide-react';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Button } from '@/components/ui/button';
@@ -10,7 +10,10 @@ import { Switch } from '@/components/ui/switch';
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@/components/ui/accordion';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Card } from '@/components/Card';
import type { LayoutNode, PanelComponents } from '@/components/Workspace';
import { WorkspaceLayout } from '@/components/Workspace';
import { appRegistry } from '../Workspaces/app-registry';
import { createSettingsPanelComponents, type SettingsSection } from './SettingsPanel';
import { useSettings } from '@/state/useSettings';
import { useUserState } from '@/state/useUserState';
import {
@@ -21,38 +24,47 @@ import {
} from '@/state/useModels';
import type { UserSettings } from '@/state/types/user-settings';
const GLOBAL_KEY = 'AI_SETTINGS_SELECTED';
const sections: SettingsSection[] = [
{ key: 'chat-defaults', icon: Terminal, title: 'Chat Defaults', description: 'Model, prompt, and temperature', content: <ChatDefaultsSection /> },
{ key: 'model-visibility', icon: Eye, title: 'Model Visibility', description: 'Enable or disable models', content: <ModelVisibilitySection /> },
];
const { Sidebar, Content } = createSettingsPanelComponents({
globalKey: GLOBAL_KEY,
sidebarIcon: Bot,
sidebarLabel: 'AI',
sections,
});
const layout: LayoutNode = {
type: 'group',
id: 'ai-root',
direction: 'horizontal',
children: [
{ node: { type: 'panel', id: 'ai-left', appType: null }, size: 20 },
{ node: { type: 'panel', id: 'ai-right', appType: null }, size: 80 },
],
};
export const AISettings = () => {
const [mainTab, setMainTab] = useUserState('ai-settings-tab', 'chat-defaults');
const panelComponents: PanelComponents = useMemo(
() => ({
'ai-left': Sidebar,
'ai-right': Content,
}),
[],
);
return (
<div className="flex justify-center h-full px-4 py-8 overflow-y-auto">
<Card className="w-full max-w-2xl h-fit p-6">
<Tabs value={mainTab} onValueChange={setMainTab}>
<TabsList className="w-full mb-6">
<TabsTrigger value="chat-defaults" className="flex-1 gap-2 cursor-pointer">
<Terminal className="h-4 w-4" />
Chat Defaults
</TabsTrigger>
<TabsTrigger value="model-visibility" className="flex-1 gap-2 cursor-pointer">
<Eye className="h-4 w-4" />
Model Visibility
</TabsTrigger>
</TabsList>
<TabsContent value="chat-defaults">
<ChatDefaultsSection />
</TabsContent>
<TabsContent value="model-visibility">
<ModelVisibilitySection />
</TabsContent>
</Tabs>
</Card>
<div className="h-full w-full pt-2">
<WorkspaceLayout layout={layout} onLayoutChange={() => {}} registry={appRegistry} components={panelComponents} />
</div>
);
};
const ChatDefaultsSection = () => {
function ChatDefaultsSection() {
const { settings, saveSettings } = useSettings();
const claudeModels = useVisibleClaudeModels();
const openCodeModels = useVisibleOpenCodeModels();
@@ -102,7 +114,7 @@ const ChatDefaultsSection = () => {
<Label className="grid gap-2">
<span className="text-duck-dark/70">Model</span>
<Select value={model ?? ''} onValueChange={(v) => setModel(v || null)}>
<SelectTrigger className="h-11 bg-white/60 border-duck-dark/20 text-duck-dark">
<SelectTrigger className="h-11 bg-background/60 border-duck-dark/20 text-duck-dark">
<SelectValue placeholder="Default" />
</SelectTrigger>
<SelectContent className="z-[600]">
@@ -119,7 +131,7 @@ const ChatDefaultsSection = () => {
<Label className="grid gap-2">
<span className="text-duck-dark/70">System Prompt</span>
<Textarea
className="bg-white/60 border-duck-dark/20 text-duck-dark placeholder:text-duck-dark/40 min-h-[100px]"
className="bg-background/60 border-duck-dark/20 text-duck-dark placeholder:text-duck-dark/40 min-h-[100px]"
placeholder="Custom instructions for the AI..."
value={systemPrompt}
onChange={(ev) => setSystemPrompt(ev.target.value)}
@@ -137,7 +149,7 @@ const ChatDefaultsSection = () => {
<Label className="grid gap-2">
<span className="text-duck-dark/70">Default Working Directory</span>
<Input
className="h-11 bg-white/60 border-duck-dark/20 text-duck-dark placeholder:text-duck-dark/40"
className="h-11 bg-background/60 border-duck-dark/20 text-duck-dark placeholder:text-duck-dark/40"
value={defaultPwd}
onChange={(ev) => setDefaultPwd(ev.target.value)}
placeholder="~"
@@ -154,9 +166,9 @@ const ChatDefaultsSection = () => {
</Button>
</div>
);
};
}
const ModelVisibilitySection = () => {
function ModelVisibilitySection() {
const { settings, saveSettings } = useSettings();
const claudeModels = useClaudeModels();
const openCodeModels = useOpenCodeModels();
@@ -234,7 +246,7 @@ const ModelVisibilitySection = () => {
{addingProvider ? (
<>
<Select value={selectedNewProvider} onValueChange={setSelectedNewProvider}>
<SelectTrigger className="h-9 flex-1 bg-white/60 border-duck-dark/20 text-duck-dark text-sm">
<SelectTrigger className="h-9 flex-1 bg-background/60 border-duck-dark/20 text-duck-dark text-sm">
<SelectValue placeholder="Select provider..." />
</SelectTrigger>
<SelectContent className="z-[600]">
@@ -282,7 +294,7 @@ const ModelVisibilitySection = () => {
</TabsContent>
</Tabs>
);
};
}
type ProviderGroup = { provider: string; models: { id: string; name: string }[] };