Initial commit to new repository
This commit is contained in:
673
src/AxCopilot/Views/HelpDetailWindow.xaml.cs
Normal file
673
src/AxCopilot/Views/HelpDetailWindow.xaml.cs
Normal file
@@ -0,0 +1,673 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace AxCopilot.Views;
|
||||
|
||||
public partial class HelpDetailWindow : Window
|
||||
{
|
||||
private const string CatAll = "전체";
|
||||
private const string CatPopular = "\u2B50 인기";
|
||||
|
||||
// ─── 상단 3탭 메뉴 ──────────────────────────────────────────────────────
|
||||
private enum TopMenu { Overview, Shortcuts, Prefixes }
|
||||
|
||||
private static readonly (TopMenu Key, string Label, string Icon)[] TopMenus =
|
||||
{
|
||||
(TopMenu.Overview, "개요", "\uE946"),
|
||||
(TopMenu.Shortcuts, "단축키 현황", "\uE765"),
|
||||
(TopMenu.Prefixes, "예약어 현황", "\uE8F4"),
|
||||
};
|
||||
|
||||
private TopMenu _currentTopMenu = TopMenu.Overview;
|
||||
|
||||
// ─── 데이터 ──────────────────────────────────────────────────────────────
|
||||
private readonly List<HelpItemModel> _allItems;
|
||||
private readonly List<HelpItemModel> _shortcutItems;
|
||||
private readonly List<HelpItemModel> _prefixItems;
|
||||
private readonly List<HelpItemModel> _overviewItems;
|
||||
private readonly string _globalHotkey;
|
||||
|
||||
private List<string> _categories = new();
|
||||
private int _currentPage;
|
||||
|
||||
public HelpDetailWindow(IEnumerable<HelpItemModel> items, int totalCount,
|
||||
string globalHotkey = "Alt+Space")
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_allItems = items.ToList();
|
||||
_globalHotkey = globalHotkey;
|
||||
|
||||
// ── 개요 항목 (전체 / 개요 / AI / 업무 보조) ───────────────────────
|
||||
_overviewItems = new List<HelpItemModel>
|
||||
{
|
||||
// ── 개요 ────────────────────────────────────────────────────
|
||||
new()
|
||||
{
|
||||
Category = "개요", Command = "AX Commander",
|
||||
Title = "키보드 하나로 모든 업무를 제어하는 AX Commander",
|
||||
Description = "단축키 한 번으로 앱 실행, 파일/폴더 검색, 계산, 클립보드 관리, 화면 캡처, 창 전환, 시스템 제어, 업무 일지, 루틴 자동화, AI 대화까지. 40개+ 명령어를 예약어로 즉시 접근.",
|
||||
Symbol = "\uE946",
|
||||
ColorBrush = ParseColor("#4B5EFC")
|
||||
},
|
||||
new()
|
||||
{
|
||||
Category = "개요", Command = "사용법",
|
||||
Title = "예약어 + 키워드 → Enter",
|
||||
Description = "특수 문자를 맨 앞에 입력하면 해당 기능 모드로 전환됩니다. 예) = 수식, # 클립보드, ? 웹검색, ! AI 대화, cap 캡처. 예약어 없이 입력하면 앱/파일/폴더 검색.",
|
||||
Symbol = "\uE765",
|
||||
ColorBrush = ParseColor("#0078D4")
|
||||
},
|
||||
new()
|
||||
{
|
||||
Category = "개요", Command = "데이터 보호",
|
||||
Title = "모든 데이터는 내 PC에만 안전하게 저장",
|
||||
Description = "설정, 클립보드, 대화 내역, 통계, 로그 등 모든 데이터가 로컬에 암호화 저장됩니다. 다른 PC에서는 열 수 없으며, 보존 기간 만료 시 자동 삭제됩니다.",
|
||||
Symbol = "\uE8B7",
|
||||
ColorBrush = ParseColor("#107C10")
|
||||
},
|
||||
|
||||
// ── AI ──────────────────────────────────────────────────────
|
||||
new()
|
||||
{
|
||||
Category = "AI", Command = "! (AX Agent)",
|
||||
Title = "AX Agent — AI 어시스턴트와 대화",
|
||||
Description = "AX Commander에서 ! 를 입력하면 AI 대화가 열립니다. 질문을 바로 물어보거나, 이전 대화를 이어갈 수 있습니다. 대화 주제별 분류, 검색, 사이드바 토글을 지원합니다.",
|
||||
Example = "! 오늘 회의 요약해줘\n! 이메일 초안 작성해줘",
|
||||
Symbol = "\uE8BD",
|
||||
ColorBrush = ParseColor("#8B2FC9")
|
||||
},
|
||||
new()
|
||||
{
|
||||
Category = "AI", Command = "3탭 구조",
|
||||
Title = "Chat · Cowork · Code — 용도별 3탭 구조",
|
||||
Description = "Chat: 자유로운 AI 대화 — 질문, 번역, 요약, 아이디어 브레인스토밍.\n" +
|
||||
"Cowork: 업무 보조 — 파일 읽기/쓰기, 문서 생성(Excel·Word·PPT·HTML), 데이터 분석, 보고서 작성.\n" +
|
||||
"Code: 코딩 도우미 — 개발, 리팩터링, 코드 리뷰, 보안 점검, 테스트 작성. 개발 환경 자동 감지.",
|
||||
Symbol = "\uE71B",
|
||||
ColorBrush = ParseColor("#3B82F6")
|
||||
},
|
||||
new()
|
||||
{
|
||||
Category = "AI", Command = "AI 서비스",
|
||||
Title = "사내 AI · 외부 AI 서비스 선택 가능",
|
||||
Description = "관리자가 설정한 AI 서비스에 자동 연결됩니다. 사내 서버와 외부 서비스 중 선택할 수 있으며, 연결 정보는 암호화되어 보호됩니다.",
|
||||
Symbol = "\uE968",
|
||||
ColorBrush = ParseColor("#0078D4")
|
||||
},
|
||||
new()
|
||||
{
|
||||
Category = "AI", Command = "실시간 응답",
|
||||
Title = "AI 응답이 실시간으로 표시",
|
||||
Description = "AI의 응답이 생성되는 즉시 화면에 표시됩니다. 긴 답변도 기다리지 않고 바로 읽기 시작할 수 있습니다.",
|
||||
Symbol = "\uE8AB",
|
||||
ColorBrush = ParseColor("#107C10")
|
||||
},
|
||||
new()
|
||||
{
|
||||
Category = "AI", Command = "대화 보호",
|
||||
Title = "모든 대화 내역이 암호화 저장",
|
||||
Description = "대화 내역은 내 PC에서만 열람할 수 있도록 암호화됩니다. 다른 PC로 복사해도 열 수 없으며, 보존 기간 만료 시 자동 삭제됩니다.",
|
||||
Symbol = "\uE72E",
|
||||
ColorBrush = ParseColor("#C50F1F")
|
||||
},
|
||||
new()
|
||||
{
|
||||
Category = "AI", Command = "AI 규칙 설정",
|
||||
Title = "관리자가 설정한 응답 규칙이 자동 적용",
|
||||
Description = "관리자가 지정한 역할, 응답 톤, 업무 규칙이 모든 대화에 자동 적용됩니다. AI의 응답 스타일을 일괄 관리할 수 있습니다.",
|
||||
Symbol = "\uE771",
|
||||
ColorBrush = ParseColor("#D97706")
|
||||
},
|
||||
new()
|
||||
{
|
||||
Category = "AI", Command = "대화 관리",
|
||||
Title = "분류 · 검색 · 타임라인",
|
||||
Description = "대화를 주제별로 분류하고, 오늘/이전 타임라인으로 구분합니다. 검색과 카테고리 필터로 빠르게 찾을 수 있습니다.",
|
||||
Symbol = "\uE8F1",
|
||||
ColorBrush = ParseColor("#4B5EFC")
|
||||
},
|
||||
|
||||
// ── 업무 보조 ───────────────────────────────────────────────
|
||||
new()
|
||||
{
|
||||
Category = "업무 보조", Command = "note",
|
||||
Title = "빠른 메모 저장 · 검색",
|
||||
Description = "note 뒤에 내용을 입력하면 즉시 메모가 저장됩니다. 나중에 note 키워드로 검색하면 이전 메모를 다시 찾을 수 있습니다. 간단한 아이디어나 할 일을 빠르게 기록하세요.",
|
||||
Example = "note 내일 보고서 마감\nnote 김부장님 연락처 확인",
|
||||
Symbol = "\uE70B",
|
||||
ColorBrush = ParseColor("#D97706")
|
||||
},
|
||||
new()
|
||||
{
|
||||
Category = "업무 보조", Command = "journal",
|
||||
Title = "업무 일지 작성",
|
||||
Description = "journal 뒤에 내용을 입력하면 날짜별 업무 일지로 자동 기록됩니다. 하루 동안의 업무 내용을 간결하게 쌓아가면 주간 보고나 회고에 활용할 수 있습니다.",
|
||||
Example = "journal 배포 완료\njournal 클라이언트 미팅 30분",
|
||||
Symbol = "\uE8F2",
|
||||
ColorBrush = ParseColor("#0078D4")
|
||||
},
|
||||
new()
|
||||
{
|
||||
Category = "업무 보조", Command = "pipe",
|
||||
Title = "클립보드 텍스트 파이프라인 변환",
|
||||
Description = "클립보드에 복사된 텍스트를 여러 변환 함수로 한 번에 처리합니다. upper(대문자), lower(소문자), trim(공백 제거), wrap(줄바꿈), sort(정렬) 등을 연결할 수 있습니다.",
|
||||
Example = "pipe upper | trim | wrap 80",
|
||||
Symbol = "\uE8AB",
|
||||
ColorBrush = ParseColor("#107C10")
|
||||
},
|
||||
new()
|
||||
{
|
||||
Category = "업무 보조", Command = "diff",
|
||||
Title = "두 텍스트 비교 (Diff)",
|
||||
Description = "클립보드의 두 텍스트를 비교하여 차이점을 한눈에 보여줍니다. 코드 변경 사항 확인, 문서 버전 비교 등에 유용합니다.",
|
||||
Example = "diff",
|
||||
Symbol = "\uE8FD",
|
||||
ColorBrush = ParseColor("#6B2C91")
|
||||
},
|
||||
new()
|
||||
{
|
||||
Category = "업무 보조", Command = "encode",
|
||||
Title = "인코딩 · 디코딩 변환",
|
||||
Description = "Base64, URL, HTML, Unicode 등 다양한 형식으로 텍스트를 인코딩하거나 디코딩합니다. 개발자에게 특히 유용한 변환 도구입니다.",
|
||||
Example = "encode base64 hello\nencode url 한글 테스트",
|
||||
Symbol = "\uE943",
|
||||
ColorBrush = ParseColor("#323130")
|
||||
},
|
||||
new()
|
||||
{
|
||||
Category = "업무 보조", Command = "routine",
|
||||
Title = "반복 작업 루틴 자동화",
|
||||
Description = "자주 하는 작업 묶음을 루틴으로 등록해 두면 한 번의 명령으로 여러 앱을 동시에 열거나 작업 환경을 구성할 수 있습니다. 출근·퇴근 루틴 등을 만들어 보세요.",
|
||||
Example = "routine start 출근\nroutine list",
|
||||
Symbol = "\uE8F5",
|
||||
ColorBrush = ParseColor("#BE185D")
|
||||
},
|
||||
new()
|
||||
{
|
||||
Category = "업무 보조", Command = "stats",
|
||||
Title = "텍스트 통계 (글자수·단어수·줄수)",
|
||||
Description = "클립보드에 복사된 텍스트의 글자 수, 단어 수, 줄 수를 즉시 계산합니다. 보고서 분량 체크, SNS 글자 수 제한 확인 등에 활용하세요.",
|
||||
Example = "stats",
|
||||
Symbol = "\uE9D9",
|
||||
ColorBrush = ParseColor("#44546A")
|
||||
},
|
||||
new()
|
||||
{
|
||||
Category = "업무 보조", Command = "json",
|
||||
Title = "JSON 파싱 · 정리 · 미리보기",
|
||||
Description = "클립보드에 복사된 JSON을 자동으로 파싱하고 읽기 좋은 형태로 정리합니다. 들여쓰기, 키 하이라이트, 복사 기능을 제공합니다.",
|
||||
Example = "json {\"key\":\"value\"}",
|
||||
Symbol = "\uE8A5",
|
||||
ColorBrush = ParseColor("#4B5EFC")
|
||||
},
|
||||
};
|
||||
|
||||
// ── 단축키 항목 생성 ─────────────────────────────────────────────────
|
||||
_shortcutItems = BuildShortcutItems(_globalHotkey);
|
||||
|
||||
// ── 예약어 항목 분류 ────────────────────────────────────────────────
|
||||
_prefixItems = _allItems.ToList();
|
||||
|
||||
SubtitleText.Text = $"총 {totalCount}개 명령어 · 단축키 {_shortcutItems.Count}개 · 예약어 {_prefixItems.Count}개";
|
||||
|
||||
BuildTopMenu();
|
||||
SwitchTopMenu(TopMenu.Overview);
|
||||
|
||||
KeyDown += OnKeyDown;
|
||||
}
|
||||
|
||||
// ─── 단축키 항목 빌드 ─────────────────────────────────────────────────────
|
||||
|
||||
private static List<HelpItemModel> BuildShortcutItems(string globalHotkey = "Alt+Space")
|
||||
{
|
||||
var items = new List<HelpItemModel>();
|
||||
|
||||
// 설정에서 변경된 글로벌 단축키를 표시에 맞게 포맷 (예: "Alt+Space" → "Alt + Space")
|
||||
var hotkeyDisplay = globalHotkey.Replace("+", " + ");
|
||||
|
||||
// ── 전역 단축키 ──────────────────────────────────────────────────────
|
||||
items.Add(MakeShortcut("전역", hotkeyDisplay,
|
||||
"AX Commander 열기/닫기",
|
||||
"어느 창에서든 눌러 AX Commander를 즉시 호출하거나 닫습니다. 설정 › 일반에서 원하는 키 조합으로 변경할 수 있습니다.",
|
||||
"\uE765", "#4B5EFC"));
|
||||
items.Add(MakeShortcut("전역", "PrintScreen",
|
||||
"화면 캡처 즉시 실행",
|
||||
"AX Commander를 열지 않고 곧바로 캡처를 시작합니다. 설정 › 캡처 탭에서 '글로벌 단축키 활성화'를 켜야 동작합니다.",
|
||||
"\uE722", "#BE185D"));
|
||||
|
||||
// ── 런처 탐색 ────────────────────────────────────────────────────────
|
||||
items.Add(MakeShortcut("AX Commander 탐색", "Escape",
|
||||
"창 닫기 / 이전 단계로",
|
||||
"액션 모드(→ 로 진입)에 있을 때는 일반 검색 화면으로 돌아갑니다. 일반 화면이면 AX Commander를 숨깁니다.",
|
||||
"\uE711", "#999999"));
|
||||
items.Add(MakeShortcut("AX Commander 탐색", "Enter",
|
||||
"선택 항목 실행",
|
||||
"파일·앱이면 열기, URL이면 브라우저 열기, 시스템 명령이면 즉시 실행, 계산기 결과면 클립보드에 복사합니다.",
|
||||
"\uE768", "#107C10"));
|
||||
items.Add(MakeShortcut("AX Commander 탐색", "Shift + Enter",
|
||||
"대형 텍스트(Large Type) 표시 / 클립보드 병합 실행",
|
||||
"선택된 텍스트·검색어를 화면 전체에 크게 띄웁니다. 클립보드 병합 항목이 있을 때는 선택한 항목들을 줄바꿈으로 합쳐 클립보드에 복사합니다.",
|
||||
"\uE8C1", "#8764B8"));
|
||||
items.Add(MakeShortcut("AX Commander 탐색", "↑ / ↓",
|
||||
"결과 목록 위/아래 이동",
|
||||
"목록 끝에서 계속 누르면 처음/끝으로 순환합니다.",
|
||||
"\uE74A", "#0078D4"));
|
||||
items.Add(MakeShortcut("AX Commander 탐색", "PageUp / PageDown",
|
||||
"목록 5칸 빠른 이동",
|
||||
"한 번에 5항목씩 건너뜁니다. 빠른 목록 탐색에 유용합니다.",
|
||||
"\uE74A", "#0078D4"));
|
||||
items.Add(MakeShortcut("AX Commander 탐색", "Home / End",
|
||||
"목록 처음 / 마지막 항목으로 점프",
|
||||
"입력창 커서가 맨 앞(또는 입력이 없을 때)이면 첫 항목으로, 맨 끝이면 마지막 항목으로 선택이 이동합니다.",
|
||||
"\uE74A", "#0078D4"));
|
||||
items.Add(MakeShortcut("AX Commander 탐색", "→ (오른쪽 화살표)",
|
||||
"액션 모드 진입",
|
||||
"파일·앱 항목을 선택한 상태에서 → 를 누르면 경로 복사, 탐색기 열기, 관리자 실행, 터미널, 속성, 이름 변경, 삭제 메뉴가 나타납니다.",
|
||||
"\uE76C", "#44546A"));
|
||||
items.Add(MakeShortcut("AX Commander 탐색", "Tab",
|
||||
"선택 항목 제목으로 자동완성",
|
||||
"현재 선택된 항목의 이름을 입력창에 채웁니다. 이후 계속 타이핑하거나 Enter로 실행합니다.",
|
||||
"\uE748", "#006EAF"));
|
||||
items.Add(MakeShortcut("AX Commander 탐색", "Shift + ↑/↓",
|
||||
"클립보드 병합 선택",
|
||||
"클립보드 히스토리(# 모드) 에서 여러 항목을 이동하면서 선택/해제합니다. Shift+Enter로 선택한 항목들을 한 번에 붙여넣을 수 있습니다.",
|
||||
"\uE8C1", "#B7791F"));
|
||||
|
||||
// ── 런처 기능 단축키 ─────────────────────────────────────────────────
|
||||
items.Add(MakeShortcut("런처 기능", "F1",
|
||||
"도움말 창 열기",
|
||||
"이 화면을 직접 엽니다. 'help' 를 입력하는 것과 동일합니다.",
|
||||
"\uE897", "#6B7280"));
|
||||
items.Add(MakeShortcut("런처 기능", "F2",
|
||||
"선택 파일 이름 변경",
|
||||
"파일·폴더 항목을 선택한 상태에서 누르면 rename [경로] 형태로 입력창에 채워지고 이름 변경 핸들러가 실행됩니다.",
|
||||
"\uE70F", "#6B2C91"));
|
||||
items.Add(MakeShortcut("런처 기능", "F5",
|
||||
"파일 인덱스 즉시 재구축",
|
||||
"백그라운드에서 파일·앱 인덱싱을 다시 실행합니다. 새 파일을 추가했거나 목록이 오래됐을 때 사용합니다.",
|
||||
"\uE72C", "#059669"));
|
||||
items.Add(MakeShortcut("런처 기능", "Delete",
|
||||
"최근 실행 목록에서 항목 제거",
|
||||
"recent 목록에 있는 항목을 제거합니다. 확인 다이얼로그가 표시되며 OK를 눌러야 실제로 제거됩니다.",
|
||||
"\uE74D", "#DC2626"));
|
||||
items.Add(MakeShortcut("런처 기능", "Ctrl + ,",
|
||||
"설정 창 열기",
|
||||
"AX Copilot 설정 창을 엽니다. 런처가 자동으로 숨겨집니다.",
|
||||
"\uE713", "#44546A"));
|
||||
items.Add(MakeShortcut("런처 기능", "Ctrl + L",
|
||||
"입력창 전체 초기화",
|
||||
"현재 입력된 검색어·예약어를 모두 지우고 커서를 빈 입력창으로 돌립니다.",
|
||||
"\uE894", "#4B5EFC"));
|
||||
items.Add(MakeShortcut("런처 기능", "Ctrl + C",
|
||||
"선택 항목 파일 이름 복사",
|
||||
"파일·앱 항목이 선택된 경우 확장자를 제외한 파일 이름을 클립보드에 복사하고 토스트로 알립니다.",
|
||||
"\uE8C8", "#8764B8"));
|
||||
items.Add(MakeShortcut("런처 기능", "Ctrl + Shift + C",
|
||||
"선택 항목 전체 경로 복사",
|
||||
"선택된 파일·폴더의 절대 경로(예: C:\\Users\\...)를 클립보드에 복사합니다.",
|
||||
"\uE8C8", "#C55A11"));
|
||||
items.Add(MakeShortcut("런처 기능", "Ctrl + Shift + E",
|
||||
"파일 탐색기에서 선택 항목 열기",
|
||||
"Windows 탐색기가 열리고 해당 파일·폴더가 하이라이트 선택된 상태로 표시됩니다.",
|
||||
"\uE838", "#107C10"));
|
||||
items.Add(MakeShortcut("런처 기능", "Ctrl + Enter",
|
||||
"관리자(UAC) 권한으로 실행",
|
||||
"선택된 파일·앱을 UAC 권한 상승 후 실행합니다. 설치 프로그램이나 시스템 설정 앱에 유용합니다.",
|
||||
"\uE7EF", "#C50F1F"));
|
||||
items.Add(MakeShortcut("런처 기능", "Alt + Enter",
|
||||
"파일 속성 대화 상자 열기",
|
||||
"Windows의 '파일 속성' 창(크기·날짜·권한 등)을 엽니다.",
|
||||
"\uE946", "#6B2C91"));
|
||||
items.Add(MakeShortcut("런처 기능", "Ctrl + T",
|
||||
"선택 항목 위치에서 터미널 열기",
|
||||
"선택된 파일이면 해당 폴더에서, 폴더이면 그 경로에서 Windows Terminal(wt.exe)이 열립니다. wt가 없으면 cmd로 대체됩니다.",
|
||||
"\uE756", "#323130"));
|
||||
items.Add(MakeShortcut("런처 기능", "Ctrl + P",
|
||||
"즐겨찾기 즉시 추가 / 제거 (핀)",
|
||||
"파일·폴더 항목을 선택한 상태에서 누르면 favorites.json 에 추가하거나 이미 있으면 제거합니다. 토스트로 결과를 알립니다.",
|
||||
"\uE734", "#D97706"));
|
||||
items.Add(MakeShortcut("런처 기능", "Ctrl + B",
|
||||
"즐겨찾기 목록 보기 / 닫기 토글",
|
||||
"입력창이 'fav' 이면 초기화하고, 아니면 'fav' 를 입력해 즐겨찾기 목록을 표시합니다.",
|
||||
"\uE735", "#D97706"));
|
||||
items.Add(MakeShortcut("런처 기능", "Ctrl + R",
|
||||
"최근 실행 목록 보기 / 닫기 토글",
|
||||
"'recent' 를 입력해 최근 실행 항목을 표시합니다.",
|
||||
"\uE81C", "#0078D4"));
|
||||
items.Add(MakeShortcut("런처 기능", "Ctrl + H",
|
||||
"클립보드 히스토리 목록 열기",
|
||||
"'#' 를 입력해 클립보드에 저장된 최근 복사 항목 목록을 표시합니다.",
|
||||
"\uE77F", "#8B2FC9"));
|
||||
items.Add(MakeShortcut("런처 기능", "Ctrl + D",
|
||||
"다운로드 폴더 바로가기",
|
||||
"사용자 홈의 Downloads 폴더 경로를 입력창에 채워 탐색기로 열 수 있게 합니다.",
|
||||
"\uE8B7", "#107C10"));
|
||||
items.Add(MakeShortcut("런처 기능", "Ctrl + F",
|
||||
"파일 검색 모드로 전환",
|
||||
"입력창을 초기화하고 포커스를 이동합니다. 이후 파일명을 바로 타이핑해 검색할 수 있습니다.",
|
||||
"\uE71E", "#4B5EFC"));
|
||||
items.Add(MakeShortcut("런처 기능", "Ctrl + W",
|
||||
"런처 창 즉시 닫기",
|
||||
"현재 입력 내용에 관계없이 런처를 즉시 숨깁니다.",
|
||||
"\uE711", "#9999BB"));
|
||||
items.Add(MakeShortcut("런처 기능", "Ctrl + K",
|
||||
"단축키 참조 모달 창 열기",
|
||||
"모든 단축키와 설명을 보여주는 별도 모달 창이 열립니다. Esc 또는 닫기 버튼으로 닫습니다.",
|
||||
"\uE8FD", "#4B5EFC"));
|
||||
items.Add(MakeShortcut("런처 기능", "Ctrl + 1 ~ 9",
|
||||
"N번째 결과 항목 바로 실행",
|
||||
"목록에 번호 배지(1~9)가 표시된 항목을 해당 숫자 키로 즉시 실행합니다. 마우스 없이 빠른 실행에 유용합니다.",
|
||||
"\uE8C4", "#107C10"));
|
||||
|
||||
// ── 기타 창 단축키 ────────────────────────────────────────────────────
|
||||
items.Add(MakeShortcut("기타 창", "← / →",
|
||||
"헬프 창 카테고리 이동",
|
||||
"이 도움말 창에서 하위 카테고리 탭을 왼쪽/오른쪽으로 이동합니다.",
|
||||
"\uE76B", "#4455AA"));
|
||||
items.Add(MakeShortcut("기타 창", "1 / 2 / 3",
|
||||
"헬프 창 상단 메뉴 전환",
|
||||
"이 도움말 창에서 개요(1), 단축키 현황(2), 예약어 현황(3)을 키보드로 전환합니다.",
|
||||
"\uE8BD", "#4455AA"));
|
||||
items.Add(MakeShortcut("기타 창", "방향키 (캡처 중)",
|
||||
"영역 선택 경계 1px 미세 조정",
|
||||
"화면 캡처의 영역 선택 모드에서 선택 영역 경계를 1픽셀씩 정밀 조정합니다.",
|
||||
"\uE745", "#BE185D"));
|
||||
items.Add(MakeShortcut("기타 창", "Shift + 방향키 (캡처 중)",
|
||||
"영역 선택 경계 10px 이동",
|
||||
"화면 캡처의 영역 선택 모드에서 선택 영역 경계를 10픽셀씩 빠르게 이동합니다.",
|
||||
"\uE745", "#BE185D"));
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private static SolidColorBrush ParseColor(string hex)
|
||||
{
|
||||
try { return new SolidColorBrush((Color)ColorConverter.ConvertFromString(hex)); }
|
||||
catch { return new SolidColorBrush(Colors.Gray); }
|
||||
}
|
||||
|
||||
private static HelpItemModel MakeShortcut(string cat, string key, string title, string desc, string symbol, string color)
|
||||
{
|
||||
return new HelpItemModel
|
||||
{
|
||||
Category = cat,
|
||||
Command = key,
|
||||
Title = title,
|
||||
Description = desc,
|
||||
Symbol = symbol,
|
||||
ColorBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(color))
|
||||
};
|
||||
}
|
||||
|
||||
// ─── 상단 3탭 메뉴 빌드 ──────────────────────────────────────────────────
|
||||
|
||||
// ─── 테마 색상 헬퍼 ───────────────────────────────────────────────────────
|
||||
private Brush ThemeAccent => TryFindResource("AccentColor") as Brush ?? ParseColor("#4B5EFC");
|
||||
private Brush ThemePrimary => TryFindResource("PrimaryText") as Brush ?? Brushes.White;
|
||||
private Brush ThemeSecondary => TryFindResource("SecondaryText") as Brush ?? ParseColor("#8899CC");
|
||||
|
||||
private void BuildTopMenu()
|
||||
{
|
||||
TopMenuBar.Children.Clear();
|
||||
foreach (var (key, label, icon) in TopMenus)
|
||||
{
|
||||
var k = key;
|
||||
var sp = new StackPanel { Orientation = Orientation.Horizontal };
|
||||
sp.Children.Add(new TextBlock
|
||||
{
|
||||
Text = icon,
|
||||
FontFamily = new FontFamily("Segoe MDL2 Assets"),
|
||||
FontSize = 14,
|
||||
Foreground = ThemeAccent,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Margin = new Thickness(0, 0, 6, 0)
|
||||
});
|
||||
sp.Children.Add(new TextBlock
|
||||
{
|
||||
Text = label,
|
||||
FontSize = 12,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
});
|
||||
|
||||
var btn = new Button
|
||||
{
|
||||
Content = sp,
|
||||
FontWeight = FontWeights.SemiBold,
|
||||
Padding = new Thickness(16, 8, 16, 8),
|
||||
Margin = new Thickness(4, 0, 4, 0),
|
||||
Cursor = Cursors.Hand,
|
||||
Background = Brushes.Transparent,
|
||||
Foreground = ThemeSecondary,
|
||||
BorderThickness = new Thickness(0, 0, 0, 2),
|
||||
BorderBrush = Brushes.Transparent,
|
||||
Tag = k,
|
||||
};
|
||||
btn.Click += (_, _) => SwitchTopMenu(k);
|
||||
TopMenuBar.Children.Add(btn);
|
||||
}
|
||||
}
|
||||
|
||||
private void SwitchTopMenu(TopMenu menu)
|
||||
{
|
||||
_currentTopMenu = menu;
|
||||
|
||||
// 상단 메뉴 하이라이트
|
||||
foreach (var child in TopMenuBar.Children)
|
||||
{
|
||||
if (child is Button btn)
|
||||
{
|
||||
var isActive = btn.Tag is TopMenu t && t == menu;
|
||||
btn.BorderBrush = isActive ? ThemeAccent : Brushes.Transparent;
|
||||
btn.Foreground = isActive ? ThemePrimary : ThemeSecondary;
|
||||
|
||||
// 내부 TextBlock의 Foreground도 업데이트
|
||||
if (btn.Content is StackPanel sp)
|
||||
{
|
||||
foreach (var spChild in sp.Children)
|
||||
{
|
||||
if (spChild is TextBlock tb && tb.FontFamily.Source != "Segoe MDL2 Assets")
|
||||
tb.Foreground = btn.Foreground;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 하위 카테고리 탭 빌드
|
||||
switch (menu)
|
||||
{
|
||||
case TopMenu.Overview:
|
||||
BuildCategoryBarFor(_overviewItems);
|
||||
break;
|
||||
case TopMenu.Shortcuts:
|
||||
BuildCategoryBarFor(_shortcutItems);
|
||||
break;
|
||||
case TopMenu.Prefixes:
|
||||
BuildCategoryBarFor(_prefixItems);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 하위 카테고리 탭 빌드 ────────────────────────────────────────────────
|
||||
|
||||
private void BuildCategoryBarFor(List<HelpItemModel> sourceItems)
|
||||
{
|
||||
var seen = new HashSet<string>();
|
||||
_categories = new List<string> { CatAll };
|
||||
|
||||
// 예약어 탭에서만 인기 카테고리 표시
|
||||
if (_currentTopMenu == TopMenu.Prefixes)
|
||||
_categories.Add(CatPopular);
|
||||
|
||||
foreach (var item in sourceItems)
|
||||
{
|
||||
if (seen.Add(item.Category))
|
||||
_categories.Add(item.Category);
|
||||
}
|
||||
|
||||
BuildCategoryBar();
|
||||
NavigateToPage(0);
|
||||
}
|
||||
|
||||
private void BuildCategoryBar()
|
||||
{
|
||||
CategoryBar.Children.Clear();
|
||||
for (int i = 0; i < _categories.Count; i++)
|
||||
{
|
||||
var cat = _categories[i];
|
||||
var idx = i;
|
||||
|
||||
var btn = new Button
|
||||
{
|
||||
Content = cat,
|
||||
FontSize = 10.5,
|
||||
FontWeight = FontWeights.SemiBold,
|
||||
Padding = new Thickness(9, 4, 9, 4),
|
||||
Margin = new Thickness(0, 0, 3, 0),
|
||||
Cursor = Cursors.Hand,
|
||||
Background = Brushes.Transparent,
|
||||
Foreground = ThemeSecondary,
|
||||
BorderThickness = new Thickness(0),
|
||||
Tag = idx,
|
||||
};
|
||||
|
||||
btn.Click += (_, _) => NavigateToPage(idx);
|
||||
CategoryBar.Children.Add(btn);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 페이지 네비게이션 ──────────────────────────────────────────────────
|
||||
|
||||
private List<HelpItemModel> GetCurrentSourceItems() => _currentTopMenu switch
|
||||
{
|
||||
TopMenu.Overview => _overviewItems,
|
||||
TopMenu.Shortcuts => _shortcutItems,
|
||||
TopMenu.Prefixes => _prefixItems,
|
||||
_ => _overviewItems,
|
||||
};
|
||||
|
||||
private void NavigateToPage(int pageIndex)
|
||||
{
|
||||
if (_categories.Count == 0) return;
|
||||
_currentPage = Math.Clamp(pageIndex, 0, _categories.Count - 1);
|
||||
|
||||
var source = GetCurrentSourceItems();
|
||||
var cat = _categories[_currentPage];
|
||||
List<HelpItemModel> filtered;
|
||||
|
||||
if (cat == CatAll)
|
||||
filtered = source;
|
||||
else if (cat == CatPopular)
|
||||
{
|
||||
var popularCmds = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{ "파일/폴더", "?", "#", "!", "clip", "pipe", "diff", "win" };
|
||||
filtered = source.Where(i =>
|
||||
popularCmds.Contains(i.Command) ||
|
||||
i.Command.StartsWith("?") ||
|
||||
i.Title.Contains("검색") ||
|
||||
i.Title.Contains("클립보드") ||
|
||||
(i.Title.Contains("파일") && i.Category != "키보드")
|
||||
).ToList();
|
||||
}
|
||||
else
|
||||
filtered = source.Where(i => i.Category == cat).ToList();
|
||||
|
||||
ItemsHost.ItemsSource = filtered;
|
||||
|
||||
// 탭 하이라이트
|
||||
for (int i = 0; i < CategoryBar.Children.Count; i++)
|
||||
{
|
||||
if (CategoryBar.Children[i] is Button btn)
|
||||
{
|
||||
if (i == _currentPage)
|
||||
{
|
||||
btn.Background = ThemeAccent;
|
||||
btn.Foreground = Brushes.White;
|
||||
}
|
||||
else
|
||||
{
|
||||
btn.Background = Brushes.Transparent;
|
||||
btn.Foreground = ThemeSecondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PageIndicator.Text = $"{cat} ({filtered.Count}개)";
|
||||
PrevBtn.Visibility = _currentPage > 0 ? Visibility.Visible : Visibility.Hidden;
|
||||
NextBtn.Visibility = _currentPage < _categories.Count - 1 ? Visibility.Visible : Visibility.Hidden;
|
||||
}
|
||||
|
||||
// ─── 이벤트 ────────────────────────────────────────────────────────────
|
||||
|
||||
private void OnKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.Escape:
|
||||
Close();
|
||||
break;
|
||||
case Key.Left:
|
||||
if (_currentPage > 0) NavigateToPage(_currentPage - 1);
|
||||
e.Handled = true;
|
||||
break;
|
||||
case Key.Right:
|
||||
if (_currentPage < _categories.Count - 1) NavigateToPage(_currentPage + 1);
|
||||
e.Handled = true;
|
||||
break;
|
||||
// 1, 2, 3 숫자키로 상단 메뉴 전환
|
||||
case Key.D1: SwitchTopMenu(TopMenu.Overview); e.Handled = true; break;
|
||||
case Key.D2: SwitchTopMenu(TopMenu.Shortcuts); e.Handled = true; break;
|
||||
case Key.D3: SwitchTopMenu(TopMenu.Prefixes); e.Handled = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
private void Prev_Click(object sender, RoutedEventArgs e) => NavigateToPage(_currentPage - 1);
|
||||
private void Next_Click(object sender, RoutedEventArgs e) => NavigateToPage(_currentPage + 1);
|
||||
private void Close_Click(object sender, RoutedEventArgs e) => Close();
|
||||
|
||||
private void SearchBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
|
||||
{
|
||||
var query = SearchBox.Text.Trim();
|
||||
if (string.IsNullOrEmpty(query))
|
||||
{
|
||||
NavigateToPage(_currentPage);
|
||||
return;
|
||||
}
|
||||
|
||||
var source = GetCurrentSourceItems();
|
||||
var filtered = source.Where(i =>
|
||||
i.Title.Contains(query, StringComparison.OrdinalIgnoreCase) ||
|
||||
i.Command.Contains(query, StringComparison.OrdinalIgnoreCase) ||
|
||||
i.Description.Contains(query, StringComparison.OrdinalIgnoreCase) ||
|
||||
i.Category.Contains(query, StringComparison.OrdinalIgnoreCase)
|
||||
).ToList();
|
||||
|
||||
ItemsHost.ItemsSource = filtered;
|
||||
PageIndicator.Text = $"검색: \"{query}\" ({filtered.Count}개)";
|
||||
}
|
||||
|
||||
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (e.ChangedButton == MouseButton.Left && e.LeftButton == MouseButtonState.Pressed)
|
||||
try { DragMove(); } catch { }
|
||||
}
|
||||
|
||||
private void Window_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Escape) Close();
|
||||
}
|
||||
}
|
||||
|
||||
public class HelpItemModel
|
||||
{
|
||||
public string Category { get; init; } = "";
|
||||
public string Command { get; init; } = "";
|
||||
public string Title { get; init; } = "";
|
||||
public string Description { get; init; } = "";
|
||||
public string Example { get; init; } = "";
|
||||
public string Symbol { get; init; } = "";
|
||||
public SolidColorBrush ColorBrush { get; init; } = new(Colors.Gray);
|
||||
public bool HasExample => !string.IsNullOrEmpty(Example);
|
||||
}
|
||||
Reference in New Issue
Block a user