enotkrutoy's picture
Upload 19 files
a992a28 verified
import React, { useState, useMemo } from 'react';
import { ToolDefinition, GeneratedCode, ToolType, FunctionDefinition, ParameterType } from './types';
import ToolForm from './components/ToolForm';
import CodeOutput from './components/CodeOutput';
import { generateAllCode } from './services/codeGenerator';
import { nmapScannerExample } from './examples';
import Button from './components/ui/Button';
import AiPrompt from './components/AiPrompt';
import { generateToolDefinitionFromPrompt } from './services/aiService';
const initialFunction: FunctionDefinition = {
id: crypto.randomUUID(),
name: 'example_function',
description: 'Краткое описание того, что делает эта функция.',
parameters: [
{
id: crypto.randomUUID(),
name: 'example_param',
type: ParameterType.STRING, // Исправлено с 'str' as any
description: 'Описание параметра.',
required: true,
},
],
returnType: ParameterType.STRING, // Исправлено с 'str' as any
returnDescription: 'Описание возвращаемого значения.',
};
const initialToolDefinition: ToolDefinition = {
name: 'my_tool',
type: ToolType.UTILITY,
dependencies: '',
functions: [initialFunction],
};
const App: React.FC = () => {
const [toolDefinition, setToolDefinition] = useState<ToolDefinition>(initialToolDefinition);
const [aiPrompt, setAiPrompt] = useState<string>('');
const [isGenerating, setIsGenerating] = useState<boolean>(false);
const [aiError, setAiError] = useState<string | null>(null);
const generatedCode = useMemo<GeneratedCode | null>(() => {
try {
if (toolDefinition.name && toolDefinition.functions.length > 0 && toolDefinition.functions.every(f => f.name)) {
return generateAllCode(toolDefinition);
}
return null;
} catch (error) {
console.error("Error generating code:", error);
return null;
}
}, [toolDefinition]);
const loadNmapExample = () => {
setToolDefinition(nmapScannerExample);
};
const handleAiGenerate = async () => {
if (!aiPrompt.trim()) {
setAiError("Пожалуйста, введите описание инструмента.");
return;
}
setIsGenerating(true);
setAiError(null);
try {
const jsonString = await generateToolDefinitionFromPrompt(aiPrompt);
// Бэкенд уже должен был очистить markdown, но на всякий случай
const cleanedJsonString = jsonString.replace(/^```json\s*|\s*```$/g, '').trim();
const newToolDefinition = JSON.parse(cleanedJsonString);
// Добавим уникальные ID, так как AI их не генерирует
if (newToolDefinition.functions) {
newToolDefinition.functions.forEach((func: FunctionDefinition) => {
func.id = crypto.randomUUID();
if (func.parameters) {
func.parameters.forEach((param: any) => {
param.id = crypto.randomUUID();
});
} else {
func.parameters = []; // Убедимся, что параметры существуют
}
});
} else {
newToolDefinition.functions = []; // Убедимся, что функции существуют
}
setToolDefinition(newToolDefinition);
} catch (error) {
console.error("AI generation failed:", error);
setAiError(`Не удалось сгенерировать инструмент: ${error.message}. Проверьте консоль.`);
} finally {
setIsGenerating(false);
}
};
return (
<div className="min-h-screen bg-gray-900 text-gray-100 p-4 sm:p-6 lg:p-8">
<div className="max-w-screen-2xl mx-auto">
<header className="mb-8">
<div className="text-center">
<h1 className="text-4xl sm:text-5xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-sky-400 to-blue-600">
Gemini CLI Tool Generator
</h1>
<p className="mt-2 text-lg text-gray-400">
Создайте кастомные расширения, которые Gemini сможет вызывать для решения ваших задач.
</p>
</div>
<div className="text-center mt-4">
<Button onClick={loadNmapExample} variant="secondary">
Загрузить пример: Nmap Scanner
</Button>
</div>
</header>
<AiPrompt
prompt={aiPrompt}
setPrompt={setAiPrompt}
onGenerate={handleAiGenerate}
isLoading={isGenerating}
error={aiError}
/>
<main className="grid grid-cols-1 xl:grid-cols-2 gap-8 mt-8">
<ToolForm toolDefinition={toolDefinition} setToolDefinition={setToolDefinition} />
<CodeOutput generatedCode={generatedCode} toolName={toolDefinition.name} />
</main>
<footer className="text-center mt-12 py-4 border-t border-gray-700">
<p className="text-gray-500">
Разработано с использованием React, Tailwind CSS и Gemini.
</p>
</footer>
</div>
</div>
);
};
export default App;