restore: bring back all custom UI enhancements from checkpoint

Restored from commit 52be710 (checkpoint before qwen oauth + todo roller):

Enhanced UI Features:
- SMART FIX button with AI code analysis
- APEX (Autonomous Programming EXecution) mode
- SHIELD (Auto-approval) mode
- MULTIX MODE multi-task pipeline interface
- Live streaming token counter
- Thinking indicator with bouncing dots animation

Components restored:
- packages/ui/src/components/chat/multi-task-chat.tsx
- packages/ui/src/components/instance/instance-shell2.tsx
- packages/ui/src/components/settings/OllamaCloudSettings.tsx
- packages/ui/src/components/settings/QwenCodeSettings.tsx
- packages/ui/src/stores/solo-store.ts
- packages/ui/src/stores/task-actions.ts
- packages/ui/src/stores/session-events.ts (autonomous mode)
- packages/server/src/integrations/ollama-cloud.ts
- packages/server/src/server/routes/ollama.ts
- packages/server/src/server/routes/qwen.ts

This ensures all custom features are preserved in source control.
This commit is contained in:
Gemini AI
2025-12-23 13:18:37 +04:00
Unverified
parent 157449a9ad
commit c4ac079660
47 changed files with 4550 additions and 527 deletions

View File

@@ -34,6 +34,8 @@ import {
getSessionFamily,
getSessionInfo,
setActiveSession,
executeCustomCommand,
runShellCommand,
} from "../../stores/sessions"
import { keyboardRegistry, type KeyboardShortcut } from "../../lib/keyboard-registry"
import { messageStoreBus } from "../../stores/message-v2/bus"
@@ -48,14 +50,25 @@ import InfoView from "../info-view"
import InstanceServiceStatus from "../instance-service-status"
import AgentSelector from "../agent-selector"
import ModelSelector from "../model-selector"
import ModelStatusSelector from "../model-status-selector"
import CommandPalette from "../command-palette"
import Kbd from "../kbd"
import MultiTaskChat from "../chat/multi-task-chat"
import { TodoListView } from "../tool-call/renderers/todo"
import ContextUsagePanel from "../session/context-usage-panel"
import SessionView from "../session/session-view"
import { Sidebar, type FileNode } from "./sidebar"
import { Editor } from "./editor"
import { serverApi } from "../../lib/api-client"
import { Sparkles, Layout as LayoutIcon, Terminal as TerminalIcon, Search, Loader2, Zap, Shield } from "lucide-solid"
import { formatTokenTotal } from "../../lib/formatters"
import { sseManager } from "../../lib/sse-manager"
import { getLogger } from "../../lib/logger"
import {
getSoloState,
toggleAutonomous,
toggleAutoApproval,
} from "../../stores/solo-store"
import {
SESSION_SIDEBAR_EVENT,
type SessionSidebarRequestAction,
@@ -128,7 +141,26 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
const [activeResizeSide, setActiveResizeSide] = createSignal<"left" | "right" | null>(null)
const [resizeStartX, setResizeStartX] = createSignal(0)
const [resizeStartWidth, setResizeStartWidth] = createSignal(0)
const [rightPanelExpandedItems, setRightPanelExpandedItems] = createSignal<string[]>(["lsp", "mcp"])
const [rightPanelExpandedItems, setRightPanelExpandedItems] = createSignal<string[]>(["lsp", "mcp", "plan"])
const [currentFile, setCurrentFile] = createSignal<FileNode | null>(null)
const [isSoloOpen, setIsSoloOpen] = createSignal(true)
// Handler to load file content when selected
const handleFileSelect = async (file: FileNode) => {
try {
const response = await serverApi.readWorkspaceFile(props.instance.id, file.path)
const language = file.name.split('.').pop() || 'text'
setCurrentFile({
...file,
content: response.contents,
language,
})
} catch (error) {
log.error('Failed to read file content', error)
// Still show the file but without content
setCurrentFile(file)
}
}
const messageStore = createMemo(() => messageStoreBus.getOrCreate(props.instance.id))
@@ -326,6 +358,58 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
showCommandPalette(props.instance.id)
}
const [isFixing, setIsFixing] = createSignal(false)
const [isBuilding, setIsBuilding] = createSignal(false)
const handleSmartFix = async () => {
const sessionId = activeSessionIdForInstance()
if (!sessionId || sessionId === "info" || isFixing()) {
return
}
setIsFixing(true)
try {
// Smart Fix targets the active task if available, otherwise general fix
const session = activeSessionForInstance()
const activeTaskId = session?.activeTaskId
const args = activeTaskId ? `task:${activeTaskId}` : ""
await executeCustomCommand(props.instance.id, sessionId, "fix", args)
// Auto-open right panel to show agent progress if it's not open
if (!rightOpen()) {
setRightOpen(true)
measureDrawerHost()
}
} catch (error) {
log.error("Failed to run Smart Fix command", error)
} finally {
setTimeout(() => setIsFixing(false), 2000) // Reset after delay
}
}
const handleBuild = async () => {
const sessionId = activeSessionIdForInstance()
if (!sessionId || sessionId === "info" || isBuilding()) {
return
}
setIsBuilding(true)
try {
await runShellCommand(props.instance.id, sessionId, "build")
// Auto-open right panel to show build logs if it's not open
if (!rightOpen()) {
setRightOpen(true)
measureDrawerHost()
}
} catch (error) {
log.error("Failed to run Build command", error)
} finally {
setTimeout(() => setIsBuilding(false), 2000) // Reset after delay
}
}
const customCommands = createMemo(() => buildCustomCommandEntries(props.instance.id, getInstanceCommands(props.instance.id)))
const instancePaletteCommands = createMemo(() => [...props.paletteCommands(), ...customCommands()])
@@ -607,7 +691,7 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
})
type DrawerViewState = "pinned" | "floating-open" | "floating-closed"
const leftDrawerState = createMemo<DrawerViewState>(() => {
if (leftPinned()) return "pinned"
@@ -648,7 +732,7 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
const pinLeftDrawer = () => {
const pinLeftDrawer = () => {
blurIfInside(leftDrawerContentEl())
batch(() => {
setLeftPinned(true)
@@ -707,11 +791,13 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
if (state === "pinned") return
if (state === "floating-closed") {
setRightOpen(true)
setIsSoloOpen(false)
measureDrawerHost()
return
}
blurIfInside(rightDrawerContentEl())
setRightOpen(false)
setIsSoloOpen(false)
focusTarget(rightToggleButtonEl())
measureDrawerHost()
}
@@ -757,90 +843,27 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
}
const LeftDrawerContent = () => (
<div class="flex flex-col h-full min-h-0" ref={setLeftDrawerContentEl}>
<div class="flex items-start justify-between gap-2 px-4 py-3 border-b border-base">
<div class="flex flex-col gap-1">
<span class="session-sidebar-title text-sm font-semibold uppercase text-primary">Sessions</span>
<div class="session-sidebar-shortcuts">
<Show when={keyboardShortcuts().length}>
<KeyboardHint shortcuts={keyboardShortcuts()} separator=" " showDescription={false} />
</Show>
</div>
</div>
<div class="flex items-center gap-2">
<Show when={!isPhoneLayout()}>
<IconButton
size="small"
color="inherit"
aria-label={leftPinned() ? "Unpin left drawer" : "Pin left drawer"}
onClick={() => (leftPinned() ? unpinLeftDrawer() : pinLeftDrawer())}
>
{leftPinned() ? <PushPinIcon fontSize="small" /> : <PushPinOutlinedIcon fontSize="small" />}
</IconButton>
</Show>
</div>
</div>
<div class="session-sidebar flex flex-col flex-1 min-h-0">
<SessionList
instanceId={props.instance.id}
sessions={activeSessions()}
activeSessionId={activeSessionIdForInstance()}
onSelect={handleSessionSelect}
onClose={(id) => {
const result = props.onCloseSession(id)
if (result instanceof Promise) {
void result.catch((error) => log.error("Failed to close session:", error))
}
}}
onNew={() => {
const result = props.onNewSession()
if (result instanceof Promise) {
void result.catch((error) => log.error("Failed to create session:", error))
}
}}
showHeader={false}
showFooter={false}
/>
<Divider />
<Show when={activeSessionForInstance()}>
{(activeSession) => (
<>
<ContextUsagePanel instanceId={props.instance.id} sessionId={activeSession().id} />
<div class="session-sidebar-controls px-4 py-4 border-t border-base flex flex-col gap-3">
<AgentSelector
instanceId={props.instance.id}
sessionId={activeSession().id}
currentAgent={activeSession().agent}
onAgentChange={(agent) => props.handleSidebarAgentChange(activeSession().id, agent)}
/>
<div class="sidebar-selector-hints" aria-hidden="true">
<span class="hint sidebar-selector-hint sidebar-selector-hint--left">
<Kbd shortcut="cmd+shift+a" />
</span>
<span class="hint sidebar-selector-hint sidebar-selector-hint--right">
<Kbd shortcut="cmd+shift+m" />
</span>
</div>
<ModelSelector
instanceId={props.instance.id}
sessionId={activeSession().id}
currentModel={activeSession().model}
onModelChange={(model) => props.handleSidebarModelChange(activeSession().id, model)}
/>
</div>
</>
)}
</Show>
</div>
</div>
<Sidebar
instanceId={props.instance.id}
isOpen={leftOpen()}
onFileSelect={handleFileSelect}
sessions={Array.from(activeSessions().values())}
activeSessionId={activeSessionIdForInstance() || undefined}
onSessionSelect={handleSessionSelect}
/>
)
const RightDrawerContent = () => {
const sessionId = activeSessionIdForInstance()
if (sessionId && sessionId !== "info") {
return (
<div class="flex flex-col h-full" ref={setRightDrawerContentEl}>
<MultiTaskChat instanceId={props.instance.id} sessionId={sessionId} />
</div>
)
}
const renderPlanSectionContent = () => {
const sessionId = activeSessionIdForInstance()
if (!sessionId || sessionId === "info") {
@@ -1011,6 +1034,8 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
const renderRightPanel = () => {
if (isSoloOpen()) return null; // MultiX Mode uses the main stream area
if (rightPinned()) {
return (
<Box
@@ -1075,215 +1100,239 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
const sessionLayout = (
<div
class="session-shell-panels flex flex-col flex-1 min-h-0 overflow-x-hidden"
class="session-shell-panels flex flex-col flex-1 min-h-0 overflow-x-hidden relative bg-[#050505]"
ref={(element) => {
setDrawerHost(element)
measureDrawerHost()
}}
>
<AppBar position="sticky" color="default" elevation={0} class="border-b border-base">
<Toolbar variant="dense" class="session-toolbar flex flex-wrap items-center gap-2 py-0 min-h-[40px]">
<Show
when={!isPhoneLayout()}
fallback={
<div class="flex flex-col w-full gap-1.5">
<div class="flex flex-wrap items-center justify-between gap-2 w-full">
<IconButton
ref={setLeftToggleButtonEl}
color="inherit"
onClick={handleLeftAppBarButtonClick}
aria-label={leftAppBarButtonLabel()}
size="small"
aria-expanded={leftDrawerState() !== "floating-closed"}
disabled={leftDrawerState() === "pinned"}
>
{leftAppBarButtonIcon()}
</IconButton>
{/* Background Decorator - Antigravity Glows */}
<div class="absolute top-[-10%] left-[-10%] w-[40%] h-[40%] bg-blue-600/10 blur-[120px] rounded-full pointer-events-none z-0" />
<div class="absolute bottom-[-10%] right-[-10%] w-[30%] h-[30%] bg-purple-600/5 blur-[100px] rounded-full pointer-events-none z-0" />
<div class="flex flex-wrap items-center gap-1 justify-center">
<button
type="button"
class="connection-status-button px-2 py-0.5 text-xs"
onClick={handleCommandPaletteClick}
aria-label="Open command palette"
style={{ flex: "0 0 auto", width: "auto" }}
>
Command Palette
</button>
<span class="connection-status-shortcut-hint">
<Kbd shortcut="cmd+shift+p" />
</span>
<span
class={`status-indicator ${connectionStatusClass()}`}
aria-label={`Connection ${connectionStatus()}`}
>
<span class="status-dot" />
</span>
</div>
<IconButton
ref={setRightToggleButtonEl}
color="inherit"
onClick={handleRightAppBarButtonClick}
aria-label={rightAppBarButtonLabel()}
size="small"
aria-expanded={rightDrawerState() !== "floating-closed"}
disabled={rightDrawerState() === "pinned"}
>
{rightAppBarButtonIcon()}
</IconButton>
</div>
<div class="flex flex-wrap items-center justify-center gap-2 pb-1">
<div class="inline-flex items-center gap-1 rounded-full border border-base px-2 py-0.5 text-xs text-primary">
<span class="uppercase text-[10px] tracking-wide text-primary/70">Used</span>
<span class="font-semibold text-primary">{formattedUsedTokens()}</span>
</div>
<div class="inline-flex items-center gap-1 rounded-full border border-base px-2 py-0.5 text-xs text-primary">
<span class="uppercase text-[10px] tracking-wide text-primary/70">Avail</span>
<span class="font-semibold text-primary">{formattedAvailableTokens()}</span>
</div>
</div>
</div>
}
>
<div class="session-toolbar-left flex items-center gap-3 min-w-0">
<IconButton
ref={setLeftToggleButtonEl}
color="inherit"
onClick={handleLeftAppBarButtonClick}
aria-label={leftAppBarButtonLabel()}
size="small"
aria-expanded={leftDrawerState() !== "floating-closed"}
disabled={leftDrawerState() === "pinned"}
>
{leftAppBarButtonIcon()}
</IconButton>
<Show when={!showingInfoView()}>
<div class="inline-flex items-center gap-1 rounded-full border border-base px-2 py-0.5 text-xs text-primary">
<span class="uppercase text-[10px] tracking-wide text-primary/70">Used</span>
<span class="font-semibold text-primary">{formattedUsedTokens()}</span>
</div>
<div class="inline-flex items-center gap-1 rounded-full border border-base px-2 py-0.5 text-xs text-primary">
<span class="uppercase text-[10px] tracking-wide text-primary/70">Avail</span>
<span class="font-semibold text-primary">{formattedAvailableTokens()}</span>
</div>
</Show>
</div>
<div class="session-toolbar-center flex-1 flex items-center justify-center gap-2 min-w-[160px]">
<button
type="button"
class="connection-status-button px-2 py-0.5 text-xs"
onClick={handleCommandPaletteClick}
aria-label="Open command palette"
style={{ flex: "0 0 auto", width: "auto" }}
>
Command Palette
</button>
<span class="connection-status-shortcut-hint">
<Kbd shortcut="cmd+shift+p" />
</span>
</div>
<div class="session-toolbar-right flex items-center gap-3">
<div class="connection-status-meta flex items-center gap-3">
<Show when={connectionStatus() === "connected"}>
<span class="status-indicator connected">
<span class="status-dot" />
<span class="status-text">Connected</span>
</span>
</Show>
<Show when={connectionStatus() === "connecting"}>
<span class="status-indicator connecting">
<span class="status-dot" />
<span class="status-text">Connecting...</span>
</span>
</Show>
<Show when={connectionStatus() === "error" || connectionStatus() === "disconnected"}>
<span class="status-indicator disconnected">
<span class="status-dot" />
<span class="status-text">Disconnected</span>
</span>
</Show>
</div>
<IconButton
ref={setRightToggleButtonEl}
color="inherit"
onClick={handleRightAppBarButtonClick}
aria-label={rightAppBarButtonLabel()}
size="small"
aria-expanded={rightDrawerState() !== "floating-closed"}
disabled={rightDrawerState() === "pinned"}
>
{rightAppBarButtonIcon()}
</IconButton>
<AppBar position="sticky" color="default" elevation={0} class="border-b border-white/5 bg-[#050505]/80 backdrop-blur-md z-20">
<Toolbar variant="dense" class="session-toolbar flex flex-wrap items-center justify-between gap-2 py-0 min-h-[40px]">
<div class="flex items-center space-x-4">
<IconButton
ref={setLeftToggleButtonEl}
color="inherit"
onClick={handleLeftAppBarButtonClick}
aria-label={leftAppBarButtonLabel()}
size="small"
class="text-zinc-500 hover:text-zinc-200"
>
<MenuIcon fontSize="small" />
</IconButton>
<div class="flex items-center space-x-2">
<div class="w-2.5 h-2.5 rounded-full bg-[#f87171] opacity-60" />
<div class="w-2.5 h-2.5 rounded-full bg-[#fbbf24] opacity-60" />
<div class="w-2.5 h-2.5 rounded-full bg-[#4ade80] opacity-60" />
</div>
</Show>
</div>
<div class="hidden md:flex items-center bg-white/5 border border-white/5 rounded-full px-3 py-1 space-x-2 text-zinc-400 group hover:border-white/10 transition-all cursor-pointer" onClick={handleCommandPaletteClick}>
<Search size={14} />
<span class="text-[11px] min-w-[200px]">Search your project...</span>
<div class="flex items-center space-x-1 opacity-40">
<Kbd shortcut="cmd+shift+p" />
</div>
</div>
<div class="flex items-center space-x-4">
<Show when={activeSessionIdForInstance() && activeSessionIdForInstance() !== "info"}>
<ModelStatusSelector
instanceId={props.instance.id}
sessionId={activeSessionIdForInstance()!}
currentModel={activeSessionForInstance()?.model || { providerId: "", modelId: "" }}
onModelChange={async (model) => {
const sid = activeSessionIdForInstance()
if (sid) await props.handleSidebarModelChange(sid, model)
}}
/>
</Show>
{/* SmartX Mode Buttons (Integrated HUD) */}
<div class="flex items-center bg-white/5 border border-white/5 rounded-full px-2 py-1 space-x-1">
<button
onClick={handleSmartFix}
disabled={isFixing()}
title="Smart Fix"
class={`transition-all flex items-center space-x-1.5 px-2 py-1 rounded-full hover:bg-white/10 ${isFixing() ? "text-blue-500" : "text-zinc-400 hover:text-white"}`}
>
<Show when={isFixing()} fallback={<Sparkles size={14} class="text-blue-400" />}>
<Loader2 size={14} class="animate-spin text-blue-400" />
</Show>
<span class="text-[10px] font-bold uppercase tracking-tight">
{isFixing() ? "FIXING..." : "SMART FIX"}
</span>
</button>
<div class="w-px h-3 bg-white/10" />
<button
onClick={handleBuild}
disabled={isBuilding()}
title="Build"
class={`transition-all flex items-center space-x-1.5 px-2 py-1 rounded-full hover:bg-white/10 ${isBuilding() ? "text-indigo-500" : "text-zinc-400 hover:text-white"}`}
>
<Show when={isBuilding()} fallback={<TerminalIcon size={14} />}>
<Loader2 size={14} class="animate-spin text-indigo-400" />
</Show>
<span class="text-[10px] font-bold uppercase tracking-tight">
{isBuilding() ? "BUILDING..." : "BUILD"}
</span>
</button>
</div>
{/* SOLO Mode & Auto-Approval Toggles */}
<div class="flex items-center bg-white/5 border border-white/5 rounded-full px-1.5 py-1 space-x-1">
<button
onClick={() => toggleAutonomous(props.instance.id)}
title="Toggle Autonomous Mode (SOLO)"
class={`flex items-center space-x-1.5 px-2 py-0.5 rounded-full transition-all ${getSoloState(props.instance.id).isAutonomous
? "bg-blue-500/20 text-blue-400 border border-blue-500/30"
: "text-zinc-500 hover:text-zinc-300"
}`}
>
<Zap size={12} class={getSoloState(props.instance.id).isAutonomous ? "animate-pulse" : ""} />
<span class="text-[9px] font-black uppercase tracking-tighter">SOLO</span>
</button>
<button
onClick={() => toggleAutoApproval(props.instance.id)}
title="Toggle Auto-Approval (SHIELD)"
class={`flex items-center space-x-1.5 px-2 py-0.5 rounded-full transition-all ${getSoloState(props.instance.id).autoApproval
? "bg-emerald-500/20 text-emerald-400 border border-emerald-500/30"
: "text-zinc-500 hover:text-zinc-300"
}`}
>
<Shield size={12} />
<span class="text-[9px] font-black uppercase tracking-tighter">Shield</span>
</button>
</div>
<button
onClick={() => {
const newState = !(rightOpen() && isSoloOpen())
setRightOpen(newState)
setIsSoloOpen(newState)
}}
class={`flex items-center space-x-1.5 px-3 py-1 rounded-full text-[11px] font-bold transition-all ${(rightOpen() && isSoloOpen()) ? 'bg-blue-600/20 text-blue-400 border border-blue-500/30' : 'bg-white/5 text-zinc-400 border border-white/5'
}`}
>
<span class={`w-1.5 h-1.5 bg-current rounded-full ${(rightOpen() && isSoloOpen()) ? 'animate-pulse' : ''}`} />
<span>MULTIX MODE</span>
</button>
<IconButton
ref={setRightToggleButtonEl}
color="inherit"
onClick={handleRightAppBarButtonClick}
aria-label={rightAppBarButtonLabel()}
size="small"
class="text-zinc-500 hover:text-zinc-200"
>
{rightAppBarButtonIcon()}
</IconButton>
</div>
</Toolbar>
</AppBar>
<Box sx={{ display: "flex", flex: 1, minHeight: 0, overflowX: "hidden" }}>
<Box sx={{ display: "flex", flex: 1, minHeight: 0, overflowX: "hidden", position: "relative", zIndex: 10 }}>
{renderLeftPanel()}
<Box
component="main"
sx={{ flexGrow: 1, minHeight: 0, display: "flex", flexDirection: "column", overflowX: "hidden" }}
class="content-area"
class="content-area relative"
>
<Show
when={showingInfoView()}
fallback={
<Show
when={cachedSessionIds().length > 0 && activeSessionIdForInstance()}
fallback={
<div class="flex items-center justify-center h-full">
<div class="text-center text-gray-500 dark:text-gray-400">
<p class="mb-2">No session selected</p>
<p class="text-sm">Select a session to view messages</p>
</div>
</div>
}
>
<For each={cachedSessionIds()}>
{(sessionId) => {
const isActive = () => activeSessionIdForInstance() === sessionId
return (
<div
class="session-cache-pane flex flex-col flex-1 min-h-0"
style={{ display: isActive() ? "flex" : "none" }}
data-session-id={sessionId}
aria-hidden={!isActive()}
>
<SessionView
sessionId={sessionId}
activeSessions={activeSessions()}
instanceId={props.instance.id}
instanceFolder={props.instance.folder}
escapeInDebounce={props.escapeInDebounce}
showSidebarToggle={showEmbeddedSidebarToggle()}
onSidebarToggle={() => setLeftOpen(true)}
forceCompactStatusLayout={showEmbeddedSidebarToggle()}
isActive={isActive()}
/>
</div>
)
}}
</For>
<div class="flex-1 flex overflow-hidden">
<Editor file={currentFile()} />
<div class="flex-1 flex flex-col relative border-l border-white/5">
<Show when={isSoloOpen()}>
<div class="flex-1 flex flex-col min-h-0">
<MultiTaskChat instanceId={props.instance.id} sessionId={activeSessionIdForInstance() || ""} />
</div>
</Show>
}
>
<div class="info-view-pane flex flex-col flex-1 min-h-0 overflow-y-auto">
<InfoView instanceId={props.instance.id} />
<div class="flex-1 flex flex-col relative"
style={{ display: isSoloOpen() ? "none" : "flex" }}>
<Show
when={showingInfoView()}
fallback={
<Show
when={cachedSessionIds().length > 0 && activeSessionIdForInstance()}
fallback={
<div class="flex items-center justify-center h-full">
<div class="text-center text-zinc-500">
<p class="mb-2">No session selected</p>
<p class="text-sm">Select a session to view messages</p>
</div>
</div>
}
>
<For each={cachedSessionIds()}>
{(sessionId) => {
const isActive = () => activeSessionIdForInstance() === sessionId
return (
<div
class="session-cache-pane flex flex-col flex-1 min-h-0"
style={{ display: isActive() ? "flex" : "none" }}
data-session-id={sessionId}
aria-hidden={!isActive()}
>
<SessionView
sessionId={sessionId}
activeSessions={activeSessions()}
instanceId={props.instance.id}
instanceFolder={props.instance.folder}
escapeInDebounce={props.escapeInDebounce}
showSidebarToggle={showEmbeddedSidebarToggle()}
onSidebarToggle={() => setLeftOpen(true)}
forceCompactStatusLayout={showEmbeddedSidebarToggle()}
isActive={isActive()}
/>
</div>
)
}}
</For>
</Show>
}
>
<div class="info-view-pane flex flex-col flex-1 min-h-0 overflow-y-auto">
<InfoView instanceId={props.instance.id} />
</div>
</Show>
</div>
</div>
</Show>
</div>
{/* Bottom Toolbar/Terminal Area */}
<footer class="h-8 glass border-t border-white/5 flex items-center justify-between px-3 text-[10px] text-zinc-500 tracking-wide z-10 shrink-0">
<div class="flex items-center space-x-4">
<div class="flex items-center space-x-1.5 cursor-pointer hover:text-zinc-300">
<TerminalIcon size={12} />
<span>TERMINAL</span>
</div>
</div>
<div class="flex items-center space-x-4 uppercase font-bold">
<div class="flex items-center space-x-1">
<span class="w-1.5 h-1.5 rounded-full bg-green-500 shadow-[0_0_5px_rgba(34,197,94,0.5)]" />
<span>Sync Active</span>
</div>
<Show when={activeSessionForInstance()}>
{(session) => (
<>
<span class="hover:text-zinc-300 cursor-pointer">{session().model.modelId}</span>
<span class="hover:text-zinc-300 cursor-pointer">{session().agent}</span>
</>
)}
</Show>
</div>
</footer>
</Box>
{renderRightPanel()}
</Box>
{/* Floating Action Buttons removed - Integrated into Header */}
</div>
)