mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-02-01 15:03:36 +00:00
resize debug viewer
This commit is contained in:
@@ -28,6 +28,7 @@ function App() {
|
|||||||
const [selectedFeature, setSelectedFeature] = useState<Feature | null>(null)
|
const [selectedFeature, setSelectedFeature] = useState<Feature | null>(null)
|
||||||
const [setupComplete, setSetupComplete] = useState(true) // Start optimistic
|
const [setupComplete, setSetupComplete] = useState(true) // Start optimistic
|
||||||
const [debugOpen, setDebugOpen] = useState(false)
|
const [debugOpen, setDebugOpen] = useState(false)
|
||||||
|
const [debugPanelHeight, setDebugPanelHeight] = useState(288) // Default height
|
||||||
|
|
||||||
const { data: projects, isLoading: projectsLoading } = useProjects()
|
const { data: projects, isLoading: projectsLoading } = useProjects()
|
||||||
const { data: features } = useFeatures(selectedProject)
|
const { data: features } = useFeatures(selectedProject)
|
||||||
@@ -154,7 +155,10 @@ function App() {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<main className={`max-w-7xl mx-auto px-4 py-8 ${debugOpen ? 'pb-80' : ''}`}>
|
<main
|
||||||
|
className="max-w-7xl mx-auto px-4 py-8"
|
||||||
|
style={{ paddingBottom: debugOpen ? debugPanelHeight + 32 : undefined }}
|
||||||
|
>
|
||||||
{!selectedProject ? (
|
{!selectedProject ? (
|
||||||
<div className="neo-empty-state mt-12">
|
<div className="neo-empty-state mt-12">
|
||||||
<h2 className="font-display text-2xl font-bold mb-2">
|
<h2 className="font-display text-2xl font-bold mb-2">
|
||||||
@@ -224,6 +228,7 @@ function App() {
|
|||||||
isOpen={debugOpen}
|
isOpen={debugOpen}
|
||||||
onToggle={() => setDebugOpen(!debugOpen)}
|
onToggle={() => setDebugOpen(!debugOpen)}
|
||||||
onClear={wsState.clearLogs}
|
onClear={wsState.clearLogs}
|
||||||
|
onHeightChange={setDebugPanelHeight}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,16 +3,23 @@
|
|||||||
*
|
*
|
||||||
* Collapsible panel at the bottom of the screen showing real-time
|
* Collapsible panel at the bottom of the screen showing real-time
|
||||||
* agent output (tool calls, results, steps). Similar to browser DevTools.
|
* agent output (tool calls, results, steps). Similar to browser DevTools.
|
||||||
|
* Features a resizable height via drag handle.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState, useCallback } from 'react'
|
||||||
import { ChevronUp, ChevronDown, Trash2, Terminal } from 'lucide-react'
|
import { ChevronUp, ChevronDown, Trash2, Terminal, GripHorizontal } from 'lucide-react'
|
||||||
|
|
||||||
|
const MIN_HEIGHT = 150
|
||||||
|
const MAX_HEIGHT = 600
|
||||||
|
const DEFAULT_HEIGHT = 288
|
||||||
|
const STORAGE_KEY = 'debug-panel-height'
|
||||||
|
|
||||||
interface DebugLogViewerProps {
|
interface DebugLogViewerProps {
|
||||||
logs: Array<{ line: string; timestamp: string }>
|
logs: Array<{ line: string; timestamp: string }>
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
onToggle: () => void
|
onToggle: () => void
|
||||||
onClear: () => void
|
onClear: () => void
|
||||||
|
onHeightChange?: (height: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogLevel = 'error' | 'warn' | 'debug' | 'info'
|
type LogLevel = 'error' | 'warn' | 'debug' | 'info'
|
||||||
@@ -22,9 +29,16 @@ export function DebugLogViewer({
|
|||||||
isOpen,
|
isOpen,
|
||||||
onToggle,
|
onToggle,
|
||||||
onClear,
|
onClear,
|
||||||
|
onHeightChange,
|
||||||
}: DebugLogViewerProps) {
|
}: DebugLogViewerProps) {
|
||||||
const scrollRef = useRef<HTMLDivElement>(null)
|
const scrollRef = useRef<HTMLDivElement>(null)
|
||||||
const [autoScroll, setAutoScroll] = useState(true)
|
const [autoScroll, setAutoScroll] = useState(true)
|
||||||
|
const [isResizing, setIsResizing] = useState(false)
|
||||||
|
const [panelHeight, setPanelHeight] = useState(() => {
|
||||||
|
// Load saved height from localStorage
|
||||||
|
const saved = localStorage.getItem(STORAGE_KEY)
|
||||||
|
return saved ? Math.min(Math.max(parseInt(saved, 10), MIN_HEIGHT), MAX_HEIGHT) : DEFAULT_HEIGHT
|
||||||
|
})
|
||||||
|
|
||||||
// Auto-scroll to bottom when new logs arrive (if user hasn't scrolled up)
|
// Auto-scroll to bottom when new logs arrive (if user hasn't scrolled up)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -33,6 +47,50 @@ export function DebugLogViewer({
|
|||||||
}
|
}
|
||||||
}, [logs, autoScroll, isOpen])
|
}, [logs, autoScroll, isOpen])
|
||||||
|
|
||||||
|
// Notify parent of height changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (onHeightChange && isOpen) {
|
||||||
|
onHeightChange(panelHeight)
|
||||||
|
}
|
||||||
|
}, [panelHeight, isOpen, onHeightChange])
|
||||||
|
|
||||||
|
// Handle mouse move during resize
|
||||||
|
const handleMouseMove = useCallback((e: MouseEvent) => {
|
||||||
|
const newHeight = window.innerHeight - e.clientY
|
||||||
|
const clampedHeight = Math.min(Math.max(newHeight, MIN_HEIGHT), MAX_HEIGHT)
|
||||||
|
setPanelHeight(clampedHeight)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// Handle mouse up to stop resizing
|
||||||
|
const handleMouseUp = useCallback(() => {
|
||||||
|
setIsResizing(false)
|
||||||
|
// Save to localStorage
|
||||||
|
localStorage.setItem(STORAGE_KEY, panelHeight.toString())
|
||||||
|
}, [panelHeight])
|
||||||
|
|
||||||
|
// Set up global mouse event listeners during resize
|
||||||
|
useEffect(() => {
|
||||||
|
if (isResizing) {
|
||||||
|
document.addEventListener('mousemove', handleMouseMove)
|
||||||
|
document.addEventListener('mouseup', handleMouseUp)
|
||||||
|
document.body.style.cursor = 'ns-resize'
|
||||||
|
document.body.style.userSelect = 'none'
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousemove', handleMouseMove)
|
||||||
|
document.removeEventListener('mouseup', handleMouseUp)
|
||||||
|
document.body.style.cursor = ''
|
||||||
|
document.body.style.userSelect = ''
|
||||||
|
}
|
||||||
|
}, [isResizing, handleMouseMove, handleMouseUp])
|
||||||
|
|
||||||
|
// Start resizing
|
||||||
|
const handleResizeStart = (e: React.MouseEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
setIsResizing(true)
|
||||||
|
}
|
||||||
|
|
||||||
// Detect if user scrolled up
|
// Detect if user scrolled up
|
||||||
const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
|
const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
|
||||||
const el = e.currentTarget
|
const el = e.currentTarget
|
||||||
@@ -87,10 +145,23 @@ export function DebugLogViewer({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`fixed bottom-0 left-0 right-0 z-40 transition-all duration-200 ${
|
className={`fixed bottom-0 left-0 right-0 z-40 ${
|
||||||
isOpen ? 'h-72' : 'h-10'
|
isResizing ? '' : 'transition-all duration-200'
|
||||||
}`}
|
}`}
|
||||||
|
style={{ height: isOpen ? panelHeight : 40 }}
|
||||||
>
|
>
|
||||||
|
{/* Resize handle - only visible when open */}
|
||||||
|
{isOpen && (
|
||||||
|
<div
|
||||||
|
className="absolute top-0 left-0 right-0 h-2 cursor-ns-resize group flex items-center justify-center -translate-y-1/2 z-50"
|
||||||
|
onMouseDown={handleResizeStart}
|
||||||
|
>
|
||||||
|
<div className="w-16 h-1.5 bg-[#333] rounded-full group-hover:bg-[#555] transition-colors flex items-center justify-center">
|
||||||
|
<GripHorizontal size={12} className="text-gray-500 group-hover:text-gray-400" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Header bar */}
|
{/* Header bar */}
|
||||||
<div
|
<div
|
||||||
className="flex items-center justify-between h-10 px-4 bg-[#1a1a1a] border-t-3 border-black cursor-pointer"
|
className="flex items-center justify-between h-10 px-4 bg-[#1a1a1a] border-t-3 border-black cursor-pointer"
|
||||||
|
|||||||
Reference in New Issue
Block a user