Compare commits

...

2 Commits

14 changed files with 1044 additions and 63 deletions

View File

@ -25,58 +25,91 @@ namespace GTPCorrgir
KeyboardHelper pasteLogic = new KeyboardHelper();
private Window _mainWindow;
protected override async void OnStartup(StartupEventArgs e)
{
ShutdownMode = ShutdownMode.OnExplicitShutdown; // Añade esta línea
base.OnStartup(e);
_cancellationTokenSource = new CancellationTokenSource();
try
{
pasteLogic.SaveCurrentWindow();
if (Opciones.Instance.AutoCopy)
if (Opciones.Instance.modo == Opciones.modoDeUso.Menu)
{
await pasteLogic.AutoCopyFromActiveWindow();
_mainWindow = new ContextMenuWindow();
(_mainWindow as ContextMenuWindow).Closed += async (s, args) =>
{
if (Opciones.Instance.modo != Opciones.modoDeUso.Menu)
{
await InitializeSelectedMode();
}
else
{
Application.Current.Shutdown();
}
};
_mainWindow.Show();
}
if (System.Windows.Clipboard.ContainsText())
else
{
GTP.TextoACorregir = await ClipboardHelper.GetText();
}
if (string.IsNullOrEmpty(GTP.TextoACorregir))
{
GTP.Log.Log("No hay texto en el portapapeles");
ShowCustomNotification("Error", "No hay texto para procesar");
Application.Current.Shutdown();
return;
}
if (Opciones.Instance.modo == Opciones.modoDeUso.Corregir ||
Opciones.Instance.modo == Opciones.modoDeUso.Ortografia)
{
GTP.Log.Log("Iniciando proceso de corrección");
stopwatch.Start();
ShowCustomNotification("Espera", $"Corrigiendo texto con {Opciones.Instance.nombreDeLLM()}...");
IniciarCronometro();
// Ejecuta la tarea de corrección con timeout
_ = ProcessCorreccionWithTimeout();
}
else if (Opciones.Instance.modo == Opciones.modoDeUso.Chat)
{
var chatWindows = new Chat(GTP);
chatWindows.Show();
await InitializeSelectedMode();
}
}
catch (Exception ex)
{
GTP.Log.Log($"Error en OnStartup: {ex.Message}");
GTP.Log.Log($"StackTrace: {ex.StackTrace}");
ShowCustomNotification("Error", "Se produjo un error al iniciar la aplicación");
Application.Current.Shutdown();
}
}
private async Task InitializeSelectedMode()
{
switch (Opciones.Instance.modo)
{
case Opciones.modoDeUso.OCRaTexto:
_mainWindow = new ScreenCaptureWindow();
_mainWindow.Closed += (s, e) => Application.Current.Shutdown();
break;
case Opciones.modoDeUso.Chat:
_mainWindow = new Chat(GTP);
_mainWindow.Closed += (s, e) => Application.Current.Shutdown();
break;
case Opciones.modoDeUso.Corregir:
case Opciones.modoDeUso.Ortografia:
case Opciones.modoDeUso.Traducir_a_Espanol:
case Opciones.modoDeUso.Traducir_a_Ingles:
case Opciones.modoDeUso.Traducir_a_Italiano:
await HandleTextCorrection();
return;
}
if (_mainWindow != null)
_mainWindow.Show();
}
private async Task HandleTextCorrection()
{
if (Opciones.Instance.AutoCopy)
{
await pasteLogic.AutoCopyFromActiveWindow();
}
if (!System.Windows.Clipboard.ContainsText())
{
ShowCustomNotification("Error", "No hay texto para procesar");
Application.Current.Shutdown();
return;
}
GTP.TextoACorregir = await ClipboardHelper.GetText();
await ProcessCorreccionWithTimeout();
}
private async Task ProcessCorreccionWithTimeout()
{
try
@ -117,19 +150,22 @@ namespace GTPCorrgir
ShowCustomNotification("Se puede pegar",
$"Corrección en: {Math.Round(stopwatch.ElapsedMilliseconds / 1000.0, 1)} s");
if (Opciones.Instance.modo == Opciones.modoDeUso.Corregir)
if (Opciones.Instance.modo == Opciones.modoDeUso.Corregir || Opciones.Instance.modo == Opciones.modoDeUso.Ortografia ||
Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Espanol || Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Ingles ||
Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Italiano)
{
GTP.Log.Log("Ejecutando pegado automático");
await pasteLogic.RestoreAndSimulatePaste();
//GTP.Log.Log("Mostrando ventana de resultado");
// var resultadoWindow = new VentanaResultado(GTP.TextoCorregido);
// resultadoWindow.Show();
// await Task.Delay(1000);
}
else if (Opciones.Instance.modo == Opciones.modoDeUso.Ortografia)
{
GTP.Log.Log("Ejecutando pegado automático");
await pasteLogic.RestoreAndSimulatePaste();
if (Opciones.Instance.FuncionesOpcionales == Opciones.funcionesOpcionales.MostrarPopUp)
{
GTP.Log.Log("Mostrando ventana de resultado");
var resultadoWindow = new VentanaResultado(GTP.TextoCorregido);
resultadoWindow.Show();
await Task.Delay(1000);
}
else
{
GTP.Log.Log("Ejecutando pegado automático");
await pasteLogic.RestoreAndSimulatePaste();
}
}
}
else

94
ContextMenuWindow.xaml Normal file
View File

@ -0,0 +1,94 @@
<Window x:Class="GTPCorrgir.ContextMenuWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:GTPCorrgir" Title="Menu"
WindowStyle="None" AllowsTransparency="True" Background="Transparent" ShowInTaskbar="False" Topmost="True"
SizeToContent="WidthAndHeight" KeyDown="Window_KeyDown" Deactivated="Window_Deactivated">
<Window.Resources>
<local:NullToBooleanConverter x:Key="NullToBooleanConverter" />
<Style x:Key="CloseButtonStyle" TargetType="Button">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="#757575" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="FontSize" Value="14" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
<Border x:Name="border" Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2"
Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
<TextBlock Text="{TemplateBinding Content}" HorizontalAlignment="Center"
VerticalAlignment="Center" FontSize="{TemplateBinding FontSize}" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="#20000000" />
<Setter Property="Foreground" Value="#FF4081" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Background" Value="#40000000" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Border Background="#F5F5F5" BorderBrush="#CCCCCC" BorderThickness="1" CornerRadius="3" Margin="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Header con título y botón de cerrar -->
<Grid Grid.Row="0" Height="20" Margin="10,5,10,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="Modo de Uso:" FontWeight="Bold" Grid.Column="0" VerticalAlignment="Center" />
<Button x:Name="CloseButton" Content="✕" Click="CloseButton_Click" Grid.Column="1" Width="20"
Height="20" Style="{StaticResource CloseButtonStyle}" />
</Grid>
<!-- Contenido principal -->
<Grid Grid.Row="1" Margin="10,0,10,10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox x:Name="ModeListBox" Height="200" Grid.Row="0" Margin="0,0,0,10">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" Padding="5,3" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Row="1" Margin="0,0,0,10">
<TextBlock Text="Modelo LLM:" FontWeight="Bold" Margin="0,0,0,5" />
<ListBox x:Name="LLMListBox" Height="120">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" Padding="5,3" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
<Button Grid.Row="2" Content="Aceptar" Height="30" Width="80" HorizontalAlignment="Right"
Click="AcceptButton_Click" Margin="0,10,0,0">
<!-- ... resto del estilo del botón ... -->
</Button>
</Grid>
</Grid>
</Border>
</Window>

170
ContextMenuWindow.xaml.cs Normal file
View File

@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Data;
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
using MessageBox = System.Windows.MessageBox;
namespace GTPCorrgir
{
public class NullToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value != null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public partial class ContextMenuWindow : Window
{
public class MenuOption
{
public string DisplayName { get; set; }
public object Value { get; set; }
}
private readonly List<MenuOption> modeOptions;
private readonly List<MenuOption> llmOptions;
private bool optionsSelected = false;
public ContextMenuWindow()
{
// Initialize lists in constructor
modeOptions = new List<MenuOption>
{
new MenuOption { DisplayName = "Corregir texto", Value = Opciones.modoDeUso.Corregir },
new MenuOption { DisplayName = "Revisar ortografía", Value = Opciones.modoDeUso.Ortografia },
new MenuOption { DisplayName = "Chat", Value = Opciones.modoDeUso.Chat },
new MenuOption { DisplayName = "Traducir a inglés", Value = Opciones.modoDeUso.Traducir_a_Ingles },
new MenuOption { DisplayName = "Traducir a italiano", Value = Opciones.modoDeUso.Traducir_a_Italiano },
new MenuOption { DisplayName = "Traducir a español", Value = Opciones.modoDeUso.Traducir_a_Espanol },
new MenuOption { DisplayName = "OCR a texto", Value = Opciones.modoDeUso.OCRaTexto }
};
llmOptions = new List<MenuOption>
{
new MenuOption { DisplayName = "OpenAI", Value = Opciones.LLM_a_Usar.OpenAI },
new MenuOption { DisplayName = "Ollama", Value = Opciones.LLM_a_Usar.Ollama },
new MenuOption { DisplayName = "Groq", Value = Opciones.LLM_a_Usar.Groq },
new MenuOption { DisplayName = "Grok", Value = Opciones.LLM_a_Usar.Grok }
};
InitializeComponent();
InitializeControls();
LoadSavedSelections();
// Posicionar la ventana después de que se haya cargado completamente
this.Loaded += (s, e) => PositionWindowAtCursor();
}
private void InitializeControls()
{
ModeListBox.ItemsSource = modeOptions;
LLMListBox.ItemsSource = llmOptions;
}
private void LoadSavedSelections()
{
var savedSettings = MenuSettings.Instance;
var savedMode = modeOptions.FirstOrDefault(x =>
(Opciones.modoDeUso)x.Value == savedSettings.Options.LastUsedMode);
var savedLLM = llmOptions.FirstOrDefault(x =>
(Opciones.LLM_a_Usar)x.Value == savedSettings.Options.LastUsedLLM);
if (savedMode != null)
ModeListBox.SelectedItem = savedMode;
if (savedLLM != null)
LLMListBox.SelectedItem = savedLLM;
}
private void PositionWindowAtCursor()
{
var cursorPosition = System.Windows.Forms.Cursor.Position;
var screen = Screen.FromPoint(cursorPosition);
// Calculate position ensuring the window stays within screen bounds
double left = cursorPosition.X;
double top = cursorPosition.Y;
// Get the window size (waiting for it to be rendered)
this.UpdateLayout();
double windowWidth = this.ActualWidth;
double windowHeight = this.ActualHeight;
// Adjust position if window would go off screen
if (left + windowWidth > screen.WorkingArea.Right)
{
left = screen.WorkingArea.Right - windowWidth;
}
if (top + windowHeight > screen.WorkingArea.Bottom)
{
top = screen.WorkingArea.Bottom - windowHeight;
}
// Ensure window doesn't go off the left or top of the screen
left = Math.Max(screen.WorkingArea.Left, left);
top = Math.Max(screen.WorkingArea.Top, top);
this.Left = left;
this.Top = top;
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
this.Close();
}
}
private void Window_Deactivated(object sender, EventArgs e)
{
// Elimina este evento o modifícalo para que no cierre automáticamente
// if (!optionsSelected)
// {
// this.DialogResult = false;
// this.Close();
// }
}
private void AcceptButton_Click(object sender, RoutedEventArgs e)
{
if (ModeListBox.SelectedItem is MenuOption selectedMode &&
LLMListBox.SelectedItem is MenuOption selectedLLM)
{
UpdateOptionsAndClose(selectedMode, selectedLLM);
}
}
private void UpdateOptionsAndClose(MenuOption selectedMode, MenuOption selectedLLM)
{
optionsSelected = true;
var modeValue = (Opciones.modoDeUso)selectedMode.Value;
var llmValue = (Opciones.LLM_a_Usar)selectedLLM.Value;
Opciones.Instance.modo = modeValue;
Opciones.Instance.LLM = llmValue;
MenuSettings.Instance.UpdateLastUsedOptions(modeValue, llmValue);
// Eliminar DialogResult
this.Close();
}
}
}

View File

@ -24,6 +24,9 @@
<PackageReference Include="LanguageDetection" Version="1.2.0" />
<PackageReference Include="Markdown.Xaml" Version="1.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Drawing.Common" Version="9.0.1" />
<PackageReference Include="Tesseract" Version="5.2.0" />
<PackageReference Include="Tesseract.Drawing" Version="5.2.0" />
</ItemGroup>
<ItemGroup>
@ -36,6 +39,15 @@
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="tessdata\eng.traineddata">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="tessdata\ita.traineddata">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="tessdata\spa.traineddata">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -39,15 +39,19 @@ namespace GTPCorrgir
// Pequeña pausa para asegurar que la ventana tiene el foco
await Task.Delay(100);
// Simular Ctrl+A
keybd_event(VK_CONTROL, 0, KEYEVENTF_EXTENDEDKEY, UIntPtr.Zero);
keybd_event(VK_A, 0, KEYEVENTF_EXTENDEDKEY, UIntPtr.Zero);
keybd_event(VK_A, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, UIntPtr.Zero);
if (Opciones.Instance.FuncionesOpcionales == Opciones.funcionesOpcionales.CtrlA)
{
// Simular Ctrl+A
keybd_event(VK_CONTROL, 0, KEYEVENTF_EXTENDEDKEY, UIntPtr.Zero);
keybd_event(VK_A, 0, KEYEVENTF_EXTENDEDKEY, UIntPtr.Zero);
keybd_event(VK_A, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, UIntPtr.Zero);
keybd_event(VK_CONTROL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, UIntPtr.Zero);
}
// Pequeña pausa entre comandos
await Task.Delay(50);
// Simular Ctrl+C
keybd_event(VK_CONTROL, 0, KEYEVENTF_EXTENDEDKEY, UIntPtr.Zero);
keybd_event(VK_C, 0, KEYEVENTF_EXTENDEDKEY, UIntPtr.Zero);
keybd_event(VK_C, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, UIntPtr.Zero);
keybd_event(VK_CONTROL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, UIntPtr.Zero);

86
MenuSettings.cs Normal file
View File

@ -0,0 +1,86 @@
using System;
using System.IO;
using Newtonsoft.Json;
using System.Diagnostics;
namespace GTPCorrgir
{
public class MenuSettings
{
private static readonly string SettingsPath =
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "menusettings.json");
private static MenuSettings instance;
private static readonly object lockObject = new object();
public class DefaultOptions
{
public Opciones.modoDeUso LastUsedMode { get; set; } = Opciones.modoDeUso.Corregir;
public Opciones.LLM_a_Usar LastUsedLLM { get; set; } = Opciones.LLM_a_Usar.OpenAI;
}
public DefaultOptions Options { get; set; }
public MenuSettings()
{
Options = new DefaultOptions();
}
public static MenuSettings Instance
{
get
{
if (instance == null)
{
lock (lockObject)
{
instance ??= Load();
}
}
return instance;
}
}
private static MenuSettings Load()
{
try
{
if (File.Exists(SettingsPath))
{
string json = File.ReadAllText(SettingsPath);
var settings = JsonConvert.DeserializeObject<MenuSettings>(json);
if (settings != null)
{
return settings;
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"Error loading menu settings: {ex.Message}");
}
return new MenuSettings();
}
public void Save()
{
try
{
string json = JsonConvert.SerializeObject(this, Formatting.Indented);
File.WriteAllText(SettingsPath, json);
Debug.WriteLine("Menu settings saved successfully");
}
catch (Exception ex)
{
Debug.WriteLine($"Error saving menu settings: {ex.Message}");
}
}
public void UpdateLastUsedOptions(Opciones.modoDeUso mode, Opciones.LLM_a_Usar llm)
{
Options.LastUsedMode = mode;
Options.LastUsedLLM = llm;
Save();
}
}
}

198
OcrTextProcessor.cs Normal file
View File

@ -0,0 +1,198 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace GTPCorrgir
{
public class OcrTextProcessor : IDisposable
{
private readonly HttpClient _httpClient;
private string _openAiApiKey;
private bool _disposed;
public OcrTextProcessor()
{
_httpClient = new HttpClient();
InitializeProcessor();
}
private void InitializeProcessor()
{
try
{
LoadApiKey();
InitializeHttpClient();
}
catch (Exception ex)
{
throw new ApplicationException("Error initializing OCR Text Processor", ex);
}
}
private void LoadApiKey()
{
string configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json");
if (!File.Exists(configPath))
{
throw new FileNotFoundException("Configuration file (appsettings.json) not found.");
}
string jsonContent = File.ReadAllText(configPath);
var settings = JsonConvert.DeserializeObject<ApiSettings>(jsonContent);
_openAiApiKey = settings?.ApiKeys?.OpenAI;
if (string.IsNullOrEmpty(_openAiApiKey))
{
throw new ApplicationException("OpenAI API key is missing");
}
}
private void InitializeHttpClient()
{
_httpClient.Timeout = TimeSpan.FromSeconds(30);
_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
}
public async Task<string> ProcessOcrText(string ocrText)
{
try
{
// Correct OCR errors using LLM
string correctedText = await CorrectOcrErrors(ocrText);
//string correctedText = ocrText;
// Apply minimal markdown formatting
string formattedText = ApplyBasicFormatting(correctedText);
// Copiar al portapapeles usando ClipboardHelper
await ClipboardHelper.SetText(formattedText);
return formattedText;
}
catch (Exception ex)
{
throw new ApplicationException("Error processing OCR text", ex);
}
}
private async Task<string> CorrectOcrErrors(string text)
{
try
{
_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_openAiApiKey}");
var requestData = new
{
model = "gpt-4",
messages = new[]
{
new {
role = "system",
content = "You are an expert at correcting OCR errors. Fix common OCR mistakes like: confused characters (0/O, l/I, rn/m), broken words, and formatting issues. Preserve the original structure and meaning. Respond only with the corrected text in JSON format: {\"corrected_text\": \"your text here\"}"
},
new {
role = "user",
content = $"Please correct any OCR errors in this text: {text}"
}
}
};
var content = new StringContent(
JsonConvert.SerializeObject(requestData),
Encoding.UTF8,
"application/json"
);
using var response = await _httpClient.PostAsync("https://api.openai.com/v1/chat/completions", content);
var responseContent = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException($"Error calling OpenAI API: {response.StatusCode} - {responseContent}");
}
var responseData = JsonConvert.DeserializeObject<dynamic>(responseContent);
string correctedText = responseData.choices[0].message.content;
// Extract the actual text from the JSON response
var jsonResponse = JsonConvert.DeserializeObject<dynamic>(correctedText);
return jsonResponse.corrected_text.ToString();
}
catch (Exception ex)
{
throw new ApplicationException("Error correcting OCR text", ex);
}
}
private string ApplyBasicFormatting(string text)
{
var lines = text.Split('\n');
var result = new StringBuilder();
for (int i = 0; i < lines.Length; i++)
{
string currentLine = lines[i].Trim();
// Skip empty lines
if (string.IsNullOrWhiteSpace(currentLine))
{
result.AppendLine();
continue;
}
// Basic heading detection (ALL CAPS lines)
if (currentLine == currentLine.ToUpper() && currentLine.Length > 20)
{
result.AppendLine($"# {currentLine}");
}
// Basic list detection
else if (currentLine.StartsWith("•") || currentLine.StartsWith("*"))
{
result.AppendLine($"- {currentLine.Substring(1).Trim()}");
}
// Numbered list detection
else if (System.Text.RegularExpressions.Regex.IsMatch(currentLine, @"^\d+[\.\)]"))
{
result.AppendLine(currentLine);
}
// Normal text
else
{
result.AppendLine(currentLine);
}
}
return result.ToString().Trim();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_httpClient?.Dispose();
}
_disposed = true;
}
}
~OcrTextProcessor()
{
Dispose(false);
}
}
}

View File

@ -17,6 +17,14 @@ namespace GTPCorrgir
Grok
}
[Flags]
public enum funcionesOpcionales
{
None = 0,
CtrlA = 1 << 0, // Selecciona todo el texto en la ventana activa
MostrarPopUp = 1 << 1 // Muestra un pop-up con el resultado
}
public enum modoDeUso
{
Corregir,
@ -25,6 +33,8 @@ namespace GTPCorrgir
Traducir_a_Ingles,
Traducir_a_Italiano,
Traducir_a_Espanol,
OCRaTexto,
Menu,
}
public Dictionary<LLM_a_Usar, string> nombreLLM = new Dictionary<LLM_a_Usar, string>
@ -45,9 +55,13 @@ namespace GTPCorrgir
if (_instance == null)
{
_instance = new Opciones();
_instance.LLM = LLM_a_Usar.OpenAI;
_instance.modo = modoDeUso.Chat;
_instance.AutoCopy = false;
// Cargar opciones guardadas si existen
var savedSettings = MenuSettings.Instance;
_instance.LLM = savedSettings.Options.LastUsedLLM;
_instance.modo = savedSettings.Options.LastUsedMode;
_instance.FuncionesOpcionales = 0;
_instance.AutoCopy = true;
}
return _instance;
}
@ -55,6 +69,7 @@ namespace GTPCorrgir
public LLM_a_Usar LLM { get; set; }
public modoDeUso modo { get; set; }
public funcionesOpcionales FuncionesOpcionales { get; set; }
public string nombreDeLLM()
{
@ -86,20 +101,29 @@ namespace GTPCorrgir
else if (arg.Contains("OpenAI"))
Opciones.Instance.LLM = Opciones.LLM_a_Usar.OpenAI;
if (arg.Contains("CtrlA"))
Opciones.Instance.FuncionesOpcionales = Opciones.funcionesOpcionales.CtrlA;
if (arg.Contains("PopUp"))
Opciones.Instance.FuncionesOpcionales |= Opciones.funcionesOpcionales.MostrarPopUp;
if (arg.Contains("Chat"))
Opciones.Instance.modo = Opciones.modoDeUso.Chat;
else if (arg.Contains("Ortografia"))
if (arg.Contains("Ortografia"))
Opciones.Instance.modo = Opciones.modoDeUso.Ortografia;
else if (arg.Contains("Corregir"))
if (arg.Contains("Corregir"))
Opciones.Instance.modo = Opciones.modoDeUso.Corregir;
else if (arg.Contains("Traducir_a_Ingles"))
if (arg.Contains("Traducir_a_Ingles"))
Opciones.Instance.modo = Opciones.modoDeUso.Traducir_a_Ingles;
else if (arg.Contains("Traducir_a_Italiano"))
if (arg.Contains("Traducir_a_Italiano"))
Opciones.Instance.modo = Opciones.modoDeUso.Traducir_a_Italiano;
else if (arg.Contains("Traducir_a_Espanol"))
if (arg.Contains("Traducir_a_Espanol"))
Opciones.Instance.modo = Opciones.modoDeUso.Traducir_a_Espanol;
else if (arg.Contains("AutoCopy"))
if (arg.Contains("OCRaTexto"))
Opciones.Instance.modo = Opciones.modoDeUso.OCRaTexto;
if (arg.Contains("AutoCopy"))
Opciones.Instance.AutoCopy = true;
if (arg.Contains("Menu"))
Opciones.Instance.modo = Opciones.modoDeUso.Menu;
}
}

View File

@ -0,0 +1,8 @@
{
"profiles": {
"GTPCorrgir": {
"commandName": "Project",
"commandLineArgs": "--Menu"
}
}
}

348
ScreenCaptureWindow.cs Normal file
View File

@ -0,0 +1,348 @@
using System;
using System.IO;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Controls;
using System.Windows.Shapes;
using Tesseract;
using System.Threading.Tasks;
using Point = System.Windows.Point;
using Rect = System.Windows.Rect;
using Size = System.Windows.Size;
using Brushes = System.Windows.Media.Brushes;
using Cursors = System.Windows.Input.Cursors;
using Cursor = System.Windows.Input.Cursor;
using Color = System.Windows.Media.Color;
using Path = System.IO.Path;
using MessageBox = System.Windows.MessageBox;
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
using Rectangle = System.Windows.Shapes.Rectangle;
using System.Windows.Data;
using Binding = System.Windows.Data.Binding;
using Image = System.Windows.Controls.Image;
namespace GTPCorrgir
{
public class ScreenCaptureWindow : Window
{
private Point startPoint;
private Point currentPoint;
private bool hasFirstPoint;
private Rectangle selectionRectangle;
private Canvas overlayCanvas;
private readonly Cursor crosshairCursor = Cursors.Cross;
private bool isSelecting;
private TextBlock dimensionsText;
public ScreenCaptureWindow()
{
InitializeOverlayWindow();
}
private void InitializeOverlayWindow()
{
// Calcular el rectángulo virtual que engloba todas las pantallas
var virtualScreen = GetVirtualScreenBounds();
// Configurar la ventana principal
this.WindowStyle = WindowStyle.None;
this.ResizeMode = ResizeMode.NoResize;
this.AllowsTransparency = true;
this.Background = new SolidColorBrush(Color.FromArgb(128, 0, 0, 0));
this.ShowInTaskbar = false;
this.Topmost = true;
this.Cursor = crosshairCursor;
// Establecer la posición y tamaño para cubrir todas las pantallas
this.Left = virtualScreen.Left;
this.Top = virtualScreen.Top;
this.Width = virtualScreen.Width;
this.Height = virtualScreen.Height;
InitializeComponents();
SetupEventHandlers();
}
private System.Drawing.Rectangle GetVirtualScreenBounds()
{
var allScreens = System.Windows.Forms.Screen.AllScreens;
int minX = allScreens.Min(s => s.Bounds.Left);
int minY = allScreens.Min(s => s.Bounds.Top);
int maxX = allScreens.Max(s => s.Bounds.Right);
int maxY = allScreens.Max(s => s.Bounds.Bottom);
return new System.Drawing.Rectangle(minX, minY, maxX - minX, maxY - minY);
}
private void InitializeComponents()
{
overlayCanvas = new Canvas();
// Obtener el área virtual que engloba todas las pantallas
var virtualScreen = GetVirtualScreenBounds();
// Capturar toda el área de pantallas
var screenBitmap = CaptureScreen(virtualScreen.Left, virtualScreen.Top, virtualScreen.Width, virtualScreen.Height);
// Crear imagen con la captura
var screenImage = new Image
{
Source = screenBitmap,
Width = virtualScreen.Width,
Height = virtualScreen.Height,
Opacity = 0.3 // Esto oscurece toda la imagen
};
// Crear imagen para el área seleccionada (la misma captura pero opacidad normal)
var selectionImage = new Image
{
Source = screenBitmap,
Width = virtualScreen.Width,
Height = virtualScreen.Height,
Opacity = 1
};
// Crear el clip para la imagen de selección
var clipRectangle = new RectangleGeometry();
selectionImage.Clip = clipRectangle;
selectionImage.Visibility = Visibility.Collapsed;
// Rectángulo de selección (solo el borde)
selectionRectangle = new Rectangle
{
Stroke = new SolidColorBrush(Color.FromRgb(120, 255, 120)),
StrokeThickness = 1.5,
Fill = Brushes.Transparent,
Visibility = Visibility.Collapsed
};
// Agregar todos los elementos al canvas
overlayCanvas.Children.Add(screenImage);
overlayCanvas.Children.Add(selectionImage);
overlayCanvas.Children.Add(selectionRectangle);
// Actualizar el clip cuando se mueve el rectángulo
selectionRectangle.LayoutUpdated += (s, e) =>
{
if (selectionRectangle.Visibility == Visibility.Visible)
{
selectionImage.Visibility = Visibility.Visible;
clipRectangle.Rect = new Rect(
Canvas.GetLeft(selectionRectangle),
Canvas.GetTop(selectionRectangle),
selectionRectangle.Width,
selectionRectangle.Height
);
}
else
{
selectionImage.Visibility = Visibility.Collapsed;
}
};
this.Content = overlayCanvas;
}
private void UpdateSelectionRectangle(Point start, Point current)
{
double left = Math.Min(start.X, current.X);
double top = Math.Min(start.Y, current.Y);
double width = Math.Abs(current.X - start.X);
double height = Math.Abs(current.Y - start.Y);
Canvas.SetLeft(selectionRectangle, left);
Canvas.SetTop(selectionRectangle, top);
selectionRectangle.Width = width;
selectionRectangle.Height = height;
}
private void SetupEventHandlers()
{
this.MouseLeftButtonDown += Overlay_MouseLeftButtonDown;
this.MouseLeftButtonUp += Overlay_MouseLeftButtonUp;
this.MouseMove += Overlay_MouseMove;
this.KeyDown += Window_KeyDown;
}
private void Overlay_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (!hasFirstPoint)
{
// Primer punto
startPoint = e.GetPosition(overlayCanvas);
hasFirstPoint = true;
isSelecting = true;
selectionRectangle.Visibility = Visibility.Visible;
UpdateSelectionRectangle(startPoint, startPoint);
}
}
private void Overlay_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (hasFirstPoint && isSelecting)
{
isSelecting = false;
currentPoint = e.GetPosition(overlayCanvas);
double width = Math.Abs(currentPoint.X - startPoint.X);
double height = Math.Abs(currentPoint.Y - startPoint.Y);
const double MIN_SIZE = 5.0;
if (width < MIN_SIZE || height < MIN_SIZE)
{
selectionRectangle.Visibility = Visibility.Collapsed;
var notification = new notificacion();
notification.UpdateNotification("Selección muy pequeña",
"Por favor, seleccione un área más grande (mínimo 5x5 píxeles)");
notification.Show();
hasFirstPoint = false;
return;
}
selectionRectangle.Visibility = Visibility.Collapsed;
var processingNotification = new notificacion();
processingNotification.UpdateNotification("Procesando", "Analizando imagen seleccionada...");
processingNotification.Show();
CaptureAndProcessArea();
}
}
private void Overlay_MouseMove(object sender, MouseEventArgs e)
{
if (hasFirstPoint && isSelecting)
{
currentPoint = e.GetPosition(overlayCanvas);
UpdateSelectionRectangle(startPoint, currentPoint);
}
}
private BitmapSource CaptureScreen(double x, double y, double width, double height)
{
try
{
// Las coordenadas ya están en el espacio de la pantalla virtual
var screenPoint = new System.Drawing.Point((int)x, (int)y);
using (var screenBmp = new System.Drawing.Bitmap(
(int)width,
(int)height,
System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
using (var bmpGraphics = System.Drawing.Graphics.FromImage(screenBmp))
{
bmpGraphics.CopyFromScreen(
screenPoint.X,
screenPoint.Y,
0,
0,
new System.Drawing.Size((int)width, (int)height)
);
}
using (var memory = new MemoryStream())
{
screenBmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
memory.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
}
catch (Exception ex)
{
MessageBox.Show($"Error al capturar pantalla: {ex.Message}");
Console.WriteLine($"Stack trace: {ex.StackTrace}");
return null;
}
}
private async void CaptureAndProcessArea()
{
try
{
double x = Canvas.GetLeft(selectionRectangle);
double y = Canvas.GetTop(selectionRectangle);
double width = selectionRectangle.Width;
double height = selectionRectangle.Height;
// Ajustar las coordenadas al origen de la pantalla virtual
x += this.Left;
y += this.Top;
var screenCapture = CaptureScreen(x, y, width, height);
if (screenCapture == null) return;
// Resto del procesamiento OCR igual que antes
using (var memoryStream = new MemoryStream())
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(screenCapture));
encoder.Save(memoryStream);
memoryStream.Position = 0;
string tesseractPath = GetTesseractExecutablePath();
using (var engine = new TesseractEngine(tesseractPath, "eng", EngineMode.Default))
{
using (var img = Pix.LoadFromMemory(memoryStream.ToArray()))
{
var result = engine.Process(img);
string ocrText = result.GetText();
using (var textProcessor = new OcrTextProcessor())
{
string processedText = await textProcessor.ProcessOcrText(ocrText);
var notificationWindow = new notificacion();
notificationWindow.UpdateNotification("OCR Completado", "Texto copiado al portapapeles");
notificationWindow.Show();
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show($"Error processing capture: {ex.Message}",
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
this.Close();
}
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
this.Close();
}
}
private string GetTesseractExecutablePath()
{
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
string tessDataDir = Path.Combine(baseDir, "tessdata");
if (!Directory.Exists(tessDataDir))
{
throw new DirectoryNotFoundException(
$"No se encontró el directorio tessdata en {tessDataDir}. " +
"Asegúrate de que la carpeta tessdata existe y contiene los archivos de entrenamiento.");
}
return tessDataDir;
}
}
}

View File

@ -263,6 +263,7 @@ namespace GTPCorrgir
}
TextoCorregido = _markdownProcessor.RemoveTechnicalTermMarkers_IgnoreCase(TextoCorregido).Trim('"');
TextoCorregido = _markdownProcessor.RemoveDoubleBrackets(TextoCorregido);
}
private async Task SimularCorreccion()
@ -303,12 +304,12 @@ namespace GTPCorrgir
return Opciones.Instance.modo switch
{
Opciones.modoDeUso.Corregir =>
"You are an engineer working in industrial automation. Your task is to review texts and rewrite them in a simple and concise manner, making sure to preserve important technical terms and markdown language if present. Please rewrite the following text in " + IdiomaDetectado + " and respond in the following JSON format: { \"Rewritten_text\": \"Your text here\" }.",
"You are an engineer working in industrial automation. Your task is to review texts and rewrite them in a simple and concise manner. If you find words enclosed in double brackets [[like this]], preserve them exactly as they appear without any modifications. For all other technical terms, write them normally without adding any brackets or special formatting. Preserve any existing markdown language if present. Please rewrite the following text in " + IdiomaDetectado + " and respond in the following JSON format: { \"Rewritten_text\": \"Your text here\" }. Important: Do not add any new brackets to words that aren't already enclosed in double brackets.",
Opciones.modoDeUso.Ortografia =>
"Please check the following text for spelling errors and provide the corrected version. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find, making sure to preserve important technical terms and markdown language if present. Please write in " + IdiomaDetectado + " and respond in the following JSON format: { \"Rewritten_text\": \"Your text here\" }.",
"Please check the following text for spelling errors and provide the corrected version. Do not change the meaning or structure of the sentences. If you find words enclosed in double brackets [[like this]], preserve them exactly as they appear. For all other words, only correct spelling mistakes while preserving technical terms and any markdown language if present. Please write in " + IdiomaDetectado + " and respond in the following JSON format: { \"Rewritten_text\": \"Your text here\" }.",
_ => "You are an engineer working specialiazed industrial automation. Please answer the following question in " + IdiomaDetectado + " and respond in the following JSON format: { \"Reply_text\": \"Your text here\" }."
_ => "You are an engineer working specialized in industrial automation. If the question contains words in double brackets [[like this]], preserve them exactly as they appear. Please answer the following question in " + IdiomaDetectado + " and respond in the following JSON format: { \"Reply_text\": \"Your text here\" }."
};
}
@ -322,13 +323,13 @@ namespace GTPCorrgir
$"Please check the following text for spelling errors and provide the corrected version. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",
Opciones.modoDeUso.Traducir_a_Ingles =>
$"Please check the following text for spelling errors and provide the corrected version in English. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",
$"Please check the following text for spelling errors and provide the corrected version tranlated to English. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",
Opciones.modoDeUso.Traducir_a_Italiano =>
$"Please check the following text for spelling errors and provide the corrected version in Italian. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",
$"Please check the following text for spelling errors and provide the corrected version tranlated to Italian. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",
Opciones.modoDeUso.Traducir_a_Espanol =>
$"Please check the following text for spelling errors and provide the corrected version in Spanish. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",
$"Please check the following text for spelling errors and provide the corrected version tranlated to Spanish. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",
_ => texto
};

BIN
tessdata/eng.traineddata Normal file

Binary file not shown.

BIN
tessdata/ita.traineddata Normal file

Binary file not shown.

BIN
tessdata/spa.traineddata Normal file

Binary file not shown.