diff --git a/frontend/src/App.css b/frontend/src/App.css index 237fd92..147ff4f 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -6356,116 +6356,405 @@ .bt-drop-run.running { background: var(--bg4); color: var(--text2); } /* ══════════════════════════════════════════════════════════════════ - 실시간 전략 체크 패널 (LiveStrategyPanel) + 실시간 전략 체크 패널 (LiveStrategyPanel) — TradeAlertModal 스타일 ══════════════════════════════════════════════════════════════════ */ -.lsp-wrap { +.lsp-overlay { + position: fixed; + inset: 0; + z-index: 9998; + pointer-events: none; +} +.lsp-overlay > .lsp-modal { + pointer-events: auto; +} + +.lsp-modal { + position: fixed; + width: 400px; + max-width: calc(100vw - 24px); + max-height: calc(100vh - 24px); display: flex; flex-direction: column; - gap: 8px; - min-width: 240px; - padding: 12px 14px; - background: var(--bg2); - border: 1px solid var(--border); - border-radius: 8px; - font-size: 12px; - color: var(--text); + background: var(--bg2, #24283b); + border: 1px solid var(--lsp-accent, var(--border, rgba(122,162,247,0.15))); + border-radius: 12px; + box-shadow: + 0 0 0 1px var(--lsp-accent-glow, rgba(122,162,247,0.08)), + 0 20px 60px rgba(0,0,0,0.65), + 0 0 40px var(--lsp-accent-glow, rgba(122,162,247,0.06)); + overflow: hidden; + font-size: 13px; + color: var(--text, #c0caf5); + font-family: var(--font, 'Noto Sans KR', sans-serif); } .lsp-header { - display: flex; - align-items: center; - gap: 6px; - padding-bottom: 8px; - border-bottom: 1px solid var(--border); + flex-shrink: 0; } -.lsp-icon { opacity: 0.75; flex-shrink: 0; color: #00BCD4; } -.lsp-title { font-weight: 700; font-size: 12.5px; flex: 1; } -.lsp-saving { font-size: 10px; color: var(--text3); } -.lsp-close-btn { +.lsp-header-left { display: flex; align-items: center; - justify-content: center; - width: 22px; - height: 22px; + gap: 9px; + flex: 1; + min-width: 0; +} +.lsp-badge { + display: inline-block; + padding: 3px 10px; + border-radius: 20px; + font-size: 11px; + font-weight: 800; + color: #fff; + letter-spacing: 1.2px; + flex-shrink: 0; +} +.lsp-header-title { + font-size: 14px; + font-weight: 700; + color: var(--text, #c0caf5); + letter-spacing: 0.4px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.lsp-header-ko { + font-size: 12px; + font-weight: 400; + color: var(--text2, #9aa5ce); +} +.lsp-saving { + font-size: 10px; + color: var(--text3, #6272a4); + flex-shrink: 0; +} +.lsp-close { background: none; border: none; - border-radius: 4px; - color: var(--text3); + color: var(--text3, #6272a4); + font-size: 16px; cursor: pointer; - padding: 0; + padding: 2px 6px; + line-height: 1; + border-radius: 4px; + transition: color 0.15s, background 0.15s; flex-shrink: 0; - transition: background 0.15s, color 0.15s; } -.lsp-close-btn:hover { background: var(--bg3); color: var(--text); } +.lsp-close:hover { + color: var(--text, #c0caf5); + background: var(--bg4, rgba(255,255,255,0.07)); +} -.lsp-row { +.lsp-summary-section { + padding: 12px 16px 10px; + background: linear-gradient( + 135deg, + var(--lsp-accent-glow, rgba(122,162,247,0.08)) 0%, + transparent 60% + ); + border-bottom: 1px solid var(--border, rgba(122,162,247,0.1)); + flex-shrink: 0; +} +.lsp-summary-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; + margin-bottom: 4px; +} +.lsp-summary-label { + font-size: 11.5px; + color: var(--text2, #9aa5ce); +} +.lsp-summary-value { + font-size: 15px; + font-weight: 800; + color: var(--text, #c0caf5); + letter-spacing: -0.3px; +} +.lsp-summary-value--active { + color: var(--lsp-accent, #3f7ef5); +} +.lsp-summary-hint { + margin: 6px 0 0; + font-size: 10.5px; + color: var(--text3, #6272a4); + line-height: 1.45; } -.lsp-row--disabled { opacity: 0.45; pointer-events: none; } -.lsp-label { color: var(--text2); white-space: nowrap; } - -/* 토글 (stg-toggle 재활용 패턴) */ -.lsp-toggle { position: relative; display: inline-flex; align-items: center; cursor: pointer; } -.lsp-toggle input { opacity: 0; width: 0; height: 0; position: absolute; } -.lsp-toggle-slider { - width: 34px; height: 18px; - background: var(--bg3); +.lsp-info-card { + display: flex; + align-items: flex-start; + gap: 12px; + margin: 12px 14px 0; + padding: 11px 14px; + background: var(--bg3, #1f2335); + border: 1px solid var(--border, rgba(122,162,247,0.12)); border-radius: 9px; - transition: background 0.2s; + flex-shrink: 0; +} +.lsp-info-icon { + flex-shrink: 0; + display: flex; + align-items: center; +} +.lsp-info-body { + display: flex; + flex-direction: column; + gap: 6px; + flex: 1; + min-width: 0; +} +.lsp-info-row { + display: flex; + flex-direction: column; + gap: 4px; +} +.lsp-info-label { + font-size: 11px; + color: var(--text3, #6272a4); +} +.lsp-info-val { + font-size: 12.5px; + font-weight: 600; + color: var(--text, #c0caf5); +} + +.lsp-body { + display: flex; + flex-direction: column; + gap: 10px; + padding: 12px 14px 14px; + overflow-y: auto; + flex: 1; + min-height: 0; +} + +.lsp-field-row { + display: flex; + align-items: center; + gap: 8px; +} +.lsp-field-row--disabled, +.lsp-section--disabled { + opacity: 0.45; + pointer-events: none; +} +.lsp-field-label, +.lsp-section-label { + font-size: 11.5px; + color: var(--text2, #9aa5ce); + flex-shrink: 0; + min-width: 62px; +} +.lsp-section-label { + display: block; + margin-bottom: 6px; +} + +.lsp-toggle { + position: relative; + display: inline-flex; + align-items: center; + cursor: pointer; + margin-left: auto; +} +.lsp-toggle input { + opacity: 0; + width: 0; + height: 0; + position: absolute; +} +.lsp-toggle-slider { + width: 38px; + height: 20px; + background: var(--bg3, #1f2335); + border: 1px solid var(--border, rgba(122,162,247,0.18)); + border-radius: 10px; + transition: background 0.2s, border-color 0.2s; position: relative; } .lsp-toggle-slider::after { - content: ''; position: absolute; - width: 14px; height: 14px; + content: ''; + position: absolute; + width: 14px; + height: 14px; border-radius: 50%; background: #fff; - top: 2px; left: 2px; + top: 2px; + left: 2px; transition: transform 0.2s; } -.lsp-toggle input:checked + .lsp-toggle-slider { background: #00BCD4; } -.lsp-toggle input:checked + .lsp-toggle-slider::after { transform: translateX(16px); } +.lsp-toggle input:checked + .lsp-toggle-slider { + background: var(--lsp-accent, #3f7ef5); + border-color: var(--lsp-accent, #3f7ef5); +} +.lsp-toggle input:checked + .lsp-toggle-slider::after { + transform: translateX(18px); +} .lsp-select { flex: 1; - padding: 3px 6px; - background: var(--bg3); - border: 1px solid var(--border); - border-radius: 4px; - color: var(--text); - font-size: 11.5px; - cursor: pointer; min-width: 0; + padding: 7px 10px; + background: var(--bg3, #1f2335); + border: 1px solid var(--border, rgba(122,162,247,0.18)); + border-radius: 6px; + color: var(--text, #c0caf5); + font-size: 12.5px; + font-weight: 600; + cursor: pointer; + outline: none; + transition: border-color 0.15s; +} +.lsp-select:focus { + border-color: var(--lsp-accent, #3f7ef5); +} +.lsp-select:disabled { + cursor: not-allowed; } -.lsp-exec-wrap { +.lsp-section { display: flex; flex-direction: column; - gap: 4px; } -.lsp-radio-group { + +.lsp-option-group { display: flex; flex-direction: column; - gap: 4px; - padding-left: 4px; + gap: 6px; } -.lsp-radio { +.lsp-option { display: flex; align-items: flex-start; - gap: 6px; + gap: 8px; + padding: 9px 11px; + border: 1px solid var(--border, rgba(122,162,247,0.12)); + border-radius: 8px; + background: var(--bg3, #1f2335); cursor: pointer; - padding: 4px 6px; - border-radius: 5px; - transition: background 0.15s; + transition: border-color 0.15s, background 0.15s; } -.lsp-radio:hover { background: var(--bg3); } -.lsp-radio input { margin-top: 2px; accent-color: #00BCD4; flex-shrink: 0; } -.lsp-radio-label { display: flex; flex-direction: column; gap: 1px; } -.lsp-radio-title { font-size: 11.5px; font-weight: 600; } -.lsp-radio-desc { font-size: 10px; color: var(--text3); } +.lsp-option:hover { + border-color: var(--lsp-accent, rgba(63,126,245,0.35)); + background: var(--bg4, #414868); +} +.lsp-option--active { + border-color: var(--lsp-accent, #3f7ef5); + background: rgba(63, 126, 245, 0.1); + box-shadow: inset 0 0 0 1px rgba(63, 126, 245, 0.15); +} +.lsp-option input { + margin-top: 3px; + accent-color: var(--lsp-accent, #3f7ef5); + flex-shrink: 0; +} +.lsp-option-text { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} +.lsp-option-title { + font-size: 12px; + font-weight: 700; + color: var(--text, #c0caf5); +} +.lsp-option-desc { + font-size: 10.5px; + color: var(--text3, #6272a4); + line-height: 1.4; +} + +.lsp-hint { + font-size: 10.5px; + color: var(--text3, #6272a4); + margin: 0; + line-height: 1.45; +} + +.lsp-monitor-chips { + display: flex; + flex-wrap: wrap; + gap: 4px; + font-weight: 400; +} +.lsp-monitor-chip { + font-size: 10px; + padding: 2px 7px; + border-radius: 4px; + background: rgba(63, 126, 245, 0.12); + color: var(--lsp-accent, #3f7ef5); + border: 1px solid rgba(63, 126, 245, 0.25); + font-weight: 600; +} + +.lsp-status-bar { + display: flex; + align-items: center; + gap: 8px; + padding: 9px 11px; + background: var(--bg3, #1f2335); + border: 1px solid var(--border, rgba(122,162,247,0.12)); + border-radius: 8px; +} +.lsp-status-text { + font-size: 11px; + color: var(--text2, #9aa5ce); + line-height: 1.4; + min-width: 0; +} +.lsp-dot { + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--text3, #6272a4); + flex-shrink: 0; +} +.lsp-dot--on { + background: var(--lsp-accent, #3f7ef5); + box-shadow: 0 0 6px var(--lsp-accent-glow, rgba(63,126,245,0.5)); + animation: lsp-pulse 1.5s ease-in-out infinite; +} +@keyframes lsp-pulse { + 0%,100% { opacity: 1; } + 50% { opacity: 0.4; } +} + +.lsp-disclaimer { + font-size: 10.5px; + color: var(--text3, #6272a4); + text-align: center; + margin: 2px 0 0; + line-height: 1.5; +} + +/* 라이트 테마 */ +.app.light .lsp-modal { + background: #ffffff; + border-color: rgba(25,118,210,0.35); + color: #212121; + box-shadow: 0 20px 60px rgba(0,0,0,0.18), 0 0 40px rgba(25,118,210,0.08); +} +.app.light .lsp-header-title { color: #1a237e; } +.app.light .lsp-header-ko { color: #546e7a; } +.app.light .lsp-close { color: #78909c; } +.app.light .lsp-close:hover { background: rgba(0,0,0,0.06); color: #37474f; } +.app.light .lsp-summary-section { border-color: rgba(0,0,0,0.08); } +.app.light .lsp-summary-label { color: #546e7a; } +.app.light .lsp-summary-value { color: #212121; } +.app.light .lsp-info-card { background: #f5f7fa; border-color: rgba(0,0,0,0.1); } +.app.light .lsp-info-label { color: #78909c; } +.app.light .lsp-info-val { color: #212121; } +.app.light .lsp-field-label, +.app.light .lsp-section-label { color: #546e7a; } +.app.light .lsp-select { background: #f0f3f8; border-color: rgba(0,0,0,0.12); color: #212121; } +.app.light .lsp-option { background: #f5f7fa; border-color: rgba(0,0,0,0.1); } +.app.light .lsp-option:hover { background: #eef2f7; } +.app.light .lsp-option--active { background: rgba(25,118,210,0.08); border-color: rgba(25,118,210,0.45); } +.app.light .lsp-option-title { color: #212121; } +.app.light .lsp-status-bar { background: #f5f7fa; border-color: rgba(0,0,0,0.1); } +.app.light .lsp-status-text { color: #546e7a; } +.app.light .lsp-monitor-chip { background: rgba(25,118,210,0.1); color: #1976d2; border-color: rgba(25,118,210,0.25); } .lsp-monitor-stats { display: flex; @@ -6476,7 +6765,7 @@ margin-bottom: 6px; } .lsp-monitor-sep { opacity: 0.5; } -.lsp-monitor-on { color: #00BCD4; font-weight: 600; } +.lsp-monitor-on { color: #3f7ef5; font-weight: 600; } .lsp-monitor-warn { font-size: 10.5px; color: #ff9800; @@ -6509,14 +6798,6 @@ gap: 4px; margin-bottom: 8px; } -.lsp-monitor-chip { - font-size: 9.5px; - padding: 2px 6px; - border-radius: 3px; - background: rgba(0, 188, 212, 0.12); - color: #00BCD4; - border: 1px solid rgba(0, 188, 212, 0.25); -} .stg-btn-secondary { background: var(--bg3); color: var(--text); @@ -6545,32 +6826,6 @@ padding: 6px 12px; } -.lsp-status { - display: flex; - align-items: center; - gap: 6px; - font-size: 10.5px; - color: var(--text3); - padding: 4px 6px; - background: var(--bg3); - border-radius: 4px; -} -.lsp-dot { - width: 6px; height: 6px; - border-radius: 50%; - background: var(--text3); - flex-shrink: 0; -} -.lsp-dot--on { - background: #00BCD4; - box-shadow: 0 0 5px rgba(0,188,212,0.6); - animation: lsp-pulse 1.5s ease-in-out infinite; -} -@keyframes lsp-pulse { - 0%,100% { opacity: 1; } - 50% { opacity: 0.4; } -} - /* ══════════════════════════════════════════════ 백테스팅 결과 통계 배지 (차트 위 오버레이) ══════════════════════════════════════════════ */ diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 8e15c12..343b33e 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -93,6 +93,7 @@ import type { LoginResponse } from './utils/backendApi'; import { invalidateAppSettingsCache } from './hooks/useAppSettings'; import { invalidateIndicatorSettingsCache } from './hooks/useIndicatorSettings'; import './App.css'; +import './styles/appPopup.css'; let _indCounter = 0; function newIndId() { return `ind_${++_indCounter}_${Date.now()}`; } @@ -1866,19 +1867,17 @@ function App() { {/* 실시간 전략 체크 패널 */} {showLivePanel && menuPage === 'chart' && layoutDef.count === 1 && ( -
- ★ 관심종목으로 등록한 종목이 자동으로 전략 체크 대상입니다. -
- )} - {monCount > 0 && ( -전략을 선택하면 관심 {watchlistCount}개 종목에 적용됩니다.
- )} - -