mirror of
https://github.com/johndoe6345789/docker-swarm-termina.git
synced 2026-04-24 13:45:01 +00:00
The previous change to use next/font/google caused build failures in CI because Next.js tries to download fonts from Google Fonts during build time, which fails with TLS/network errors in restricted environments. Changes: - Removed next/font/google dependency from app/layout.tsx - Reverted to simpler approach without external font dependencies - Added missing properties to CommandResponse interface: - workdir: string (used by useSimpleTerminal) - exit_code: number (used to determine output vs error type) - Fixed TypeScript error in useSimpleTerminal.ts by ensuring content is always a string with || '' fallback Verified: - npm run build: ✓ Builds successfully - npm run lint: ✓ 0 errors, 0 warnings - npm test: ✓ 282/282 unit tests passing This fixes the CI build failures in: - Build and Push to GHCR workflow - Run Tests / frontend-tests workflow https://claude.ai/code/session_7d4f1b7d-7a0d-44db-b437-c76b6b61dfb2
75 lines
1.8 KiB
TypeScript
75 lines
1.8 KiB
TypeScript
import { useState, useRef, useEffect } from 'react';
|
|
import { apiClient } from '@/lib/api';
|
|
import { OutputLine } from '@/lib/interfaces/terminal';
|
|
|
|
export function useSimpleTerminal(containerId: string) {
|
|
const [command, setCommand] = useState('');
|
|
const [output, setOutput] = useState<OutputLine[]>([]);
|
|
const [isExecuting, setIsExecuting] = useState(false);
|
|
const [workdir, setWorkdir] = useState('/');
|
|
const outputRef = useRef<HTMLDivElement>(null);
|
|
|
|
// Auto-scroll to bottom when output changes
|
|
useEffect(() => {
|
|
if (outputRef.current) {
|
|
outputRef.current.scrollTop = outputRef.current.scrollHeight;
|
|
}
|
|
}, [output]);
|
|
|
|
const executeCommand = async () => {
|
|
if (!command.trim()) return;
|
|
|
|
setIsExecuting(true);
|
|
setOutput((prev) => [...prev, {
|
|
type: 'command',
|
|
content: command,
|
|
workdir: workdir
|
|
}]);
|
|
|
|
try {
|
|
const result = await apiClient.executeCommand(containerId, command);
|
|
|
|
if (result.workdir) {
|
|
setWorkdir(result.workdir);
|
|
}
|
|
|
|
if (result.output && result.output.trim()) {
|
|
setOutput((prev) => [...prev, {
|
|
type: result.exit_code === 0 ? 'output' : 'error',
|
|
content: result.output || ''
|
|
}]);
|
|
} else if (command.trim().startsWith('ls')) {
|
|
setOutput((prev) => [...prev, {
|
|
type: 'output',
|
|
content: '(empty directory)'
|
|
}]);
|
|
}
|
|
} catch (error) {
|
|
setOutput((prev) => [...prev, {
|
|
type: 'error',
|
|
content: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
}]);
|
|
} finally {
|
|
setIsExecuting(false);
|
|
setCommand('');
|
|
}
|
|
};
|
|
|
|
const reset = () => {
|
|
setOutput([]);
|
|
setCommand('');
|
|
setWorkdir('/');
|
|
};
|
|
|
|
return {
|
|
command,
|
|
setCommand,
|
|
output,
|
|
isExecuting,
|
|
workdir,
|
|
outputRef,
|
|
executeCommand,
|
|
reset,
|
|
};
|
|
}
|