Enhance AIImages mod by adding cancellation support for image generation, improving user experience with localized strings for cancellation actions in English and Russian. Refactor service integration for better dependency management and update AIImages.dll to reflect these changes.

This commit is contained in:
Leonid Pershin
2025-10-26 19:10:45 +03:00
parent 3434927342
commit 02b0143186
11 changed files with 974 additions and 174 deletions

View File

@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AIImages.Helpers;
using AIImages.Models;
using AIImages.Services;
using AIImages.Settings;
using RimWorld;
using UnityEngine;
@@ -19,7 +21,11 @@ namespace AIImages
private static string widthBuffer;
private static string heightBuffer;
public static void DoSettingsWindowContents(Rect inRect, AIImagesModSettings settings)
public static void DoSettingsWindowContents(
Rect inRect,
AIImagesModSettings settings,
ServiceContainer serviceContainer
)
{
InitializeBuffers(settings);
@@ -29,7 +35,7 @@ namespace AIImages
Widgets.BeginScrollView(inRect, ref scrollPosition, viewRect);
listingStandard.Begin(viewRect);
DrawApiSettings(listingStandard, settings);
DrawApiSettings(listingStandard, settings, serviceContainer);
DrawGenerationSettings(listingStandard, settings);
DrawSamplerSchedulerSettings(listingStandard, settings);
DrawPromptsSettings(listingStandard, settings);
@@ -51,7 +57,8 @@ namespace AIImages
private static void DrawApiSettings(
Listing_Standard listingStandard,
AIImagesModSettings settings
AIImagesModSettings settings,
ServiceContainer serviceContainer
)
{
listingStandard.Label(
@@ -61,18 +68,36 @@ namespace AIImages
);
listingStandard.GapLine();
string oldEndpoint = settings.apiEndpoint;
listingStandard.Label("AIImages.Settings.ApiEndpoint".Translate() + ":");
settings.apiEndpoint = listingStandard.TextEntry(settings.apiEndpoint);
listingStandard.Gap(8f);
// Если endpoint изменился, пересоздаем API сервис
if (oldEndpoint != settings.apiEndpoint)
{
try
{
serviceContainer.RecreateApiService();
}
catch (Exception ex)
{
Log.Error($"[AI Images] Failed to recreate API service: {ex.Message}");
Messages.Message(
"Failed to update API endpoint. Check the log.",
MessageTypeDefOf.RejectInput
);
}
}
if (listingStandard.ButtonText("AIImages.Settings.TestConnection".Translate()))
{
_ = TestApiConnection(settings.apiEndpoint);
TestApiConnection(serviceContainer.ApiService, settings.apiEndpoint);
}
if (listingStandard.ButtonText("AIImages.Settings.LoadFromApi".Translate()))
{
_ = LoadAllFromApi(settings);
LoadAllFromApi(serviceContainer.ApiService, settings);
}
DrawModelDropdown(listingStandard, settings);
@@ -343,120 +368,112 @@ namespace AIImages
settings.savePath = listingStandard.TextEntry(settings.savePath);
}
private static async System.Threading.Tasks.Task TestApiConnection(string endpoint)
private static void TestApiConnection(
IStableDiffusionApiService apiService,
string endpoint
)
{
try
{
Log.Message($"[AI Images] Testing connection to {endpoint}...");
bool available = await AIImagesMod.ApiService.CheckApiAvailability(endpoint);
_ = AsyncHelper.RunAsync(
async () =>
{
Log.Message($"[AI Images] Testing connection to {endpoint}...");
bool available = await apiService.CheckApiAvailability(endpoint);
if (available)
{
Messages.Message(
"AIImages.Settings.ConnectionSuccess".Translate(),
MessageTypeDefOf.PositiveEvent
);
}
else
{
Messages.Message(
"AIImages.Settings.ConnectionFailed".Translate(),
MessageTypeDefOf.RejectInput
);
}
}
catch (Exception ex)
{
Messages.Message($"Error: {ex.Message}", MessageTypeDefOf.RejectInput);
}
if (available)
{
Messages.Message(
"AIImages.Settings.ConnectionSuccess".Translate(),
MessageTypeDefOf.PositiveEvent
);
}
else
{
Messages.Message(
"AIImages.Settings.ConnectionFailed".Translate(),
MessageTypeDefOf.RejectInput
);
}
},
"API Connection Test"
);
}
private static async System.Threading.Tasks.Task LoadAllFromApi(
private static void LoadAllFromApi(
IStableDiffusionApiService apiService,
AIImagesModSettings settings
)
{
try
{
Log.Message("[AI Images] Loading models, samplers and schedulers from API...");
// Загружаем модели
var models = await AIImagesMod.ApiService.GetAvailableModels(settings.apiEndpoint);
settings.availableModels = models;
// Загружаем семплеры
var samplers = await AIImagesMod.ApiService.GetAvailableSamplers(
settings.apiEndpoint
);
settings.availableSamplers = samplers;
// Загружаем schedulers
var schedulers = await AIImagesMod.ApiService.GetAvailableSchedulers(
settings.apiEndpoint
);
settings.availableSchedulers = schedulers;
int totalCount = models.Count + samplers.Count + schedulers.Count;
if (totalCount > 0)
_ = AsyncHelper.RunAsync(
async () =>
{
Messages.Message(
"AIImages.Settings.AllLoaded".Translate(
models.Count,
samplers.Count,
schedulers.Count
),
MessageTypeDefOf.PositiveEvent
);
Log.Message("[AI Images] Loading models, samplers and schedulers from API...");
// Автовыбор модели
if (
(
string.IsNullOrEmpty(settings.selectedModel)
|| !models.Contains(settings.selectedModel)
)
&& models.Count > 0
)
{
settings.selectedModel = models[0];
}
// Загружаем модели
var models = await apiService.GetAvailableModels(settings.apiEndpoint);
settings.availableModels = models;
// Автовыбор семплера
if (
(
string.IsNullOrEmpty(settings.selectedSampler)
|| !samplers.Contains(settings.selectedSampler)
)
&& samplers.Count > 0
)
{
settings.selectedSampler = samplers[0];
}
// Загружаем семплеры
var samplers = await apiService.GetAvailableSamplers(settings.apiEndpoint);
settings.availableSamplers = samplers;
// Автовыбор scheduler
if (
(
string.IsNullOrEmpty(settings.selectedScheduler)
|| !schedulers.Contains(settings.selectedScheduler)
)
&& schedulers.Count > 0
)
// Загружаем schedulers
var schedulers = await apiService.GetAvailableSchedulers(settings.apiEndpoint);
settings.availableSchedulers = schedulers;
int totalCount = models.Count + samplers.Count + schedulers.Count;
if (totalCount > 0)
{
settings.selectedScheduler = schedulers[0];
ShowSuccessMessage(models.Count, samplers.Count, schedulers.Count);
AutoSelectDefaults(settings, models, samplers, schedulers);
}
}
else
{
Messages.Message(
"AIImages.Settings.NothingLoaded".Translate(),
MessageTypeDefOf.RejectInput
);
}
}
catch (Exception ex)
else
{
Messages.Message(
"AIImages.Settings.NothingLoaded".Translate(),
MessageTypeDefOf.RejectInput
);
}
},
"Load API Data"
);
}
private static void ShowSuccessMessage(int modelCount, int samplerCount, int schedulerCount)
{
Messages.Message(
"AIImages.Settings.AllLoaded".Translate(modelCount, samplerCount, schedulerCount),
MessageTypeDefOf.PositiveEvent
);
}
private static void AutoSelectDefaults(
AIImagesModSettings settings,
List<string> models,
List<string> samplers,
List<string> schedulers
)
{
AutoSelectIfNeeded(ref settings.selectedModel, models, settings.selectedModel);
AutoSelectIfNeeded(ref settings.selectedSampler, samplers, settings.selectedSampler);
AutoSelectIfNeeded(
ref settings.selectedScheduler,
schedulers,
settings.selectedScheduler
);
}
private static void AutoSelectIfNeeded(
ref string selectedValue,
List<string> availableValues,
string currentValue
)
{
bool needsSelection =
string.IsNullOrEmpty(currentValue) || !availableValues.Contains(currentValue);
if (needsSelection && availableValues.Count > 0)
{
Messages.Message(
$"Error loading from API: {ex.Message}",
MessageTypeDefOf.RejectInput
);
selectedValue = availableValues[0];
}
}
}