From eb2d10af715fe04ab4a6b02d6dfc508b66f0a8ed Mon Sep 17 00:00:00 2001 From: Anso Date: Wed, 6 May 2026 20:19:34 -0400 Subject: [PATCH] refactor(frontend): migrate Bash, Log, and Deploy feedback dialogs (#952) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - modal.tsx Modal now accepts a showClose prop that passes through to DialogContent, so callers that render their own close affordance (DeployFeedbackModal) can suppress the Radix close button - DeployFeedbackModal -> Modal with showClose=false. Custom header, scroll body, embedded terminal, and footer stay in place; Modal provides the consistent overlay/blur chrome. DialogTitle is still imported from the dialog primitive purely as an sr-only accessibility wrapper, since this status panel doesn't fit the §10 ModalHeader chrome - BashExecModal -> Modal + ModalHeader. Kicker BASH · {CONTAINER}. The xterm container sits in the body - LogViewer -> Modal + ModalHeader. Kicker LOGS · {CONTAINER}. The scroll region sits in the body --- frontend/src/components/BashExecModal.tsx | 41 +++++++------ .../src/components/DeployFeedbackModal.tsx | 23 ++++---- frontend/src/components/LogViewer.tsx | 57 +++++++++++-------- frontend/src/components/ui/modal.tsx | 5 +- 4 files changed, 67 insertions(+), 59 deletions(-) diff --git a/frontend/src/components/BashExecModal.tsx b/frontend/src/components/BashExecModal.tsx index b5ff1da3..b38d6971 100644 --- a/frontend/src/components/BashExecModal.tsx +++ b/frontend/src/components/BashExecModal.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef, useState, useCallback } from 'react'; -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from './ui/dialog'; +import { Modal, ModalHeader } from './ui/modal'; import { Terminal as TerminalIcon } from 'lucide-react'; import { loadXtermModules, type Terminal, type FitAddon, type XtermModules } from '@/lib/xtermLoader'; import { buildXtermMinimalTheme } from '@/lib/terminalTheme'; @@ -214,31 +214,28 @@ export default function BashExecModal({ isOpen, onClose, containerId, containerN }; return ( - - - - - - Bash: {containerName} + + + + Bash session {isConnected && ( Connected )} - - - Interactive bash terminal session for {containerName} - - - {/* Styling wrapper - padding and rounded corners go here */} -
- {/* Clean xterm container - NO padding, NO overflow-hidden, explicit dimensions */} -
-
- -
+ + } + description={`Interactive bash terminal session for ${containerName}`} + /> +
+
+
+ ); } diff --git a/frontend/src/components/DeployFeedbackModal.tsx b/frontend/src/components/DeployFeedbackModal.tsx index 0c9d2d6d..e5b45dad 100644 --- a/frontend/src/components/DeployFeedbackModal.tsx +++ b/frontend/src/components/DeployFeedbackModal.tsx @@ -7,11 +7,8 @@ import { Minimize2, Terminal as TerminalIcon, } from 'lucide-react'; -import { - Dialog, - DialogContent, - DialogTitle, -} from '@/components/ui/dialog'; +import { Modal } from '@/components/ui/modal'; +import { DialogTitle } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { StructuredLogRow } from '@/components/log-rendering/StructuredLogRow'; import TerminalComponent from '@/components/Terminal'; @@ -156,11 +153,15 @@ export function DeployFeedbackModal({ isMinimized, onMinimize }: DeployFeedbackM const verbLabel = VERB_LABELS[action]; return ( - - +
@@ -273,8 +274,8 @@ export function DeployFeedbackModal({ isMinimized, onMinimize }: DeployFeedbackM
- - + + ); } diff --git a/frontend/src/components/LogViewer.tsx b/frontend/src/components/LogViewer.tsx index 1c82ef11..bdc9f73a 100644 --- a/frontend/src/components/LogViewer.tsx +++ b/frontend/src/components/LogViewer.tsx @@ -1,5 +1,5 @@ import { useEffect, useState, useRef } from 'react'; -import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; +import { Modal, ModalHeader } from "@/components/ui/modal"; import { Loader2, Terminal } from "lucide-react"; interface LogViewerProps { @@ -56,30 +56,37 @@ export function LogViewer({ containerId, containerName, isOpen, onClose }: LogVi }, [isOpen, containerId]); return ( - !open && onClose()}> - - - - - {containerName} {isConnected ? (connected) : } - - + !open && onClose()} className="max-w-4xl h-[80vh] flex flex-col"> + + + Container logs + {isConnected ? ( + (connected) + ) : ( + + )} + + } + description={`Live log stream for ${containerName}`} + /> -
- {logs.length === 0 && !isConnected ? ( -
Connecting to container stream...
- ) : ( - logs.map((log, i) => ( -
- {log} -
- )) - )} -
-
-
+
+ {logs.length === 0 && !isConnected ? ( +
Connecting to container stream...
+ ) : ( + logs.map((log, i) => ( +
+ {log} +
+ )) + )} +
+ ); } diff --git a/frontend/src/components/ui/modal.tsx b/frontend/src/components/ui/modal.tsx index dee9ab42..054dfbf5 100644 --- a/frontend/src/components/ui/modal.tsx +++ b/frontend/src/components/ui/modal.tsx @@ -34,12 +34,15 @@ interface ModalProps { children: React.ReactNode; size?: ModalSize; className?: string; + /** Pass through to DialogContent — set to false when the modal renders its own close affordance. */ + showClose?: boolean; } -export function Modal({ open, onOpenChange, children, size = 'md', className }: ModalProps) { +export function Modal({ open, onOpenChange, children, size = 'md', className, showClose }: ModalProps) { return (