extracted terminal to a widget

This commit is contained in:
2026-02-17 18:46:18 +00:00
parent c6999ea66f
commit 0746844d6f
98 changed files with 487 additions and 3513 deletions
@@ -11,7 +11,6 @@ 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 { DashboardLayout } from '../Layout';
import { useSettings } from '@/state/useSettings';
import { useUserState } from '@/state/useUserState';
import {
@@ -26,9 +25,8 @@ export const AISettings = () => {
const [mainTab, setMainTab] = useUserState('ai-settings-tab', 'chat-defaults');
return (
<DashboardLayout>
<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">
<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">
@@ -49,9 +47,8 @@ export const AISettings = () => {
<ModelVisibilitySection />
</TabsContent>
</Tabs>
</Card>
</div>
</DashboardLayout>
</Card>
</div>
);
};
@@ -3,12 +3,10 @@ import { Search, User, Lock, Globe, ListChecks, Palette } from 'lucide-react';
import { Input } from '@/components/ui/input';
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@/components/ui/accordion';
import { Card } from '@/components/Card';
import { DashboardLayout } from '../../Layout';
import { UserData } from './UserData';
import { ChangePassword } from './ChangePassword';
import { Languages } from './Languages';
import { TaskDefaults } from './TaskDefaults';
import { Appearance } from './Appearance';
const sections = [
{
@@ -19,11 +17,11 @@ const sections = [
content: <UserData />,
},
{
key: 'appearance',
icon: Palette,
title: 'Appearance',
description: 'Color theme and light/dark mode.',
content: <Appearance />,
key: 'change-password',
icon: Lock,
title: 'Change Password',
description: 'Update your account password.',
content: <ChangePassword />,
},
{
key: 'tasks',
@@ -39,13 +37,6 @@ const sections = [
description: 'Set your spoken languages and translation preferences.',
content: <Languages />,
},
{
key: 'change-password',
icon: Lock,
title: 'Change Password',
description: 'Update your account password.',
content: <ChangePassword />,
},
];
const allKeys = sections.map((s) => s.key);
@@ -71,42 +62,40 @@ export const ProfileSettings = () => {
}, [search, matchingKeys]);
return (
<DashboardLayout>
<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">
<div className="relative mb-6">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-duck-dark/40" />
<Input
placeholder="Search settings..."
value={search}
onChange={(ev) => setSearch(ev.target.value)}
className="pl-9"
/>
</div>
<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">
<div className="relative mb-6">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-duck-dark/40" />
<Input
placeholder="Search settings..."
value={search}
onChange={(ev) => setSearch(ev.target.value)}
className="pl-9"
/>
</div>
<Accordion type="multiple" value={expanded} onValueChange={setExpanded}>
{sections.map((section) => (
<div
key={section.key}
ref={(el) => {
sectionRefs.current[section.key] = el;
}}
className={search && !matchingKeys.includes(section.key) ? 'hidden' : ''}
>
<AccordionItem value={section.key}>
<AccordionTrigger className="hover:no-underline">
<div className="flex items-center gap-3">
<section.icon className="h-5 w-5 text-duck-forest shrink-0" />
<div className="text-base font-bold text-duck-dark">{section.title}</div>
</div>
</AccordionTrigger>
<AccordionContent>{section.content}</AccordionContent>
</AccordionItem>
</div>
))}
</Accordion>
</Card>
</div>
</DashboardLayout>
<Accordion type="multiple" value={expanded} onValueChange={setExpanded}>
{sections.map((section) => (
<div
key={section.key}
ref={(el) => {
sectionRefs.current[section.key] = el;
}}
className={search && !matchingKeys.includes(section.key) ? 'hidden' : ''}
>
<AccordionItem value={section.key}>
<AccordionTrigger className="hover:no-underline">
<div className="flex items-center gap-3">
<section.icon className="h-5 w-5 text-duck-forest shrink-0" />
<div className="text-base font-bold text-duck-dark">{section.title}</div>
</div>
</AccordionTrigger>
<AccordionContent>{section.content}</AccordionContent>
</AccordionItem>
</div>
))}
</Accordion>
</Card>
</div>
);
};
@@ -5,7 +5,6 @@ import { toast } from 'sonner';
import { Button } from '@/components/ui/button';
import { Card } from '@/components/Card';
import { useClient } from 'hooks/useClient';
import { DashboardLayout } from '../../Layout';
import { EmbeddableChat, useClaude, useOpenCode } from 'plugins/Chat/client';
import { useVisibleClaudeModels, useVisibleOpenCodeModels } from '@/state/useModels';
@@ -82,8 +81,7 @@ export const ResourceSettings = () => {
const [provider, setProvider] = useState<'claude' | 'opencode'>('claude');
return (
<DashboardLayout>
<div className="flex h-full gap-4 px-4 py-8 overflow-hidden">
<div className="flex h-full gap-4 px-4 py-8 overflow-hidden">
<Card className="w-full max-w-2xl h-fit p-6 overflow-y-auto">
<h2 className="text-lg font-bold text-duck-dark mb-1">Applications</h2>
<p className="text-sm text-duck-dark/60 mb-6">System tools and dependencies used by Officer.dev</p>
@@ -175,8 +173,7 @@ export const ResourceSettings = () => {
<OpenCodeChat key="opencode" onProviderChange={setProvider} />
)}
</Card>
</div>
</DashboardLayout>
</div>
);
};
@@ -1,11 +1,10 @@
import { useState, useEffect, useRef, useMemo } from 'react';
import { Search, Terminal, Puzzle, Shield } from 'lucide-react';
import { Search, Terminal, Shield } from 'lucide-react';
import { Input } from '@/components/ui/input';
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@/components/ui/accordion';
import { Card } from '@/components/Card';
import { DashboardLayout } from '../../Layout';
import { AIHarnessesSection } from './AIHarnessesSection';
import { PluginsSection } from './PluginsSection';
// import { PluginsSection } from './PluginsSection';
import { TerminalSection } from './TerminalSection';
const sections = [
@@ -16,13 +15,13 @@ const sections = [
description: 'Which AI coding tools do you use?',
content: <AIHarnessesSection />,
},
{
key: 'plugins',
icon: Puzzle,
title: 'Plugins',
description: 'Enable or disable installed plugins.',
content: <PluginsSection />,
},
// {
// key: 'plugins',
// icon: Puzzle,
// title: 'Plugins',
// description: 'Enable or disable installed plugins.',
// content: <PluginsSection />,
// },
{
key: 'terminal',
icon: Shield,
@@ -55,45 +54,43 @@ export const ServerSettings = () => {
}, [search, matchingKeys]);
return (
<DashboardLayout>
<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">
<div className="relative mb-6">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-duck-dark/40" />
<Input
placeholder="Search settings..."
value={search}
onChange={(ev) => setSearch(ev.target.value)}
className="pl-9"
/>
</div>
<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">
<div className="relative mb-6">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-duck-dark/40" />
<Input
placeholder="Search settings..."
value={search}
onChange={(ev) => setSearch(ev.target.value)}
className="pl-9"
/>
</div>
<Accordion type="multiple" value={expanded} onValueChange={setExpanded}>
{sections.map((section) => (
<div
key={section.key}
ref={(el) => {
sectionRefs.current[section.key] = el;
}}
className={search && !matchingKeys.includes(section.key) ? 'hidden' : ''}
>
<AccordionItem value={section.key}>
<AccordionTrigger className="hover:no-underline">
<div className="flex items-center gap-3">
<section.icon className="h-5 w-5 text-duck-forest shrink-0" />
<div className="text-left">
<div className="text-base font-bold text-duck-dark">{section.title}</div>
<div className="text-sm font-normal text-duck-dark/70">{section.description}</div>
</div>
<Accordion type="multiple" value={expanded} onValueChange={setExpanded}>
{sections.map((section) => (
<div
key={section.key}
ref={(el) => {
sectionRefs.current[section.key] = el;
}}
className={search && !matchingKeys.includes(section.key) ? 'hidden' : ''}
>
<AccordionItem value={section.key}>
<AccordionTrigger className="hover:no-underline">
<div className="flex items-center gap-3">
<section.icon className="h-5 w-5 text-duck-forest shrink-0" />
<div className="text-left">
<div className="text-base font-bold text-duck-dark">{section.title}</div>
<div className="text-sm font-normal text-duck-dark/70">{section.description}</div>
</div>
</AccordionTrigger>
<AccordionContent forceMount>{section.content}</AccordionContent>
</AccordionItem>
</div>
))}
</Accordion>
</Card>
</div>
</DashboardLayout>
</div>
</AccordionTrigger>
<AccordionContent forceMount>{section.content}</AccordionContent>
</AccordionItem>
</div>
))}
</Accordion>
</Card>
</div>
);
};
@@ -1,29 +1,4 @@
import { Link } from 'react-router';
import { Bot, ChevronRight } from 'lucide-react';
import { Card } from '@/components/Card';
import { DashboardLayout } from '../Layout';
export const Settings = () => {
return (
<DashboardLayout>
<div className="flex items-center justify-center h-full px-4">
<div className="w-full max-w-4xl grid grid-cols-1 lg:grid-cols-2 gap-8">
<Link to="/settings/ai">
<Card className="p-6 hover:border-duck-teal/50 transition-colors cursor-pointer">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Bot className="h-5 w-5 text-duck-forest" />
<div>
<h2 className="text-xl font-bold text-duck-dark">AI Settings</h2>
<p className="text-sm text-duck-dark/60">Chat defaults, model visibility</p>
</div>
</div>
<ChevronRight className="h-5 w-5 text-duck-dark/40" />
</div>
</Card>
</Link>
</div>
</div>
</DashboardLayout>
);
};
export * from './ProfileSettings';
export * from './AISettings';
export * from './ServerSettings';
export * from './ResourceSettings';