From 9c075174bd32633b9bb61d3dd1281f15d40748fe Mon Sep 17 00:00:00 2001 From: Dave Friedel Date: Wed, 10 Dec 2025 21:59:12 -0500 Subject: [PATCH] Initial --- .gitignore | 103 + API_Reference.md | 1107 ++++++++ README.md | 445 +++ Replicate.Maui.slnx | 4 + .../Controls/ButtonConfiguration.cs | 517 ++++ .../Controls/ConfigurableButton.xaml | 10 + .../Controls/ConfigurableButton.xaml.cs | 230 ++ .../Controls/ReplicateTransformerView.xaml | 444 +++ .../Controls/ReplicateTransformerView.xaml.cs | 2448 +++++++++++++++++ Replicate.Maui/EventArgs.cs | 177 ++ Replicate.Maui/Exceptions.cs | 23 + Replicate.Maui/IReplicateTransformer.cs | 426 +++ .../Localization/ReplicateStrings.cs | 608 ++++ Replicate.Maui/ModelPresets.cs | 410 +++ Replicate.Maui/PredictionTracker.cs | 440 +++ Replicate.Maui/Replicate.Maui.csproj | 88 + Replicate.Maui/ReplicateSettings.cs | 72 + Replicate.Maui/ReplicateTransformer.cs | 522 ++++ Replicate.Maui/ReplicateTransformerFactory.cs | 109 + Replicate.Maui/ServiceCollectionExtensions.cs | 125 + Replicate.Maui/Services/FileSaveService.cs | 154 ++ Replicate.Maui/Services/IFileSaveService.cs | 98 + Replicate.Maui/TransformerState.cs | 81 + .../ViewModels/TransformerViewModel.cs | 731 +++++ Replicate.Maui/icon.png | Bin 0 -> 1209 bytes Test.Replicate/App.xaml | 14 + Test.Replicate/App.xaml.cs | 29 + Test.Replicate/AppShell.xaml | 35 + Test.Replicate/AppShell.xaml.cs | 10 + Test.Replicate/MauiProgram.cs | 44 + Test.Replicate/Pages/HistoryPage.xaml | 115 + Test.Replicate/Pages/HistoryPage.xaml.cs | 142 + Test.Replicate/Pages/ImageTransformPage.xaml | 126 + .../Pages/ImageTransformPage.xaml.cs | 249 ++ Test.Replicate/Pages/SettingsPage.xaml | 101 + Test.Replicate/Pages/SettingsPage.xaml.cs | 68 + Test.Replicate/Pages/VideoGenerationPage.xaml | 170 ++ .../Pages/VideoGenerationPage.xaml.cs | 329 +++ .../Platforms/Android/AndroidManifest.xml | 6 + .../Platforms/Android/MainActivity.cs | 11 + .../Platforms/Android/MainApplication.cs | 16 + .../Android/Resources/values/colors.xml | 6 + .../Platforms/MacCatalyst/AppDelegate.cs | 10 + .../Platforms/MacCatalyst/Entitlements.plist | 14 + .../Platforms/MacCatalyst/Info.plist | 38 + .../Platforms/MacCatalyst/Program.cs | 16 + Test.Replicate/Platforms/Tizen/Main.cs | 17 + .../Platforms/Tizen/tizen-manifest.xml | 15 + Test.Replicate/Platforms/Windows/App.xaml | 8 + Test.Replicate/Platforms/Windows/App.xaml.cs | 25 + .../Platforms/Windows/Package.appxmanifest | 46 + Test.Replicate/Platforms/Windows/app.manifest | 15 + Test.Replicate/Platforms/iOS/AppDelegate.cs | 10 + Test.Replicate/Platforms/iOS/Info.plist | 32 + Test.Replicate/Platforms/iOS/Program.cs | 16 + .../iOS/Resources/PrivacyInfo.xcprivacy | 51 + Test.Replicate/Properties/launchSettings.json | 8 + Test.Replicate/Resources/AppIcon/appicon.svg | 4 + .../Resources/AppIcon/appiconfg.svg | 8 + .../Resources/Fonts/OpenSans-Regular.ttf | Bin 0 -> 107304 bytes .../Resources/Fonts/OpenSans-Semibold.ttf | Bin 0 -> 111180 bytes .../Resources/Images/dotnet_bot.png | Bin 0 -> 93437 bytes Test.Replicate/Resources/Raw/AboutAssets.txt | 15 + Test.Replicate/Resources/Splash/splash.svg | 8 + Test.Replicate/Resources/Styles/Colors.xaml | 45 + Test.Replicate/Resources/Styles/Styles.xaml | 440 +++ Test.Replicate/Services/AppSettings.cs | 116 + Test.Replicate/Services/HistoryService.cs | 94 + Test.Replicate/Test.Replicate.csproj | 71 + 69 files changed, 11965 insertions(+) create mode 100644 .gitignore create mode 100644 API_Reference.md create mode 100644 README.md create mode 100644 Replicate.Maui.slnx create mode 100644 Replicate.Maui/Controls/ButtonConfiguration.cs create mode 100644 Replicate.Maui/Controls/ConfigurableButton.xaml create mode 100644 Replicate.Maui/Controls/ConfigurableButton.xaml.cs create mode 100644 Replicate.Maui/Controls/ReplicateTransformerView.xaml create mode 100644 Replicate.Maui/Controls/ReplicateTransformerView.xaml.cs create mode 100644 Replicate.Maui/EventArgs.cs create mode 100644 Replicate.Maui/Exceptions.cs create mode 100644 Replicate.Maui/IReplicateTransformer.cs create mode 100644 Replicate.Maui/Localization/ReplicateStrings.cs create mode 100644 Replicate.Maui/ModelPresets.cs create mode 100644 Replicate.Maui/PredictionTracker.cs create mode 100644 Replicate.Maui/Replicate.Maui.csproj create mode 100644 Replicate.Maui/ReplicateSettings.cs create mode 100644 Replicate.Maui/ReplicateTransformer.cs create mode 100644 Replicate.Maui/ReplicateTransformerFactory.cs create mode 100644 Replicate.Maui/ServiceCollectionExtensions.cs create mode 100644 Replicate.Maui/Services/FileSaveService.cs create mode 100644 Replicate.Maui/Services/IFileSaveService.cs create mode 100644 Replicate.Maui/TransformerState.cs create mode 100644 Replicate.Maui/ViewModels/TransformerViewModel.cs create mode 100644 Replicate.Maui/icon.png create mode 100644 Test.Replicate/App.xaml create mode 100644 Test.Replicate/App.xaml.cs create mode 100644 Test.Replicate/AppShell.xaml create mode 100644 Test.Replicate/AppShell.xaml.cs create mode 100644 Test.Replicate/MauiProgram.cs create mode 100644 Test.Replicate/Pages/HistoryPage.xaml create mode 100644 Test.Replicate/Pages/HistoryPage.xaml.cs create mode 100644 Test.Replicate/Pages/ImageTransformPage.xaml create mode 100644 Test.Replicate/Pages/ImageTransformPage.xaml.cs create mode 100644 Test.Replicate/Pages/SettingsPage.xaml create mode 100644 Test.Replicate/Pages/SettingsPage.xaml.cs create mode 100644 Test.Replicate/Pages/VideoGenerationPage.xaml create mode 100644 Test.Replicate/Pages/VideoGenerationPage.xaml.cs create mode 100644 Test.Replicate/Platforms/Android/AndroidManifest.xml create mode 100644 Test.Replicate/Platforms/Android/MainActivity.cs create mode 100644 Test.Replicate/Platforms/Android/MainApplication.cs create mode 100644 Test.Replicate/Platforms/Android/Resources/values/colors.xml create mode 100644 Test.Replicate/Platforms/MacCatalyst/AppDelegate.cs create mode 100644 Test.Replicate/Platforms/MacCatalyst/Entitlements.plist create mode 100644 Test.Replicate/Platforms/MacCatalyst/Info.plist create mode 100644 Test.Replicate/Platforms/MacCatalyst/Program.cs create mode 100644 Test.Replicate/Platforms/Tizen/Main.cs create mode 100644 Test.Replicate/Platforms/Tizen/tizen-manifest.xml create mode 100644 Test.Replicate/Platforms/Windows/App.xaml create mode 100644 Test.Replicate/Platforms/Windows/App.xaml.cs create mode 100644 Test.Replicate/Platforms/Windows/Package.appxmanifest create mode 100644 Test.Replicate/Platforms/Windows/app.manifest create mode 100644 Test.Replicate/Platforms/iOS/AppDelegate.cs create mode 100644 Test.Replicate/Platforms/iOS/Info.plist create mode 100644 Test.Replicate/Platforms/iOS/Program.cs create mode 100644 Test.Replicate/Platforms/iOS/Resources/PrivacyInfo.xcprivacy create mode 100644 Test.Replicate/Properties/launchSettings.json create mode 100644 Test.Replicate/Resources/AppIcon/appicon.svg create mode 100644 Test.Replicate/Resources/AppIcon/appiconfg.svg create mode 100644 Test.Replicate/Resources/Fonts/OpenSans-Regular.ttf create mode 100644 Test.Replicate/Resources/Fonts/OpenSans-Semibold.ttf create mode 100644 Test.Replicate/Resources/Images/dotnet_bot.png create mode 100644 Test.Replicate/Resources/Raw/AboutAssets.txt create mode 100644 Test.Replicate/Resources/Splash/splash.svg create mode 100644 Test.Replicate/Resources/Styles/Colors.xaml create mode 100644 Test.Replicate/Resources/Styles/Styles.xaml create mode 100644 Test.Replicate/Services/AppSettings.cs create mode 100644 Test.Replicate/Services/HistoryService.cs create mode 100644 Test.Replicate/Test.Replicate.csproj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ffb5dd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,103 @@ +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio +.vs/ +*.suo +*.user +*.userosscache +*.sln.docstates +*.rsuser +*.userprefs + +# Visual Studio Code +.vscode/ + +# Rider +.idea/ + +# NuGet +*.nupkg +*.snupkg +project.lock.json +project.fragment.lock.json +artifacts/ + +# MSBuild +*.csproj.user +*.vbproj.user + +# Windows +[Tt]humbs.db +ehthumbs.db +Desktop.ini +$RECYCLE.BIN/ + +# macOS +.DS_Store +.AppleDouble +.LSOverride +._* + +# MAUI / Xamarin +*.ipa +*.dSYM.zip +*.dSYM +*.apk +*.aab + +# Test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* +*.trx + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# Resharper +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JetBrains Rider +*.sln.iml + +# TeamCity +.TeamCity* + +# DotCover +*.dotCover + +# Publish profiles +*.pubxml +*.publishproj + +# Backup files +*~ +*.bak +*.swp + +# Local app settings +appsettings.*.json +!appsettings.json +!appsettings.Development.json + +# Secrets +*.pfx +*.snk +secrets.json diff --git a/API_Reference.md b/API_Reference.md new file mode 100644 index 0000000..4730a58 --- /dev/null +++ b/API_Reference.md @@ -0,0 +1,1107 @@ +# MarketAlly.Replicate.Maui - API Reference + +Complete API documentation for the MarketAlly.Replicate.Maui library. + +## Table of Contents + +- [Namespaces](#namespaces) +- [Core API Classes](#core-api-classes) + - [IReplicateTransformer](#ireplicatetransformer) + - [ReplicateTransformer](#replicatetransformer) + - [IReplicateTransformerFactory](#ireplicatetransformerfactory) + - [ReplicateSettings](#replicatesettings) +- [Model Presets](#model-presets) + - [ModelPresets](#modelpresets-static-class) + - [ModelPreset](#modelpreset) +- [Prediction Classes](#prediction-classes) + - [PredictionResult](#predictionresult) + - [PredictionOptions](#predictionoptions) + - [PredictionStatus](#predictionstatus) + - [PredictionMetrics](#predictionmetrics) +- [Prediction Tracking](#prediction-tracking) + - [IPredictionTracker](#ipredictiontracker) + - [PredictionTracker](#predictiontracker) + - [TrackedPrediction](#trackedprediction) +- [MAUI Controls](#maui-controls) + - [ReplicateTransformerView](#replicatetransformerview) + - [TransformerLayoutMode](#transformerlayoutmode) + - [TransformerState](#transformerstate) +- [Button Configuration](#button-configuration) + - [ButtonConfig](#buttonconfig) + - [TransformerButtonConfigs](#transformerbuttonconfigs) + - [ButtonDisplayMode](#buttondisplaymode) + - [OverlayButtonPosition](#overlaybuttonposition) +- [Localization](#localization) + - [ReplicateStrings](#replicatestrings) +- [Events](#events) +- [Exceptions](#exceptions) +- [Extension Methods](#extension-methods) + +--- + +## Namespaces + +| Namespace | Description | Available In | +|-----------|-------------|--------------| +| `MarketAlly.Replicate.Maui` | Core API classes, interfaces, settings, and presets | net9.0, MAUI | +| `MarketAlly.Replicate.Maui.Controls` | MAUI UI controls and button configuration | MAUI only | +| `MarketAlly.Replicate.Maui.Services` | File save service | MAUI only | +| `MarketAlly.Replicate.Maui.ViewModels` | MVVM view models | MAUI only | +| `MarketAlly.Replicate.Maui.Localization` | Localization support | MAUI only | + +--- + +## Core API Classes + +### IReplicateTransformer + +Interface for Replicate API image and video transformations. + +```csharp +public interface IReplicateTransformer +``` + +#### Events + +| Event | Type | Description | +|-------|------|-------------| +| `PredictionCreated` | `EventHandler` | Raised immediately when a prediction is created (before polling starts) | + +#### Methods + +##### Image Transformation + +```csharp +Task TransformToAnimeAsync( + byte[] imageBytes, + string? customPrompt = null, + PredictionOptions? options = null, + CancellationToken cancellationToken = default); +``` +Transform an image to anime style using raw bytes. + +```csharp +Task TransformToAnimeFromBase64Async( + string base64Image, + string? customPrompt = null, + PredictionOptions? options = null, + CancellationToken cancellationToken = default); +``` +Transform an image to anime style using base64 encoded string. + +```csharp +Task TransformToAnimeFromUrlAsync( + string imageUrl, + string? customPrompt = null, + PredictionOptions? options = null, + CancellationToken cancellationToken = default); +``` +Transform an image to anime style from a URL. + +##### Video Generation + +```csharp +Task TransformToVideoAsync( + byte[] imageBytes, + string? customPrompt = null, + PredictionOptions? options = null, + CancellationToken cancellationToken = default); +``` +Generate a video from an image using raw bytes. + +```csharp +Task TransformToVideoFromBase64Async( + string base64Image, + string? customPrompt = null, + PredictionOptions? options = null, + CancellationToken cancellationToken = default); +``` +Generate a video from a base64 encoded image. + +```csharp +Task TransformToVideoFromUrlAsync( + string imageUrl, + string? customPrompt = null, + PredictionOptions? options = null, + CancellationToken cancellationToken = default); +``` +Generate a video from an image URL. + +##### Model Presets + +```csharp +Task RunPresetAsync( + ModelPreset preset, + byte[] imageBytes, + string? customPrompt = null, + Dictionary? customParameters = null, + PredictionOptions? options = null, + CancellationToken cancellationToken = default); +``` +Run a prediction using a model preset with image bytes. + +```csharp +Task RunPresetFromBase64Async( + ModelPreset preset, + string base64Image, + string? customPrompt = null, + Dictionary? customParameters = null, + PredictionOptions? options = null, + CancellationToken cancellationToken = default); +``` +Run a prediction using a model preset with base64 image. + +```csharp +Task RunPresetFromUrlAsync( + ModelPreset preset, + string imageUrl, + string? customPrompt = null, + Dictionary? customParameters = null, + PredictionOptions? options = null, + CancellationToken cancellationToken = default); +``` +Run a prediction using a model preset with image URL. + +```csharp +Task RunPresetTextOnlyAsync( + ModelPreset preset, + string prompt, + Dictionary? customParameters = null, + PredictionOptions? options = null, + CancellationToken cancellationToken = default); +``` +Run a text-only prediction using a model preset (no image input). + +##### Prediction Management + +```csharp +Task CancelPredictionAsync( + string predictionId, + CancellationToken cancellationToken = default); +``` +Cancel a running prediction. + +```csharp +Task GetPredictionAsync( + string predictionId, + CancellationToken cancellationToken = default); +``` +Get the status of a prediction. + +--- + +### ReplicateTransformer + +Default implementation of `IReplicateTransformer`. + +```csharp +public class ReplicateTransformer : IReplicateTransformer +``` + +#### Constructor + +```csharp +public ReplicateTransformer( + HttpClient httpClient, + IOptions settings, + ILogger? logger = null) +``` + +| Parameter | Type | Description | +|-----------|------|-------------| +| `httpClient` | `HttpClient` | HTTP client for API requests | +| `settings` | `IOptions` | Configuration settings | +| `logger` | `ILogger?` | Optional logger | + +--- + +### IReplicateTransformerFactory + +Factory for creating `IReplicateTransformer` instances with custom API tokens. Useful for BYOK (Bring Your Own Key) scenarios. + +```csharp +public interface IReplicateTransformerFactory +``` + +#### Methods + +```csharp +IReplicateTransformer Create(); +``` +Creates a transformer using the default configured settings. + +```csharp +IReplicateTransformer CreateWithToken(string apiToken); +``` +Creates a transformer with a custom API token. + +```csharp +IReplicateTransformer CreateWithSettings(Action configure); +``` +Creates a transformer with fully custom settings. + +--- + +### ReplicateSettings + +Configuration settings for the Replicate API. + +```csharp +public class ReplicateSettings +``` + +#### Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `ApiToken` | `string` | `""` | Replicate API token | +| `ApiUrl` | `string` | `"https://api.replicate.com/v1/predictions"` | API endpoint URL | +| `AuthScheme` | `string` | `"Token"` | Authentication scheme ("Bearer" or "Token") | +| `ModelVersion` | `string` | `""` | Default model version for image transformation | +| `ModelName` | `string` | `""` | Default model name | +| `TimeoutSeconds` | `int` | `300` | Timeout for image predictions | +| `PollingDelayMs` | `int` | `1500` | Delay between status polls | +| `ImagePrompt` | `string` | `"anime style portrait..."` | Default image transformation prompt | +| `VideoModelName` | `string` | `""` | Default video model name | +| `VideoModelVersion` | `string` | `""` | Default video model version | +| `VideoPrompt` | `string` | `"animate and loop..."` | Default video generation prompt | +| `VideoTimeoutSeconds` | `int` | `600` | Timeout for video predictions | +| `VideoPollingDelayMs` | `int` | `3000` | Polling delay for video predictions | +| `DefaultSettings` | `ImageTransformSettings` | `new()` | Default image transform parameters | +| `VideoSettings` | `VideoTransformSettings` | `new()` | Default video transform parameters | + +#### ImageTransformSettings + +```csharp +public class ImageTransformSettings +{ + public int Seed { get; set; } = 42; + public double GuidanceScale { get; set; } = 9; + public double Strength { get; set; } = 0.8; + public int NumInferenceSteps { get; set; } = 30; +} +``` + +#### VideoTransformSettings + +```csharp +public class VideoTransformSettings +{ + public int? Seed { get; set; } + public int Duration { get; set; } = 5; + public string Size { get; set; } = "1280*720"; + public string? AudioUrl { get; set; } + public string? NegativePrompt { get; set; } + public bool EnablePromptExpansion { get; set; } = true; +} +``` + +--- + +## Model Presets + +### ModelPresets (Static Class) + +Predefined model presets for popular Replicate models. + +```csharp +public static class ModelPresets +``` + +#### Image Model Properties + +| Property | Model | Description | +|----------|-------|-------------| +| `HiDreamE1` | prunaai/hidream-e1.1 | Fast high-quality image generation/editing | +| `StableDiffusionXL` | stability-ai/sdxl | Popular open-source image generation | +| `IdeogramV3Turbo` | ideogram-ai/ideogram-v3-turbo | Best for images with realistic text | +| `RecraftV3Svg` | recraft-ai/recraft-v3-svg | High-quality SVG images, logos, icons | + +#### Video Model Properties + +| Property | Model | Description | +|----------|-------|-------------| +| `GoogleVeo3Fast` | google/veo-3-fast | State-of-the-art video generation | +| `Kling25TurboPro` | kwaivgi/kling-v2.5-turbo-pro | Latest high-quality video | +| `Kling16Pro` | kwaivgi/kling-v1.6-pro | Professional video generation | +| `SeedancePro` | bytedance/seedance-1-pro | High-quality image-to-video | +| `LumaRayFlash` | luma/ray-flash-2-720p | Fast video with camera control | +| `Wan21` | alibaba/wan-2.1-i2v-480p | Image-to-video 480p/720p | +| `MiniMaxVideo` | minimax/video-01 | Text and image to video | + +#### Collection Properties + +```csharp +public static IReadOnlyList All { get; } +public static IReadOnlyList ImageModels { get; } +public static IReadOnlyList VideoModels { get; } +``` + +#### Methods + +```csharp +public static ModelPreset? FindByName(string modelName) +``` +Find a preset by model name or display name. + +--- + +### ModelPreset + +A preset configuration for a specific Replicate model. + +```csharp +public class ModelPreset +``` + +#### Properties + +| Property | Type | Description | +|----------|------|-------------| +| `Name` | `string` | Display name for the preset | +| `ModelName` | `string` | Replicate model identifier (owner/model-name) | +| `ModelVersion` | `string` | Specific model version hash | +| `Type` | `ModelType` | Output type (Image or Video) | +| `DefaultPrompt` | `string` | Default prompt for this model | +| `DefaultParameters` | `Dictionary` | Default API parameters | +| `ImageInputKey` | `string` | Key name for image input (default: "image") | +| `PromptKey` | `string` | Key name for prompt (default: "prompt") | +| `TimeoutSeconds` | `int` | Timeout in seconds | +| `PollingDelayMs` | `int` | Polling delay in milliseconds | + +#### Methods + +```csharp +public Dictionary CloneParameters() +``` +Create a copy of the default parameters. + +```csharp +public Dictionary BuildInput( + string? prompt = null, + string? imageData = null, + Dictionary? customParameters = null) +``` +Build the input dictionary for an API call. + +--- + +### ModelType + +```csharp +public enum ModelType +{ + Image, + Video +} +``` + +--- + +## Prediction Classes + +### PredictionResult + +Result of a prediction. + +```csharp +public class PredictionResult +``` + +#### Properties + +| Property | Type | Description | +|----------|------|-------------| +| `Id` | `string` | Unique prediction identifier | +| `Status` | `string` | Status string: starting, processing, succeeded, failed, canceled | +| `StatusEnum` | `PredictionStatus` | Strongly-typed status enum | +| `Output` | `string?` | Output URL if succeeded | +| `Outputs` | `string[]?` | All output URLs if multiple outputs | +| `Error` | `string?` | Error message if failed | +| `Metrics` | `PredictionMetrics?` | Performance metrics | +| `CreatedAt` | `DateTimeOffset?` | When the prediction was created | +| `StartedAt` | `DateTimeOffset?` | When processing started | +| `CompletedAt` | `DateTimeOffset?` | When the prediction completed | +| `CancelUrl` | `string?` | URL to cancel the prediction | + +#### Computed Properties + +| Property | Type | Description | +|----------|------|-------------| +| `IsCompleted` | `bool` | Whether completed (succeeded, failed, or canceled) | +| `IsSucceeded` | `bool` | Whether succeeded | +| `IsPending` | `bool` | Whether pending (starting or processing) | +| `IsFailed` | `bool` | Whether failed | +| `IsCanceled` | `bool` | Whether canceled | + +--- + +### PredictionOptions + +Options for creating a prediction. + +```csharp +public class PredictionOptions +``` + +#### Properties + +| Property | Type | Description | +|----------|------|-------------| +| `WebhookUrl` | `string?` | Webhook URL to receive prediction updates | +| `WebhookEventsFilter` | `string[]?` | Filter events: "start", "output", "logs", "completed" | +| `SyncModeWaitSeconds` | `int?` | Sync mode wait time (1-60 seconds), null for async | +| `WebhookOnly` | `bool` | If true with sync mode, don't poll - return immediately | + +--- + +### PredictionStatus + +```csharp +public enum PredictionStatus +{ + Starting, // Prediction is starting up + Processing, // Prediction is actively processing + Succeeded, // Prediction completed successfully + Failed, // Prediction failed with an error + Canceled, // Prediction was canceled + Unknown // Unknown or unrecognized status +} +``` + +#### Extension Methods + +```csharp +public static PredictionStatus ToPredictionStatus(this string? status) +public static string ToApiString(this PredictionStatus status) +public static bool IsCompleted(this PredictionStatus status) +public static bool IsPending(this PredictionStatus status) +``` + +--- + +### PredictionMetrics + +```csharp +public class PredictionMetrics +{ + public double? PredictTime { get; set; } // Time running the model (seconds) + public double? TotalTime { get; set; } // Total time including queue (seconds) +} +``` + +--- + +## Prediction Tracking + +### IPredictionTracker + +Interface for tracking prediction history and monitoring pending predictions. + +```csharp +public interface IPredictionTracker : IDisposable +``` + +#### Properties + +| Property | Type | Description | +|----------|------|-------------| +| `PollingIntervalMs` | `int` | Interval between status checks (default: 3000) | +| `MaxHistorySize` | `int` | Maximum predictions to keep (default: 50) | +| `History` | `IReadOnlyList` | All tracked predictions (newest first) | +| `PendingPredictions` | `IReadOnlyList` | Pending predictions only | + +#### Events + +| Event | Type | Description | +|-------|------|-------------| +| `PredictionStatusChanged` | `EventHandler` | Status changed | +| `PredictionCompleted` | `EventHandler` | Prediction completed | + +#### Methods + +```csharp +TrackedPrediction Track(PredictionResult result, TransformationType type, byte[]? sourceImageBytes = null) +TrackedPrediction? Get(string predictionId) +Task RefreshAsync(string predictionId, CancellationToken cancellationToken = default) +void ClearCompleted() +void ClearAll() +``` + +--- + +### TrackedPrediction + +A tracked prediction with history information. + +```csharp +public class TrackedPrediction +``` + +#### Properties + +| Property | Type | Description | +|----------|------|-------------| +| `Id` | `string` | Prediction ID | +| `Type` | `TransformationType` | Image or Video | +| `Status` | `string` | Current status string | +| `StatusEnum` | `PredictionStatus` | Strongly-typed status | +| `Output` | `string?` | Output URL | +| `Outputs` | `string[]?` | All output URLs | +| `Error` | `string?` | Error message | +| `Metrics` | `PredictionMetrics?` | Performance metrics | +| `CreatedAt` | `DateTimeOffset` | Creation time | +| `StartedAt` | `DateTimeOffset?` | Start time | +| `CompletedAt` | `DateTimeOffset?` | Completion time | +| `LastCheckedAt` | `DateTimeOffset?` | Last status check | +| `SourceImageBytes` | `byte[]?` | Source image if stored | +| `Elapsed` | `TimeSpan` | Time since creation | + +--- + +## MAUI Controls + +### ReplicateTransformerView + +The main UI control for image/video transformation. + +```csharp +public partial class ReplicateTransformerView : ContentView +``` + +#### Bindable Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `LayoutMode` | `TransformerLayoutMode` | `ButtonsBelow` | Layout mode for the control | +| `OverlayButtonPosition` | `OverlayButtonPosition` | `Bottom` | Position of overlay buttons | +| `UseLocalization` | `bool` | `false` | Enable localized strings | +| `ProcessingMessage` | `string` | `"Transforming..."` | Message during processing | +| `ProcessingSubMessage` | `string` | `"This may take..."` | Sub-message during processing | +| `PlaceholderText` | `string` | `"Select or capture..."` | Placeholder text | +| `ShowCaptureButton` | `bool` | `true` | Show camera capture button | +| `ShowPickButton` | `bool` | `true` | Show image picker button | +| `ShowVideoButtons` | `bool` | `false` | Show video-related buttons | +| `ButtonConfigs` | `TransformerButtonConfigs` | `new()` | Button configurations | +| `ImagePreset` | `ModelPreset?` | `null` | Current image model preset | +| `VideoPreset` | `ModelPreset?` | `null` | Current video model preset | +| `CustomImagePrompt` | `string?` | `null` | Custom image prompt override | +| `CustomVideoPrompt` | `string?` | `null` | Custom video prompt override | + +#### Read-Only Properties + +| Property | Type | Description | +|----------|------|-------------| +| `CurrentState` | `TransformerState` | Current control state | +| `IsProcessing` | `bool` | Whether transformation is in progress | +| `HasSourceImage` | `bool` | Whether source image is set | +| `HasResult` | `bool` | Whether result is available | +| `SourceImageBytes` | `byte[]?` | Current source image bytes | +| `ResultUrl` | `string?` | URL of the transformation result | +| `VideoUrl` | `string?` | URL of video result | +| `PredictionTracker` | `IPredictionTracker?` | Internal prediction tracker | + +#### Events + +| Event | Type | +|-------|------| +| `ImageSelected` | `EventHandler` | +| `TransformationStarted` | `EventHandler` | +| `TransformationCompleted` | `EventHandler` | +| `TransformationError` | `EventHandler` | +| `PredictionTracked` | `EventHandler` | +| `PredictionCompleted` | `EventHandler` | +| `FileSaved` | `EventHandler` | +| `FileSaveError` | `EventHandler` | +| `StateChanged` | `EventHandler` | +| `ImageTapped` | `EventHandler` | + +#### Methods + +```csharp +void Initialize(IReplicateTransformer transformer) +void Initialize(IReplicateTransformerFactory factory) +void ConfigureSettings(Action configure) +void SetImagePreset(ModelPreset preset, Dictionary? customParameters = null) +void SetVideoPreset(ModelPreset preset, Dictionary? customParameters = null) +Task SetImageAsync(byte[] imageBytes) +Task SetImageAsync(Stream imageStream) +Task TransformImageAsync() +Task TransformVideoAsync() +Task CancelTransformationAsync() +void Reset() +void ClearResult() +void RefreshLocalization() +``` + +--- + +### TransformerLayoutMode + +```csharp +public enum TransformerLayoutMode +{ + ImageOnly, // Image only, no buttons + ButtonsBelow, // Buttons below the image (default) + ButtonsOverlay, // Buttons overlaid on the image + SideBySide // Source and result side by side +} +``` + +--- + +### TransformerState + +```csharp +public enum TransformerState +{ + Empty, // No image selected + ImageSelected, // Source image is displayed + Processing, // Transformation in progress + ImageResult, // Image transformation completed + VideoResult, // Video transformation completed + PlayingVideo, // Video player is active + Error // Error occurred +} +``` + +#### Extension Methods + +```csharp +public static bool HasResult(this TransformerState state) +public static bool IsBusy(this TransformerState state) +public static bool CanTransform(this TransformerState state) +public static bool CanSelectImage(this TransformerState state) +``` + +--- + +## Button Configuration + +### ButtonConfig + +Configuration for a single button's appearance. + +```csharp +public class ButtonConfig : BindableObject +``` + +#### Properties + +| Property | Type | Description | +|----------|------|-------------| +| `Text` | `string` | Button text label | +| `IconText` | `string` | Unicode/emoji icon (e.g., "📸") | +| `ImageSource` | `ImageSource?` | Image icon (takes precedence over IconText) | +| `DisplayMode` | `ButtonDisplayMode?` | Override display mode | +| `BackgroundColor` | `Color?` | Button background color | +| `TextColor` | `Color` | Button text color | +| `IsVisible` | `bool` | Whether button is visible | + +#### Events + +| Event | Description | +|-------|-------------| +| `ConfigurationChanged` | Raised when any property changes | + +#### Methods + +```csharp +string GetDisplayText(ButtonDisplayMode defaultMode = ButtonDisplayMode.Both) +bool ShouldShowImage(ButtonDisplayMode defaultMode = ButtonDisplayMode.Both) +ImageSource? GetEffectiveImageSource(ButtonDisplayMode defaultMode = ButtonDisplayMode.Both) +void ApplyTo(Button button, ButtonDisplayMode defaultMode = ButtonDisplayMode.Both) +ButtonConfig Clone() +static ButtonConfig Create(string text, string iconText, ImageSource? imageSource = null, ButtonDisplayMode? displayMode = null) +``` + +--- + +### TransformerButtonConfigs + +Container for all transformer button configurations. + +```csharp +public class TransformerButtonConfigs : BindableObject +``` + +#### Properties + +| Property | Type | Description | +|----------|------|-------------| +| `DefaultDisplayMode` | `ButtonDisplayMode` | Default mode for all buttons | +| `OverlaySelect` | `ButtonConfig` | Overlay select button | +| `OverlayTransform` | `ButtonConfig` | Overlay transform button | +| `SideBySideCapture` | `ButtonConfig` | Side-by-side capture button | +| `SideBySidePick` | `ButtonConfig` | Side-by-side pick button | +| `SideBySideTransform` | `ButtonConfig` | Side-by-side transform button | +| `SideBySideClear` | `ButtonConfig` | Side-by-side clear button | +| `SideBySideRedo` | `ButtonConfig` | Side-by-side redo button | + +#### Methods + +```csharp +void ApplyConfig(ButtonConfig config, Button button) +static TransformerButtonConfigs CreateDefault() +``` + +--- + +### ButtonDisplayMode + +```csharp +public enum ButtonDisplayMode +{ + Label, // Show only text + Icon, // Show only icon + Both // Show both icon and text +} +``` + +--- + +### OverlayButtonPosition + +```csharp +public enum OverlayButtonPosition +{ + Top, // Buttons at top of image + Bottom // Buttons at bottom of image (default) +} +``` + +--- + +## Localization + +### ReplicateStrings + +Provides localized strings for controls. + +```csharp +public static class ReplicateStrings +``` + +#### Properties + +```csharp +public static string CurrentCulture { get; set; } +public static IEnumerable SupportedCultures { get; } +``` + +#### Methods + +```csharp +public static string Get(string key) +public static string Get(string key, string culture) +public static void SetCulture(CultureInfo culture) +public static void UseSystemCulture() +public static void RegisterTranslations(string culture, Dictionary translations) +public static void RegisterTranslation(string culture, string key, string value) +public static bool HasCulture(string culture) +public static void ResetToDefaults() +``` + +#### Keys (ReplicateStrings.Keys) + +| Key | English Value | +|-----|---------------| +| `Take` | "Take" | +| `Pick` | "Pick" | +| `Select` | "Select" | +| `Transform` | "Transform" | +| `Clear` | "Clear" | +| `Redo` | "Redo" | +| `Reset` | "Reset" | +| `Cancel` | "Cancel" | +| `Image` | "Image" | +| `GenerateVideo` | "Generate Video" | +| `Transforming` | "Transforming..." | +| `GeneratingVideo` | "Generating video..." | +| `Processing` | "Processing" | +| `PleaseWait` | "Please wait" | +| `TransformationComplete` | "Transformation complete" | +| `TapToSelectImage` | "Tap to select image" | +| `SourceImage` | "Source" | +| `Result` | "Result" | +| `Error` | "Error" | +| `Dismiss` | "Dismiss" | +| `Starting` | "Starting" | +| `Succeeded` | "Succeeded" | +| `Failed` | "Failed" | +| `Canceled` | "Canceled" | +| `Pending` | "Pending" | + +#### Supported Cultures + +- English (en) +- Spanish (es) +- French (fr) +- German (de) +- Chinese (zh) +- Japanese (ja) +- Portuguese (pt) +- Italian (it) + +--- + +## Events + +### Event Args Classes + +#### PredictionCreatedEventArgs + +```csharp +public class PredictionCreatedEventArgs : EventArgs +{ + public PredictionResult Prediction { get; } +} +``` + +#### TransformationStartedEventArgs + +```csharp +public class TransformationStartedEventArgs : EventArgs +{ + public TransformationType Type { get; } +} +``` + +#### TransformationCompletedEventArgs + +```csharp +public class TransformationCompletedEventArgs : EventArgs +{ + public TransformationType Type { get; } + public string? ResultUrl { get; } + public PredictionResult Result { get; } +} +``` + +#### TransformationErrorEventArgs + +```csharp +public class TransformationErrorEventArgs : EventArgs +{ + public TransformationType Type { get; } + public Exception Error { get; } +} +``` + +#### ImageSelectedEventArgs + +```csharp +public class ImageSelectedEventArgs : EventArgs +{ + public byte[] ImageBytes { get; } +} +``` + +#### ImageTappedEventArgs + +```csharp +public class ImageTappedEventArgs : EventArgs +{ + public bool IsSourceImage { get; } + public string? ResultUrl { get; } + public byte[]? ImageBytes { get; } +} +``` + +#### FileSavedEventArgs + +```csharp +public class FileSavedEventArgs : EventArgs +{ + public string FilePath { get; } + public TransformationType Type { get; } + public long FileSizeBytes { get; } +} +``` + +#### FileSaveErrorEventArgs + +```csharp +public class FileSaveErrorEventArgs : EventArgs +{ + public TransformationType Type { get; } + public Exception Error { get; } +} +``` + +#### StateChangedEventArgs + +```csharp +public class StateChangedEventArgs : EventArgs +{ + public TransformerState PreviousState { get; } + public TransformerState NewState { get; } +} +``` + +#### PredictionStatusChangedEventArgs + +```csharp +public class PredictionStatusChangedEventArgs : EventArgs +{ + public TrackedPrediction Prediction { get; } + public string PreviousStatus { get; } + public string NewStatus { get; } + public PredictionStatus PreviousStatusEnum { get; } + public PredictionStatus NewStatusEnum { get; } +} +``` + +#### PredictionCompletedEventArgs + +```csharp +public class PredictionCompletedEventArgs : EventArgs +{ + public TrackedPrediction Prediction { get; } + public bool Succeeded { get; } + public bool Failed { get; } + public bool Canceled { get; } +} +``` + +--- + +## Exceptions + +### ReplicateApiException + +Thrown when the Replicate API returns an error. + +```csharp +public class ReplicateApiException : Exception +{ + public HttpStatusCode StatusCode { get; } + public string ResponseBody { get; } +} +``` + +### ReplicateTransformationException + +Thrown when a transformation fails. + +```csharp +public class ReplicateTransformationException : Exception +{ + public ReplicateTransformationException(string message) + public ReplicateTransformationException(string message, Exception innerException) +} +``` + +--- + +## Extension Methods + +### ServiceCollectionExtensions + +```csharp +public static IServiceCollection AddReplicateTransformer( + this IServiceCollection services, + Action configure) + +public static IServiceCollection AddReplicateTransformer( + this IServiceCollection services, + ReplicateSettings settings) +``` + +### MauiAppBuilderExtensions + +```csharp +public static MauiAppBuilder UseReplicateMaui(this MauiAppBuilder builder) + +public static MauiAppBuilder UseReplicateMaui( + this MauiAppBuilder builder, + Action configure) + +public static MauiAppBuilder UseReplicateMaui( + this MauiAppBuilder builder, + ReplicateSettings settings) +``` + +### LocalizationExtensions + +```csharp +public static string Localized(this string key) +public static string Localized(this string key, string culture) +``` + +--- + +## TransformationType + +```csharp +public enum TransformationType +{ + Image, + Video +} +``` + +--- + +## Complete Example + +```csharp +using MarketAlly.Replicate.Maui; +using MarketAlly.Replicate.Maui.Controls; +using MarketAlly.Replicate.Maui.Localization; + +public partial class MainPage : ContentPage +{ + private readonly IReplicateTransformerFactory _factory; + + public MainPage(IReplicateTransformerFactory factory) + { + InitializeComponent(); + _factory = factory; + + // Initialize control + TransformerView.Initialize(_factory); + + // Configure appearance + TransformerView.LayoutMode = TransformerLayoutMode.SideBySide; + TransformerView.UseLocalization = true; + ReplicateStrings.CurrentCulture = "en"; + + // Set model presets + TransformerView.SetImagePreset(ModelPresets.HiDreamE1, new Dictionary + { + { "speed_mode", "Juiced 🔥 (more speed)" } + }); + + TransformerView.SetVideoPreset(ModelPresets.GoogleVeo3Fast, new Dictionary + { + { "duration", 8 } + }); + + // Subscribe to events + TransformerView.TransformationCompleted += OnCompleted; + TransformerView.TransformationError += OnError; + TransformerView.PredictionTracked += OnStatusChanged; + } + + private void OnCompleted(object? sender, TransformationCompletedEventArgs e) + { + Console.WriteLine($"Completed: {e.Result.Output}"); + Console.WriteLine($"Predict time: {e.Result.Metrics?.PredictTime}s"); + } + + private void OnError(object? sender, TransformationErrorEventArgs e) + { + DisplayAlert("Error", e.Error.Message, "OK"); + } + + private void OnStatusChanged(object? sender, PredictionStatusChangedEventArgs e) + { + Console.WriteLine($"Status: {e.NewStatusEnum}"); + } +} +``` + +--- + +*This API Reference is for MarketAlly.Replicate.Maui version 1.5.0* diff --git a/README.md b/README.md new file mode 100644 index 0000000..d505791 --- /dev/null +++ b/README.md @@ -0,0 +1,445 @@ +# MarketAlly.Replicate.Maui + +[![NuGet Version](https://img.shields.io/nuget/v/MarketAlly.Replicate.Maui.svg?style=flat)](https://www.nuget.org/packages/MarketAlly.Replicate.Maui/) +[![NuGet Downloads](https://img.shields.io/nuget/dt/MarketAlly.Replicate.Maui.svg)](https://www.nuget.org/packages/MarketAlly.Replicate.Maui/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![.NET](https://img.shields.io/badge/.NET-9.0-512BD4)](https://dotnet.microsoft.com/download) +[![Platform](https://img.shields.io/badge/Platform-iOS%20%7C%20Android%20%7C%20Windows%20%7C%20macOS-lightgray)](https://dotnet.microsoft.com/apps/maui) + +A production-ready .NET library for [Replicate](https://replicate.com) AI API integration. Generate AI images and videos using popular models like Stable Diffusion XL, HiDream, Google Veo 3, Kling, Luma Ray, and more. + +## Features + +- **Dual-target support**: Use with .NET MAUI apps (iOS, Android, Windows, macOS) or plain .NET 9 (console, ASP.NET Core, Blazor) +- **Pre-configured model presets**: Ready-to-use configurations for popular AI models +- **Ready-to-use MAUI controls**: Drop-in UI components for image/video transformation +- **Prediction tracking**: Real-time status updates and history management +- **BYOK support**: Bring Your Own Key for multi-tenant applications +- **Localization**: Built-in support for 8 languages with custom translation capability +- **Webhook integration**: Support for async processing with webhooks + +## Supported Models + +### Image Models +| Model | Description | +|-------|-------------| +| **HiDream E1.1** | Fast high-quality image generation/editing | +| **Stable Diffusion XL** | Popular open-source image generation | +| **Ideogram V3 Turbo** | Best for images with realistic, legible text | +| **Recraft V3 SVG** | High-quality SVG images, logos, and icons | + +### Video Models +| Model | Description | +|-------|-------------| +| **Google Veo 3 Fast** | State-of-the-art video generation from Google | +| **Kling 2.5 Turbo Pro** | Latest high-quality video generation | +| **Kling 1.6 Pro** | Professional video generation | +| **Seedance 1 Pro** | High-quality video with image-to-video support | +| **Luma Ray Flash 2** | Fast video generation with camera control | +| **Wan 2.1** | Image-to-video with 480p/720p support | +| **MiniMax Video 01** | Text and image to video generation | + +## Installation + +```bash +dotnet add package MarketAlly.Replicate.Maui +``` + +Or via Package Manager: +```powershell +Install-Package MarketAlly.Replicate.Maui +``` + +## Quick Start + +### Option 1: .NET MAUI Application (Full Experience) + +**1. Configure in MauiProgram.cs:** + +```csharp +using MarketAlly.Replicate.Maui; + +public static MauiApp CreateMauiApp() +{ + var builder = MauiApp.CreateBuilder(); + builder + .UseMauiApp() + .UseReplicateMaui(settings => + { + settings.ApiToken = "your-replicate-api-token"; + }); + + return builder.Build(); +} +``` + +**2. Add the control to your XAML:** + +```xml + + + + +``` + +**3. Initialize and handle events:** + +```csharp +using MarketAlly.Replicate.Maui; +using MarketAlly.Replicate.Maui.Controls; + +public partial class MainPage : ContentPage +{ + public MainPage() + { + InitializeComponent(); + + // Use a preset model + TransformerView.SetImagePreset(ModelPresets.HiDreamE1); + } + + private void OnTransformationCompleted(object sender, TransformationCompletedEventArgs e) + { + // e.Result.Output contains the URL to the generated image/video + Console.WriteLine($"Result: {e.Result.Output}"); + } +} +``` + +### Option 2: API-Only Usage (Console, ASP.NET Core, Blazor) + +```csharp +using MarketAlly.Replicate.Maui; +using Microsoft.Extensions.Options; + +// Create transformer directly +var settings = Options.Create(new ReplicateSettings +{ + ApiToken = "your-replicate-api-token" +}); + +using var httpClient = new HttpClient(); +var transformer = new ReplicateTransformer(httpClient, settings); + +// Use a model preset +var preset = ModelPresets.HiDreamE1; +var imageBytes = File.ReadAllBytes("input.jpg"); + +var result = await transformer.RunPresetAsync( + preset, + imageBytes, + customPrompt: "anime style portrait, studio ghibli inspired" +); + +Console.WriteLine($"Status: {result.Status}"); +Console.WriteLine($"Output: {result.Output}"); +``` + +### Option 3: Dependency Injection (ASP.NET Core) + +```csharp +// In Program.cs or Startup.cs +services.Configure(options => +{ + options.ApiToken = Configuration["Replicate:ApiToken"]; +}); +services.AddHttpClient(); + +// In your controller or service +public class ImageController : ControllerBase +{ + private readonly IReplicateTransformer _transformer; + + public ImageController(IReplicateTransformer transformer) + { + _transformer = transformer; + } + + [HttpPost("generate")] + public async Task Generate([FromBody] GenerateRequest request) + { + var result = await _transformer.RunPresetAsync( + ModelPresets.StableDiffusionXL, + Convert.FromBase64String(request.ImageBase64), + request.Prompt + ); + + return Ok(new { result.Output, result.Status }); + } +} +``` + +## Using Model Presets + +Model presets provide pre-configured settings for popular models: + +```csharp +// Image Models +var hidream = ModelPresets.HiDreamE1; +var sdxl = ModelPresets.StableDiffusionXL; +var ideogram = ModelPresets.IdeogramV3Turbo; +var recraft = ModelPresets.RecraftV3Svg; + +// Video Models +var veo = ModelPresets.GoogleVeo3Fast; +var kling = ModelPresets.Kling25TurboPro; +var seedance = ModelPresets.SeedancePro; +var luma = ModelPresets.LumaRayFlash; + +// Get all presets +var allPresets = ModelPresets.All; +var imageModels = ModelPresets.ImageModels; +var videoModels = ModelPresets.VideoModels; + +// Find by name +var preset = ModelPresets.FindByName("HiDream E1.1"); +``` + +### Custom Parameters + +Override default preset parameters: + +```csharp +var customParams = new Dictionary +{ + { "guidance_scale", 7.5 }, + { "num_inference_steps", 30 }, + { "seed", 42 } +}; + +var result = await transformer.RunPresetAsync( + ModelPresets.StableDiffusionXL, + imageBytes, + customPrompt: "cyberpunk cityscape", + customParameters: customParams +); +``` + +## MAUI Control Features + +### Layout Modes + +```csharp +// Image only - no buttons (for custom UI) +TransformerView.LayoutMode = TransformerLayoutMode.ImageOnly; + +// Buttons below the image (default) +TransformerView.LayoutMode = TransformerLayoutMode.ButtonsBelow; + +// Overlay buttons on the image +TransformerView.LayoutMode = TransformerLayoutMode.ButtonsOverlay; + +// Side-by-side source and result +TransformerView.LayoutMode = TransformerLayoutMode.SideBySide; +``` + +### Overlay Button Position + +```csharp +// Position overlay buttons at top or bottom +TransformerView.OverlayButtonPosition = OverlayButtonPosition.Top; +TransformerView.OverlayButtonPosition = OverlayButtonPosition.Bottom; // default +``` + +### Localization + +Built-in support for 8 languages: + +```csharp +// Enable localization +TransformerView.UseLocalization = true; + +// Set language +ReplicateStrings.CurrentCulture = "es"; // Spanish + +// Custom translations +ReplicateStrings.RegisterTranslations("es", new Dictionary +{ + [ReplicateStrings.Keys.Transform] = "Convertir", + [ReplicateStrings.Keys.GenerateVideo] = "Crear vídeo" +}); +``` + +Supported languages: English (en), Spanish (es), French (fr), German (de), Chinese (zh), Japanese (ja), Portuguese (pt), Italian (it) + +### Button Configuration + +Customize button appearance: + +```csharp +// Configure individual buttons +TransformerView.ButtonConfigs.OverlaySelect.Text = "Choose Image"; +TransformerView.ButtonConfigs.OverlaySelect.IconText = "📷"; +TransformerView.ButtonConfigs.OverlayTransform.BackgroundColor = Colors.Purple; + +// Set display mode +TransformerView.ButtonConfigs.DefaultDisplayMode = ButtonDisplayMode.Icon; // Icon only +TransformerView.ButtonConfigs.DefaultDisplayMode = ButtonDisplayMode.Label; // Text only +TransformerView.ButtonConfigs.DefaultDisplayMode = ButtonDisplayMode.Both; // Both (default) +``` + +## Prediction Tracking + +Track prediction status in real-time: + +```csharp +// Enable tracking (enabled by default) +var tracker = new PredictionTracker(transformer); + +// Subscribe to events +tracker.PredictionStatusChanged += (s, e) => +{ + Console.WriteLine($"Prediction {e.Prediction.Id}: {e.PreviousStatus} -> {e.NewStatus}"); +}; + +tracker.PredictionCompleted += (s, e) => +{ + if (e.Succeeded) + Console.WriteLine($"Completed: {e.Prediction.Output}"); + else + Console.WriteLine($"Failed: {e.Prediction.Error}"); +}; + +// Access history +var history = tracker.History; +var pending = tracker.PendingPredictions; +``` + +## BYOK (Bring Your Own Key) + +Support multi-tenant scenarios where users provide their own API keys: + +```csharp +// Inject the factory +public class MyService +{ + private readonly IReplicateTransformerFactory _factory; + + public MyService(IReplicateTransformerFactory factory) + { + _factory = factory; + } + + public async Task GenerateForUser(string userApiKey, byte[] image) + { + // Create transformer with user's API key + var transformer = _factory.CreateWithToken(userApiKey); + + var result = await transformer.RunPresetAsync( + ModelPresets.HiDreamE1, + image + ); + + return result.Output; + } +} +``` + +## Webhook Support + +Use webhooks for long-running predictions: + +```csharp +var options = new PredictionOptions +{ + WebhookUrl = "https://your-server.com/webhook/replicate", + WebhookEventsFilter = new[] { "completed" }, + WebhookOnly = true // Don't poll, just return immediately +}; + +var result = await transformer.RunPresetAsync( + ModelPresets.GoogleVeo3Fast, + imageBytes, + options: options +); + +// result.Id can be used to track the prediction +// Your webhook will receive the completion notification +``` + +## Events Reference + +### ReplicateTransformerView Events + +| Event | Description | +|-------|-------------| +| `ImageSelected` | Fired when user selects an image | +| `TransformationStarted` | Fired when transformation begins | +| `TransformationCompleted` | Fired when transformation succeeds | +| `TransformationError` | Fired when transformation fails | +| `PredictionTracked` | Fired when prediction status changes | +| `PredictionCompleted` | Fired when prediction completes | +| `FileSaved` | Fired when result is saved to file | +| `FileSaveError` | Fired when file save fails | +| `StateChanged` | Fired when control state changes | +| `ImageTapped` | Fired when image is tapped | + +### IReplicateTransformer Events + +| Event | Description | +|-------|-------------| +| `PredictionCreated` | Fired immediately when prediction is created (before polling) | + +## Error Handling + +```csharp +try +{ + var result = await transformer.RunPresetAsync(preset, imageBytes); + + if (result.IsFailed) + { + Console.WriteLine($"Prediction failed: {result.Error}"); + } + else if (result.IsCanceled) + { + Console.WriteLine("Prediction was canceled"); + } + else if (result.IsSucceeded) + { + Console.WriteLine($"Success: {result.Output}"); + } +} +catch (ReplicateApiException ex) +{ + Console.WriteLine($"API Error {ex.StatusCode}: {ex.Message}"); + Console.WriteLine($"Response: {ex.ResponseBody}"); +} +catch (ReplicateTransformationException ex) +{ + Console.WriteLine($"Transformation Error: {ex.Message}"); +} +``` + +## Requirements + +- .NET 9.0 or later +- For MAUI targets: iOS 15.0+, Android 24+, Windows 10.0.17763+, macOS 15.0+ +- Replicate API token ([Get one here](https://replicate.com/account/api-tokens)) + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## Links + +- [GitHub Repository](https://github.com/MarketAlly/Replicate.Maui) +- [NuGet Package](https://www.nuget.org/packages/MarketAlly.Replicate.Maui/) +- [API Reference](https://github.com/MarketAlly/Replicate.Maui/blob/main/API_Reference.md) +- [Replicate Documentation](https://replicate.com/docs) + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. + +## Support + +For issues and feature requests, please use the [GitHub Issues](https://github.com/MarketAlly/Replicate.Maui/issues) page. + +--- + +Made with care by [MarketAlly](https://github.com/MarketAlly) diff --git a/Replicate.Maui.slnx b/Replicate.Maui.slnx new file mode 100644 index 0000000..f106563 --- /dev/null +++ b/Replicate.Maui.slnx @@ -0,0 +1,4 @@ + + + + diff --git a/Replicate.Maui/Controls/ButtonConfiguration.cs b/Replicate.Maui/Controls/ButtonConfiguration.cs new file mode 100644 index 0000000..16f4d55 --- /dev/null +++ b/Replicate.Maui/Controls/ButtonConfiguration.cs @@ -0,0 +1,517 @@ +namespace MarketAlly.Replicate.Maui.Controls; + +/// +/// Display mode for transformer buttons. +/// +public enum ButtonDisplayMode +{ + /// + /// Show only the text label. + /// + Label, + + /// + /// Show only the icon (ImageSource or IconText). + /// + Icon, + + /// + /// Show both icon and label. + /// + Both +} + +/// +/// Position for overlay buttons in the ImageOverlay layout mode. +/// +public enum OverlayButtonPosition +{ + /// + /// Buttons appear at the top of the image. + /// + Top, + + /// + /// Buttons appear at the bottom of the image (default). + /// + Bottom +} + +/// +/// Configuration for a transformer button's appearance. +/// +public class ButtonConfig : BindableObject +{ + /// + /// Raised when any configuration property changes. + /// + public event EventHandler? ConfigurationChanged; + + public static readonly BindableProperty TextProperty = + BindableProperty.Create(nameof(Text), typeof(string), typeof(ButtonConfig), string.Empty, + propertyChanged: OnConfigPropertyChanged); + + public static readonly BindableProperty IconTextProperty = + BindableProperty.Create(nameof(IconText), typeof(string), typeof(ButtonConfig), string.Empty, + propertyChanged: OnConfigPropertyChanged); + + public static readonly BindableProperty ImageSourceProperty = + BindableProperty.Create(nameof(ImageSource), typeof(ImageSource), typeof(ButtonConfig), null, + propertyChanged: OnConfigPropertyChanged); + + public static readonly BindableProperty DisplayModeProperty = + BindableProperty.Create(nameof(DisplayMode), typeof(ButtonDisplayMode?), typeof(ButtonConfig), null, + propertyChanged: OnConfigPropertyChanged); + + public static readonly BindableProperty BackgroundColorProperty = + BindableProperty.Create(nameof(BackgroundColor), typeof(Color), typeof(ButtonConfig), null, + propertyChanged: OnConfigPropertyChanged); + + public static readonly BindableProperty TextColorProperty = + BindableProperty.Create(nameof(TextColor), typeof(Color), typeof(ButtonConfig), null, + propertyChanged: OnConfigPropertyChanged); + + public static readonly BindableProperty IsVisibleProperty = + BindableProperty.Create(nameof(IsVisible), typeof(bool), typeof(ButtonConfig), true, + propertyChanged: OnConfigPropertyChanged); + + private static void OnConfigPropertyChanged(BindableObject bindable, object oldValue, object newValue) + { + if (bindable is ButtonConfig config) + { + config.ConfigurationChanged?.Invoke(config, EventArgs.Empty); + } + } + + /// + /// The text label for the button. + /// + public string Text + { + get => (string)GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + /// + /// Unicode/emoji icon text (e.g., "📸", "🖼"). + /// Used when ImageSource is null and DisplayMode includes icon. + /// + public string IconText + { + get => (string)GetValue(IconTextProperty); + set => SetValue(IconTextProperty, value); + } + + /// + /// Image source for the button icon. + /// Takes precedence over IconText when set. + /// + public ImageSource? ImageSource + { + get => (ImageSource?)GetValue(ImageSourceProperty); + set => SetValue(ImageSourceProperty, value); + } + + /// + /// How to display the button content. If null, uses the parent's default display mode. + /// + public ButtonDisplayMode? DisplayMode + { + get => (ButtonDisplayMode?)GetValue(DisplayModeProperty); + set => SetValue(DisplayModeProperty, value); + } + + /// + /// Background color for the button. + /// + public Color? BackgroundColor + { + get => (Color?)GetValue(BackgroundColorProperty); + set => SetValue(BackgroundColorProperty, value); + } + + /// + /// Text color for the button. + /// + public Color TextColor + { + get => (Color)GetValue(TextColorProperty); + set => SetValue(TextColorProperty, value); + } + + /// + /// Whether the button is visible. + /// + public bool IsVisible + { + get => (bool)GetValue(IsVisibleProperty); + set => SetValue(IsVisibleProperty, value); + } + + /// + /// Gets the display text based on DisplayMode. + /// + /// The default display mode to use if DisplayMode is null. + public string GetDisplayText(ButtonDisplayMode defaultMode = ButtonDisplayMode.Both) + { + var effectiveMode = DisplayMode ?? defaultMode; + return effectiveMode switch + { + ButtonDisplayMode.Label => Text, + ButtonDisplayMode.Icon => ImageSource == null ? IconText : string.Empty, + ButtonDisplayMode.Both => ImageSource == null ? $"{IconText} {Text}".Trim() : Text, + _ => Text + }; + } + + /// + /// Gets whether to show the image based on effective display mode. + /// + /// The default display mode to use if DisplayMode is null. + public bool ShouldShowImage(ButtonDisplayMode defaultMode = ButtonDisplayMode.Both) + { + var effectiveMode = DisplayMode ?? defaultMode; + return ImageSource != null && effectiveMode != ButtonDisplayMode.Label; + } + + /// + /// Gets the effective image source based on display mode. + /// + /// The default display mode to use if DisplayMode is null. + public ImageSource? GetEffectiveImageSource(ButtonDisplayMode defaultMode = ButtonDisplayMode.Both) + { + return ShouldShowImage(defaultMode) ? ImageSource : null; + } + + /// + /// Apply this configuration to a MAUI Button. + /// + /// The button to configure. + /// The default display mode to use if DisplayMode is null. + public void ApplyTo(Button button, ButtonDisplayMode defaultMode = ButtonDisplayMode.Both) + { + button.Text = GetDisplayText(defaultMode); + button.ImageSource = GetEffectiveImageSource(defaultMode); + + if (BackgroundColor != null) + button.BackgroundColor = BackgroundColor; + + if (TextColor != null) + button.TextColor = TextColor; + + button.IsVisible = IsVisible; + } + + /// + /// Create a new ButtonConfig with the specified values. + /// + public static ButtonConfig Create(string text, string iconText, ImageSource? imageSource = null, ButtonDisplayMode? displayMode = null) + { + return new ButtonConfig + { + Text = text, + IconText = iconText, + ImageSource = imageSource, + DisplayMode = displayMode + }; + } + + /// + /// Create a clone of this configuration. + /// + public ButtonConfig Clone() + { + return new ButtonConfig + { + Text = Text, + IconText = IconText, + ImageSource = ImageSource, + DisplayMode = DisplayMode, + BackgroundColor = BackgroundColor, + TextColor = TextColor, + IsVisible = IsVisible + }; + } +} + +/// +/// Predefined button configurations for common transformer buttons. +/// +public static class DefaultButtonConfigs +{ + // Source buttons + public static ButtonConfig CapturePhoto => new() + { + Text = "Take", + IconText = "📸" + }; + + public static ButtonConfig PickImage => new() + { + Text = "Pick", + IconText = "🖼" + }; + + public static ButtonConfig Select => new() + { + Text = "Select", + IconText = "🖼" + }; + + public static ButtonConfig CaptureVideo => new() + { + Text = "Record", + IconText = "🎬" + }; + + public static ButtonConfig PickVideo => new() + { + Text = "Pick Video", + IconText = "🎥" + }; + + // Transform buttons + public static ButtonConfig TransformImage => new() + { + Text = "Transform", + IconText = "✨" + }; + + public static ButtonConfig TransformAnime => new() + { + Text = "Transform to Anime", + IconText = "✨" + }; + + public static ButtonConfig GenerateVideo => new() + { + Text = "Generate Video", + IconText = "🎬" + }; + + // Result buttons + public static ButtonConfig Clear => new() + { + Text = "Clear", + IconText = "🗑" + }; + + public static ButtonConfig Redo => new() + { + Text = "Redo", + IconText = "↺" + }; + + public static ButtonConfig Reset => new() + { + Text = "Reset", + IconText = "↺" + }; + + public static ButtonConfig BackToImage => new() + { + Text = "Image", + IconText = "🖼" + }; +} + +/// +/// Container for all transformer button configurations. +/// Allows grouping and customizing button appearance in a single bindable object. +/// +public class TransformerButtonConfigs : BindableObject +{ + /// + /// Raised when any button configuration changes. + /// + public event EventHandler? ConfigurationChanged; + + #region Bindable Properties + + public static readonly BindableProperty DefaultDisplayModeProperty = + BindableProperty.Create(nameof(DefaultDisplayMode), typeof(ButtonDisplayMode), typeof(TransformerButtonConfigs), + ButtonDisplayMode.Both, propertyChanged: OnPropertyChanged); + + // Overlay buttons + public static readonly BindableProperty OverlaySelectProperty = + BindableProperty.Create(nameof(OverlaySelect), typeof(ButtonConfig), typeof(TransformerButtonConfigs), + null, propertyChanged: OnButtonConfigChanged); + + public static readonly BindableProperty OverlayTransformProperty = + BindableProperty.Create(nameof(OverlayTransform), typeof(ButtonConfig), typeof(TransformerButtonConfigs), + null, propertyChanged: OnButtonConfigChanged); + + // SideBySide buttons + public static readonly BindableProperty SideBySideCaptureProperty = + BindableProperty.Create(nameof(SideBySideCapture), typeof(ButtonConfig), typeof(TransformerButtonConfigs), + null, propertyChanged: OnButtonConfigChanged); + + public static readonly BindableProperty SideBySidePickProperty = + BindableProperty.Create(nameof(SideBySidePick), typeof(ButtonConfig), typeof(TransformerButtonConfigs), + null, propertyChanged: OnButtonConfigChanged); + + public static readonly BindableProperty SideBySideTransformProperty = + BindableProperty.Create(nameof(SideBySideTransform), typeof(ButtonConfig), typeof(TransformerButtonConfigs), + null, propertyChanged: OnButtonConfigChanged); + + public static readonly BindableProperty SideBySideClearProperty = + BindableProperty.Create(nameof(SideBySideClear), typeof(ButtonConfig), typeof(TransformerButtonConfigs), + null, propertyChanged: OnButtonConfigChanged); + + public static readonly BindableProperty SideBySideRedoProperty = + BindableProperty.Create(nameof(SideBySideRedo), typeof(ButtonConfig), typeof(TransformerButtonConfigs), + null, propertyChanged: OnButtonConfigChanged); + + #endregion + + #region Property Accessors + + /// + /// Default display mode for all buttons. Individual buttons can override this. + /// + public ButtonDisplayMode DefaultDisplayMode + { + get => (ButtonDisplayMode)GetValue(DefaultDisplayModeProperty); + set => SetValue(DefaultDisplayModeProperty, value); + } + + /// + /// Configuration for the overlay select button. + /// + public ButtonConfig OverlaySelect + { + get => (ButtonConfig?)GetValue(OverlaySelectProperty) ?? CreateDefaultOverlaySelect(); + set => SetValue(OverlaySelectProperty, value); + } + + /// + /// Configuration for the overlay transform button. + /// + public ButtonConfig OverlayTransform + { + get => (ButtonConfig?)GetValue(OverlayTransformProperty) ?? CreateDefaultOverlayTransform(); + set => SetValue(OverlayTransformProperty, value); + } + + /// + /// Configuration for the side-by-side capture button. + /// + public ButtonConfig SideBySideCapture + { + get => (ButtonConfig?)GetValue(SideBySideCaptureProperty) ?? CreateDefaultSideBySideCapture(); + set => SetValue(SideBySideCaptureProperty, value); + } + + /// + /// Configuration for the side-by-side pick button. + /// + public ButtonConfig SideBySidePick + { + get => (ButtonConfig?)GetValue(SideBySidePickProperty) ?? CreateDefaultSideBySidePick(); + set => SetValue(SideBySidePickProperty, value); + } + + /// + /// Configuration for the side-by-side transform button. + /// + public ButtonConfig SideBySideTransform + { + get => (ButtonConfig?)GetValue(SideBySideTransformProperty) ?? CreateDefaultSideBySideTransform(); + set => SetValue(SideBySideTransformProperty, value); + } + + /// + /// Configuration for the side-by-side clear button. + /// + public ButtonConfig SideBySideClear + { + get => (ButtonConfig?)GetValue(SideBySideClearProperty) ?? CreateDefaultSideBySideClear(); + set => SetValue(SideBySideClearProperty, value); + } + + /// + /// Configuration for the side-by-side redo button. + /// + public ButtonConfig SideBySideRedo + { + get => (ButtonConfig?)GetValue(SideBySideRedoProperty) ?? CreateDefaultSideBySideRedo(); + set => SetValue(SideBySideRedoProperty, value); + } + + #endregion + + #region Default Factory Methods + + private static ButtonConfig CreateDefaultOverlaySelect() => new() { Text = "Select", IconText = "🖼" }; + private static ButtonConfig CreateDefaultOverlayTransform() => new() { Text = "Transform", IconText = "✨" }; + private static ButtonConfig CreateDefaultSideBySideCapture() => new() { Text = "Take", IconText = "📸" }; + private static ButtonConfig CreateDefaultSideBySidePick() => new() { Text = "Pick", IconText = "🖼" }; + private static ButtonConfig CreateDefaultSideBySideTransform() => new() { Text = "Transform", IconText = "✨" }; + private static ButtonConfig CreateDefaultSideBySideClear() => new() { Text = "Clear", IconText = "🗑" }; + private static ButtonConfig CreateDefaultSideBySideRedo() => new() { Text = "Redo", IconText = "↺" }; + + #endregion + + #region Change Handlers + + private static void OnPropertyChanged(BindableObject bindable, object oldValue, object newValue) + { + if (bindable is TransformerButtonConfigs configs) + { + configs.ConfigurationChanged?.Invoke(configs, EventArgs.Empty); + } + } + + private static void OnButtonConfigChanged(BindableObject bindable, object oldValue, object newValue) + { + if (bindable is TransformerButtonConfigs configs) + { + // Unsubscribe from old config + if (oldValue is ButtonConfig oldConfig) + { + oldConfig.ConfigurationChanged -= configs.OnChildConfigurationChanged; + } + + // Subscribe to new config + if (newValue is ButtonConfig newConfig) + { + newConfig.ConfigurationChanged += configs.OnChildConfigurationChanged; + } + + configs.ConfigurationChanged?.Invoke(configs, EventArgs.Empty); + } + } + + private void OnChildConfigurationChanged(object? sender, EventArgs e) + { + ConfigurationChanged?.Invoke(this, EventArgs.Empty); + } + + #endregion + + /// + /// Apply a button configuration to a button control. + /// + public void ApplyConfig(ButtonConfig config, Button button) + { + config.ApplyTo(button, DefaultDisplayMode); + } + + /// + /// Create a default TransformerButtonConfigs instance with all defaults applied. + /// + public static TransformerButtonConfigs CreateDefault() + { + return new TransformerButtonConfigs + { + DefaultDisplayMode = ButtonDisplayMode.Both, + OverlaySelect = CreateDefaultOverlaySelect(), + OverlayTransform = CreateDefaultOverlayTransform(), + SideBySideCapture = CreateDefaultSideBySideCapture(), + SideBySidePick = CreateDefaultSideBySidePick(), + SideBySideTransform = CreateDefaultSideBySideTransform(), + SideBySideClear = CreateDefaultSideBySideClear(), + SideBySideRedo = CreateDefaultSideBySideRedo() + }; + } +} diff --git a/Replicate.Maui/Controls/ConfigurableButton.xaml b/Replicate.Maui/Controls/ConfigurableButton.xaml new file mode 100644 index 0000000..cf562df --- /dev/null +++ b/Replicate.Maui/Controls/ConfigurableButton.xaml @@ -0,0 +1,10 @@ + + + +