diff --git a/frontend/src/utils/ChartManager.ts b/frontend/src/utils/ChartManager.ts index 47486d5..f070252 100644 --- a/frontend/src/utils/ChartManager.ts +++ b/frontend/src/utils/ChartManager.ts @@ -348,6 +348,7 @@ export class ChartManager { for (const entry of this.indicators.values()) { if (entry.cloudPlugin) { + try { this.mainSeries?.detachPrimitive(entry.cloudPlugin); } catch { /* ok */ } for (const s of entry.seriesList) { try { s.detachPrimitive(entry.cloudPlugin); } catch { /* ok */ } } } detachBbBandFill(entry); @@ -377,6 +378,7 @@ export class ChartManager { // 기존 보조지표 시리즈를 모두 제거 (새 데이터로 재계산하기 위해) for (const entry of this.indicators.values()) { if (entry.cloudPlugin) { + try { this.mainSeries?.detachPrimitive(entry.cloudPlugin); } catch { /* ok */ } for (const s of entry.seriesList) { try { s.detachPrimitive(entry.cloudPlugin); } catch { /* ok */ } } } detachBbBandFill(entry); @@ -867,6 +869,7 @@ export class ChartManager { } } if (entry.cloudPlugin) { + try { this.mainSeries?.detachPrimitive(entry.cloudPlugin); } catch { /* ok */ } for (const s of entry.seriesList) { try { s.detachPrimitive(entry.cloudPlugin); } catch { /* ok */ } } @@ -891,6 +894,7 @@ export class ChartManager { // ① 기존 보조지표 시리즈 모두 제거 (메인·볼륨 제외) for (const entry of this.indicators.values()) { if (entry.cloudPlugin) { + try { this.mainSeries?.detachPrimitive(entry.cloudPlugin); } catch { /* ok */ } for (const s of entry.seriesList) { try { s.detachPrimitive(entry.cloudPlugin); } catch { /* ok */ } } } if (entry.fillPrimitive && entry.seriesList[0]) { @@ -1306,12 +1310,15 @@ export class ChartManager { } // 구름 플러그인 (과거 + 미래 포함) + // mainSeries 에 attach: mainSeries 는 항상 visible:true 이고 메인 priceScale 을 사용하므로 + // primitive 가 확실하게 렌더링된다. SpanA(plot3) 시리즈의 visibility/priceScale 변화에 + // 영향을 받지 않도록 mainSeries 우선, 없으면 SpanA→첫 번째 시리즈 순으로 fallback. const cloudPlugin = new IchimokuCloudPlugin(); const colors = resolveIchimokuCloudColors(_config.cloudColors); cloudPlugin.setColors(colors.bullishColor, colors.bearishColor); - const host = spanASeriesRef ?? seriesList[0]; - if (host) { - host.attachPrimitive(cloudPlugin); + const cloudHost = this.mainSeries ?? spanASeriesRef ?? seriesList[0] ?? null; + if (cloudHost) { + cloudHost.attachPrimitive(cloudPlugin); this._applyIchimokuCloudPlugin(cloudPlugin, result.plots, _config); } return { cloudPlugin, seriesMeta }; @@ -2437,16 +2444,6 @@ export class ChartManager { const plotById = Object.fromEntries(plotDefs.map((p: PlotDef) => [p.id, p])); - // 일목균형표: 구름 플러그인이 붙은 호스트 시리즈(plot3 우선, 없으면 seriesList[0]) 식별 - // LWC는 visible:false 시리즈에 붙은 primitive를 렌더링하지 않으므로 - // 호스트 시리즈는 항상 visible:true 로 유지하고, 사용자가 라인을 숨길 경우 - // 투명 색상으로 처리한다. - const ichimokuCloudHost: ISeriesApi | null = (() => { - if (entry.type !== 'IchimokuCloud' || !entry.cloudPlugin) return null; - const plot3Idx = entry.seriesMeta.findIndex(m => m.plotId === 'plot3'); - return plot3Idx >= 0 ? entry.seriesList[plot3Idx] : (entry.seriesList[0] ?? null); - })(); - entry.seriesList.forEach((series, i) => { let plot: PlotDef | undefined; const pid = entry.seriesMeta[i]?.plotId; @@ -2456,19 +2453,16 @@ export class ChartManager { const visible = indicatorVisible && config.plotVisibility?.[plot.id] !== false; const paneIdx = entry.paneIndex ?? 0; const indicatorPriceFormat = priceFormatForIndicatorPane(paneIdx); - const isCloudHost = series === ichimokuCloudHost; // eslint-disable-next-line @typescript-eslint/no-explicit-any const opts: Record = { - // 구름 호스트 시리즈: 지표 전체가 hidden 일 때만 실제로 visible:false - visible: isCloudHost ? indicatorVisible : visible, + visible, }; if (indicatorPriceFormat) { opts['priceFormat'] = indicatorPriceFormat; } if (!entry.seriesMeta[i]?.isHistogram) { - // 구름 호스트 시리즈가 사용자에 의해 숨겨진 경우 → 라인을 투명 처리 - opts['color'] = (isCloudHost && !visible) ? 'rgba(0,0,0,0)' : (plot.color ?? '#aaa'); + opts['color'] = plot.color ?? '#aaa'; if (plot.lineWidth != null) opts['lineWidth'] = plot.lineWidth; opts['lineStyle'] = plotSeriesLineStyle(plot.lineStyle); let plotAllows = true;