AX Commander 비교본 런처 기능 대량 이식
변경 목적: Agent Compare 아래 비교본의 개발 문서와 런처 소스를 기준으로 현재 AX Commander에 빠져 있던 신규 런처 기능을 동일한 흐름으로 옮겨, 비교본 수준의 기능 폭을 현재 제품에 반영했습니다. 핵심 수정사항: 비교본의 신규 런처 핸들러 다수를 src/AxCopilot/Handlers로 이식하고 App.xaml.cs 등록 흐름에 연결했습니다. 빠른 링크, 파일 태그, 알림 센터, 포모도로, 파일 브라우저, 핫키 관리, OCR, 세션/스케줄/매크로, Git/정규식/네트워크/압축/해시/UUID/JWT/QR 등 AX Commander 기능을 추가했습니다. 핵심 수정사항: 신규 기능이 실제 동작하도록 AppSettings 확장, SchedulerService/FileTagService/NotificationCenterService/IconCacheService/UrlTemplateEngine/PomodoroService 추가, 배치 이름변경/세션/스케줄/매크로 편집 창 추가, NotificationService와 Symbols 보강, QR/OCR용 csproj 의존성과 Windows 타겟 프레임워크를 반영했습니다. 문서 반영: README.md와 docs/DEVELOPMENT.md에 비교본 기반 런처 기능 이식 이력과 검증 결과를 업데이트했습니다. 검증 결과: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ 실행 기준 경고 0개, 오류 0개를 확인했습니다.
This commit is contained in:
284
src/AxCopilot/Handlers/LoremHandler.cs
Normal file
284
src/AxCopilot/Handlers/LoremHandler.cs
Normal file
@@ -0,0 +1,284 @@
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using AxCopilot.SDK;
|
||||
using AxCopilot.Services;
|
||||
using AxCopilot.Themes;
|
||||
|
||||
namespace AxCopilot.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// L10-4: Lorem Ipsum / 더미 텍스트 생성기 핸들러. "lorem" 프리픽스로 사용합니다.
|
||||
///
|
||||
/// 예: lorem → 1단락 생성 (기본)
|
||||
/// lorem 3 → 3단락 생성
|
||||
/// lorem words 20 → 단어 20개
|
||||
/// lorem sentences 5 → 문장 5개
|
||||
/// lorem ko → 한국어 더미 텍스트 1단락
|
||||
/// lorem ko 3 → 한국어 더미 텍스트 3단락
|
||||
/// lorem email 5 → 더미 이메일 주소 5개
|
||||
/// lorem name 5 → 더미 이름 5개 (한국어)
|
||||
/// Enter → 결과를 클립보드에 복사.
|
||||
/// </summary>
|
||||
public class LoremHandler : IActionHandler
|
||||
{
|
||||
public string? Prefix => "lorem";
|
||||
|
||||
public PluginMetadata Metadata => new(
|
||||
"Lorem",
|
||||
"더미 텍스트 생성기 — Lorem Ipsum · 한국어 · 이메일 · 이름",
|
||||
"1.0",
|
||||
"AX");
|
||||
|
||||
// ── Lorem Ipsum 단어 풀 ─────────────────────────────────────────────────
|
||||
private static readonly string[] LoremWords =
|
||||
[
|
||||
"lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit",
|
||||
"sed", "do", "eiusmod", "tempor", "incididunt", "ut", "labore", "et", "dolore",
|
||||
"magna", "aliqua", "enim", "ad", "minim", "veniam", "quis", "nostrud",
|
||||
"exercitation", "ullamco", "laboris", "nisi", "aliquip", "ex", "ea", "commodo",
|
||||
"consequat", "duis", "aute", "irure", "in", "reprehenderit", "voluptate",
|
||||
"velit", "esse", "cillum", "eu", "fugiat", "nulla", "pariatur", "excepteur",
|
||||
"sint", "occaecat", "cupidatat", "non", "proident", "sunt", "culpa", "qui",
|
||||
"officia", "deserunt", "mollit", "anim", "id", "est", "laborum", "perspiciatis",
|
||||
"unde", "omnis", "iste", "natus", "error", "voluptatem", "accusantium",
|
||||
"doloremque", "laudantium", "totam", "rem", "aperiam", "eaque", "ipsa", "quae",
|
||||
"ab", "illo", "inventore", "veritatis", "quasi", "architecto", "beatae", "vitae",
|
||||
"dicta", "explicabo", "nemo", "ipsam", "quia", "voluptas", "aspernatur", "aut",
|
||||
"odit", "fugit", "consequuntur", "magni", "dolores", "ratione", "sequi",
|
||||
"nesciunt", "neque", "porro", "quisquam", "dolorem", "numquam", "eius", "modi",
|
||||
"temporibus", "incidunt", "magnam", "aliquam", "quaerat", "minima", "nostrum",
|
||||
"exercitationem", "ullam", "corporis", "suscipit", "laboriosam", "nisi",
|
||||
"aliquid", "commodi", "consequatur", "quidem", "rerum", "facilis",
|
||||
];
|
||||
|
||||
// ── 한국어 더미 단어 풀 ──────────────────────────────────────────────────
|
||||
private static readonly string[] KorWords =
|
||||
[
|
||||
"가나다라", "마바사아", "자차카타", "파하", "데이터", "처리", "시스템", "네트워크",
|
||||
"소프트웨어", "알고리즘", "데이터베이스", "인터페이스", "프레임워크", "모듈", "클래스",
|
||||
"메서드", "함수", "변수", "구조체", "배열", "목록", "사전", "집합", "스택", "큐",
|
||||
"트리", "그래프", "정렬", "탐색", "분석", "설계", "구현", "테스트", "배포", "운영",
|
||||
"서비스", "플랫폼", "클라우드", "컨테이너", "가상화", "보안", "암호화", "인증", "권한",
|
||||
"로그", "모니터링", "알림", "이벤트", "트랜잭션", "세션", "쿠키", "토큰", "키",
|
||||
"값", "객체", "인스턴스", "프로세스", "스레드", "비동기", "병렬", "동기화", "잠금",
|
||||
"버퍼", "캐시", "인덱스", "쿼리", "뷰", "프로시저", "스키마", "테이블", "컬럼",
|
||||
"행", "열", "기본키", "외래키", "조인", "집계", "필터", "정렬", "그룹화", "분류",
|
||||
];
|
||||
|
||||
private static readonly string[] KorSentenceStarters =
|
||||
[
|
||||
"이 시스템은", "해당 모듈은", "기능을 구현하면", "데이터를 처리하는",
|
||||
"네트워크 연결이", "서비스가 시작되면", "사용자 인터페이스는", "알고리즘이",
|
||||
"처리 과정에서", "설계 단계에서", "구현 방식은", "테스트 결과",
|
||||
];
|
||||
|
||||
private static readonly string[] KorSentenceEnders =
|
||||
[
|
||||
"처리됩니다.", "구현되어 있습니다.", "필요합니다.", "중요한 역할을 합니다.",
|
||||
"확인할 수 있습니다.", "설계되어 있습니다.", "활용됩니다.", "반환됩니다.",
|
||||
"저장됩니다.", "업데이트됩니다.", "삭제됩니다.", "초기화됩니다.",
|
||||
];
|
||||
|
||||
// ── 더미 이름/이메일 데이터 ───────────────────────────────────────────────
|
||||
private static readonly string[] KorLastNames = ["김", "이", "박", "최", "정", "강", "조", "윤", "장", "임", "한", "오", "서", "신", "권", "황", "안", "송", "류", "전"];
|
||||
private static readonly string[] KorFirstNames = ["민준", "서연", "예준", "서현", "도윤", "지우", "시우", "수아", "지호", "하은", "준서", "하린", "건우", "소연", "현우", "지민", "우진", "지유", "연우", "채원"];
|
||||
private static readonly string[] EmailDomains = ["example.com", "test.co.kr", "dummy.net", "sample.org", "mock.io", "placeholder.dev"];
|
||||
|
||||
private static readonly Random Rng = new();
|
||||
|
||||
public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
|
||||
{
|
||||
var q = query.Trim();
|
||||
var items = new List<LauncherItem>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(q))
|
||||
{
|
||||
var para = GenerateParagraph(false);
|
||||
items.Add(new LauncherItem(
|
||||
"Lorem Ipsum 1단락",
|
||||
para.Length > 80 ? para[..80] + "…" : para,
|
||||
null,
|
||||
("copy", para),
|
||||
Symbol: "\uE8BD"));
|
||||
items.Add(new LauncherItem("lorem 3", "3단락 생성", null, null, Symbol: "\uE8BD"));
|
||||
items.Add(new LauncherItem("lorem words 20", "단어 20개", null, null, Symbol: "\uE8BD"));
|
||||
items.Add(new LauncherItem("lorem sentences 3", "문장 3개", null, null, Symbol: "\uE8BD"));
|
||||
items.Add(new LauncherItem("lorem ko", "한국어 더미 텍스트", null, null, Symbol: "\uE8BD"));
|
||||
items.Add(new LauncherItem("lorem email 5", "더미 이메일 5개", null, null, Symbol: "\uE8BD"));
|
||||
items.Add(new LauncherItem("lorem name 5", "더미 이름 5개", null, null, Symbol: "\uE8BD"));
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
}
|
||||
|
||||
var parts = q.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
var sub = parts[0].ToLowerInvariant();
|
||||
|
||||
switch (sub)
|
||||
{
|
||||
case "words":
|
||||
case "word":
|
||||
case "w":
|
||||
{
|
||||
var cnt = parts.Length > 1 && int.TryParse(parts[1], out var n) ? Math.Clamp(n, 1, 500) : 20;
|
||||
var text = GenerateWords(cnt, false);
|
||||
items.Add(new LauncherItem(
|
||||
$"단어 {cnt}개",
|
||||
text.Length > 80 ? text[..80] + "…" : text,
|
||||
null, ("copy", text), Symbol: "\uE8BD"));
|
||||
break;
|
||||
}
|
||||
|
||||
case "sentences":
|
||||
case "sentence":
|
||||
case "s":
|
||||
{
|
||||
var cnt = parts.Length > 1 && int.TryParse(parts[1], out var n) ? Math.Clamp(n, 1, 50) : 5;
|
||||
var text = GenerateSentences(cnt, false);
|
||||
items.Add(new LauncherItem(
|
||||
$"문장 {cnt}개",
|
||||
text.Length > 80 ? text[..80] + "…" : text,
|
||||
null, ("copy", text), Symbol: "\uE8BD"));
|
||||
break;
|
||||
}
|
||||
|
||||
case "ko":
|
||||
case "kor":
|
||||
case "korean":
|
||||
{
|
||||
var cnt = parts.Length > 1 && int.TryParse(parts[1], out var n) ? Math.Clamp(n, 1, 10) : 1;
|
||||
var text = GenerateParagraphs(cnt, true);
|
||||
items.Add(new LauncherItem(
|
||||
$"한국어 더미 텍스트 {cnt}단락",
|
||||
text.Length > 80 ? text[..80] + "…" : text,
|
||||
null, ("copy", text), Symbol: "\uE8BD"));
|
||||
break;
|
||||
}
|
||||
|
||||
case "email":
|
||||
{
|
||||
var cnt = parts.Length > 1 && int.TryParse(parts[1], out var n) ? Math.Clamp(n, 1, 20) : 5;
|
||||
var emails = Enumerable.Range(0, cnt).Select(_ => GenerateEmail()).ToList();
|
||||
var all = string.Join("\n", emails);
|
||||
items.Add(new LauncherItem(
|
||||
$"더미 이메일 {cnt}개",
|
||||
"전체 복사: Enter",
|
||||
null, ("copy", all), Symbol: "\uE8BD"));
|
||||
foreach (var email in emails)
|
||||
items.Add(new LauncherItem(email, "Enter 복사", null, ("copy", email), Symbol: "\uE8BD"));
|
||||
break;
|
||||
}
|
||||
|
||||
case "name":
|
||||
case "names":
|
||||
{
|
||||
var cnt = parts.Length > 1 && int.TryParse(parts[1], out var n) ? Math.Clamp(n, 1, 20) : 5;
|
||||
var names = Enumerable.Range(0, cnt).Select(_ => GenerateKorName()).ToList();
|
||||
var all = string.Join("\n", names);
|
||||
items.Add(new LauncherItem(
|
||||
$"더미 이름 {cnt}개",
|
||||
"전체 복사: Enter",
|
||||
null, ("copy", all), Symbol: "\uE8BD"));
|
||||
foreach (var name in names)
|
||||
items.Add(new LauncherItem(name, "한국어 이름 · Enter 복사", null, ("copy", name), Symbol: "\uE8BD"));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
// 숫자 단독 → 단락 수
|
||||
var cnt = int.TryParse(sub, out var n) ? Math.Clamp(n, 1, 10) : 1;
|
||||
var text = GenerateParagraphs(cnt, false);
|
||||
items.Add(new LauncherItem(
|
||||
$"Lorem Ipsum {cnt}단락",
|
||||
text.Length > 80 ? text[..80] + "…" : text,
|
||||
null, ("copy", text), Symbol: "\uE8BD"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
}
|
||||
|
||||
public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
|
||||
{
|
||||
if (item.Data is ("copy", string text))
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(
|
||||
() => Clipboard.SetText(text));
|
||||
NotificationService.Notify("Lorem", "클립보드에 복사했습니다.");
|
||||
}
|
||||
catch { /* 비핵심 */ }
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// ── 생성 헬퍼 ─────────────────────────────────────────────────────────────
|
||||
|
||||
private static string GenerateParagraphs(int count, bool korean)
|
||||
{
|
||||
var paras = Enumerable.Range(0, count).Select(_ => GenerateParagraph(korean));
|
||||
return string.Join("\n\n", paras);
|
||||
}
|
||||
|
||||
private static string GenerateParagraph(bool korean)
|
||||
{
|
||||
var sentenceCount = Rng.Next(4, 8);
|
||||
return GenerateSentences(sentenceCount, korean);
|
||||
}
|
||||
|
||||
private static string GenerateSentences(int count, bool korean)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if (i > 0) sb.Append(' ');
|
||||
sb.Append(GenerateSentence(korean));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string GenerateSentence(bool korean)
|
||||
{
|
||||
if (korean)
|
||||
{
|
||||
var starter = KorSentenceStarters[Rng.Next(KorSentenceStarters.Length)];
|
||||
var wordCnt = Rng.Next(3, 8);
|
||||
var words = Enumerable.Range(0, wordCnt).Select(_ => KorWords[Rng.Next(KorWords.Length)]);
|
||||
var ender = KorSentenceEnders[Rng.Next(KorSentenceEnders.Length)];
|
||||
return $"{starter} {string.Join(" ", words)} {ender}";
|
||||
}
|
||||
else
|
||||
{
|
||||
var wordCnt = Rng.Next(6, 15);
|
||||
var words = Enumerable.Range(0, wordCnt).Select((_, idx) =>
|
||||
{
|
||||
var w = LoremWords[Rng.Next(LoremWords.Length)];
|
||||
return idx == 0 ? char.ToUpper(w[0]) + w[1..] : w;
|
||||
});
|
||||
return string.Join(" ", words) + ".";
|
||||
}
|
||||
}
|
||||
|
||||
private static string GenerateWords(int count, bool korean)
|
||||
{
|
||||
var pool = korean ? KorWords : LoremWords;
|
||||
return string.Join(" ", Enumerable.Range(0, count).Select(_ => pool[Rng.Next(pool.Length)]));
|
||||
}
|
||||
|
||||
private static string GenerateEmail()
|
||||
{
|
||||
var first = LoremWords[Rng.Next(LoremWords.Length)];
|
||||
var second = LoremWords[Rng.Next(LoremWords.Length)];
|
||||
var num = Rng.Next(10, 999);
|
||||
var domain = EmailDomains[Rng.Next(EmailDomains.Length)];
|
||||
return $"{first}.{second}{num}@{domain}";
|
||||
}
|
||||
|
||||
private static string GenerateKorName()
|
||||
{
|
||||
var last = KorLastNames[Rng.Next(KorLastNames.Length)];
|
||||
var first = KorFirstNames[Rng.Next(KorFirstNames.Length)];
|
||||
return last + first;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user