/** * GoldenChart 화면·기능별 재현 경로 카탈로그 (검증 이슈 재현경로 자동완성) * * path: 사용자에게 표시·저장되는 경로 (메뉴-화면-… 형식) * keywords: 입력 검색용 별칭 (메뉴명, 기능명, 영문 등) */ export interface AppNavigationPathEntry { path: string; keywords: string[]; } const P = (path: string, ...keywords: string[]): AppNavigationPathEntry => ({ path, keywords: [path, ...keywords], }); /** 프로젝트에서 제공하는 주요 화면·기능 경로 */ export const APP_NAVIGATION_PATHS: AppNavigationPathEntry[] = [ // ── 상단 메뉴 ── P('메뉴-대시보드', 'dashboard', '홈'), P('메뉴-실시간 차트', 'chart', '실시간차트', '캔들', '차트'), P('메뉴-투자관리', 'paper', '투자관리', '투자금', '페이퍼'), P('메뉴-투자관리-투자금 관리', 'paper', 'allocation', '한도', 'KPI'), P('메뉴-가상매매', 'virtual', '가상', '가상매매'), P('메뉴-추세검색', 'trend', '추세', '검색'), P('메뉴-검증게시판', 'verification', 'QA', '이슈', '검증'), P('메뉴-전략편집기', 'strategy-editor', '전략', '편집'), P('메뉴-백테스팅', 'backtest', '백테스트', '히스토리'), P('메뉴-분석레포트', 'analysis-report', '레포트', '분석', '투자분석'), P('메뉴-설정', 'settings', '환경설정'), // ── 실시간 차트 · 툴바 ── P('메뉴-실시간 차트-종목 변경', '심볼', '검색', '마켓'), P('메뉴-실시간 차트-워치리스트', 'watchlist', '관심'), P('메뉴-실시간 차트-시간봉 변경', 'timeframe', '분봉', '일봉'), P('메뉴-실시간 차트-차트 유형', '캔들', '봉', 'line', 'heikin'), P('메뉴-실시간 차트-지표 추가 버튼', '지표', 'indicator', '추가', 'Ctrl+I', '보조지표'), P('메뉴-실시간 차트-지표 추가 팝업-지표 검색', '지표검색', 'RSI', 'MACD', '이동평균'), P('메뉴-실시간 차트-지표 추가 팝업-보조지표 설정', '지표설정', '활성지표'), P('메뉴-실시간 차트-보조지표 설정 버튼', '일괄설정', '보조지표설정'), P('메뉴-실시간 차트-실행 취소', 'undo', 'Ctrl+Z'), P('메뉴-실시간 차트-다시 실행', 'redo', 'Ctrl+Y'), P('메뉴-실시간 차트-자석모드', 'magnet', '스냅'), P('메뉴-실시간 차트-전체 드로잉 삭제', '드로잉', 'drawing'), P('메뉴-실시간 차트-통계 패널', 'stats', '통계'), P('메뉴-실시간 차트-자동 피보나치', 'fib', '피보'), P('메뉴-실시간 차트-그리드 표시', 'grid', '격자'), P('메뉴-실시간 차트-트레이딩/차트 모드 전환', 'mode', '트레이딩'), P('메뉴-실시간 차트-백테스팅 전략 선택', '백테', '전략선택'), P('메뉴-실시간 차트-백테스팅 설정', 'bt설정'), P('메뉴-실시간 차트-백테스팅 결과 보기', 'bt결과'), P('메뉴-실시간 차트-실시간 전략 체크', 'live', '라이브전략'), P('메뉴-실시간 차트-마켓 패널', '마켓', '좌측패널'), P('메뉴-실시간 차트-확대경', 'magnifier', '돋보기'), P('메뉴-실시간 차트-레이아웃 설정', 'layout', '분할'), P('메뉴-실시간 차트-화면 맞춤', 'fit', 'F키'), P('메뉴-실시간 차트-현재 시각으로 이동', 'realtime', 'now'), P('메뉴-실시간 차트-매매 시그널 알림', 'trade', '시그널', '알림'), P('메뉴-실시간 차트-가격 알림', 'alert', '가격'), P('메뉴-실시간 차트-스크린샷', 'screenshot', '캡처'), P('메뉴-실시간 차트-전체화면', 'fullscreen'), // ── 설정 카테고리 ── P('메뉴-설정-일반 설정', 'general', '언어', '테마'), P('메뉴-설정-차트 설정', 'chart설정', '캔들색', '범례'), P('메뉴-설정-보조지표 설정', 'indicators', '지표기본값', 'Main탭'), P('메뉴-설정-백테스팅', 'bt옵션', '자본'), P('메뉴-설정-전략 설정', 'strategy', '전략기본'), P('메뉴-설정-모의투자', 'paper', '모의투자', '모의자본', '수수료'), P('메뉴-설정-가상매매', 'virtual', '투자대상', '한도'), P('메뉴-설정-실거래', 'live', '업비트', 'API'), P('메뉴-설정-추세검색', 'trend설정', '배점'), P('메뉴-설정-알림 설정', 'alert설정', '소리', '팝업'), P('메뉴-설정-네트워크', 'network', 'API', 'WebSocket'), P('메뉴-설정-관리자 설정', 'admin', '권한', '역할'), // ── 가상매매 ── P('메뉴-가상매매-투자대상 그리드', '타겟', '종목'), P('메뉴-가상매매-우측 매매 탭', '주문', 'trade'), P('메뉴-가상매매-우측 호가 탭', '호가', 'orderbook'), P('메뉴-가상매매-우측 체결 탭', '체결', 'history'), // ── 추세검색 ── P('메뉴-추세검색-검색 실행', 'run', '스캔'), P('메뉴-추세검색-결과 카드', '결과', '차트미리보기'), // ── 검증게시판 ── P('메뉴-검증게시판-이슈 등록', '등록', '추가'), P('메뉴-검증게시판-이슈 수정', '수정', '편집'), P('메뉴-검증게시판-이슈 삭제', '삭제'), P('메뉴-검증게시판-첨부 이미지', '이미지', '스크린샷', '첨부'), P('메뉴-검증게시판-댓글', 'comment', '코멘트'), // ── 전략편집기 ── P('메뉴-전략편집기-전략 목록', '목록', '리스트'), P('메뉴-전략편집기-노드 캔버스', 'flow', '노드', '조건'), P('메뉴-전략편집기-지표 팔레트', 'palette', '팔레트'), P('메뉴-전략편집기-전략 저장', 'save'), P('메뉴-전략편집기-전략 JSON보내기', 'export', '보내기'), P('메뉴-전략편집기-전략 JSON 가져오기', 'import', '가져오기'), // ── 백테스팅 ── P('메뉴-백테스팅-결과 목록', '히스토리', 'history'), P('메뉴-백테스팅-분석 차트', 'analysis'), // ── 상단 메뉴바 기타 ── P('메뉴-알림목록', 'notifications', '알림목록'), P('메뉴-테마 전환', 'theme', '다크', '라이트'), P('메뉴-로그인', 'login', 'auth'), ]; function normalizeForSearch(s: string): string { return s.toLowerCase().replace(/\s+/g, ''); } /** * 입력 문자열과 관련된 경로 후보 (최대 limit건, 관련도 순) */ export function searchAppNavigationPaths(query: string, limit = 12): AppNavigationPathEntry[] { const q = query.trim(); if (!q) return APP_NAVIGATION_PATHS.slice(0, limit); const nq = normalizeForSearch(q); const tokens = q.toLowerCase().split(/\s+/).filter(Boolean); type Scored = { entry: AppNavigationPathEntry; score: number }; const scored: Scored[] = []; for (const entry of APP_NAVIGATION_PATHS) { let score = 0; const pathNorm = normalizeForSearch(entry.path); if (entry.path.includes(q)) score += 80; if (pathNorm.includes(nq)) score += 60; if (pathNorm.startsWith(nq)) score += 40; for (const kw of entry.keywords) { const kn = normalizeForSearch(kw); if (kw.includes(q)) score += 50; if (kn.includes(nq)) score += 35; if (kn.startsWith(nq)) score += 25; for (const t of tokens) { if (kn.includes(t) || kw.toLowerCase().includes(t)) score += 15; } } if (score > 0) scored.push({ entry, score }); } scored.sort((a, b) => b.score - a.score || a.entry.path.localeCompare(b.entry.path, 'ko')); return scored.slice(0, limit).map(s => s.entry); }