[Phase L24] 양식 6종·단축키 4앱·맞춤법 핸들러·약어 커스텀 추가
FormHandler.cs (양식 6종 추가, 카테고리 2개 신설): - 업무 인수인계서 (handover): 인계 목록·시스템·연락처 포함 - 업무 지시서 (handover): 우선순위·산출물·보고 방법 포함 - 품의서 (handover): 결재란·예산·기대효과 포함 - 업무 일지 (daily): 일별 업무·미완료·협의사항 포함 - 일일 업무 보고 (daily): 완료·진행·예정·이슈 포함 - 주요 업무 계획표 (daily): 월간 목표·주차별·세부 계획 포함 - Categories에 handover/daily 2개 신설, 안내 문자열 갱신 KeyHandler.cs (단축키 4개 앱 추가): - Word 단축키 25개 (서식·맞춤법·스타일·이동 포함) - PowerPoint 단축키 18개 (슬라이드 쇼·그룹화·정렬 포함) - Teams 단축키 18개 (회의·탭 전환·채널 이동 포함) - Outlook 단축키 21개 (메일·일정·플래그·정크 포함) - catKeys·catName switch·AddAppOverview·안내 문자열 모두 갱신 SpellHandler.cs (신규 생성, 63개 항목): - 되/돼·안/않·혼동어·맞춤법·띄어쓰기·외래어 6개 카테고리 - 빈 쿼리 시 클립보드 자동 맞춤법 검사 - list/카테고리/키워드 검색 지원 - Enter: 올바른 표현 클립보드 복사 AbbrHandler.cs (사용자 정의 약어 기능 추가): - abbr_custom.json 로컬 저장 (CustomAbbr 모델) - abbr add <약어> <풀이> [설명] → 커스텀 약어 등록 - abbr del <약어> → 커스텀 약어 삭제 - abbr custom → 사용자 정의 목록 보기 - 검색 시 내장+커스텀 통합 검색 App.xaml.cs: SpellHandler 등록 (L24-3) 빌드: 경고 0, 오류 0
This commit is contained in:
@@ -359,6 +359,8 @@ public partial class App : System.Windows.Application
|
||||
commandResolver.RegisterHandler(new WorkTimeHandler());
|
||||
// L23-4: 한/영 타이핑 오류 교정 (prefix=fix)
|
||||
commandResolver.RegisterHandler(new FixHandler());
|
||||
// L24-3: 자주 틀리는 맞춤법 (prefix=spell)
|
||||
commandResolver.RegisterHandler(new SpellHandler());
|
||||
|
||||
// ─── 플러그인 로드 ────────────────────────────────────────────────────
|
||||
var pluginHost = new PluginHost(settings, commandResolver);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Windows;
|
||||
using AxCopilot.SDK;
|
||||
using AxCopilot.Services;
|
||||
@@ -201,16 +203,112 @@ public class AbbrHandler : IActionHandler
|
||||
|
||||
private static readonly string[] Categories = Entries.Select(e => e.Category).Distinct().ToArray();
|
||||
|
||||
// ── 커스텀 약어 ───────────────────────────────────────────────────────────
|
||||
private static readonly string CustomPath = System.IO.Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"AxCopilot", "abbr_custom.json");
|
||||
|
||||
private sealed class CustomAbbr
|
||||
{
|
||||
[JsonPropertyName("short")] public string Short { get; set; } = "";
|
||||
[JsonPropertyName("full")] public string Full { get; set; } = "";
|
||||
[JsonPropertyName("desc")] public string Description { get; set; } = "";
|
||||
[JsonPropertyName("cat")] public string Category { get; set; } = "사용자정의";
|
||||
}
|
||||
|
||||
private static List<CustomAbbr> LoadCustom()
|
||||
{
|
||||
try {
|
||||
if (System.IO.File.Exists(CustomPath))
|
||||
return JsonSerializer.Deserialize<List<CustomAbbr>>(
|
||||
System.IO.File.ReadAllText(CustomPath)) ?? [];
|
||||
} catch { }
|
||||
return [];
|
||||
}
|
||||
|
||||
private static void SaveCustom(List<CustomAbbr> list)
|
||||
{
|
||||
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(CustomPath)!);
|
||||
System.IO.File.WriteAllText(CustomPath, JsonSerializer.Serialize(list,
|
||||
new JsonSerializerOptions { WriteIndented = true,
|
||||
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping }));
|
||||
}
|
||||
|
||||
public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
|
||||
{
|
||||
var q = query.Trim();
|
||||
var items = new List<LauncherItem>();
|
||||
|
||||
// ── 서브커맨드 처리 ───────────────────────────────────────────────────
|
||||
// abbr custom → 사용자 정의 약어 목록
|
||||
if (q.Equals("custom", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var customList = LoadCustom();
|
||||
if (customList.Count == 0)
|
||||
{
|
||||
items.Add(new LauncherItem("사용자 정의 약어 없음",
|
||||
"abbr add <약어> <풀이> [설명] 으로 추가하세요",
|
||||
null, null, Symbol: "\uE82D"));
|
||||
}
|
||||
else
|
||||
{
|
||||
items.Add(new LauncherItem($"사용자 정의 약어 {customList.Count}개", "", null, null, Symbol: "\uE82D"));
|
||||
foreach (var c in customList)
|
||||
items.Add(new LauncherItem(c.Short,
|
||||
$"{c.Full} · {c.Description}",
|
||||
null, ("copy", $"{c.Short}: {c.Full}"), Symbol: "\uE82D"));
|
||||
}
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
}
|
||||
|
||||
// abbr add <약어> <풀이> [설명]
|
||||
if (q.StartsWith("add ", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var rest = q[4..].Trim();
|
||||
var parts = rest.Split(' ', 3, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length < 2)
|
||||
{
|
||||
items.Add(new LauncherItem("사용법: abbr add <약어> <풀이> [설명]",
|
||||
"예: abbr add ROI 투자수익률 Return on Investment",
|
||||
null, null, Symbol: "\uE82D"));
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
}
|
||||
var abbr = parts[0].ToUpper();
|
||||
var full = parts[1];
|
||||
var desc = parts.Length > 2 ? parts[2] : "";
|
||||
var encoded = $"{abbr}|{full}|{desc}";
|
||||
items.Add(new LauncherItem($"약어 추가: {abbr} = {full}",
|
||||
desc.Length > 0 ? desc : "Enter: 저장",
|
||||
null, ("add", encoded), Symbol: "\uE82D"));
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
}
|
||||
|
||||
// abbr del <약어>
|
||||
if (q.StartsWith("del ", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var abbr = q[4..].Trim().ToUpper();
|
||||
if (string.IsNullOrEmpty(abbr))
|
||||
{
|
||||
items.Add(new LauncherItem("사용법: abbr del <약어>",
|
||||
"예: abbr del ROI",
|
||||
null, null, Symbol: "\uE82D"));
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
}
|
||||
items.Add(new LauncherItem($"약어 삭제: {abbr}",
|
||||
"Enter: 삭제 확인",
|
||||
null, ("del", abbr), Symbol: "\uE82D"));
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(q))
|
||||
{
|
||||
items.Add(new LauncherItem($"IT·개발 약어 사전 {Entries.Length}개",
|
||||
"abbr <약어> 예: abbr api / abbr crud / abbr jwt",
|
||||
null, null, Symbol: "\uE82D"));
|
||||
// 커스텀 약어 추가 안내
|
||||
items.Add(new LauncherItem("abbr add <약어> <풀이> — 나만의 약어 추가",
|
||||
"예: abbr add ROI 투자수익률",
|
||||
null, null, Symbol: "\uE82D"));
|
||||
items.Add(new LauncherItem("── 카테고리 ──", "", null, null, Symbol: "\uE82D"));
|
||||
foreach (var cat in Categories)
|
||||
{
|
||||
@@ -240,11 +338,15 @@ public class AbbrHandler : IActionHandler
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
}
|
||||
|
||||
// 약어 검색 (정확 일치 우선, 그 다음 부분 일치)
|
||||
var exact = Entries.Where(e =>
|
||||
// 약어 검색 (정확 일치 우선, 그 다음 부분 일치) — 내장 + 커스텀 통합
|
||||
var customSearch = LoadCustom();
|
||||
var customEntries = customSearch.Select(c => new AbbrEntry(c.Short, c.Full, c.Description, c.Category)).ToList();
|
||||
var allEntries = Entries.Concat(customEntries).ToList();
|
||||
|
||||
var exact = allEntries.Where(e =>
|
||||
e.Short.Equals(q, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
|
||||
var partial = Entries.Where(e =>
|
||||
var partial = allEntries.Where(e =>
|
||||
!exact.Contains(e) &&
|
||||
(e.Short.Contains(q, StringComparison.OrdinalIgnoreCase) ||
|
||||
e.Full.Contains(q, StringComparison.OrdinalIgnoreCase) ||
|
||||
@@ -283,15 +385,37 @@ public class AbbrHandler : IActionHandler
|
||||
|
||||
public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
|
||||
{
|
||||
if (item.Data is ("copy", string text))
|
||||
switch (item.Data)
|
||||
{
|
||||
try
|
||||
{
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(
|
||||
() => Clipboard.SetText(text));
|
||||
NotificationService.Notify("Abbr", "클립보드에 복사했습니다.");
|
||||
}
|
||||
catch { }
|
||||
case ("copy", string text):
|
||||
try
|
||||
{
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(
|
||||
() => Clipboard.SetText(text));
|
||||
NotificationService.Notify("Abbr", "클립보드에 복사했습니다.");
|
||||
}
|
||||
catch { }
|
||||
break;
|
||||
|
||||
case ("add", string encoded):
|
||||
var parts = encoded.Split('|', 3);
|
||||
var custom = LoadCustom();
|
||||
custom.RemoveAll(c => c.Short.Equals(parts[0], StringComparison.OrdinalIgnoreCase));
|
||||
custom.Add(new CustomAbbr {
|
||||
Short = parts[0].ToUpper(),
|
||||
Full = parts.Length > 1 ? parts[1] : "",
|
||||
Description = parts.Length > 2 ? parts[2] : "",
|
||||
});
|
||||
SaveCustom(custom);
|
||||
NotificationService.Notify("약어", $"{parts[0].ToUpper()} 등록 완료");
|
||||
break;
|
||||
|
||||
case ("del", string abbr):
|
||||
var customDel = LoadCustom();
|
||||
customDel.RemoveAll(c => c.Short.Equals(abbr, StringComparison.OrdinalIgnoreCase));
|
||||
SaveCustom(customDel);
|
||||
NotificationService.Notify("약어", $"{abbr.ToUpper()} 삭제 완료");
|
||||
break;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -107,6 +107,38 @@ public class FormHandler : IActionHandler
|
||||
"첫 1·2·4주 온보딩 단계별 체크리스트",
|
||||
"onboard", ["onboard", "온보딩", "신입", "입사"],
|
||||
() => BuildOnboard()),
|
||||
|
||||
// ── 인수인계 (handover) ──────────────────────────────────────────────
|
||||
new("업무 인수인계서",
|
||||
"퇴직·이동 시 업무 인수인계 목록 및 주의사항",
|
||||
"handover", ["handover", "인수인계", "transfer", "인계"],
|
||||
() => BuildHandover()),
|
||||
|
||||
new("업무 지시서",
|
||||
"담당자·기한·우선순위 포함 업무 지시 양식",
|
||||
"handover", ["handover", "지시", "task", "업무지시"],
|
||||
() => BuildTaskOrder()),
|
||||
|
||||
new("품의서",
|
||||
"결재 요청을 위한 품의서 양식",
|
||||
"handover", ["handover", "품의", "approval", "결재"],
|
||||
() => BuildApproval()),
|
||||
|
||||
// ── 일지 (daily) ────────────────────────────────────────────────────
|
||||
new("업무 일지",
|
||||
"일별 업무 처리 현황 기록 양식",
|
||||
"daily", ["daily", "일지", "업무일지", "log"],
|
||||
() => BuildWorkLog()),
|
||||
|
||||
new("일일 업무 보고",
|
||||
"당일 완료·진행·내일 예정 업무 보고 양식",
|
||||
"daily", ["daily", "일일보고", "daily report", "eod"],
|
||||
() => BuildDailyReport()),
|
||||
|
||||
new("주요 업무 계획표",
|
||||
"월간·분기 주요 업무 계획 및 일정표",
|
||||
"daily", ["daily", "계획표", "plan", "일정"],
|
||||
() => BuildWorkPlan()),
|
||||
];
|
||||
|
||||
private static readonly (string Key, string[] Aliases, string Label)[] Categories =
|
||||
@@ -117,6 +149,8 @@ public class FormHandler : IActionHandler
|
||||
("project", ["project", "프로젝트"], "프로젝트 문서"),
|
||||
("review", ["review", "리뷰", "평가"], "리뷰·평가"),
|
||||
("onboard", ["onboard", "온보딩", "입사"], "온보딩"),
|
||||
("handover", ["handover", "인수인계", "지시", "품의"], "인수인계·지시·품의"),
|
||||
("daily", ["daily", "일지", "일일", "계획"], "업무 일지·계획"),
|
||||
];
|
||||
|
||||
public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
|
||||
@@ -127,7 +161,7 @@ public class FormHandler : IActionHandler
|
||||
if (string.IsNullOrWhiteSpace(q))
|
||||
{
|
||||
items.Add(new LauncherItem("업무 양식 템플릿",
|
||||
"카테고리: meeting · report · email · project · review · onboard",
|
||||
"카테고리: meeting · report · email · project · review · onboard · handover · daily",
|
||||
null, null, Symbol: "\uE8A5"));
|
||||
|
||||
foreach (var (key, _, label) in Categories)
|
||||
@@ -163,7 +197,7 @@ public class FormHandler : IActionHandler
|
||||
if (searched.Count == 0)
|
||||
{
|
||||
items.Add(new LauncherItem($"'{q}' 양식을 찾을 수 없습니다",
|
||||
"카테고리: meeting · report · email · project · review · onboard",
|
||||
"카테고리: meeting · report · email · project · review · onboard · handover · daily",
|
||||
null, null, Symbol: "\uE783"));
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
}
|
||||
@@ -779,6 +813,300 @@ $"""
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
▸ 메모
|
||||
|
||||
-
|
||||
""";
|
||||
}
|
||||
|
||||
private static string BuildHandover()
|
||||
{
|
||||
var today = DateTime.Today;
|
||||
return $"""
|
||||
업무 인수인계서
|
||||
|
||||
인계자: ___________ 인수자: ___________
|
||||
인계일: {today:yyyy년 MM월 dd일} 완료 예정일: ___________
|
||||
부서: ___________
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
1. 담당 업무 목록
|
||||
|
||||
번호 | 업무명 | 진행상태 | 담당기간 | 비고
|
||||
-----|--------|----------|----------|----
|
||||
1 | | | |
|
||||
2 | | | |
|
||||
3 | | | |
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
2. 진행 중인 업무 상세
|
||||
|
||||
[업무명]
|
||||
- 현재 진행 단계:
|
||||
- 다음 처리 내용:
|
||||
- 관련 담당자:
|
||||
- 위치/경로: (파일/시스템/폴더)
|
||||
- 주의사항:
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
3. 관련 시스템·계정
|
||||
|
||||
시스템명 | ID | 비고
|
||||
---------|----|----
|
||||
| |
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
4. 주요 연락처
|
||||
|
||||
이름 | 소속 | 연락처 | 관계
|
||||
-----|------|--------|----
|
||||
| | |
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
5. 인수인계 특이사항 및 요청사항
|
||||
|
||||
-
|
||||
|
||||
인계자 서명: ___________ 인수자 서명: ___________
|
||||
""";
|
||||
}
|
||||
|
||||
private static string BuildTaskOrder()
|
||||
{
|
||||
var today = DateTime.Today;
|
||||
return $"""
|
||||
업무 지시서
|
||||
|
||||
지시일: {today:yyyy년 MM월 dd일}
|
||||
지시자: ___________ 수신자: ___________
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 업무 개요
|
||||
|
||||
업무명:
|
||||
우선순위: □ 긴급 □ 높음 □ 보통 □ 낮음
|
||||
완료 기한:
|
||||
관련 프로젝트:
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 업무 내용 및 지시 사항
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 세부 요구사항
|
||||
|
||||
-
|
||||
-
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 산출물·보고 방법
|
||||
|
||||
산출물:
|
||||
보고 방법: □ 구두 □ 이메일 □ 문서 □ 시스템 등록
|
||||
보고 기한:
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 참고 자료
|
||||
|
||||
-
|
||||
|
||||
수신 확인: ___________ 확인일: ___________
|
||||
""";
|
||||
}
|
||||
|
||||
private static string BuildApproval()
|
||||
{
|
||||
var today = DateTime.Today;
|
||||
return $"""
|
||||
품 의 서
|
||||
|
||||
기안일: {today:yyyy년 MM월 dd일}
|
||||
기안자: ___________ 부서: ___________
|
||||
|
||||
결재 | 담당 | 팀장 | 부장 | 이사
|
||||
-----|------|------|------|----
|
||||
| | | |
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 제목:
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 목적 및 배경
|
||||
|
||||
-
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 추진 내용
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 예산 현황 (해당 시)
|
||||
|
||||
항목 | 금액 | 비고
|
||||
-----|------|----
|
||||
| |
|
||||
합계 | |
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 일정
|
||||
|
||||
착수: ___________ 완료: ___________
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 기대 효과
|
||||
|
||||
-
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 첨부 자료
|
||||
|
||||
□ 없음 □ 견적서 □ 계획서 □ 기타: ___________
|
||||
""";
|
||||
}
|
||||
|
||||
private static string BuildWorkLog()
|
||||
{
|
||||
var today = DateTime.Today;
|
||||
var dow = today.DayOfWeek switch {
|
||||
DayOfWeek.Monday => "월", DayOfWeek.Tuesday => "화",
|
||||
DayOfWeek.Wednesday => "수", DayOfWeek.Thursday => "목",
|
||||
DayOfWeek.Friday => "금", DayOfWeek.Saturday => "토", _ => "일"
|
||||
};
|
||||
return $"""
|
||||
업무 일지
|
||||
|
||||
날짜: {today:yyyy년 MM월 dd일} ({dow}요일) 작성자: ___________
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 오늘 처리한 업무
|
||||
|
||||
번호 | 업무 내용 | 소요 시간 | 완료 여부
|
||||
-----|-----------|-----------|----------
|
||||
1 | | | □
|
||||
2 | | | □
|
||||
3 | | | □
|
||||
4 | | | □
|
||||
5 | | | □
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 미완료·이월 업무
|
||||
|
||||
-
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 협의·회의 사항
|
||||
|
||||
-
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 특이사항 / 이슈
|
||||
|
||||
-
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 내일 예정 업무
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
""";
|
||||
}
|
||||
|
||||
private static string BuildDailyReport()
|
||||
{
|
||||
var today = DateTime.Today;
|
||||
return $"""
|
||||
일일 업무 보고
|
||||
|
||||
날짜: {today:yyyy년 MM월 dd일} 보고자: ___________ 팀: ___________
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 금일 완료 업무
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 진행 중 업무
|
||||
|
||||
업무명 | 진행률 | 완료 예정일 | 비고
|
||||
-------|--------|-------------|----
|
||||
| % | |
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 내일 예정 업무
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 이슈 / 협조 요청
|
||||
|
||||
□ 없음
|
||||
-
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 기타 공유 사항
|
||||
|
||||
-
|
||||
""";
|
||||
}
|
||||
|
||||
private static string BuildWorkPlan()
|
||||
{
|
||||
var today = DateTime.Today;
|
||||
var firstDay = new DateTime(today.Year, today.Month, 1);
|
||||
return $"""
|
||||
주요 업무 계획표
|
||||
|
||||
기간: {today:yyyy년 MM월} 작성자: ___________ 부서: ___________
|
||||
작성일: {today:yyyy-MM-dd}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 월간 주요 목표
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 주차별 업무 계획
|
||||
|
||||
▶ 1주차 ({firstDay:MM/dd} ~)
|
||||
-
|
||||
|
||||
▶ 2주차 ({firstDay.AddDays(7):MM/dd} ~)
|
||||
-
|
||||
|
||||
▶ 3주차 ({firstDay.AddDays(14):MM/dd} ~)
|
||||
-
|
||||
|
||||
▶ 4주차 ({firstDay.AddDays(21):MM/dd} ~)
|
||||
-
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 프로젝트·업무별 세부 계획
|
||||
|
||||
번호 | 업무명 | 담당자 | 시작일 | 완료 예정일 | 우선순위 | 진행률
|
||||
-----|--------|--------|--------|-------------|----------|-------
|
||||
1 | | | | | | %
|
||||
2 | | | | | | %
|
||||
3 | | | | | | %
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 리스크 및 이슈
|
||||
|
||||
-
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
■ 협조 필요 사항
|
||||
|
||||
-
|
||||
""";
|
||||
}
|
||||
|
||||
@@ -177,6 +177,96 @@ public class KeyHandler : IActionHandler
|
||||
new("Ctrl + F", "터미널 내 텍스트 검색", "terminal"),
|
||||
new("Ctrl + Shift + P", "명령 팔레트", "terminal"),
|
||||
new("Ctrl + Shift + F", "전체 화면 토글", "terminal"),
|
||||
|
||||
// Word
|
||||
new("Ctrl + B", "굵게", "word"),
|
||||
new("Ctrl + I", "기울임꼴", "word"),
|
||||
new("Ctrl + U", "밑줄", "word"),
|
||||
new("Ctrl + Z", "실행 취소", "word"),
|
||||
new("Ctrl + Y", "다시 실행", "word"),
|
||||
new("Ctrl + S", "저장", "word"),
|
||||
new("Ctrl + P", "인쇄", "word"),
|
||||
new("Ctrl + F", "찾기", "word"),
|
||||
new("Ctrl + H", "찾아 바꾸기", "word"),
|
||||
new("Ctrl + A", "전체 선택", "word"),
|
||||
new("Ctrl + C / X / V", "복사 / 잘라내기 / 붙여넣기", "word"),
|
||||
new("Ctrl + K", "하이퍼링크 삽입", "word"),
|
||||
new("Ctrl + Enter", "페이지 나누기 삽입", "word"),
|
||||
new("Alt + Shift + D", "현재 날짜 삽입", "word"),
|
||||
new("Ctrl + Shift + >/<","글꼴 크기 늘리기/줄이기", "word"),
|
||||
new("Ctrl + ] / [", "글꼴 크기 +1 / -1", "word"),
|
||||
new("Ctrl + L/E/R/J", "왼쪽/가운데/오른쪽/양쪽 정렬", "word"),
|
||||
new("Ctrl + 1 / 2 / 5", "줄 간격 1 / 2 / 1.5배", "word"),
|
||||
new("Ctrl + Alt + 1/2/3","제목1 / 제목2 / 제목3 스타일", "word"),
|
||||
new("Ctrl + Shift + N", "기본 스타일 적용", "word"),
|
||||
new("F7", "맞춤법 및 문법 검사", "word"),
|
||||
new("Ctrl + Shift + C", "서식 복사", "word"),
|
||||
new("Ctrl + Shift + V", "서식 붙여넣기", "word"),
|
||||
new("Shift + F3", "대/소문자 전환", "word"),
|
||||
new("Ctrl + Home / End","문서 처음 / 끝으로 이동", "word"),
|
||||
|
||||
// PowerPoint
|
||||
new("F5", "처음부터 슬라이드 쇼 시작", "ppt"),
|
||||
new("Shift + F5", "현재 슬라이드부터 시작", "ppt"),
|
||||
new("Esc", "슬라이드 쇼 종료", "ppt"),
|
||||
new("B", "슬라이드 쇼 중 화면 검정", "ppt"),
|
||||
new("W", "슬라이드 쇼 중 화면 흰색", "ppt"),
|
||||
new("Ctrl + M", "새 슬라이드 삽입", "ppt"),
|
||||
new("Ctrl + D", "슬라이드 복제", "ppt"),
|
||||
new("Ctrl + Z / Y", "실행 취소 / 다시 실행", "ppt"),
|
||||
new("Ctrl + S", "저장", "ppt"),
|
||||
new("Ctrl + G", "개체 그룹화", "ppt"),
|
||||
new("Ctrl + Shift + G", "그룹 해제", "ppt"),
|
||||
new("Tab", "다음 개체로 포커스 이동", "ppt"),
|
||||
new("Shift + Tab", "이전 개체로 포커스 이동", "ppt"),
|
||||
new("Ctrl + A", "모든 개체 선택", "ppt"),
|
||||
new("Alt + Shift + ←/→","슬라이드 내 개체 정렬 (왼쪽/오른쪽)","ppt"),
|
||||
new("Ctrl + Shift + >/<","글꼴 크기 늘리기/줄이기", "ppt"),
|
||||
new("Arrow Keys", "슬라이드 쇼 중 다음/이전", "ppt"),
|
||||
new("숫자 + Enter", "슬라이드 쇼 중 특정 슬라이드로 이동", "ppt"),
|
||||
|
||||
// Teams
|
||||
new("Ctrl + E", "검색창 포커스", "teams"),
|
||||
new("Ctrl + /", "명령 팔레트 열기", "teams"),
|
||||
new("Ctrl + N", "새 채팅 시작", "teams"),
|
||||
new("Ctrl + Shift + M", "오디오 뮤트 토글", "teams"),
|
||||
new("Ctrl + Shift + O", "비디오 카메라 토글", "teams"),
|
||||
new("Ctrl + Shift + H", "회의 종료", "teams"),
|
||||
new("Ctrl + Shift + K", "손 들기/내리기", "teams"),
|
||||
new("Ctrl + Shift + B", "배경 흐림 토글", "teams"),
|
||||
new("Ctrl + Shift + F", "전체 화면 토글", "teams"),
|
||||
new("Ctrl + Shift + A", "팀 활동 피드 열기", "teams"),
|
||||
new("Ctrl + 1", "활동 탭", "teams"),
|
||||
new("Ctrl + 2", "채팅 탭", "teams"),
|
||||
new("Ctrl + 3", "팀 탭", "teams"),
|
||||
new("Ctrl + 4", "캘린더 탭", "teams"),
|
||||
new("Ctrl + 5", "통화 탭", "teams"),
|
||||
new("Alt + ↑/↓", "채널 목록 위/아래 이동", "teams"),
|
||||
new("Ctrl + R", "메시지에 답장", "teams"),
|
||||
new("Enter", "회의 참가 (알림에서)", "teams"),
|
||||
|
||||
// Outlook
|
||||
new("Ctrl + N", "새 이메일 작성", "outlook"),
|
||||
new("Ctrl + R", "답장", "outlook"),
|
||||
new("Ctrl + Shift + R", "전체 답장", "outlook"),
|
||||
new("Ctrl + F", "이메일 전달", "outlook"),
|
||||
new("Ctrl + Enter", "이메일 전송", "outlook"),
|
||||
new("Ctrl + S", "초안 저장", "outlook"),
|
||||
new("Delete", "이메일 삭제", "outlook"),
|
||||
new("Ctrl + Z", "실행 취소", "outlook"),
|
||||
new("Ctrl + 1", "메일 보기", "outlook"),
|
||||
new("Ctrl + 2", "일정 보기", "outlook"),
|
||||
new("Ctrl + 3", "연락처 보기", "outlook"),
|
||||
new("Ctrl + 4", "작업 보기", "outlook"),
|
||||
new("F9", "모든 계정 보내기/받기", "outlook"),
|
||||
new("Ctrl + Shift + I", "받은 편지함으로 이동", "outlook"),
|
||||
new("Ctrl + Shift + V", "이메일 이동", "outlook"),
|
||||
new("Ctrl + Shift + G", "플래그 설정", "outlook"),
|
||||
new("Ctrl + Q", "읽음 표시", "outlook"),
|
||||
new("Ctrl + U", "읽지 않음 표시", "outlook"),
|
||||
new("Ctrl + Shift + J", "정크 메일 처리", "outlook"),
|
||||
new("Ctrl + K", "이름 확인 (주소 자동 완성)", "outlook"),
|
||||
new("Alt + S", "이메일 보내기", "outlook"),
|
||||
];
|
||||
|
||||
public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
|
||||
@@ -187,7 +277,7 @@ public class KeyHandler : IActionHandler
|
||||
if (string.IsNullOrWhiteSpace(q))
|
||||
{
|
||||
items.Add(new LauncherItem("키보드 단축키 참조 사전",
|
||||
"key win / vscode / chrome / vim / excel / terminal / key <키워드 검색>",
|
||||
"key win / vscode / chrome / vim / excel / terminal / word / ppt / teams / outlook / key <키워드 검색>",
|
||||
null, null, Symbol: "\uE92E"));
|
||||
AddAppOverview(items);
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
@@ -205,6 +295,10 @@ public class KeyHandler : IActionHandler
|
||||
["vim"] = ["vim", "vi", "neovim"],
|
||||
["excel"] = ["excel", "spreadsheet"],
|
||||
["terminal"] = ["terminal", "wt"],
|
||||
["word"] = ["word", "워드"],
|
||||
["ppt"] = ["ppt", "powerpoint", "파워포인트", "프레젠테이션"],
|
||||
["teams"] = ["teams", "팀즈"],
|
||||
["outlook"] = ["outlook", "아웃룩", "mail"],
|
||||
};
|
||||
|
||||
foreach (var (cat, aliases) in catKeys)
|
||||
@@ -220,6 +314,10 @@ public class KeyHandler : IActionHandler
|
||||
"vim" => "Vim",
|
||||
"excel" => "Excel",
|
||||
"terminal" => "Windows Terminal",
|
||||
"word" => "Word",
|
||||
"ppt" => "PowerPoint",
|
||||
"teams" => "Teams",
|
||||
"outlook" => "Outlook",
|
||||
_ => cat
|
||||
};
|
||||
items.Add(new LauncherItem($"{catName} 단축키 {catItems.Count}개", "", null, null, Symbol: "\uE92E"));
|
||||
@@ -248,7 +346,7 @@ public class KeyHandler : IActionHandler
|
||||
else
|
||||
{
|
||||
items.Add(new LauncherItem($"'{keyword}' 결과 없음",
|
||||
"key win / vscode / chrome / vim / excel / terminal",
|
||||
"key win / vscode / chrome / vim / excel / terminal / word / ppt / teams / outlook",
|
||||
null, null, Symbol: "\uE783"));
|
||||
AddAppOverview(items);
|
||||
}
|
||||
@@ -281,6 +379,10 @@ public class KeyHandler : IActionHandler
|
||||
("vim", "Vim 명령", All.Count(s => s.App == "vim")),
|
||||
("excel", "Excel 단축키", All.Count(s => s.App == "excel")),
|
||||
("terminal", "Windows Terminal 단축키", All.Count(s => s.App == "terminal")),
|
||||
("word", "Word 단축키", All.Count(s => s.App == "word")),
|
||||
("ppt", "PowerPoint 단축키", All.Count(s => s.App == "ppt")),
|
||||
("teams", "Teams 단축키", All.Count(s => s.App == "teams")),
|
||||
("outlook", "Outlook 단축키", All.Count(s => s.App == "outlook")),
|
||||
};
|
||||
foreach (var (key, label, count) in apps)
|
||||
items.Add(new LauncherItem($"key {key}", $"{label} ({count}개)",
|
||||
|
||||
276
src/AxCopilot/Handlers/SpellHandler.cs
Normal file
276
src/AxCopilot/Handlers/SpellHandler.cs
Normal file
@@ -0,0 +1,276 @@
|
||||
using System.Windows;
|
||||
using AxCopilot.SDK;
|
||||
using AxCopilot.Services;
|
||||
|
||||
namespace AxCopilot.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// L24-3: 자주 틀리는 한국어 맞춤법 핸들러. "spell" 프리픽스로 사용합니다.
|
||||
///
|
||||
/// 예: spell → 클립보드 텍스트 맞춤법 검사
|
||||
/// spell 되 → "되/돼" 관련 오류 목록
|
||||
/// spell <단어> → 해당 단어 맞춤법 확인
|
||||
/// spell list → 전체 오류 목록 카테고리
|
||||
/// Enter → 올바른 표현 클립보드 복사
|
||||
/// </summary>
|
||||
public class SpellHandler : IActionHandler
|
||||
{
|
||||
public string? Prefix => "spell";
|
||||
|
||||
public PluginMetadata Metadata => new(
|
||||
"맞춤법",
|
||||
"자주 틀리는 한국어 맞춤법 참조 — 되/돼·안/않·혼동어·외래어 등",
|
||||
"1.0",
|
||||
"AX");
|
||||
|
||||
private sealed record SpellEntry(string Wrong, string Correct, string Explanation, string Category);
|
||||
|
||||
private static readonly SpellEntry[] Entries =
|
||||
[
|
||||
// ── 되/돼 ────────────────────────────────────────────────────────────
|
||||
new("됬다", "됐다", "됐다(되었다의 준말)", "되/돼"),
|
||||
new("됬어", "됐어", "됐어(되었어의 준말)", "되/돼"),
|
||||
new("됬나요", "됐나요", "됐나요(되었나요의 준말)", "되/돼"),
|
||||
new("됬습니다", "됐습니다", "됐습니다(되었습니다의 준말)", "되/돼"),
|
||||
new("돼었다", "됐다", "됐다(되었다의 준말)", "되/돼"),
|
||||
new("안됬어", "안 됐어", "됐다(되었다의 준말), 안은 띄어씀", "되/돼"),
|
||||
new("어떻게됬나요", "어떻게 됐나요","됐나요(되었나요의 준말)", "되/돼"),
|
||||
new("잘됬다", "잘됐다", "잘됐다(잘 되었다의 준말)", "되/돼"),
|
||||
new("해됬다", "해 됐다", "됐다(되었다의 준말)", "되/돼"),
|
||||
new("이렇게됬다", "이렇게 됐다", "됐다(되었다의 준말), 띄어씀 주의", "되/돼"),
|
||||
|
||||
// ── 안/않 ────────────────────────────────────────────────────────────
|
||||
new("안되다", "안 되다", "'안'은 부사이므로 띄어씀", "안/않"),
|
||||
new("안하다", "안 하다", "'안'은 부사이므로 띄어씀", "안/않"),
|
||||
new("않되다", "안 되다", "'않다'는 '아니하다'의 준말로 용언", "안/않"),
|
||||
new("하지않다", "하지 않다", "'않다'는 용언, 앞 단어와 띄어씀", "안/않"),
|
||||
new("하지않고", "하지 않고", "'않고'는 용언, 앞 단어와 띄어씀", "안/않"),
|
||||
new("하지않아", "하지 않아", "'않아'는 용언, 앞 단어와 띄어씀", "안/않"),
|
||||
new("되지않다", "되지 않다", "'않다'는 용언, 앞 단어와 띄어씀", "안/않"),
|
||||
new("않됩니다", "안 됩니다", "'않다'는 '아니하다', '안'은 부사", "안/않"),
|
||||
|
||||
// ── 혼동어 ──────────────────────────────────────────────────────────
|
||||
new("로서/로써", "로서(자격), 로써(수단)", "의미 구분: 학생으로서(자격), 말로써(수단)", "혼동어"),
|
||||
new("낫다/낳다", "낫다(회복), 낳다(출산)", "의미 구분: 병이 낫다 / 아이를 낳다", "혼동어"),
|
||||
new("맞히다/맞추다","맞히다(정답), 맞추다(조합)","의미 구분: 문제를 맞히다 / 퍼즐을 맞추다", "혼동어"),
|
||||
new("반드시/반듯이","반드시(꼭), 반듯이(곧게)", "의미 구분: 반드시 와라 / 반듯이 서라", "혼동어"),
|
||||
new("부치다/붙이다","부치다(편지), 붙이다(접착)","의미 구분: 편지를 부치다 / 우표를 붙이다", "혼동어"),
|
||||
new("이따가/있다가","이따가(나중에), 있다가(머물다가)","의미 구분: 이따가 와라 / 집에 있다가 나와", "혼동어"),
|
||||
new("웬/왠", "웬(어떤), 왠지(왜인지)", "의미 구분: 웬 일이니 / 왠지 모르게", "혼동어"),
|
||||
new("어떻게/어떡해","어떻게(방법), 어떡해(어떻게 해)","의미 구분: 어떻게 해야 하나 / 이걸 어떡해", "혼동어"),
|
||||
new("바라다/바래다","바라다(희망), 바래다(색이 변함)","의미 구분: 합격을 바란다 / 색이 바랬다", "혼동어"),
|
||||
new("~던지/~든지", "~던지(과거경험), ~든지(선택)","의미 구분: 얼마나 좋았던지 / 뭐든지 해라", "혼동어"),
|
||||
new("틀리다/다르다","틀리다(오답), 다르다(차이)", "의미 구분: 답이 틀렸다 / 나와 다르다", "혼동어"),
|
||||
|
||||
// ── 맞춤법 ──────────────────────────────────────────────────────────
|
||||
new("설레임", "설렘", "표준어는 '설렘'", "맞춤법"),
|
||||
new("오랫만에", "오랜만에", "표준어는 '오랜만에'", "맞춤법"),
|
||||
new("왠만하면", "웬만하면", "표준어는 '웬만하면'", "맞춤법"),
|
||||
new("몇일", "며칠", "표준어는 '며칠'", "맞춤법"),
|
||||
new("어의없다", "어이없다", "표준어는 '어이없다'", "맞춤법"),
|
||||
new("내노라하는", "내로라하는", "표준어는 '내로라하는'", "맞춤법"),
|
||||
new("금새", "금세", "표준어는 '금세' ('금시에'의 준말)", "맞춤법"),
|
||||
new("새벽녁", "새벽녘", "표준어는 '새벽녘'", "맞춤법"),
|
||||
new("무릎쓰고", "무릅쓰고", "표준어는 '무릅쓰고'", "맞춤법"),
|
||||
new("짜집기", "짜깁기", "표준어는 '짜깁기'", "맞춤법"),
|
||||
new("역활", "역할", "표준어는 '역할'", "맞춤법"),
|
||||
new("희안하다", "희한하다", "표준어는 '희한하다'", "맞춤법"),
|
||||
new("요컨데", "요컨대", "표준어는 '요컨대'", "맞춤법"),
|
||||
new("알맞는", "알맞은", "형용사이므로 '알맞은'이 맞음", "맞춤법"),
|
||||
new("예쁘다/이쁘다","예쁘다가 표준어","'이쁘다'는 비표준어", "맞춤법"),
|
||||
new("이따위", "이따위", "표준어 (맞음)", "맞춤법"),
|
||||
new("구렛나루", "구레나룻", "표준어는 '구레나룻'", "맞춤법"),
|
||||
new("꼭두각시", "꼭두각시", "표준어 (맞음)", "맞춤법"),
|
||||
|
||||
// ── 띄어쓰기 ────────────────────────────────────────────────────────
|
||||
new("할수있다", "할 수 있다", "의존명사 '수'는 띄어씀", "띄어쓰기"),
|
||||
new("해야한다", "해야 한다", "'한다'는 독립 서술어, 띄어씀", "띄어쓰기"),
|
||||
new("것같다", "것 같다", "의존명사 '것'은 띄어씀", "띄어쓰기"),
|
||||
new("수밖에없다", "수밖에 없다", "'없다'는 독립 서술어, 띄어씀", "띄어쓰기"),
|
||||
new("한번", "한 번(횟수), 한번(시도)","횟수=한 번 / 시도=한번", "띄어쓰기"),
|
||||
new("이상한거같다", "이상한 것 같다","'것'은 의존명사로 띄어씀", "띄어쓰기"),
|
||||
new("될것같다", "될 것 같다", "'것'은 의존명사로 띄어씀", "띄어쓰기"),
|
||||
new("할것이다", "할 것이다", "'것'은 의존명사로 띄어씀", "띄어쓰기"),
|
||||
new("있을때", "있을 때", "'때'는 의존명사로 띄어씀", "띄어쓰기"),
|
||||
new("모를때", "모를 때", "'때'는 의존명사로 띄어씀", "띄어쓰기"),
|
||||
|
||||
// ── 외래어 ──────────────────────────────────────────────────────────
|
||||
new("리더쉽", "리더십", "표준 외래어: ~ship은 '십'으로", "외래어"),
|
||||
new("멤버쉽", "멤버십", "표준 외래어: ~ship은 '십'으로", "외래어"),
|
||||
new("파트너쉽", "파트너십", "표준 외래어: ~ship은 '십'으로", "외래어"),
|
||||
new("인턴쉽", "인턴십", "표준 외래어: ~ship은 '십'으로", "외래어"),
|
||||
new("메세지", "메시지", "표준 외래어: message → 메시지", "외래어"),
|
||||
new("써비스", "서비스", "표준 외래어: service → 서비스", "외래어"),
|
||||
new("케릭터", "캐릭터", "표준 외래어: character → 캐릭터", "외래어"),
|
||||
new("컨텐츠", "콘텐츠", "표준 외래어: contents → 콘텐츠", "외래어"),
|
||||
new("엑기스", "엑스", "표준 외래어: extract → 엑기스 (비표준)","외래어"),
|
||||
new("렌트카", "렌터카", "표준 외래어: rent-a-car → 렌터카", "외래어"),
|
||||
new("쥬스", "주스", "표준 외래어: juice → 주스", "외래어"),
|
||||
new("후라이", "프라이", "표준 외래어: fry → 프라이", "외래어"),
|
||||
new("후라이팬", "프라이팬", "표준 외래어: frying pan → 프라이팬", "외래어"),
|
||||
new("비스켓", "비스킷", "표준 외래어: biscuit → 비스킷", "외래어"),
|
||||
new("떼레비", "텔레비전", "표준 외래어: television → 텔레비전", "외래어"),
|
||||
];
|
||||
|
||||
private static readonly string[] CategoryOrder = ["되/돼", "안/않", "혼동어", "맞춤법", "띄어쓰기", "외래어"];
|
||||
|
||||
public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
|
||||
{
|
||||
var q = query.Trim();
|
||||
var items = new List<LauncherItem>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(q))
|
||||
{
|
||||
// 클립보드 텍스트 맞춤법 검사
|
||||
string clipText = "";
|
||||
try
|
||||
{
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
if (Clipboard.ContainsText())
|
||||
clipText = Clipboard.GetText();
|
||||
});
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(clipText))
|
||||
{
|
||||
var found = Entries.Where(e =>
|
||||
clipText.Contains(e.Wrong, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
|
||||
if (found.Count > 0)
|
||||
{
|
||||
items.Add(new LauncherItem($"클립보드에서 맞춤법 오류 {found.Count}개 발견",
|
||||
"Enter: 올바른 표현 복사", null, null, Symbol: "\uE7BA"));
|
||||
foreach (var e in found)
|
||||
items.Add(MakeSpellItem(e));
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
}
|
||||
else
|
||||
{
|
||||
items.Add(new LauncherItem("클립보드 텍스트에서 오류를 찾지 못했습니다",
|
||||
"카테고리: 되/돼 · 안/않 · 맞춤법 · 띄어쓰기 · 혼동어 · 외래어",
|
||||
null, null, Symbol: "\uE7BA"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
items.Add(new LauncherItem($"한국어 맞춤법 참조 {Entries.Length}개",
|
||||
"카테고리: 되/돼 · 안/않 · 맞춤법 · 띄어쓰기 · 혼동어 · 외래어",
|
||||
null, null, Symbol: "\uE7BA"));
|
||||
}
|
||||
|
||||
// 카테고리 목록
|
||||
foreach (var cat in CategoryOrder)
|
||||
{
|
||||
var cnt = Entries.Count(e => e.Category == cat);
|
||||
items.Add(new LauncherItem($"spell {cat}", $"{cat} ({cnt}개)",
|
||||
null, null, Symbol: "\uE7BA"));
|
||||
}
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
}
|
||||
|
||||
var kw = q.ToLowerInvariant();
|
||||
|
||||
// "list" → 카테고리별 개수
|
||||
if (kw == "list")
|
||||
{
|
||||
items.Add(new LauncherItem($"맞춤법 오류 목록 총 {Entries.Length}개", "", null, null, Symbol: "\uE7BA"));
|
||||
foreach (var cat in CategoryOrder)
|
||||
{
|
||||
var cnt = Entries.Count(e => e.Category == cat);
|
||||
items.Add(new LauncherItem(cat, $"{cnt}개 · spell {cat}로 목록 보기",
|
||||
null, null, Symbol: "\uE7BA"));
|
||||
}
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
}
|
||||
|
||||
// 카테고리 키워드 매핑
|
||||
var catMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["되"] = "되/돼",
|
||||
["돼"] = "됩니다",
|
||||
["됐"] = "됩니다",
|
||||
["됩"] = "됩니다",
|
||||
["되/돼"] = "됩니다",
|
||||
["안"] = "안/않",
|
||||
["않"] = "안/않",
|
||||
["안/않"] = "안/않",
|
||||
["혼동"] = "혼동어",
|
||||
["혼동어"] = "혼동어",
|
||||
["맞춤법"] = "맞춤법",
|
||||
["맞춤"] = "맞춤법",
|
||||
["띄어"] = "띄어쓰기",
|
||||
["띄어쓰기"] = "띄어쓰기",
|
||||
["외래어"] = "외래어",
|
||||
["외래"] = "외래어",
|
||||
};
|
||||
|
||||
// 카테고리 직접 매핑
|
||||
foreach (var cat in CategoryOrder)
|
||||
{
|
||||
if (cat.Equals(q, StringComparison.OrdinalIgnoreCase) ||
|
||||
catMap.TryGetValue(q, out var mappedCat) && mappedCat == cat)
|
||||
{
|
||||
var catList = Entries.Where(e => e.Category == cat).ToList();
|
||||
items.Add(new LauncherItem($"{cat} {catList.Count}개",
|
||||
"Enter: 올바른 표현 복사", null, null, Symbol: "\uE7BA"));
|
||||
foreach (var e in catList)
|
||||
items.Add(MakeSpellItem(e));
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
}
|
||||
}
|
||||
|
||||
// catMap으로 카테고리 찾기
|
||||
if (catMap.TryGetValue(q, out var foundCat) && CategoryOrder.Contains(foundCat))
|
||||
{
|
||||
var catList = Entries.Where(e => e.Category == foundCat).ToList();
|
||||
items.Add(new LauncherItem($"{foundCat} {catList.Count}개",
|
||||
"Enter: 올바른 표현 복사", null, null, Symbol: "\uE7BA"));
|
||||
foreach (var e in catList)
|
||||
items.Add(MakeSpellItem(e));
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
}
|
||||
|
||||
// 키워드 검색
|
||||
var searched = Entries.Where(e =>
|
||||
e.Wrong.Contains(kw, StringComparison.OrdinalIgnoreCase) ||
|
||||
e.Correct.Contains(kw, StringComparison.OrdinalIgnoreCase) ||
|
||||
e.Explanation.Contains(kw, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
|
||||
if (searched.Count == 0)
|
||||
{
|
||||
items.Add(new LauncherItem($"'{q}' 검색 결과 없음",
|
||||
"카테고리: 되/돼 · 안/않 · 맞춤법 · 띄어쓰기 · 혼동어 · 외래어",
|
||||
null, null, Symbol: "\uE783"));
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
}
|
||||
|
||||
items.Add(new LauncherItem($"'{q}' 검색 결과 {searched.Count}개",
|
||||
"Enter: 올바른 표현 복사", null, null, Symbol: "\uE7BA"));
|
||||
foreach (var e in searched)
|
||||
items.Add(MakeSpellItem(e));
|
||||
|
||||
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("맞춤법", "올바른 표현을 복사했습니다.");
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static LauncherItem MakeSpellItem(SpellEntry e) =>
|
||||
new($"❌ {e.Wrong} → ✅ {e.Correct}",
|
||||
e.Explanation,
|
||||
null, ("copy", e.Correct), Symbol: "\uE7BA");
|
||||
}
|
||||
Reference in New Issue
Block a user