diff --git a/components/ConfirmDialog.tsx b/components/ConfirmDialog.tsx
new file mode 100644
index 0000000..563e6e7
--- /dev/null
+++ b/components/ConfirmDialog.tsx
@@ -0,0 +1,62 @@
+'use client'
+
+type Props = {
+ open: boolean
+ title?: string
+ commandPreview: string
+ onConfirm: () => void
+ onCancel: () => void
+ loading?: boolean
+}
+
+export default function ConfirmDialog({
+ open,
+ title = 'Confirm action',
+ commandPreview,
+ onConfirm,
+ onCancel,
+ loading = false,
+}: Props) {
+ if (!open) return null
+
+ return (
+
+
+
+
{title}
+
+ The following command will be executed on the server:
+
+
+
+ {commandPreview}
+
+
+
+ This action cannot be undone. Review the command above before confirming.
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/components/StreamingTerminal.tsx b/components/StreamingTerminal.tsx
new file mode 100644
index 0000000..856806e
--- /dev/null
+++ b/components/StreamingTerminal.tsx
@@ -0,0 +1,89 @@
+'use client'
+
+import { useEffect, useRef } from 'react'
+
+export type TerminalLine = {
+ type: 'stdout' | 'stderr'
+ text: string
+}
+
+export type TerminalStatus = 'idle' | 'running' | 'done' | 'failed' | 'error'
+
+type Props = {
+ lines: TerminalLine[]
+ status: TerminalStatus
+ error?: string | null
+ className?: string
+}
+
+export default function StreamingTerminal({ lines, status, error, className = '' }: Props) {
+ const bottomRef = useRef(null)
+
+ useEffect(() => {
+ bottomRef.current?.scrollIntoView({ behavior: 'smooth' })
+ }, [lines])
+
+ const statusBar = () => {
+ if (status === 'running') {
+ return (
+
+
+ Running…
+
+ )
+ }
+ if (status === 'done') {
+ return (
+
+
+ Completed successfully
+
+ )
+ }
+ if (status === 'failed') {
+ return (
+
+
+ Exited with error
+
+ )
+ }
+ if (status === 'error') {
+ return (
+
+
+ {error ?? 'Unknown error'}
+
+ )
+ }
+ return null
+ }
+
+ return (
+
+
+ {lines.length === 0 && status === 'running' && (
+
Waiting for output…
+ )}
+ {lines.map((line, i) => (
+
+ {line.text}
+
+ ))}
+
+
+ {statusBar()}
+
+ )
+}