using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows; using AxCopilot.SDK; namespace AxCopilot.Handlers; public class ColorHandler : IActionHandler { private static readonly Dictionary _namedColors = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["red"] = "#FF0000", ["빨강"] = "#FF0000", ["빨간색"] = "#FF0000", ["green"] = "#008000", ["초록"] = "#008000", ["초록색"] = "#008000", ["blue"] = "#0000FF", ["파랑"] = "#0000FF", ["파란색"] = "#0000FF", ["white"] = "#FFFFFF", ["흰색"] = "#FFFFFF", ["하양"] = "#FFFFFF", ["black"] = "#000000", ["검정"] = "#000000", ["검은색"] = "#000000", ["yellow"] = "#FFFF00", ["노랑"] = "#FFFF00", ["노란색"] = "#FFFF00", ["orange"] = "#FFA500", ["주황"] = "#FFA500", ["주황색"] = "#FFA500", ["purple"] = "#800080", ["보라"] = "#800080", ["보라색"] = "#800080", ["pink"] = "#FFC0CB", ["분홍"] = "#FFC0CB", ["분홍색"] = "#FFC0CB", ["hotpink"] = "#FF69B4", ["핫핑크"] = "#FF69B4", ["cyan"] = "#00FFFF", ["시안"] = "#00FFFF", ["magenta"] = "#FF00FF", ["마젠타"] = "#FF00FF", ["brown"] = "#A52A2A", ["갈색"] = "#A52A2A", ["gray"] = "#808080", ["grey"] = "#808080", ["회색"] = "#808080", ["회"] = "#808080", ["silver"] = "#C0C0C0", ["은색"] = "#C0C0C0", ["gold"] = "#FFD700", ["금색"] = "#FFD700", ["navy"] = "#000080", ["남색"] = "#000080", ["teal"] = "#008080", ["틸"] = "#008080", ["lime"] = "#00FF00", ["라임"] = "#00FF00", ["maroon"] = "#800000", ["밤색"] = "#800000", ["olive"] = "#808000", ["올리브"] = "#808000", ["coral"] = "#FF7F50", ["코랄"] = "#FF7F50", ["salmon"] = "#FA8072", ["연어색"] = "#FA8072", ["skyblue"] = "#87CEEB", ["하늘색"] = "#87CEEB", ["lightblue"] = "#ADD8E6", ["연파랑"] = "#ADD8E6", ["darkblue"] = "#00008B", ["진파랑"] = "#00008B", ["darkgreen"] = "#006400", ["진초록"] = "#006400", ["lightgreen"] = "#90EE90", ["연초록"] = "#90EE90", ["indigo"] = "#4B0082", ["인디고"] = "#4B0082", ["violet"] = "#EE82EE", ["바이올렛"] = "#EE82EE", ["beige"] = "#F5F5DC", ["베이지"] = "#F5F5DC", ["ivory"] = "#FFFFF0", ["아이보리"] = "#FFFFF0", ["khaki"] = "#F0E68C", ["카키"] = "#F0E68C", ["lavender"] = "#E6E6FA", ["라벤더"] = "#E6E6FA", ["turquoise"] = "#40E0D0", ["터키옥"] = "#40E0D0", ["chocolate"] = "#D2691E", ["초콜릿"] = "#D2691E", ["crimson"] = "#DC143C", ["크림슨"] = "#DC143C", ["transparent"] = "#00000000" }; private static readonly Regex _hexRe = new Regex("^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$"); private static readonly Regex _rgbRe = new Regex("^(?:rgb\\s*\\()?\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*\\)?$", RegexOptions.IgnoreCase); private static readonly Regex _rgbaRe = new Regex("^rgba\\s*\\(\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*,\\s*([\\d.]+)\\s*\\)$", RegexOptions.IgnoreCase); private static readonly Regex _hslRe = new Regex("^hsl\\s*\\(\\s*([\\d.]+)\\s*,\\s*([\\d.]+)%?\\s*,\\s*([\\d.]+)%?\\s*\\)$", RegexOptions.IgnoreCase); public string? Prefix => "color"; public PluginMetadata Metadata => new PluginMetadata("Color", "색상 변환기 — color #FF5500 · color 255,85,0 · color red", "1.0", "AX"); public Task> GetItemsAsync(string query, CancellationToken ct) { if (string.IsNullOrWhiteSpace(query)) { return Task.FromResult((IEnumerable)new _003C_003Ez__ReadOnlySingleElementList(new LauncherItem("색상 코드를 입력하세요", "예: #FF5500 · 255,85,0 · red · hsl(20,100%,50%)", null, null, null, "\ue771"))); } string text = query.Trim(); List list = new List(); if (_hexRe.IsMatch(text)) { string text2 = text.TrimStart('#'); if (text2.Length == 3) { text2 = $"{text2[0]}{text2[0]}{text2[1]}{text2[1]}{text2[2]}{text2[2]}"; } byte a = byte.MaxValue; int r; int g; int b; if (text2.Length == 8) { a = Convert.ToByte(text2.Substring(0, 2), 16); r = Convert.ToInt32(text2.Substring(2, 2), 16); g = Convert.ToInt32(text2.Substring(4, 2), 16); b = Convert.ToInt32(text2.Substring(6, 2), 16); } else { r = Convert.ToInt32(text2.Substring(0, 2), 16); g = Convert.ToInt32(text2.Substring(2, 2), 16); b = Convert.ToInt32(text2.Substring(4, 2), 16); } AddColorItems(list, r, g, b, a, "#" + text2.ToUpperInvariant()); return Task.FromResult((IEnumerable)list); } Match match = _rgbRe.Match(text); if (match.Success) { int num = int.Parse(match.Groups[1].Value); int num2 = int.Parse(match.Groups[2].Value); int num3 = int.Parse(match.Groups[3].Value); if (num <= 255 && num2 <= 255 && num3 <= 255) { AddColorItems(list, num, num2, num3, byte.MaxValue, $"rgb({num},{num2},{num3})"); return Task.FromResult((IEnumerable)list); } } Match match2 = _rgbaRe.Match(text); if (match2.Success) { int num4 = int.Parse(match2.Groups[1].Value); int num5 = int.Parse(match2.Groups[2].Value); int num6 = int.Parse(match2.Groups[3].Value); double num7 = double.Parse(match2.Groups[4].Value, CultureInfo.InvariantCulture); byte a2 = (byte)Math.Clamp((int)(num7 * 255.0), 0, 255); if (num4 <= 255 && num5 <= 255 && num6 <= 255) { AddColorItems(list, num4, num5, num6, a2, $"rgba({num4},{num5},{num6},{num7:G3})"); return Task.FromResult((IEnumerable)list); } } Match match3 = _hslRe.Match(text); if (match3.Success) { double num8 = double.Parse(match3.Groups[1].Value, CultureInfo.InvariantCulture); double num9 = double.Parse(match3.Groups[2].Value, CultureInfo.InvariantCulture) / 100.0; double num10 = double.Parse(match3.Groups[3].Value, CultureInfo.InvariantCulture) / 100.0; var (r2, g2, b2) = HslToRgb(num8, num9, num10); AddColorItems(list, r2, g2, b2, byte.MaxValue, $"hsl({num8:G},{num9 * 100.0:G}%,{num10 * 100.0:G}%)"); return Task.FromResult((IEnumerable)list); } if (_namedColors.TryGetValue(text, out string value)) { string text3 = value.TrimStart('#'); int r3 = Convert.ToInt32(text3.Substring(0, 2), 16); int g3 = Convert.ToInt32(text3.Substring(2, 2), 16); int b3 = Convert.ToInt32(text3.Substring(4, 2), 16); AddColorItems(list, r3, g3, b3, byte.MaxValue, value.ToUpperInvariant()); return Task.FromResult((IEnumerable)list); } string searchQ = text.ToLowerInvariant(); IEnumerable> enumerable = _namedColors.Where>((KeyValuePair kv) => kv.Key.Contains(searchQ, StringComparison.OrdinalIgnoreCase)).Take(8); foreach (KeyValuePair item in enumerable) { list.Add(new LauncherItem(item.Key + " → " + item.Value.ToUpperInvariant(), "Enter로 HEX 복사", null, item.Value.ToUpperInvariant(), null, "\ue771")); } if (!list.Any()) { list.Add(new LauncherItem("인식할 수 없는 색상", "#RRGGBB · RGB(r,g,b) · hsl(h,s%,l%) · red 등 색상 이름", null, null, null, "\uea39")); } return Task.FromResult((IEnumerable)list); } public Task ExecuteAsync(LauncherItem item, CancellationToken ct) { if (item.Data is string text) { try { Clipboard.SetText(text); } catch { } } return Task.CompletedTask; } private static void AddColorItems(List items, int r, int g, int b, byte a, string source) { string text = ((a == byte.MaxValue) ? $"#{r:X2}{g:X2}{b:X2}" : $"#{a:X2}{r:X2}{g:X2}{b:X2}"); string text2 = ((a == byte.MaxValue) ? $"rgb({r}, {g}, {b})" : $"rgba({r}, {g}, {b}, {(double)(int)a / 255.0:G3})"); (double h, double s, double l) tuple = RgbToHsl(r, g, b); double item = tuple.h; double item2 = tuple.s; double item3 = tuple.l; string text3 = $"hsl({item:G3}, {item2 * 100.0:G3}%, {item3 * 100.0:G3}%)"; (double h, double s, double v) tuple2 = RgbToHsv(r, g, b); double item4 = tuple2.h; double item5 = tuple2.s; double item6 = tuple2.v; string text4 = $"hsv({item4:G3}, {item5 * 100.0:G3}%, {item6 * 100.0:G3}%)"; items.Add(new LauncherItem("HEX → " + text.ToUpperInvariant(), source + " · Enter로 복사", null, text.ToUpperInvariant(), null, "\ue771")); items.Add(new LauncherItem("RGB → " + text2, source + " · Enter로 복사", null, text2, null, "\ue771")); items.Add(new LauncherItem("HSL → " + text3, source + " · Enter로 복사", null, text3, null, "\ue771")); items.Add(new LauncherItem("HSV → " + text4, source + " · Enter로 복사", null, text4, null, "\ue771")); } private static (double h, double s, double l) RgbToHsl(int r, int g, int b) { double num = (double)r / 255.0; double num2 = (double)g / 255.0; double num3 = (double)b / 255.0; double num4 = Math.Max(num, Math.Max(num2, num3)); double num5 = Math.Min(num, Math.Min(num2, num3)); double num6 = (num4 + num5) / 2.0; double num7 = 0.0; double value = 0.0; if (num4 != num5) { double num8 = num4 - num5; value = ((num6 > 0.5) ? (num8 / (2.0 - num4 - num5)) : (num8 / (num4 + num5))); num7 = ((num4 == num) ? ((num2 - num3) / num8 + (double)((num2 < num3) ? 6 : 0)) : ((num4 == num2) ? ((num3 - num) / num8 + 2.0) : ((num - num2) / num8 + 4.0))); num7 /= 6.0; } return (h: Math.Round(num7 * 360.0, 1), s: Math.Round(value, 4), l: Math.Round(num6, 4)); } private static (double h, double s, double v) RgbToHsv(int r, int g, int b) { double num = (double)r / 255.0; double num2 = (double)g / 255.0; double num3 = (double)b / 255.0; double num4 = Math.Max(num, Math.Max(num2, num3)); double num5 = Math.Min(num, Math.Min(num2, num3)); double value = num4; double value2 = 0.0; double num6 = 0.0; double num7 = num4 - num5; if (num4 != 0.0) { value2 = num7 / num4; } if (num7 != 0.0) { num6 = ((num4 == num) ? ((num2 - num3) / num7 + (double)((num2 < num3) ? 6 : 0)) : ((num4 == num2) ? ((num3 - num) / num7 + 2.0) : ((num - num2) / num7 + 4.0))); num6 /= 6.0; } return (h: Math.Round(num6 * 360.0, 1), s: Math.Round(value2, 4), v: Math.Round(value, 4)); } private static (int r, int g, int b) HslToRgb(double h, double s, double l) { double num; double num2; double num3; if (s == 0.0) { num = (num2 = (num3 = l)); } else { double num4 = ((l < 0.5) ? (l * (1.0 + s)) : (l + s - l * s)); double p = 2.0 * l - num4; h /= 360.0; num = Hue2Rgb(p, num4, h + 1.0 / 3.0); num2 = Hue2Rgb(p, num4, h); num3 = Hue2Rgb(p, num4, h - 1.0 / 3.0); } return (r: (int)Math.Round(num * 255.0), g: (int)Math.Round(num2 * 255.0), b: (int)Math.Round(num3 * 255.0)); } private static double Hue2Rgb(double p, double q, double t) { if (t < 0.0) { t += 1.0; } if (t > 1.0) { t -= 1.0; } if (t < 1.0 / 6.0) { return p + (q - p) * 6.0 * t; } if (t < 0.5) { return q; } if (t < 2.0 / 3.0) { return p + (q - p) * (2.0 / 3.0 - t) * 6.0; } return p; } }