[Phase51] 대규모 파일 분리 — 9개 파일 → 19개 파일

SettingsWindow.Tools:
- SettingsWindow.Tools.cs: 605 → 238줄 (BuildToolRegistryPanel 유지)
- SettingsWindow.SkillListPanel.cs (신규): BuildSkillListSection, CreateSkillGroupCard (295줄)

LauncherWindow.Keyboard:
- LauncherWindow.Keyboard.cs: 593 → 454줄
- LauncherWindow.ShortcutHelp.cs (신규): ShowShortcutHelp, ShowToast, TryHandleSpecialAction (139줄)

CalculatorHandler:
- CalculatorHandler.cs: 566 → ~240줄 (CalculatorHandler 클래스만 유지)
- UnitConverter.cs (신규): 단위변환 클래스 독립 파일 (152줄)
- MathEvaluator.cs (신규): 수식파서 클래스 독립 파일 (183줄)

EmojiHandler:
- EmojiHandler.cs: 553 → 70줄 (핸들러 메서드만 유지)
- EmojiHandler.Data.cs (신규): 이모지 데이터베이스 배열 (~490줄)

DocumentPlannerTool:
- DocumentPlannerTool.cs: 598 → 324줄 (Execute 메서드 유지)
- DocumentPlannerTool.Generators.cs (신규): GenerateHtml/Docx/Markdown, BuildSections 등 (274줄)

DocumentReaderTool:
- DocumentReaderTool.cs: 571 → 338줄 (ExecuteAsync + PDF 메서드 유지)
- DocumentReaderTool.Formats.cs (신규): BibTeX/RIS/DOCX/XLSX/Text/Helpers (233줄)

CodeIndexService:
- CodeIndexService.cs: 588 → 285줄 (DB/인덱싱 유지)
- CodeIndexService.Search.cs (신규): Search, TF-IDF, Tokenize, Dispose (199줄)

ClipboardHistoryService:
- ClipboardHistoryService.cs: 575 → 458줄
- ClipboardHistoryService.ImageCache.cs (신규): 이미지 캐시 유틸리티 (120줄)

IndexService:
- IndexService.cs: 568 → 412줄
- IndexService.Helpers.cs (신규): 경로 탐색 헬퍼 + 검색 캐시 (163줄)

빌드: 경고 0, 오류 0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-03 21:34:51 +09:00
parent 5bed67f64e
commit 55befebf34
19 changed files with 2396 additions and 2288 deletions

View File

@@ -111,335 +111,6 @@ public class CalculatorHandler : IActionHandler
}
}
// ─── 단위 변환 ─────────────────────────────────────────────────────────────────
/// <summary>
/// "100km in miles", "32f in c", "5lb to kg" 형식의 단위 변환.
/// </summary>
internal static class UnitConverter
{
// 패턴: <숫자> <단위> in|to <단위>
private static readonly Regex Pattern = new(
@"^(-?\d+(?:\.\d+)?)\s*([a-z°/²³µ]+)\s+(?:in|to)\s+([a-z°/²³µ]+)$",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
public static bool TryConvert(string input, out string? result)
{
result = null;
var m = Pattern.Match(input.Trim());
if (!m.Success) return false;
if (!double.TryParse(m.Groups[1].Value,
System.Globalization.NumberStyles.Float,
System.Globalization.CultureInfo.InvariantCulture,
out var value))
return false;
var from = m.Groups[2].Value.ToLowerInvariant();
var to = m.Groups[3].Value.ToLowerInvariant();
// 온도는 비선형 → 별도 처리
if (TryConvertTemperature(value, from, to, out var tResult))
{
result = $"{FormatNum(tResult)} {TemperatureLabel(to)}";
return true;
}
// 나머지 범주(선형 변환)
foreach (var table in _tables)
{
if (table.TryGetValue(from, out var fromFactor) &&
table.TryGetValue(to, out var toFactor))
{
var converted = value * fromFactor / toFactor;
result = $"{FormatNum(converted)} {to}";
return true;
}
}
return false;
}
// ─── 온도 ────────────────────────────────────────────────────────────────
private static bool TryConvertTemperature(double v, string from, string to, out double r)
{
r = 0;
// 섭씨 표준화
double celsius;
switch (from)
{
case "c": case "°c": case "celsius": celsius = v; break;
case "f": case "°f": case "fahrenheit": celsius = (v - 32) * 5 / 9; break;
case "k": case "kelvin": celsius = v - 273.15; break;
default: return false;
}
switch (to)
{
case "c": case "°c": case "celsius": r = celsius; break;
case "f": case "°f": case "fahrenheit": r = celsius * 9 / 5 + 32; break;
case "k": case "kelvin": r = celsius + 273.15; break;
default: return false;
}
return true;
}
private static string TemperatureLabel(string unit) => unit switch
{
"c" or "°c" or "celsius" => "°C",
"f" or "°f" or "fahrenheit" => "°F",
"k" or "kelvin" => "K",
_ => unit
};
// ─── 선형 변환 테이블 (기준 단위 = 1) ────────────────────────────────────
// 길이 (기준: m)
private static readonly Dictionary<string, double> _length = new()
{
["km"] = 1000, ["m"] = 1, ["cm"] = 0.01, ["mm"] = 0.001,
["mi"] = 1609.344, ["mile"] = 1609.344, ["miles"] = 1609.344,
["ft"] = 0.3048, ["feet"] = 0.3048, ["foot"] = 0.3048,
["in"] = 0.0254, ["inch"] = 0.0254, ["inches"] = 0.0254,
["yd"] = 0.9144, ["yard"] = 0.9144, ["yards"] = 0.9144,
["nm"] = 1e-9,
};
// 무게 (기준: kg)
private static readonly Dictionary<string, double> _weight = new()
{
["t"] = 1000, ["ton"] = 1000, ["tonnes"] = 1000,
["kg"] = 1, ["g"] = 0.001, ["mg"] = 1e-6,
["lb"] = 0.453592, ["lbs"] = 0.453592, ["pound"] = 0.453592, ["pounds"] = 0.453592,
["oz"] = 0.0283495, ["ounce"] = 0.0283495, ["ounces"] = 0.0283495,
};
// 속도 (기준: m/s)
private static readonly Dictionary<string, double> _speed = new()
{
["m/s"] = 1, ["mps"] = 1,
["km/h"] = 1.0 / 3.6, ["kmh"] = 1.0 / 3.6, ["kph"] = 1.0 / 3.6,
["mph"] = 0.44704,
["kn"] = 0.514444, ["knot"] = 0.514444, ["knots"] = 0.514444,
};
// 데이터 (기준: byte)
private static readonly Dictionary<string, double> _data = new()
{
["b"] = 1, ["byte"] = 1, ["bytes"] = 1,
["kb"] = 1024, ["kib"] = 1024,
["mb"] = 1024 * 1024, ["mib"] = 1024 * 1024,
["gb"] = 1024.0 * 1024 * 1024, ["gib"] = 1024.0 * 1024 * 1024,
["tb"] = 1024.0 * 1024 * 1024 * 1024, ["tib"] = 1024.0 * 1024 * 1024 * 1024,
["pb"] = 1024.0 * 1024 * 1024 * 1024 * 1024,
};
// 넓이 (기준: m²)
private static readonly Dictionary<string, double> _area = new()
{
["m²"] = 1, ["m2"] = 1,
["km²"] = 1e6, ["km2"] = 1e6,
["cm²"] = 1e-4, ["cm2"] = 1e-4,
["ha"] = 10000,
["acre"] = 4046.86, ["acres"] = 4046.86,
["ft²"] = 0.092903, ["ft2"] = 0.092903,
};
private static readonly List<Dictionary<string, double>> _tables = new()
{ _length, _weight, _speed, _data, _area };
private static string FormatNum(double v)
{
if (v == Math.Floor(v) && Math.Abs(v) < 1e12)
return ((long)v).ToString("N0", System.Globalization.CultureInfo.CurrentCulture);
return v.ToString("G6", System.Globalization.CultureInfo.InvariantCulture);
}
}
// ─── 수식 파서 ─────────────────────────────────────────────────────────────────
/// <summary>
/// 재귀 하강 파서 기반 수학 수식 평가기.
/// 지원: +, -, *, /, %, ^ (거듭제곱), 괄호, 단항 음수,
/// sqrt, abs, ceil, floor, round, sin, cos, tan (도 단위),
/// log (밑 10), ln (자연로그), pi, e
/// </summary>
internal static class MathEvaluator
{
public static double Evaluate(string expr)
{
var evaluator = new Evaluator(
expr.Replace(" ", "")
.Replace("×", "*")
.Replace("÷", "/")
.Replace("", ",")
.ToLowerInvariant());
return evaluator.Parse();
}
private class Evaluator
{
private readonly string _s;
private int _i;
public Evaluator(string s) { _s = s; _i = 0; }
public double Parse()
{
var result = ParseExpr();
if (_i < _s.Length)
throw new InvalidOperationException($"예기치 않은 문자: '{_s[_i]}'");
return result;
}
// 덧셈 / 뺄셈
private double ParseExpr()
{
var left = ParseTerm();
while (_i < _s.Length && (_s[_i] == '+' || _s[_i] == '-'))
{
var op = _s[_i++];
var right = ParseTerm();
left = op == '+' ? left + right : left - right;
}
return left;
}
// 곱셈 / 나눗셈 / 나머지
private double ParseTerm()
{
var left = ParsePower();
while (_i < _s.Length && (_s[_i] == '*' || _s[_i] == '/' || _s[_i] == '%'))
{
var op = _s[_i++];
var right = ParsePower();
left = op == '*' ? left * right
: op == '/' ? left / right
: left % right;
}
return left;
}
// 거듭제곱 (오른쪽 결합)
private double ParsePower()
{
var b = ParseUnary();
if (_i < _s.Length && _s[_i] == '^')
{
_i++;
var exp = ParseUnary();
return Math.Pow(b, exp);
}
return b;
}
// 단항 부호
private double ParseUnary()
{
if (_i < _s.Length && _s[_i] == '-') { _i++; return -ParsePrimary(); }
if (_i < _s.Length && _s[_i] == '+') { _i++; return ParsePrimary(); }
return ParsePrimary();
}
// 리터럴 / 괄호 / 함수 호출
private double ParsePrimary()
{
if (_i >= _s.Length)
throw new InvalidOperationException("수식이 불완전합니다.");
// 16진수 리터럴 0x...
if (_i + 1 < _s.Length && _s[_i] == '0' && _s[_i + 1] == 'x')
{
_i += 2;
var hexStart = _i;
while (_i < _s.Length && "0123456789abcdef".Contains(_s[_i])) _i++;
return Convert.ToInt64(_s[hexStart.._i], 16);
}
// 숫자
if (char.IsDigit(_s[_i]) || _s[_i] == '.')
{
var start = _i;
while (_i < _s.Length && (char.IsDigit(_s[_i]) || _s[_i] == '.')) _i++;
// 과학적 표기: 1.5e3
if (_i < _s.Length && _s[_i] == 'e')
{
_i++;
if (_i < _s.Length && (_s[_i] == '+' || _s[_i] == '-')) _i++;
while (_i < _s.Length && char.IsDigit(_s[_i])) _i++;
}
return double.Parse(_s[start.._i],
System.Globalization.NumberStyles.Float,
System.Globalization.CultureInfo.InvariantCulture);
}
// 괄호
if (_s[_i] == '(')
{
_i++;
var val = ParseExpr();
if (_i < _s.Length && _s[_i] == ')') _i++;
return val;
}
// 식별자 (상수 또는 함수)
if (char.IsLetter(_s[_i]))
{
var start = _i;
while (_i < _s.Length && (char.IsLetterOrDigit(_s[_i]) || _s[_i] == '_')) _i++;
var name = _s[start.._i];
// 상수
if (name == "pi") return Math.PI;
if (name == "e") return Math.E;
if (name == "inf") return double.PositiveInfinity;
// 함수 호출
if (_i < _s.Length && _s[_i] == '(')
{
_i++; // (
var arg = ParseExpr();
// 두 번째 인자 (pow, log2 등)
double? arg2 = null;
if (_i < _s.Length && _s[_i] == ',')
{
_i++;
arg2 = ParseExpr();
}
if (_i < _s.Length && _s[_i] == ')') _i++;
return name switch
{
"sqrt" => Math.Sqrt(arg),
"abs" => Math.Abs(arg),
"ceil" => Math.Ceiling(arg),
"floor" => Math.Floor(arg),
"round" => arg2.HasValue ? Math.Round(arg, (int)arg2.Value) : Math.Round(arg),
"sin" => Math.Sin(arg * Math.PI / 180), // 도 단위
"cos" => Math.Cos(arg * Math.PI / 180),
"tan" => Math.Tan(arg * Math.PI / 180),
"asin" => Math.Asin(arg) * 180 / Math.PI,
"acos" => Math.Acos(arg) * 180 / Math.PI,
"atan" => Math.Atan(arg) * 180 / Math.PI,
"log" => arg2.HasValue ? Math.Log(arg, arg2.Value) : Math.Log10(arg),
"log2" => Math.Log2(arg),
"ln" => Math.Log(arg),
"exp" => Math.Exp(arg),
"pow" => arg2.HasValue ? Math.Pow(arg, arg2.Value) : throw new InvalidOperationException("pow(x,y) 형식으로 사용하세요."),
"min" => arg2.HasValue ? Math.Min(arg, arg2.Value) : arg,
"max" => arg2.HasValue ? Math.Max(arg, arg2.Value) : arg,
_ => throw new InvalidOperationException($"알 수 없는 함수: {name}()")
};
}
throw new InvalidOperationException($"알 수 없는 식별자: {name}");
}
throw new InvalidOperationException($"예기치 않은 문자: '{_s[_i]}'");
}
}
}
// ─── 통화 변환 ──────────────────────────────────────────────────────────────────
/// <summary>

View File

@@ -0,0 +1,488 @@
namespace AxCopilot.Handlers;
public partial class EmojiHandler
{
// ─── 이모지 데이터베이스 (이모지, 이름(한/영), 태그) ──────────────────────
private static readonly (string Emoji, string Name, string Tags)[] _emojis =
{
// 표정 / 감정
("😀", "크게 웃는 얼굴", "smile happy grin 웃음 행복"),
("😃", "웃는 얼굴", "smile happy joy 웃음"),
("😄", "눈 웃음", "smile laugh 웃음 기쁨"),
("😁", "히죽 웃음", "grin beam 씩 웃다"),
("😆", "크게 웃음", "laughing 폭소"),
("😅", "식은땀 웃음", "sweat smile 안도"),
("🤣", "바닥 구르며 웃음", "rofl lol 빵 웃음"),
("😂", "눈물 나게 웃음", "joy tears laugh 폭소"),
("🙂", "살짝 웃음", "slightly smiling 미소"),
("🙃", "거꾸로 웃음", "upside down 뒤집힌"),
("😉", "윙크", "wink 윙크"),
("😊", "볼 빨개진 웃음", "blush 부끄러움 미소"),
("😇", "천사", "angel halo 천사 선량"),
("🥰", "사랑스러운 얼굴", "love hearts 사랑 하트"),
("😍", "하트 눈", "heart eyes 사랑 반함"),
("🤩", "별 눈", "star struck 감동 황홀"),
("😘", "뽀뽀", "kiss blow 키스 뽀뽀"),
("😗", "오므린 입", "kiss whistle 키스"),
("😚", "눈 감고 뽀뽀", "kiss 키스"),
("😙", "볼 뽀뽀", "kiss 키스"),
("😋", "맛있다", "yum delicious 맛 음식"),
("😛", "혀 내밀기", "tongue out 혀 놀림"),
("😜", "윙크하며 혀", "wink tongue 장난"),
("🤪", "미친 표정", "zany crazy 정신없음"),
("😝", "눈 감고 혀", "tongue 혀"),
("🤑", "돈 눈", "money face 돈 부자"),
("🤗", "포옹", "hugging hug 안아줘 포옹"),
("🤭", "입 가리고", "hand over mouth 헉 깜짝"),
("🤫", "쉿", "shushing quiet 조용 쉿"),
("🤔", "생각 중", "thinking 고민 생각"),
("🤐", "입 막음", "zipper mouth 비밀"),
("🤨", "의심", "raised eyebrow 의심 의아"),
("😐", "무표정", "neutral 무감각 무표정"),
("😑", "표정 없음", "expressionless 냉담"),
("😶", "입 없는 얼굴", "no mouth 침묵"),
("😏", "비웃음", "smirk 비웃 냉소"),
("😒", "불만", "unamused 불만 짜증"),
("🙄", "눈 굴리기", "eye roll 어이없음"),
("😬", "이 드러냄", "grimace 으 민망"),
("🤥", "거짓말", "lying pinocchio 거짓말"),
("😌", "안도/평온", "relieved 안도 평온"),
("😔", "슬픔", "pensive sad 슬픔 우울"),
("😪", "졸림", "sleepy 졸음"),
("🤤", "침 흘림", "drooling 군침 식욕"),
("😴", "잠", "sleeping sleep 수면 잠"),
("😷", "마스크", "mask sick 마스크 아픔"),
("🤒", "열 나는", "sick fever 열 아픔"),
("🤕", "머리 붕대", "injured hurt 부상"),
("🤢", "구역질", "nauseated sick 구역 메스꺼움"),
("🤮", "토하는", "vomit 구토"),
("🤧", "재채기", "sneezing sick 재채기 감기"),
("🥵", "더운", "hot overheated 더움 열"),
("🥶", "추운", "cold freezing 추움 냉기"),
("🥴", "어지러운", "woozy 어지럼 취함"),
("😵", "어질어질", "dizzy 어지럼 충격"),
("🤯", "머리 폭발", "exploding head 충격 대박"),
("🤠", "카우보이", "cowboy hat 카우보이"),
("🥸", "변장", "disguise 변장 선글라스"),
("😎", "쿨한", "cool sunglasses 선글라스 쿨"),
("🤓", "공부벌레", "nerd glasses 공부 안경"),
("🧐", "모노클", "monocle curious 고상 탐정"),
("😕", "당황", "confused 당황 모호"),
("😟", "걱정", "worried concern 걱정"),
("🙁", "살짝 찡그림", "frown 슬픔"),
("☹️", "찡그린 얼굴", "frown sad 슬픔"),
("😮", "입 벌림", "open mouth surprised 놀람"),
("😯", "놀람", "hushed surprised 깜짝"),
("😲", "충격", "astonished 충격 놀람"),
("😳", "얼굴 빨개짐", "flushed embarrassed 부끄럼 당황"),
("🥺", "애원", "pleading eyes 부탁 눈빛"),
("😦", "찡그리며 벌린 입", "frowning 불안"),
("😧", "고통", "anguished 고통"),
("😨", "무서움", "fearful scared 무서움 공포"),
("😰", "식은땀", "anxious sweat 불안 걱정"),
("😥", "눈물 조금", "sad disappointed 실망 눈물"),
("😢", "울음", "cry sad 슬픔 눈물"),
("😭", "엉엉 울음", "loudly crying sob 통곡"),
("😱", "공포에 질림", "screaming fear 비명 공포"),
("😖", "혼란", "confounded 혼란"),
("😣", "힘듦", "persevering 고생"),
("😞", "실망", "disappointed 실망"),
("😓", "땀", "downcast sweat 땀 힘듦"),
("😩", "피곤", "weary tired 지침 피곤"),
("😫", "극도로 지침", "tired exhausted 탈진"),
("🥱", "하품", "yawning bored 하품 지루함"),
("😤", "콧김", "triumph snort 분노 콧김"),
("😡", "화남", "angry mad 화남 분노"),
("😠", "성남", "angry 화 성남"),
("🤬", "욕", "cursing swearing 욕 분노"),
("😈", "나쁜 미소", "smiling devil 악마 장난"),
("👿", "화난 악마", "angry devil 악마"),
("💀", "해골", "skull death 해골 죽음"),
("☠️", "해골 십자", "skull crossbones 독"),
("💩", "응가", "poop 똥 응가"),
("🤡", "피에로", "clown 광대"),
("👹", "도깨비", "ogre 도깨비 귀신"),
("👺", "텐구", "goblin 텐구"),
("👻", "유령", "ghost 유령 귀신"),
("👾", "우주인", "alien monster 외계인 게임"),
("🤖", "로봇", "robot 로봇"),
// 손 / 몸
("👋", "손 흔들기", "wave waving hi bye 안녕"),
("🤚", "손 뒤", "raised back hand 손"),
("🖐️", "손바닥", "hand palm 다섯 손가락"),
("✋", "손 들기", "raised hand 손 들기 멈춤"),
("🖖", "스팍 손인사", "vulcan salute 스타트렉"),
("👌", "오케이", "ok perfect 오케이 좋아"),
("🤌", "손가락 모아", "pinched fingers 이탈리아"),
("✌️", "브이", "victory peace v 브이 평화"),
("🤞", "행운 손가락", "crossed fingers lucky 행운 기도"),
("🤟", "아이 러브 유", "love you 사랑해"),
("🤘", "록 손", "rock on metal 록"),
("🤙", "전화해", "call me shaka 전화 샤카"),
("👈", "왼쪽 가리킴", "backhand left 왼쪽"),
("👉", "오른쪽 가리킴", "backhand right 오른쪽"),
("👆", "위 가리킴", "backhand up 위"),
("🖕", "욕", "middle finger 욕"),
("👇", "아래 가리킴", "backhand down 아래"),
("☝️", "검지 들기", "index pointing up 하나 포인트"),
("👍", "좋아요", "thumbs up like good 좋아 최고"),
("👎", "싫어요", "thumbs down dislike 싫어 별로"),
("✊", "주먹", "fist punch 주먹"),
("👊", "주먹 치기", "punch fist 주먹"),
("🤛", "왼 주먹", "left fist 주먹"),
("🤜", "오른 주먹", "right fist 주먹"),
("👏", "박수", "clapping applause 박수 응원"),
("🙌", "만세", "raising hands celebrate 만세"),
("👐", "양손 펼침", "open hands 환영"),
("🤲", "두 손 모음", "palms up together 기도 바람"),
("🙏", "두 손 합장", "pray please thanks 감사 부탁 기도"),
("✍️", "글쓰기", "writing pen 글쓰기"),
("💅", "네일", "nail polish manicure 네일 손톱"),
("🤳", "셀카", "selfie 셀카"),
("💪", "근육", "muscle strong 근육 힘"),
("🦾", "기계 팔", "mechanical arm 로봇 팔"),
("🦿", "기계 다리", "mechanical leg 로봇 다리"),
("🦵", "다리", "leg kick 다리"),
("🦶", "발", "foot kick 발"),
("👂", "귀", "ear hear 귀"),
("🦻", "보청기 귀", "ear hearing aid 보청기"),
("👃", "코", "nose smell 코"),
("🫀", "심장", "heart anatomical 심장"),
("🫁", "폐", "lungs 폐"),
("🧠", "뇌", "brain mind 뇌 지능"),
("🦷", "치아", "tooth dental 치아"),
("🦴", "뼈", "bone 뼈"),
("👀", "눈", "eyes look see 눈 보기"),
("👁️", "한쪽 눈", "eye 눈"),
("👅", "혀", "tongue 혀"),
("👄", "입술", "lips mouth 입술"),
("💋", "입맞춤", "kiss lips 키스 입술"),
("🩸", "피", "blood drop 피 혈액"),
// 하트 / 감정 기호
("❤️", "빨간 하트", "red heart love 사랑 빨강"),
("🧡", "주황 하트", "orange heart 사랑"),
("💛", "노란 하트", "yellow heart 사랑"),
("💚", "초록 하트", "green heart 사랑"),
("💙", "파란 하트", "blue heart 사랑"),
("💜", "보라 하트", "purple heart 사랑"),
("🖤", "검은 하트", "black heart 사랑 다크"),
("🤍", "흰 하트", "white heart 사랑"),
("🤎", "갈색 하트", "brown heart 사랑"),
("💔", "깨진 하트", "broken heart 이별 상처"),
("❣️", "느낌표 하트", "heart exclamation 사랑"),
("💕", "두 하트", "two hearts 사랑"),
("💞", "회전 하트", "revolving hearts 사랑"),
("💓", "뛰는 하트", "beating heart 설렘"),
("💗", "성장 하트", "growing heart 사랑"),
("💖", "반짝 하트", "sparkling heart 사랑"),
("💘", "화살 하트", "heart arrow 큐피드"),
("💝", "리본 하트", "heart ribbon 선물 사랑"),
("💟", "하트 장식", "heart decoration 사랑"),
("☮️", "평화", "peace 평화"),
("✝️", "십자가", "cross 기독교"),
("☯️", "음양", "yin yang 음양 균형"),
("🔮", "수정구", "crystal ball magic 마법 점"),
("✨", "반짝임", "sparkles glitter 빛 반짝"),
("⭐", "별", "star 별"),
("🌟", "빛나는 별", "glowing star 별빛"),
("💫", "현기증", "dizzy star 빙글"),
("⚡", "번개", "lightning bolt 번개 전기"),
("🔥", "불", "fire hot 불 열정"),
("💥", "폭발", "explosion boom 폭발"),
("❄️", "눈송이", "snowflake cold 눈 추위"),
("🌈", "무지개", "rainbow 무지개"),
("☀️", "태양", "sun sunny 태양 맑음"),
("🌙", "달", "moon crescent 달"),
("🌊", "파도", "wave ocean 파도 바다"),
("💨", "바람", "wind dash 바람"),
("💦", "물방울", "sweat droplets water 물"),
("🌸", "벚꽃", "cherry blossom 벚꽃 봄"),
("🌹", "장미", "rose 장미 꽃"),
("🌺", "히비스커스", "hibiscus 꽃"),
("🌻", "해바라기", "sunflower 해바라기"),
("🌼", "꽃", "blossom flower 꽃"),
("🌷", "튤립", "tulip 튤립"),
("💐", "꽃다발", "bouquet flowers 꽃다발"),
("🍀", "네잎클로버", "four leaf clover lucky 행운"),
("🌿", "허브", "herb green 풀 허브"),
("🍃", "잎사귀", "leaf 잎"),
// 음식
("🍕", "피자", "pizza 피자"),
("🍔", "햄버거", "hamburger burger 버거"),
("🌮", "타코", "taco 타코"),
("🍜", "라면", "ramen noodles 라면 국수"),
("🍱", "도시락", "bento box 도시락"),
("🍣", "초밥", "sushi 초밥"),
("🍚", "밥", "rice 밥"),
("🍛", "카레", "curry rice 카레"),
("🍝", "파스타", "pasta spaghetti 파스타"),
("🍦", "소프트 아이스크림", "ice cream soft serve 아이스크림"),
("🎂", "생일 케이크", "cake birthday 생일 케이크"),
("🍰", "케이크 조각", "cake slice 케이크"),
("🧁", "컵케이크", "cupcake 컵케이크"),
("🍩", "도넛", "donut 도넛"),
("🍪", "쿠키", "cookie 쿠키"),
("🍫", "초콜릿", "chocolate bar 초콜릿"),
("🍬", "사탕", "candy 사탕"),
("🍭", "막대 사탕", "lollipop 막대사탕"),
("🍺", "맥주", "beer mug 맥주"),
("🍻", "건배", "clinking beer 건배"),
("🥂", "샴페인 건배", "champagne 샴페인 건배"),
("🍷", "와인", "wine 와인"),
("☕", "커피", "coffee hot 커피"),
("🧃", "주스", "juice 주스"),
("🥤", "음료", "drink cup 음료 컵"),
("🧋", "버블티", "bubble tea boba 버블티"),
("🍵", "녹차", "tea matcha 차 녹차"),
// 동물
("🐶", "강아지", "dog puppy 강아지 개"),
("🐱", "고양이", "cat kitten 고양이"),
("🐭", "쥐", "mouse 쥐"),
("🐹", "햄스터", "hamster 햄스터"),
("🐰", "토끼", "rabbit bunny 토끼"),
("🦊", "여우", "fox 여우"),
("🐻", "곰", "bear 곰"),
("🐼", "판다", "panda 판다"),
("🐨", "코알라", "koala 코알라"),
("🐯", "호랑이", "tiger 호랑이"),
("🦁", "사자", "lion 사자"),
("🐮", "소", "cow 소"),
("🐷", "돼지", "pig 돼지"),
("🐸", "개구리", "frog 개구리"),
("🐵", "원숭이", "monkey 원숭이"),
("🙈", "눈 가린 원숭이", "see no evil monkey 안 봐"),
("🙉", "귀 가린 원숭이", "hear no evil monkey 안 들어"),
("🙊", "입 가린 원숭이", "speak no evil monkey 안 말해"),
("🐔", "닭", "chicken 닭"),
("🐧", "펭귄", "penguin 펭귄"),
("🐦", "새", "bird 새"),
("🦆", "오리", "duck 오리"),
("🦅", "독수리", "eagle 독수리"),
("🦉", "부엉이", "owl 부엉이"),
("🐍", "뱀", "snake 뱀"),
("🐢", "거북이", "turtle 거북이"),
("🦋", "나비", "butterfly 나비"),
("🐌", "달팽이", "snail 달팽이"),
("🐛", "애벌레", "bug caterpillar 애벌레"),
("🐝", "꿀벌", "bee honeybee 벌"),
("🦑", "오징어", "squid 오징어"),
("🐙", "문어", "octopus 문어"),
("🐠", "열대어", "tropical fish 열대어"),
("🐡", "복어", "blowfish puffer 복어"),
("🦈", "상어", "shark 상어"),
("🐬", "돌고래", "dolphin 돌고래"),
("🐳", "고래", "whale 고래"),
("🐲", "용", "dragon 용"),
("🦄", "유니콘", "unicorn 유니콘"),
// 물건 / 도구
("📱", "스마트폰", "phone mobile smartphone 폰"),
("💻", "노트북", "laptop computer 노트북"),
("🖥️", "데스크톱", "desktop computer 컴퓨터"),
("⌨️", "키보드", "keyboard 키보드"),
("🖱️", "마우스", "mouse 마우스"),
("🖨️", "프린터", "printer 프린터"),
("📷", "카메라", "camera 카메라"),
("📸", "플래시 카메라", "camera flash 사진"),
("📹", "비디오 카메라", "video camera 동영상"),
("🎥", "영화 카메라", "movie camera film 영화"),
("📺", "TV", "television tv 텔레비전"),
("📻", "라디오", "radio 라디오"),
("🎙️", "마이크", "microphone studio 마이크"),
("🎤", "마이크 핸드헬드", "microphone karaoke 마이크"),
("🎧", "헤드폰", "headphones 헤드폰"),
("📡", "안테나", "satellite antenna 안테나"),
("🔋", "배터리", "battery 배터리"),
("🔌", "전원 플러그", "plug electric 플러그"),
("💡", "전구", "bulb idea light 전구 아이디어"),
("🔦", "손전등", "flashlight torch 손전등"),
("🕯️", "양초", "candle 양초"),
("📚", "책", "books stack 책"),
("📖", "열린 책", "open book read 독서"),
("📝", "메모", "memo note pencil 메모 노트"),
("✏️", "연필", "pencil 연필"),
("🖊️", "펜", "pen 펜"),
("📌", "압정", "pushpin pin 압정"),
("📎", "클립", "paperclip 클립"),
("✂️", "가위", "scissors cut 가위"),
("🗂️", "파일 폴더", "card index dividers folder 파일"),
("📁", "폴더", "folder 폴더"),
("📂", "열린 폴더", "open folder 폴더"),
("🗃️", "파일 박스", "card file box 서류함"),
("🗑️", "휴지통", "wastebasket trash 휴지통"),
("🔒", "잠금", "locked lock 잠금"),
("🔓", "열림", "unlocked 열림"),
("🔑", "열쇠", "key 열쇠"),
("🗝️", "구식 열쇠", "old key 열쇠"),
("🔨", "망치", "hammer 망치"),
("🔧", "렌치", "wrench tool 렌치"),
("🔩", "나사", "nut bolt 나사"),
("⚙️", "톱니바퀴", "gear settings 설정 톱니"),
("🛠️", "도구", "tools hammer wrench 도구"),
("💊", "알약", "pill medicine 약 알약"),
("💉", "주사기", "syringe injection 주사"),
("🩺", "청진기", "stethoscope doctor 청진기"),
("🏆", "트로피", "trophy award 트로피 우승"),
("🥇", "금메달", "first gold medal 금메달"),
("🥈", "은메달", "second silver 은메달"),
("🥉", "동메달", "third bronze 동메달"),
("🎖️", "훈장", "medal military 훈장"),
("🎗️", "리본", "ribbon awareness 리본"),
("🎫", "티켓", "ticket admission 티켓"),
("🎟️", "입장권", "admission tickets 티켓"),
("🎪", "서커스", "circus tent 서커스"),
("🎨", "팔레트", "art palette paint 그림 예술"),
("🎭", "연극", "performing arts theater 연극"),
("🎬", "클래퍼보드", "clapper film 영화 촬영"),
("🎮", "게임 컨트롤러", "video game controller 게임"),
("🎲", "주사위", "dice game 주사위"),
("🎯", "다트", "bullseye target dart 다트 목표"),
("🎳", "볼링", "bowling 볼링"),
("⚽", "축구", "soccer football 축구"),
("🏀", "농구", "basketball 농구"),
("🏈", "미식축구", "american football 미식축구"),
("⚾", "야구", "baseball 야구"),
("🎾", "테니스", "tennis 테니스"),
("🏐", "배구", "volleyball 배구"),
("🏉", "럭비", "rugby 럭비"),
("🎱", "당구", "billiards pool 당구"),
("🏓", "탁구", "ping pong table tennis 탁구"),
("🏸", "배드민턴", "badminton 배드민턴"),
("🥊", "권투 장갑", "boxing glove 권투"),
("🎣", "낚시", "fishing 낚시"),
("🏋️", "역도", "weightlifting gym 헬스 역도"),
("🧘", "명상", "yoga meditation 명상 요가"),
// 이동수단
("🚗", "자동차", "car automobile 자동차"),
("🚕", "택시", "taxi cab 택시"),
("🚙", "SUV", "suv car 차"),
("🚌", "버스", "bus 버스"),
("🚎", "무궤도 전차", "trolleybus 버스"),
("🏎️", "레이싱카", "racing car 레이싱"),
("🚓", "경찰차", "police car 경찰"),
("🚑", "구급차", "ambulance 구급차"),
("🚒", "소방차", "fire truck 소방차"),
("🚐", "미니밴", "minibus van 밴"),
("🚚", "트럭", "truck delivery 트럭"),
("✈️", "비행기", "airplane flight plane 비행기"),
("🚀", "로켓", "rocket space launch 로켓"),
("🛸", "UFO", "flying saucer ufo 유에프오"),
("🚁", "헬리콥터", "helicopter 헬리콥터"),
("🚂", "기차", "train locomotive 기차"),
("🚆", "고속열차", "train 기차"),
("🚇", "지하철", "metro subway 지하철"),
("⛵", "돛단배", "sailboat 요트"),
("🚢", "배", "ship cruise 배"),
("🚲", "자전거", "bicycle bike 자전거"),
("🛵", "스쿠터", "scooter moped 스쿠터"),
("🏍️", "오토바이", "motorcycle 오토바이"),
// 장소
("🏠", "집", "house home 집"),
("🏡", "마당 있는 집", "house garden 집"),
("🏢", "빌딩", "office building 빌딩"),
("🏣", "우체국", "post office 우체국"),
("🏥", "병원", "hospital 병원"),
("🏦", "은행", "bank 은행"),
("🏨", "호텔", "hotel 호텔"),
("🏫", "학교", "school 학교"),
("🏪", "편의점", "convenience store shop 편의점"),
("🏬", "백화점", "department store 백화점"),
("🏰", "성", "castle 성"),
("⛪", "교회", "church 교회"),
("🕌", "모스크", "mosque 모스크"),
("🗼", "에펠탑", "eiffel tower paris 파리"),
("🗽", "자유의 여신상", "statue of liberty new york 뉴욕"),
("🏔️", "산", "mountain snow 산"),
("🌋", "화산", "volcano 화산"),
("🗻", "후지산", "mount fuji japan 후지산"),
("🏕️", "캠핑", "camping tent 캠핑"),
("🏖️", "해변", "beach summer 해변 해수욕"),
("🌏", "지구", "earth globe asia 지구"),
// 기호 / 숫자
("💯", "100점", "hundred percent perfect 완벽 100"),
("🔢", "숫자", "numbers 숫자"),
("🆗", "OK", "ok button 오케이"),
("🆙", "업", "up button 업"),
("🆒", "쿨", "cool button 쿨"),
("🆕", "새것", "new button 새"),
("🆓", "무료", "free button 무료"),
("🆘", "SOS", "sos emergency 긴급 구조"),
("⚠️", "경고", "warning caution 경고 주의"),
("🚫", "금지", "prohibited no 금지"),
("✅", "체크", "check mark done 완료 확인"),
("❌", "엑스", "x cross error 실패 오류"),
("❓", "물음표", "question mark 물음표"),
("❗", "느낌표", "exclamation mark 느낌표"),
("", "더하기", "plus add 더하기"),
("", "빼기", "minus subtract 빼기"),
("➗", "나누기", "divide 나누기"),
("✖️", "곱하기", "multiply times 곱하기"),
("♾️", "무한대", "infinity 무한"),
("🔁", "반복", "repeat loop 반복"),
("🔀", "셔플", "shuffle random 랜덤"),
("▶️", "재생", "play 재생"),
("⏸️", "일시정지", "pause 일시정지"),
("⏹️", "정지", "stop 정지"),
("⏩", "빨리 감기", "fast forward 빨리감기"),
("⏪", "되감기", "rewind 되감기"),
("🔔", "알림", "bell notification 알림 벨"),
("🔕", "알림 끔", "bell off 알림끔"),
("🔊", "볼륨 크게", "loud speaker volume up 볼륨"),
("🔇", "음소거", "muted speaker 음소거"),
("📣", "메가폰", "megaphone loud 확성기"),
("📢", "스피커", "loudspeaker 스피커"),
("💬", "말풍선", "speech bubble chat 대화"),
("💭", "생각 말풍선", "thought bubble thinking 생각"),
("📧", "이메일", "email mail 이메일 메일"),
("📨", "수신 봉투", "incoming envelope 수신"),
("📩", "발신 봉투", "envelope outbox 발신"),
("📬", "우편함", "mailbox 우편함"),
("📦", "택배 박스", "package box parcel 택배 상자"),
("🎁", "선물", "gift present 선물"),
("🎀", "리본 묶음", "ribbon bow 리본"),
("🎊", "색종이", "confetti 파티 축하"),
("🎉", "파티 폭죽", "party popper celebrate 파티 축하"),
("🎈", "풍선", "balloon party 풍선"),
("🕐", "1시", "one o'clock 1시 시간"),
("🕒", "3시", "three o'clock 3시 시간"),
("🕔", "4시", "four o'clock 4시 시간"),
("⏰", "알람 시계", "alarm clock 알람 시계"),
("⏱️", "스톱워치", "stopwatch timer 스톱워치 타이머"),
("📅", "달력", "calendar date 달력 날짜"),
("📆", "찢는 달력", "tear-off calendar 달력"),
("💰", "돈 가방", "money bag 돈 부자"),
("💳", "신용카드", "credit card payment 카드 결제"),
("💵", "달러", "dollar banknote 달러"),
("💴", "엔화", "yen banknote 엔"),
("💶", "유로", "euro banknote 유로"),
("💷", "파운드", "pound banknote 파운드"),
("📊", "막대 그래프", "bar chart graph 그래프"),
("📈", "상승 그래프", "chart increasing trend 상승 트렌드"),
("📉", "하락 그래프", "chart decreasing trend 하락"),
("🔍", "돋보기", "magnifying glass search 검색 돋보기"),
("🔎", "오른쪽 돋보기", "magnifying glass right search 검색"),
("🏳️", "흰 깃발", "white flag 항복"),
("🏴", "검은 깃발", "black flag 해적"),
("🚩", "빨간 삼각기", "triangular flag 경고 깃발"),
("🏁", "체크무늬 깃발", "chequered flag finish race 결승"),
("🌐", "지구본", "globe internet web 인터넷 웹"),
("⚓", "닻", "anchor 닻"),
("🎵", "음표", "music note 음악 음표"),
("🎶", "음표들", "musical notes 음악"),
("🎼", "악보", "musical score 악보"),
("🎹", "피아노", "piano keyboard 피아노"),
("🎸", "기타", "guitar 기타"),
("🥁", "드럼", "drum 드럼"),
("🪗", "아코디언", "accordion 아코디언"),
("🎷", "색소폰", "saxophone 색소폰"),
("🎺", "트럼펫", "trumpet 트럼펫"),
("🎻", "바이올린", "violin 바이올린"),
};
}

View File

@@ -10,7 +10,7 @@ namespace AxCopilot.Handlers;
/// emoji wave → 👋 검색
/// emoji → 자주 쓰는 이모지 목록
/// </summary>
public class EmojiHandler : IActionHandler
public partial class EmojiHandler : IActionHandler
{
public string? Prefix => "emoji";
@@ -20,490 +20,6 @@ public class EmojiHandler : IActionHandler
"1.0",
"AX");
// ─── 이모지 데이터베이스 (이모지, 이름(한/영), 태그) ──────────────────────
private static readonly (string Emoji, string Name, string Tags)[] _emojis =
{
// 표정 / 감정
("😀", "크게 웃는 얼굴", "smile happy grin 웃음 행복"),
("😃", "웃는 얼굴", "smile happy joy 웃음"),
("😄", "눈 웃음", "smile laugh 웃음 기쁨"),
("😁", "히죽 웃음", "grin beam 씩 웃다"),
("😆", "크게 웃음", "laughing 폭소"),
("😅", "식은땀 웃음", "sweat smile 안도"),
("🤣", "바닥 구르며 웃음", "rofl lol 빵 웃음"),
("😂", "눈물 나게 웃음", "joy tears laugh 폭소"),
("🙂", "살짝 웃음", "slightly smiling 미소"),
("🙃", "거꾸로 웃음", "upside down 뒤집힌"),
("😉", "윙크", "wink 윙크"),
("😊", "볼 빨개진 웃음", "blush 부끄러움 미소"),
("😇", "천사", "angel halo 천사 선량"),
("🥰", "사랑스러운 얼굴", "love hearts 사랑 하트"),
("😍", "하트 눈", "heart eyes 사랑 반함"),
("🤩", "별 눈", "star struck 감동 황홀"),
("😘", "뽀뽀", "kiss blow 키스 뽀뽀"),
("😗", "오므린 입", "kiss whistle 키스"),
("😚", "눈 감고 뽀뽀", "kiss 키스"),
("😙", "볼 뽀뽀", "kiss 키스"),
("😋", "맛있다", "yum delicious 맛 음식"),
("😛", "혀 내밀기", "tongue out 혀 놀림"),
("😜", "윙크하며 혀", "wink tongue 장난"),
("🤪", "미친 표정", "zany crazy 정신없음"),
("😝", "눈 감고 혀", "tongue 혀"),
("🤑", "돈 눈", "money face 돈 부자"),
("🤗", "포옹", "hugging hug 안아줘 포옹"),
("🤭", "입 가리고", "hand over mouth 헉 깜짝"),
("🤫", "쉿", "shushing quiet 조용 쉿"),
("🤔", "생각 중", "thinking 고민 생각"),
("🤐", "입 막음", "zipper mouth 비밀"),
("🤨", "의심", "raised eyebrow 의심 의아"),
("😐", "무표정", "neutral 무감각 무표정"),
("😑", "표정 없음", "expressionless 냉담"),
("😶", "입 없는 얼굴", "no mouth 침묵"),
("😏", "비웃음", "smirk 비웃 냉소"),
("😒", "불만", "unamused 불만 짜증"),
("🙄", "눈 굴리기", "eye roll 어이없음"),
("😬", "이 드러냄", "grimace 으 민망"),
("🤥", "거짓말", "lying pinocchio 거짓말"),
("😌", "안도/평온", "relieved 안도 평온"),
("😔", "슬픔", "pensive sad 슬픔 우울"),
("😪", "졸림", "sleepy 졸음"),
("🤤", "침 흘림", "drooling 군침 식욕"),
("😴", "잠", "sleeping sleep 수면 잠"),
("😷", "마스크", "mask sick 마스크 아픔"),
("🤒", "열 나는", "sick fever 열 아픔"),
("🤕", "머리 붕대", "injured hurt 부상"),
("🤢", "구역질", "nauseated sick 구역 메스꺼움"),
("🤮", "토하는", "vomit 구토"),
("🤧", "재채기", "sneezing sick 재채기 감기"),
("🥵", "더운", "hot overheated 더움 열"),
("🥶", "추운", "cold freezing 추움 냉기"),
("🥴", "어지러운", "woozy 어지럼 취함"),
("😵", "어질어질", "dizzy 어지럼 충격"),
("🤯", "머리 폭발", "exploding head 충격 대박"),
("🤠", "카우보이", "cowboy hat 카우보이"),
("🥸", "변장", "disguise 변장 선글라스"),
("😎", "쿨한", "cool sunglasses 선글라스 쿨"),
("🤓", "공부벌레", "nerd glasses 공부 안경"),
("🧐", "모노클", "monocle curious 고상 탐정"),
("😕", "당황", "confused 당황 모호"),
("😟", "걱정", "worried concern 걱정"),
("🙁", "살짝 찡그림", "frown 슬픔"),
("☹️", "찡그린 얼굴", "frown sad 슬픔"),
("😮", "입 벌림", "open mouth surprised 놀람"),
("😯", "놀람", "hushed surprised 깜짝"),
("😲", "충격", "astonished 충격 놀람"),
("😳", "얼굴 빨개짐", "flushed embarrassed 부끄럼 당황"),
("🥺", "애원", "pleading eyes 부탁 눈빛"),
("😦", "찡그리며 벌린 입", "frowning 불안"),
("😧", "고통", "anguished 고통"),
("😨", "무서움", "fearful scared 무서움 공포"),
("😰", "식은땀", "anxious sweat 불안 걱정"),
("😥", "눈물 조금", "sad disappointed 실망 눈물"),
("😢", "울음", "cry sad 슬픔 눈물"),
("😭", "엉엉 울음", "loudly crying sob 통곡"),
("😱", "공포에 질림", "screaming fear 비명 공포"),
("😖", "혼란", "confounded 혼란"),
("😣", "힘듦", "persevering 고생"),
("😞", "실망", "disappointed 실망"),
("😓", "땀", "downcast sweat 땀 힘듦"),
("😩", "피곤", "weary tired 지침 피곤"),
("😫", "극도로 지침", "tired exhausted 탈진"),
("🥱", "하품", "yawning bored 하품 지루함"),
("😤", "콧김", "triumph snort 분노 콧김"),
("😡", "화남", "angry mad 화남 분노"),
("😠", "성남", "angry 화 성남"),
("🤬", "욕", "cursing swearing 욕 분노"),
("😈", "나쁜 미소", "smiling devil 악마 장난"),
("👿", "화난 악마", "angry devil 악마"),
("💀", "해골", "skull death 해골 죽음"),
("☠️", "해골 십자", "skull crossbones 독"),
("💩", "응가", "poop 똥 응가"),
("🤡", "피에로", "clown 광대"),
("👹", "도깨비", "ogre 도깨비 귀신"),
("👺", "텐구", "goblin 텐구"),
("👻", "유령", "ghost 유령 귀신"),
("👾", "우주인", "alien monster 외계인 게임"),
("🤖", "로봇", "robot 로봇"),
// 손 / 몸
("👋", "손 흔들기", "wave waving hi bye 안녕"),
("🤚", "손 뒤", "raised back hand 손"),
("🖐️", "손바닥", "hand palm 다섯 손가락"),
("✋", "손 들기", "raised hand 손 들기 멈춤"),
("🖖", "스팍 손인사", "vulcan salute 스타트렉"),
("👌", "오케이", "ok perfect 오케이 좋아"),
("🤌", "손가락 모아", "pinched fingers 이탈리아"),
("✌️", "브이", "victory peace v 브이 평화"),
("🤞", "행운 손가락", "crossed fingers lucky 행운 기도"),
("🤟", "아이 러브 유", "love you 사랑해"),
("🤘", "록 손", "rock on metal 록"),
("🤙", "전화해", "call me shaka 전화 샤카"),
("👈", "왼쪽 가리킴", "backhand left 왼쪽"),
("👉", "오른쪽 가리킴", "backhand right 오른쪽"),
("👆", "위 가리킴", "backhand up 위"),
("🖕", "욕", "middle finger 욕"),
("👇", "아래 가리킴", "backhand down 아래"),
("☝️", "검지 들기", "index pointing up 하나 포인트"),
("👍", "좋아요", "thumbs up like good 좋아 최고"),
("👎", "싫어요", "thumbs down dislike 싫어 별로"),
("✊", "주먹", "fist punch 주먹"),
("👊", "주먹 치기", "punch fist 주먹"),
("🤛", "왼 주먹", "left fist 주먹"),
("🤜", "오른 주먹", "right fist 주먹"),
("👏", "박수", "clapping applause 박수 응원"),
("🙌", "만세", "raising hands celebrate 만세"),
("👐", "양손 펼침", "open hands 환영"),
("🤲", "두 손 모음", "palms up together 기도 바람"),
("🙏", "두 손 합장", "pray please thanks 감사 부탁 기도"),
("✍️", "글쓰기", "writing pen 글쓰기"),
("💅", "네일", "nail polish manicure 네일 손톱"),
("🤳", "셀카", "selfie 셀카"),
("💪", "근육", "muscle strong 근육 힘"),
("🦾", "기계 팔", "mechanical arm 로봇 팔"),
("🦿", "기계 다리", "mechanical leg 로봇 다리"),
("🦵", "다리", "leg kick 다리"),
("🦶", "발", "foot kick 발"),
("👂", "귀", "ear hear 귀"),
("🦻", "보청기 귀", "ear hearing aid 보청기"),
("👃", "코", "nose smell 코"),
("🫀", "심장", "heart anatomical 심장"),
("🫁", "폐", "lungs 폐"),
("🧠", "뇌", "brain mind 뇌 지능"),
("🦷", "치아", "tooth dental 치아"),
("🦴", "뼈", "bone 뼈"),
("👀", "눈", "eyes look see 눈 보기"),
("👁️", "한쪽 눈", "eye 눈"),
("👅", "혀", "tongue 혀"),
("👄", "입술", "lips mouth 입술"),
("💋", "입맞춤", "kiss lips 키스 입술"),
("🩸", "피", "blood drop 피 혈액"),
// 하트 / 감정 기호
("❤️", "빨간 하트", "red heart love 사랑 빨강"),
("🧡", "주황 하트", "orange heart 사랑"),
("💛", "노란 하트", "yellow heart 사랑"),
("💚", "초록 하트", "green heart 사랑"),
("💙", "파란 하트", "blue heart 사랑"),
("💜", "보라 하트", "purple heart 사랑"),
("🖤", "검은 하트", "black heart 사랑 다크"),
("🤍", "흰 하트", "white heart 사랑"),
("🤎", "갈색 하트", "brown heart 사랑"),
("💔", "깨진 하트", "broken heart 이별 상처"),
("❣️", "느낌표 하트", "heart exclamation 사랑"),
("💕", "두 하트", "two hearts 사랑"),
("💞", "회전 하트", "revolving hearts 사랑"),
("💓", "뛰는 하트", "beating heart 설렘"),
("💗", "성장 하트", "growing heart 사랑"),
("💖", "반짝 하트", "sparkling heart 사랑"),
("💘", "화살 하트", "heart arrow 큐피드"),
("💝", "리본 하트", "heart ribbon 선물 사랑"),
("💟", "하트 장식", "heart decoration 사랑"),
("☮️", "평화", "peace 평화"),
("✝️", "십자가", "cross 기독교"),
("☯️", "음양", "yin yang 음양 균형"),
("🔮", "수정구", "crystal ball magic 마법 점"),
("✨", "반짝임", "sparkles glitter 빛 반짝"),
("⭐", "별", "star 별"),
("🌟", "빛나는 별", "glowing star 별빛"),
("💫", "현기증", "dizzy star 빙글"),
("⚡", "번개", "lightning bolt 번개 전기"),
("🔥", "불", "fire hot 불 열정"),
("💥", "폭발", "explosion boom 폭발"),
("❄️", "눈송이", "snowflake cold 눈 추위"),
("🌈", "무지개", "rainbow 무지개"),
("☀️", "태양", "sun sunny 태양 맑음"),
("🌙", "달", "moon crescent 달"),
("🌊", "파도", "wave ocean 파도 바다"),
("💨", "바람", "wind dash 바람"),
("💦", "물방울", "sweat droplets water 물"),
("🌸", "벚꽃", "cherry blossom 벚꽃 봄"),
("🌹", "장미", "rose 장미 꽃"),
("🌺", "히비스커스", "hibiscus 꽃"),
("🌻", "해바라기", "sunflower 해바라기"),
("🌼", "꽃", "blossom flower 꽃"),
("🌷", "튤립", "tulip 튤립"),
("💐", "꽃다발", "bouquet flowers 꽃다발"),
("🍀", "네잎클로버", "four leaf clover lucky 행운"),
("🌿", "허브", "herb green 풀 허브"),
("🍃", "잎사귀", "leaf 잎"),
// 음식
("🍕", "피자", "pizza 피자"),
("🍔", "햄버거", "hamburger burger 버거"),
("🌮", "타코", "taco 타코"),
("🍜", "라면", "ramen noodles 라면 국수"),
("🍱", "도시락", "bento box 도시락"),
("🍣", "초밥", "sushi 초밥"),
("🍚", "밥", "rice 밥"),
("🍛", "카레", "curry rice 카레"),
("🍝", "파스타", "pasta spaghetti 파스타"),
("🍦", "소프트 아이스크림", "ice cream soft serve 아이스크림"),
("🎂", "생일 케이크", "cake birthday 생일 케이크"),
("🍰", "케이크 조각", "cake slice 케이크"),
("🧁", "컵케이크", "cupcake 컵케이크"),
("🍩", "도넛", "donut 도넛"),
("🍪", "쿠키", "cookie 쿠키"),
("🍫", "초콜릿", "chocolate bar 초콜릿"),
("🍬", "사탕", "candy 사탕"),
("🍭", "막대 사탕", "lollipop 막대사탕"),
("🍺", "맥주", "beer mug 맥주"),
("🍻", "건배", "clinking beer 건배"),
("🥂", "샴페인 건배", "champagne 샴페인 건배"),
("🍷", "와인", "wine 와인"),
("☕", "커피", "coffee hot 커피"),
("🧃", "주스", "juice 주스"),
("🥤", "음료", "drink cup 음료 컵"),
("🧋", "버블티", "bubble tea boba 버블티"),
("🍵", "녹차", "tea matcha 차 녹차"),
// 동물
("🐶", "강아지", "dog puppy 강아지 개"),
("🐱", "고양이", "cat kitten 고양이"),
("🐭", "쥐", "mouse 쥐"),
("🐹", "햄스터", "hamster 햄스터"),
("🐰", "토끼", "rabbit bunny 토끼"),
("🦊", "여우", "fox 여우"),
("🐻", "곰", "bear 곰"),
("🐼", "판다", "panda 판다"),
("🐨", "코알라", "koala 코알라"),
("🐯", "호랑이", "tiger 호랑이"),
("🦁", "사자", "lion 사자"),
("🐮", "소", "cow 소"),
("🐷", "돼지", "pig 돼지"),
("🐸", "개구리", "frog 개구리"),
("🐵", "원숭이", "monkey 원숭이"),
("🙈", "눈 가린 원숭이", "see no evil monkey 안 봐"),
("🙉", "귀 가린 원숭이", "hear no evil monkey 안 들어"),
("🙊", "입 가린 원숭이", "speak no evil monkey 안 말해"),
("🐔", "닭", "chicken 닭"),
("🐧", "펭귄", "penguin 펭귄"),
("🐦", "새", "bird 새"),
("🦆", "오리", "duck 오리"),
("🦅", "독수리", "eagle 독수리"),
("🦉", "부엉이", "owl 부엉이"),
("🐍", "뱀", "snake 뱀"),
("🐢", "거북이", "turtle 거북이"),
("🦋", "나비", "butterfly 나비"),
("🐌", "달팽이", "snail 달팽이"),
("🐛", "애벌레", "bug caterpillar 애벌레"),
("🐝", "꿀벌", "bee honeybee 벌"),
("🦑", "오징어", "squid 오징어"),
("🐙", "문어", "octopus 문어"),
("🐠", "열대어", "tropical fish 열대어"),
("🐡", "복어", "blowfish puffer 복어"),
("🦈", "상어", "shark 상어"),
("🐬", "돌고래", "dolphin 돌고래"),
("🐳", "고래", "whale 고래"),
("🐲", "용", "dragon 용"),
("🦄", "유니콘", "unicorn 유니콘"),
// 물건 / 도구
("📱", "스마트폰", "phone mobile smartphone 폰"),
("💻", "노트북", "laptop computer 노트북"),
("🖥️", "데스크톱", "desktop computer 컴퓨터"),
("⌨️", "키보드", "keyboard 키보드"),
("🖱️", "마우스", "mouse 마우스"),
("🖨️", "프린터", "printer 프린터"),
("📷", "카메라", "camera 카메라"),
("📸", "플래시 카메라", "camera flash 사진"),
("📹", "비디오 카메라", "video camera 동영상"),
("🎥", "영화 카메라", "movie camera film 영화"),
("📺", "TV", "television tv 텔레비전"),
("📻", "라디오", "radio 라디오"),
("🎙️", "마이크", "microphone studio 마이크"),
("🎤", "마이크 핸드헬드", "microphone karaoke 마이크"),
("🎧", "헤드폰", "headphones 헤드폰"),
("📡", "안테나", "satellite antenna 안테나"),
("🔋", "배터리", "battery 배터리"),
("🔌", "전원 플러그", "plug electric 플러그"),
("💡", "전구", "bulb idea light 전구 아이디어"),
("🔦", "손전등", "flashlight torch 손전등"),
("🕯️", "양초", "candle 양초"),
("📚", "책", "books stack 책"),
("📖", "열린 책", "open book read 독서"),
("📝", "메모", "memo note pencil 메모 노트"),
("✏️", "연필", "pencil 연필"),
("🖊️", "펜", "pen 펜"),
("📌", "압정", "pushpin pin 압정"),
("📎", "클립", "paperclip 클립"),
("✂️", "가위", "scissors cut 가위"),
("🗂️", "파일 폴더", "card index dividers folder 파일"),
("📁", "폴더", "folder 폴더"),
("📂", "열린 폴더", "open folder 폴더"),
("🗃️", "파일 박스", "card file box 서류함"),
("🗑️", "휴지통", "wastebasket trash 휴지통"),
("🔒", "잠금", "locked lock 잠금"),
("🔓", "열림", "unlocked 열림"),
("🔑", "열쇠", "key 열쇠"),
("🗝️", "구식 열쇠", "old key 열쇠"),
("🔨", "망치", "hammer 망치"),
("🔧", "렌치", "wrench tool 렌치"),
("🔩", "나사", "nut bolt 나사"),
("⚙️", "톱니바퀴", "gear settings 설정 톱니"),
("🛠️", "도구", "tools hammer wrench 도구"),
("💊", "알약", "pill medicine 약 알약"),
("💉", "주사기", "syringe injection 주사"),
("🩺", "청진기", "stethoscope doctor 청진기"),
("🏆", "트로피", "trophy award 트로피 우승"),
("🥇", "금메달", "first gold medal 금메달"),
("🥈", "은메달", "second silver 은메달"),
("🥉", "동메달", "third bronze 동메달"),
("🎖️", "훈장", "medal military 훈장"),
("🎗️", "리본", "ribbon awareness 리본"),
("🎫", "티켓", "ticket admission 티켓"),
("🎟️", "입장권", "admission tickets 티켓"),
("🎪", "서커스", "circus tent 서커스"),
("🎨", "팔레트", "art palette paint 그림 예술"),
("🎭", "연극", "performing arts theater 연극"),
("🎬", "클래퍼보드", "clapper film 영화 촬영"),
("🎮", "게임 컨트롤러", "video game controller 게임"),
("🎲", "주사위", "dice game 주사위"),
("🎯", "다트", "bullseye target dart 다트 목표"),
("🎳", "볼링", "bowling 볼링"),
("⚽", "축구", "soccer football 축구"),
("🏀", "농구", "basketball 농구"),
("🏈", "미식축구", "american football 미식축구"),
("⚾", "야구", "baseball 야구"),
("🎾", "테니스", "tennis 테니스"),
("🏐", "배구", "volleyball 배구"),
("🏉", "럭비", "rugby 럭비"),
("🎱", "당구", "billiards pool 당구"),
("🏓", "탁구", "ping pong table tennis 탁구"),
("🏸", "배드민턴", "badminton 배드민턴"),
("🥊", "권투 장갑", "boxing glove 권투"),
("🎣", "낚시", "fishing 낚시"),
("🏋️", "역도", "weightlifting gym 헬스 역도"),
("🧘", "명상", "yoga meditation 명상 요가"),
// 이동수단
("🚗", "자동차", "car automobile 자동차"),
("🚕", "택시", "taxi cab 택시"),
("🚙", "SUV", "suv car 차"),
("🚌", "버스", "bus 버스"),
("🚎", "무궤도 전차", "trolleybus 버스"),
("🏎️", "레이싱카", "racing car 레이싱"),
("🚓", "경찰차", "police car 경찰"),
("🚑", "구급차", "ambulance 구급차"),
("🚒", "소방차", "fire truck 소방차"),
("🚐", "미니밴", "minibus van 밴"),
("🚚", "트럭", "truck delivery 트럭"),
("✈️", "비행기", "airplane flight plane 비행기"),
("🚀", "로켓", "rocket space launch 로켓"),
("🛸", "UFO", "flying saucer ufo 유에프오"),
("🚁", "헬리콥터", "helicopter 헬리콥터"),
("🚂", "기차", "train locomotive 기차"),
("🚆", "고속열차", "train 기차"),
("🚇", "지하철", "metro subway 지하철"),
("⛵", "돛단배", "sailboat 요트"),
("🚢", "배", "ship cruise 배"),
("🚲", "자전거", "bicycle bike 자전거"),
("🛵", "스쿠터", "scooter moped 스쿠터"),
("🏍️", "오토바이", "motorcycle 오토바이"),
// 장소
("🏠", "집", "house home 집"),
("🏡", "마당 있는 집", "house garden 집"),
("🏢", "빌딩", "office building 빌딩"),
("🏣", "우체국", "post office 우체국"),
("🏥", "병원", "hospital 병원"),
("🏦", "은행", "bank 은행"),
("🏨", "호텔", "hotel 호텔"),
("🏫", "학교", "school 학교"),
("🏪", "편의점", "convenience store shop 편의점"),
("🏬", "백화점", "department store 백화점"),
("🏰", "성", "castle 성"),
("⛪", "교회", "church 교회"),
("🕌", "모스크", "mosque 모스크"),
("🗼", "에펠탑", "eiffel tower paris 파리"),
("🗽", "자유의 여신상", "statue of liberty new york 뉴욕"),
("🏔️", "산", "mountain snow 산"),
("🌋", "화산", "volcano 화산"),
("🗻", "후지산", "mount fuji japan 후지산"),
("🏕️", "캠핑", "camping tent 캠핑"),
("🏖️", "해변", "beach summer 해변 해수욕"),
("🌏", "지구", "earth globe asia 지구"),
// 기호 / 숫자
("💯", "100점", "hundred percent perfect 완벽 100"),
("🔢", "숫자", "numbers 숫자"),
("🆗", "OK", "ok button 오케이"),
("🆙", "업", "up button 업"),
("🆒", "쿨", "cool button 쿨"),
("🆕", "새것", "new button 새"),
("🆓", "무료", "free button 무료"),
("🆘", "SOS", "sos emergency 긴급 구조"),
("⚠️", "경고", "warning caution 경고 주의"),
("🚫", "금지", "prohibited no 금지"),
("✅", "체크", "check mark done 완료 확인"),
("❌", "엑스", "x cross error 실패 오류"),
("❓", "물음표", "question mark 물음표"),
("❗", "느낌표", "exclamation mark 느낌표"),
("", "더하기", "plus add 더하기"),
("", "빼기", "minus subtract 빼기"),
("➗", "나누기", "divide 나누기"),
("✖️", "곱하기", "multiply times 곱하기"),
("♾️", "무한대", "infinity 무한"),
("🔁", "반복", "repeat loop 반복"),
("🔀", "셔플", "shuffle random 랜덤"),
("▶️", "재생", "play 재생"),
("⏸️", "일시정지", "pause 일시정지"),
("⏹️", "정지", "stop 정지"),
("⏩", "빨리 감기", "fast forward 빨리감기"),
("⏪", "되감기", "rewind 되감기"),
("🔔", "알림", "bell notification 알림 벨"),
("🔕", "알림 끔", "bell off 알림끔"),
("🔊", "볼륨 크게", "loud speaker volume up 볼륨"),
("🔇", "음소거", "muted speaker 음소거"),
("📣", "메가폰", "megaphone loud 확성기"),
("📢", "스피커", "loudspeaker 스피커"),
("💬", "말풍선", "speech bubble chat 대화"),
("💭", "생각 말풍선", "thought bubble thinking 생각"),
("📧", "이메일", "email mail 이메일 메일"),
("📨", "수신 봉투", "incoming envelope 수신"),
("📩", "발신 봉투", "envelope outbox 발신"),
("📬", "우편함", "mailbox 우편함"),
("📦", "택배 박스", "package box parcel 택배 상자"),
("🎁", "선물", "gift present 선물"),
("🎀", "리본 묶음", "ribbon bow 리본"),
("🎊", "색종이", "confetti 파티 축하"),
("🎉", "파티 폭죽", "party popper celebrate 파티 축하"),
("🎈", "풍선", "balloon party 풍선"),
("🕐", "1시", "one o'clock 1시 시간"),
("🕒", "3시", "three o'clock 3시 시간"),
("🕔", "4시", "four o'clock 4시 시간"),
("⏰", "알람 시계", "alarm clock 알람 시계"),
("⏱️", "스톱워치", "stopwatch timer 스톱워치 타이머"),
("📅", "달력", "calendar date 달력 날짜"),
("📆", "찢는 달력", "tear-off calendar 달력"),
("💰", "돈 가방", "money bag 돈 부자"),
("💳", "신용카드", "credit card payment 카드 결제"),
("💵", "달러", "dollar banknote 달러"),
("💴", "엔화", "yen banknote 엔"),
("💶", "유로", "euro banknote 유로"),
("💷", "파운드", "pound banknote 파운드"),
("📊", "막대 그래프", "bar chart graph 그래프"),
("📈", "상승 그래프", "chart increasing trend 상승 트렌드"),
("📉", "하락 그래프", "chart decreasing trend 하락"),
("🔍", "돋보기", "magnifying glass search 검색 돋보기"),
("🔎", "오른쪽 돋보기", "magnifying glass right search 검색"),
("🏳️", "흰 깃발", "white flag 항복"),
("🏴", "검은 깃발", "black flag 해적"),
("🚩", "빨간 삼각기", "triangular flag 경고 깃발"),
("🏁", "체크무늬 깃발", "chequered flag finish race 결승"),
("🌐", "지구본", "globe internet web 인터넷 웹"),
("⚓", "닻", "anchor 닻"),
("🎵", "음표", "music note 음악 음표"),
("🎶", "음표들", "musical notes 음악"),
("🎼", "악보", "musical score 악보"),
("🎹", "피아노", "piano keyboard 피아노"),
("🎸", "기타", "guitar 기타"),
("🥁", "드럼", "drum 드럼"),
("🪗", "아코디언", "accordion 아코디언"),
("🎷", "색소폰", "saxophone 색소폰"),
("🎺", "트럼펫", "trumpet 트럼펫"),
("🎻", "바이올린", "violin 바이올린"),
};
public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
{
IEnumerable<(string Emoji, string Name, string Tags)> matches;

View File

@@ -0,0 +1,193 @@
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;
using System.Net.Http;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Windows;
namespace AxCopilot.Handlers;
// ─── 수식 파서 ─────────────────────────────────────────────────────────────────
/// <summary>
/// 재귀 하강 파서 기반 수학 수식 평가기.
/// 지원: +, -, *, /, %, ^ (거듭제곱), 괄호, 단항 음수,
/// sqrt, abs, ceil, floor, round, sin, cos, tan (도 단위),
/// log (밑 10), ln (자연로그), pi, e
/// </summary>
internal static class MathEvaluator
{
public static double Evaluate(string expr)
{
var evaluator = new Evaluator(
expr.Replace(" ", "")
.Replace("×", "*")
.Replace("÷", "/")
.Replace("", ",")
.ToLowerInvariant());
return evaluator.Parse();
}
private class Evaluator
{
private readonly string _s;
private int _i;
public Evaluator(string s) { _s = s; _i = 0; }
public double Parse()
{
var result = ParseExpr();
if (_i < _s.Length)
throw new InvalidOperationException($"예기치 않은 문자: '{_s[_i]}'");
return result;
}
// 덧셈 / 뺄셈
private double ParseExpr()
{
var left = ParseTerm();
while (_i < _s.Length && (_s[_i] == '+' || _s[_i] == '-'))
{
var op = _s[_i++];
var right = ParseTerm();
left = op == '+' ? left + right : left - right;
}
return left;
}
// 곱셈 / 나눗셈 / 나머지
private double ParseTerm()
{
var left = ParsePower();
while (_i < _s.Length && (_s[_i] == '*' || _s[_i] == '/' || _s[_i] == '%'))
{
var op = _s[_i++];
var right = ParsePower();
left = op == '*' ? left * right
: op == '/' ? left / right
: left % right;
}
return left;
}
// 거듭제곱 (오른쪽 결합)
private double ParsePower()
{
var b = ParseUnary();
if (_i < _s.Length && _s[_i] == '^')
{
_i++;
var exp = ParseUnary();
return Math.Pow(b, exp);
}
return b;
}
// 단항 부호
private double ParseUnary()
{
if (_i < _s.Length && _s[_i] == '-') { _i++; return -ParsePrimary(); }
if (_i < _s.Length && _s[_i] == '+') { _i++; return ParsePrimary(); }
return ParsePrimary();
}
// 리터럴 / 괄호 / 함수 호출
private double ParsePrimary()
{
if (_i >= _s.Length)
throw new InvalidOperationException("수식이 불완전합니다.");
// 16진수 리터럴 0x...
if (_i + 1 < _s.Length && _s[_i] == '0' && _s[_i + 1] == 'x')
{
_i += 2;
var hexStart = _i;
while (_i < _s.Length && "0123456789abcdef".Contains(_s[_i])) _i++;
return Convert.ToInt64(_s[hexStart.._i], 16);
}
// 숫자
if (char.IsDigit(_s[_i]) || _s[_i] == '.')
{
var start = _i;
while (_i < _s.Length && (char.IsDigit(_s[_i]) || _s[_i] == '.')) _i++;
// 과학적 표기: 1.5e3
if (_i < _s.Length && _s[_i] == 'e')
{
_i++;
if (_i < _s.Length && (_s[_i] == '+' || _s[_i] == '-')) _i++;
while (_i < _s.Length && char.IsDigit(_s[_i])) _i++;
}
return double.Parse(_s[start.._i],
System.Globalization.NumberStyles.Float,
System.Globalization.CultureInfo.InvariantCulture);
}
// 괄호
if (_s[_i] == '(')
{
_i++;
var val = ParseExpr();
if (_i < _s.Length && _s[_i] == ')') _i++;
return val;
}
// 식별자 (상수 또는 함수)
if (char.IsLetter(_s[_i]))
{
var start = _i;
while (_i < _s.Length && (char.IsLetterOrDigit(_s[_i]) || _s[_i] == '_')) _i++;
var name = _s[start.._i];
// 상수
if (name == "pi") return Math.PI;
if (name == "e") return Math.E;
if (name == "inf") return double.PositiveInfinity;
// 함수 호출
if (_i < _s.Length && _s[_i] == '(')
{
_i++; // (
var arg = ParseExpr();
// 두 번째 인자 (pow, log2 등)
double? arg2 = null;
if (_i < _s.Length && _s[_i] == ',')
{
_i++;
arg2 = ParseExpr();
}
if (_i < _s.Length && _s[_i] == ')') _i++;
return name switch
{
"sqrt" => Math.Sqrt(arg),
"abs" => Math.Abs(arg),
"ceil" => Math.Ceiling(arg),
"floor" => Math.Floor(arg),
"round" => arg2.HasValue ? Math.Round(arg, (int)arg2.Value) : Math.Round(arg),
"sin" => Math.Sin(arg * Math.PI / 180), // 도 단위
"cos" => Math.Cos(arg * Math.PI / 180),
"tan" => Math.Tan(arg * Math.PI / 180),
"asin" => Math.Asin(arg) * 180 / Math.PI,
"acos" => Math.Acos(arg) * 180 / Math.PI,
"atan" => Math.Atan(arg) * 180 / Math.PI,
"log" => arg2.HasValue ? Math.Log(arg, arg2.Value) : Math.Log10(arg),
"log2" => Math.Log2(arg),
"ln" => Math.Log(arg),
"exp" => Math.Exp(arg),
"pow" => arg2.HasValue ? Math.Pow(arg, arg2.Value) : throw new InvalidOperationException("pow(x,y) 형식으로 사용하세요."),
"min" => arg2.HasValue ? Math.Min(arg, arg2.Value) : arg,
"max" => arg2.HasValue ? Math.Max(arg, arg2.Value) : arg,
_ => throw new InvalidOperationException($"알 수 없는 함수: {name}()")
};
}
throw new InvalidOperationException($"알 수 없는 식별자: {name}");
}
throw new InvalidOperationException($"예기치 않은 문자: '{_s[_i]}'");
}
}
}

View File

@@ -0,0 +1,154 @@
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;
using System.Net.Http;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Windows;
namespace AxCopilot.Handlers;
// ─── 단위 변환 ─────────────────────────────────────────────────────────────────
/// <summary>
/// "100km in miles", "32f in c", "5lb to kg" 형식의 단위 변환.
/// </summary>
internal static class UnitConverter
{
// 패턴: <숫자> <단위> in|to <단위>
private static readonly Regex Pattern = new(
@"^(-?\d+(?:\.\d+)?)\s*([a-z°/²³µ]+)\s+(?:in|to)\s+([a-z°/²³µ]+)$",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
public static bool TryConvert(string input, out string? result)
{
result = null;
var m = Pattern.Match(input.Trim());
if (!m.Success) return false;
if (!double.TryParse(m.Groups[1].Value,
System.Globalization.NumberStyles.Float,
System.Globalization.CultureInfo.InvariantCulture,
out var value))
return false;
var from = m.Groups[2].Value.ToLowerInvariant();
var to = m.Groups[3].Value.ToLowerInvariant();
// 온도는 비선형 → 별도 처리
if (TryConvertTemperature(value, from, to, out var tResult))
{
result = $"{FormatNum(tResult)} {TemperatureLabel(to)}";
return true;
}
// 나머지 범주(선형 변환)
foreach (var table in _tables)
{
if (table.TryGetValue(from, out var fromFactor) &&
table.TryGetValue(to, out var toFactor))
{
var converted = value * fromFactor / toFactor;
result = $"{FormatNum(converted)} {to}";
return true;
}
}
return false;
}
// ─── 온도 ────────────────────────────────────────────────────────────────
private static bool TryConvertTemperature(double v, string from, string to, out double r)
{
r = 0;
// 섭씨 표준화
double celsius;
switch (from)
{
case "c": case "°c": case "celsius": celsius = v; break;
case "f": case "°f": case "fahrenheit": celsius = (v - 32) * 5 / 9; break;
case "k": case "kelvin": celsius = v - 273.15; break;
default: return false;
}
switch (to)
{
case "c": case "°c": case "celsius": r = celsius; break;
case "f": case "°f": case "fahrenheit": r = celsius * 9 / 5 + 32; break;
case "k": case "kelvin": r = celsius + 273.15; break;
default: return false;
}
return true;
}
private static string TemperatureLabel(string unit) => unit switch
{
"c" or "°c" or "celsius" => "°C",
"f" or "°f" or "fahrenheit" => "°F",
"k" or "kelvin" => "K",
_ => unit
};
// ─── 선형 변환 테이블 (기준 단위 = 1) ────────────────────────────────────
// 길이 (기준: m)
private static readonly Dictionary<string, double> _length = new()
{
["km"] = 1000, ["m"] = 1, ["cm"] = 0.01, ["mm"] = 0.001,
["mi"] = 1609.344, ["mile"] = 1609.344, ["miles"] = 1609.344,
["ft"] = 0.3048, ["feet"] = 0.3048, ["foot"] = 0.3048,
["in"] = 0.0254, ["inch"] = 0.0254, ["inches"] = 0.0254,
["yd"] = 0.9144, ["yard"] = 0.9144, ["yards"] = 0.9144,
["nm"] = 1e-9,
};
// 무게 (기준: kg)
private static readonly Dictionary<string, double> _weight = new()
{
["t"] = 1000, ["ton"] = 1000, ["tonnes"] = 1000,
["kg"] = 1, ["g"] = 0.001, ["mg"] = 1e-6,
["lb"] = 0.453592, ["lbs"] = 0.453592, ["pound"] = 0.453592, ["pounds"] = 0.453592,
["oz"] = 0.0283495, ["ounce"] = 0.0283495, ["ounces"] = 0.0283495,
};
// 속도 (기준: m/s)
private static readonly Dictionary<string, double> _speed = new()
{
["m/s"] = 1, ["mps"] = 1,
["km/h"] = 1.0 / 3.6, ["kmh"] = 1.0 / 3.6, ["kph"] = 1.0 / 3.6,
["mph"] = 0.44704,
["kn"] = 0.514444, ["knot"] = 0.514444, ["knots"] = 0.514444,
};
// 데이터 (기준: byte)
private static readonly Dictionary<string, double> _data = new()
{
["b"] = 1, ["byte"] = 1, ["bytes"] = 1,
["kb"] = 1024, ["kib"] = 1024,
["mb"] = 1024 * 1024, ["mib"] = 1024 * 1024,
["gb"] = 1024.0 * 1024 * 1024, ["gib"] = 1024.0 * 1024 * 1024,
["tb"] = 1024.0 * 1024 * 1024 * 1024, ["tib"] = 1024.0 * 1024 * 1024 * 1024,
["pb"] = 1024.0 * 1024 * 1024 * 1024 * 1024,
};
// 넓이 (기준: m²)
private static readonly Dictionary<string, double> _area = new()
{
["m²"] = 1, ["m2"] = 1,
["km²"] = 1e6, ["km2"] = 1e6,
["cm²"] = 1e-4, ["cm2"] = 1e-4,
["ha"] = 10000,
["acre"] = 4046.86, ["acres"] = 4046.86,
["ft²"] = 0.092903, ["ft2"] = 0.092903,
};
private static readonly List<Dictionary<string, double>> _tables = new()
{ _length, _weight, _speed, _data, _area };
private static string FormatNum(double v)
{
if (v == Math.Floor(v) && Math.Abs(v) < 1e12)
return ((long)v).ToString("N0", System.Globalization.CultureInfo.CurrentCulture);
return v.ToString("G6", System.Globalization.CultureInfo.InvariantCulture);
}
}