AI Image Generator — C# source
Turn a text prompt into a 1024×1024 image with FLUX.1 [schnell] on Cloudflare Workers AI. No account, no API key — rate-limited for fair use. Your prompt goes to the model through our Worker; we store no prompts and no images.
This is the C# implementation — the same logic the interactive tool runs, in a shareable, citable form.
// AI Image Generator — prompt validation, usage-gate verdicts, and ?prompt=
// URL state for FLUX.1-schnell image generation.
//
// Language: C# (C# 12 / .NET 8, standard library only)
// Source: CosmoDev polyglot showcase port, from src/lib/image-gen.ts.
namespace ImageGen;
public enum PromptInvalid { Empty, TooLong, ControlChars }
/// <summary>Daily cap wins; the retry hint is in minutes.</summary>
/// <param name="Error">"RATE_LIMITED_IP" | "RATE_LIMITED_DAILY", null when ok.</param>
public sealed record GateVerdict(bool Ok, string? Error = null, int RetryAfterMinutes = 0);
public static class ImageGen
{
public const string Model = "@cf/black-forest-labs/flux-1-schnell";
public const int Steps = 4; // flux-1-schnell's design point -> 57.6 neurons/image
public const int PromptMaxChars = 600; // UX clamp, below the model's 2048 limit
public const int IpHourCap = 10;
public const int DailyCap = 100;
/// <summary>Normalize CRLF/CR to \n, trim, enforce 1..600 chars, and
/// reject control chars except \n (C0 minus U+000A, DEL, C1). Returns
/// (ok, prompt, reason).</summary>
public static (bool Ok, string Prompt, PromptInvalid Reason) ValidatePrompt(string raw)
{
var prompt = raw.Replace("\r\n", "\n").Replace('\r', '\n').Trim();
if (prompt.Length == 0) return (false, prompt, PromptInvalid.Empty);
if (prompt.Length > PromptMaxChars) return (false, prompt, PromptInvalid.TooLong);
foreach (var c in prompt)
{
var isCtrl = c < '\n' || (c > '\n' && c < ' ') || (c >= (char) 0x7F && c <= (char) 0x9F);
if (isCtrl) return (false, prompt, PromptInvalid.ControlChars);
}
return (true, prompt, default);
}
/// <summary>Minutes (>= 1) until the next UTC midnight — the daily reset.</summary>
private static int MinutesToUtcMidnight(DateTimeOffset now)
{
var utc = now.UtcDateTime;
return Math.Max(1, (int) Math.Ceiling((utc.Date.AddDays(1) - utc).TotalMinutes));
}
/// <summary>Daily cap wins: the site-global budget resets at UTC midnight.</summary>
public static GateVerdict CheckGate(int ipCount, int dailyCount, DateTimeOffset? now = null)
{
if (dailyCount >= DailyCap)
return new(false, "RATE_LIMITED_DAILY", MinutesToUtcMidnight(now ?? DateTimeOffset.UtcNow));
if (ipCount >= IpHourCap)
return new(false, "RATE_LIMITED_IP", 60);
return new(true);
}
/// <summary>Encode the shareable state: "" when there is nothing to share.</summary>
public static string EncodeUrlState(string prompt) =>
prompt.Length == 0 ? "" : $"?prompt={Uri.EscapeDataString(prompt)}";
/// <summary>Inverse of EncodeUrlState — a missing param decodes to "".</summary>
public static string DecodeUrlState(string search)
{
foreach (var pair in (search.StartsWith('?') ? search[1..] : search).Split('&'))
{
if (pair.StartsWith("prompt=", StringComparison.Ordinal))
return Uri.UnescapeDataString(pair["prompt=".Length..]);
}
return "";
}
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →