334 lines
11 KiB
C#
334 lines
11 KiB
C#
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<string, string> _namedColors = new Dictionary<string, string>(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<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(query))
|
|
{
|
|
return Task.FromResult((IEnumerable<LauncherItem>)new _003C_003Ez__ReadOnlySingleElementList<LauncherItem>(new LauncherItem("색상 코드를 입력하세요", "예: #FF5500 · 255,85,0 · red · hsl(20,100%,50%)", null, null, null, "\ue771")));
|
|
}
|
|
string text = query.Trim();
|
|
List<LauncherItem> list = new List<LauncherItem>();
|
|
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<LauncherItem>)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<LauncherItem>)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<LauncherItem>)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<LauncherItem>)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<LauncherItem>)list);
|
|
}
|
|
string searchQ = text.ToLowerInvariant();
|
|
IEnumerable<KeyValuePair<string, string>> enumerable = _namedColors.Where<KeyValuePair<string, string>>((KeyValuePair<string, string> kv) => kv.Key.Contains(searchQ, StringComparison.OrdinalIgnoreCase)).Take(8);
|
|
foreach (KeyValuePair<string, string> 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<LauncherItem>)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<LauncherItem> 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;
|
|
}
|
|
}
|