feat: window management system with minimize, fullscreen, and AI chat split

- Window manager store (Zustand) for managing open/minimized/fullscreen windows
- Window component with header controls: KI-Chat toggle, fullscreen, minimize, close
- AI chat panel with streaming chat via existing /ai/sessions API
- WindowContainer renders all non-minimized windows
- TopBar shows minimized windows as clickable pills
- ContactEditForm extracted from ContactEditModal for window rendering
- ContactsList and ContactDetailPage use openWindow() instead of modal state
- Draggable windows with z-index management
This commit is contained in:
Agent Zero
2026-07-24 23:42:53 +02:00
parent c1d49e4dfe
commit 35e87b5d51
9 changed files with 936 additions and 39 deletions
@@ -9,6 +9,7 @@ import { useAIContext } from '@/hooks/useAIContext';
import { useAIUIControl } from '@/hooks/useAIUIControl';
import { PluginRegistry } from '@/components/plugins/PluginRegistry';
import { AIUIControlIndicator } from '@/components/ai-ui-control/AIUIControlIndicator';
import { WindowContainer } from '@/components/window/WindowContainer';
export function AppShell() {
const location = useLocation();
@@ -44,6 +45,7 @@ export function AppShell() {
{/* Message Sidebar — full height, right of TopBar and Toolbar */}
{showMessageSidebar && <MessageSidebar />}
<AIUIControlIndicator />
<WindowContainer />
<ToastContainer />
</div>
);
+20 -1
View File
@@ -8,7 +8,8 @@ import { useLogout } from '@/api/hooks';
import { Avatar } from '@/components/ui/Avatar';
import { SearchDropdown } from '@/components/shared/SearchDropdown';
import { SuggestionBadge } from '@/components/ai/SuggestionBadge';
import { Building, ChevronDown, Menu, Zap, Bot } from 'lucide-react';
import { Building, ChevronDown, Menu, Zap, Bot, Layers } from 'lucide-react';
import { useWindowStore } from '@/store/windowStore';
export function TopBar() {
const { t } = useTranslation();
@@ -17,6 +18,8 @@ export function TopBar() {
const tenants = user?.tenants || [];
const { toggleSidebar, toggleMessageSidebar } = useUIStore();
const logoutMutation = useLogout();
const minimizedWindows = useWindowStore((s) => s.windows.filter((w) => w.state === 'minimized'));
const restoreWindow = useWindowStore((s) => s.restoreWindow);
const [userMenuOpen, setUserMenuOpen] = useState(false);
const userRef = useRef<HTMLDivElement>(null);
@@ -77,6 +80,22 @@ export function TopBar() {
</div>
<div className="flex items-center gap-2">
{/* Minimized windows */}
{minimizedWindows.length > 0 && (
<div className="flex items-center gap-1.5">
{minimizedWindows.map((win) => (
<button
key={win.id}
onClick={() => restoreWindow(win.id)}
className="flex items-center gap-1.5 bg-secondary-100 text-secondary-700 px-3 py-1 rounded-md text-sm hover:bg-secondary-200"
title={win.title}
>
<Layers className="w-3.5 h-3.5" />
<span className="max-w-32 truncate">{win.title}</span>
</button>
))}
</div>
)}
{/* User menu */}
<div ref={userRef} className="relative">
<button