From cab2ed13a39d54f63d95420ce4ff049fe49b640c Mon Sep 17 00:00:00 2001 From: claude-owner Date: Fri, 29 May 2026 00:39:45 +0900 Subject: [PATCH] feat(chart): Shift+Click adds horizontal line at clicked y-coordinate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit H key and toolbar button use hover-or-last-close, so mouse users had no way to drop a line at an arbitrary price they hadn't hovered. Shift+Click reads the click's y via candle.coordinateToPrice — direct positional control. - Plain click stays as native chart interaction (crosshair/drag) - Only intercepts when shiftKey held; preventDefault to avoid text selection - Honors the same 10-line cap as the H key / button - ShortcutsHelp adds "Shift+클릭" entry next to H so it's discoverable --- web/components/ShortcutsHelp.tsx | 1 + web/components/StockChart.tsx | 30 +++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/web/components/ShortcutsHelp.tsx b/web/components/ShortcutsHelp.tsx index 35c1f44..e9da136 100644 --- a/web/components/ShortcutsHelp.tsx +++ b/web/components/ShortcutsHelp.tsx @@ -23,6 +23,7 @@ const SHORTCUTS: Shortcut[] = [ { scope: "전역", keys: ["?"], label: "이 도움말 열기 / 닫기" }, { scope: "종목 페이지", keys: ["F"], label: "차트 전체화면 토글" }, { scope: "종목 페이지", keys: ["H"], label: "현재 가격에 수평선 추가" }, + { scope: "종목 페이지", keys: ["Shift", "클릭"], label: "차트 위 클릭 위치에 수평선 추가" }, { scope: "종목 페이지", keys: ["←", "→"], label: "기간 탭 이전 / 다음" }, { scope: "종목 페이지", keys: ["S"], label: "관심종목 추가 / 제거" }, { scope: "검색 팝오버", keys: ["↑", "↓"], label: "결과 이동" }, diff --git a/web/components/StockChart.tsx b/web/components/StockChart.tsx index 61bda21..63acd83 100644 --- a/web/components/StockChart.tsx +++ b/web/components/StockChart.tsx @@ -712,6 +712,34 @@ export function StockChart({ chart, prediction, targets }: Props) { currentPriceRef.current = p; }, [hover, lastSummary]); + // Shift+Click 으로 차트 위 임의 가격에 수평선 추가. + // H 키 / 버튼은 hover 가격(또는 마지막 봉)에 묶여 있어 마우스 사용자가 "지금 hover 한 곳이 + // 아닌, 다른 위치" 에 그릴 방법이 없었음. Shift+Click 은 클릭 y 좌표를 candle.coordinateToPrice + // 로 변환해 직접 지정하게 함 — TradingView 의 보조 도구 모디파이어 클릭 컨벤션과 호환. + // 단순 Click 은 기존 차트 인터랙션(crosshair/drag) 그대로 — 모디파이어가 있을 때만 가로챔. + useEffect(() => { + const el = containerRef.current; + if (!el) return; + const onClick = (e: MouseEvent) => { + if (!e.shiftKey) return; + // 최대치 도달 시 silently 무시 — 사용자가 chips 의 (N/10) 카운터로 확인 가능. + if (hlines.length >= 10) return; + const candle = candleRef.current; + if (!candle) return; + const rect = el.getBoundingClientRect(); + const y = e.clientY - rect.top; + // 차트 영역 밖(헤더 위 등) → 음수/큰값 → coordinateToPrice 가 null 또는 비정상값. + const raw = candle.coordinateToPrice(y); + if (raw == null) return; + const price = Number(raw); + if (!Number.isFinite(price) || price <= 0) return; + e.preventDefault(); + setHlines(addLine(chart.code, price)); + }; + el.addEventListener("click", onClick); + return () => el.removeEventListener("click", onClick); + }, [chart.code, hlines.length]); + useEffect(() => { const c = chartRef.current; if (!c) return; @@ -950,7 +978,7 @@ export function StockChart({ chart, prediction, targets }: Props) { }} disabled={hlines.length >= 10 || (hover?.close ?? lastSummary?.close ?? null) == null} className="ml-auto flex items-center gap-1 rounded-full border border-zinc-800 px-2 py-0.5 text-[10px] text-zinc-400 transition hover:border-zinc-600 hover:text-zinc-200 disabled:cursor-not-allowed disabled:opacity-40" - title="현재 가격(hover 또는 마지막 봉 종가)에 수평선 추가 (H)" + title="현재 가격(hover 또는 마지막 봉 종가)에 수평선 추가 (H · Shift+클릭으로 임의 위치)" > ─ 수평선 ({hlines.length}/10)