Files
docker-swarm-termina/frontend/lib/hooks/useSimpleTerminal.ts
Claude 888dc3a200 Fix build failure caused by Google Fonts network dependency
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
2026-02-01 22:22:08 +00:00

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,
};
}