@coderyo/renderer-lite 1.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,135 @@
1
+ import { Bar } from '@coderyo/data';
2
+ import { IndicatorConfig } from '@coderyo/indicators';
3
+ import { IChartApi, LineData, UTCTimestamp } from 'lightweight-charts';
4
+
5
+ interface TransformState {
6
+ visibleFromMs: number;
7
+ visibleToMs: number;
8
+ width: number;
9
+ height: number;
10
+ }
11
+ type TransformListener = (state: TransformState) => void;
12
+ /** Sync visible logical range across multiple LWC panes (§10.4.1). */
13
+ declare class TimeScaleBus {
14
+ private charts;
15
+ private listeners;
16
+ private syncing;
17
+ visibleFromMs: number;
18
+ visibleToMs: number;
19
+ register(chart: IChartApi): void;
20
+ subscribeTransform(listener: TransformListener): () => void;
21
+ setBarsTimeRange(fromMs: number, toMs: number): void;
22
+ private syncFrom;
23
+ private emit;
24
+ }
25
+
26
+ type ScaleMode = 'linear' | 'log';
27
+ interface CrosshairPayload {
28
+ time: number;
29
+ price: number | null;
30
+ ohlcv: {
31
+ o: number;
32
+ h: number;
33
+ l: number;
34
+ c: number;
35
+ v?: number;
36
+ } | null;
37
+ }
38
+ interface PaneOrchestratorOptions {
39
+ container: HTMLElement;
40
+ indicatorRoot?: HTMLElement;
41
+ theme?: 'dark' | 'light';
42
+ scaleMode?: ScaleMode;
43
+ maxRenderPoints?: number;
44
+ /** Show chart grid lines (default false). */
45
+ showGrid?: boolean;
46
+ /** null = no MA overlays and no MACD/RSI/KDJ panes. */
47
+ indicatorConfig?: IndicatorConfig | null;
48
+ }
49
+ declare class PaneOrchestrator {
50
+ readonly bus: TimeScaleBus;
51
+ private readonly mainChart;
52
+ private readonly volumeChart;
53
+ private readonly mainSeries;
54
+ private readonly volumeSeries;
55
+ private readonly maSeries;
56
+ private readonly volMaSeries;
57
+ private readonly indicatorRoot?;
58
+ private indicators;
59
+ private overlayCanvas;
60
+ private dark;
61
+ private showGrid;
62
+ private readonly maxRenderPoints;
63
+ private barByTime;
64
+ private didInitialFit;
65
+ private indicatorConfig;
66
+ constructor(opts: PaneOrchestratorOptions);
67
+ setTheme(theme: 'dark' | 'light'): void;
68
+ setIndicatorConfig(config: IndicatorConfig | null): void;
69
+ setShowGrid(show: boolean): void;
70
+ setBars(bars: Bar[], gaps?: number[]): void;
71
+ subscribeCrosshair(listener: (payload: CrosshairPayload | null) => void): () => void;
72
+ private findNearestBar;
73
+ private createIndicatorStack;
74
+ resetViewState(): void;
75
+ /** Clear series while symbol/interval data reloads (avoids overlapping candles). */
76
+ clearBars(): void;
77
+ fitContent(): void;
78
+ scrollToRealtime(): void;
79
+ setLogScale(enabled: boolean): void;
80
+ resize(): void;
81
+ getOverlayCanvas(): HTMLCanvasElement | null;
82
+ timeToX(tMs: number): number | null;
83
+ priceToY(price: number): number | null;
84
+ xToTime(x: number): number | null;
85
+ yToPrice(y: number): number | null;
86
+ destroy(): void;
87
+ private initOverlay;
88
+ /** Let drawing overlay receive clicks; cursor mode keeps pan/zoom on LWC. */
89
+ setOverlayPointerEvents(mode: 'auto' | 'none'): void;
90
+ private syncOverlaySize;
91
+ private syncChartSize;
92
+ private layoutForTheme;
93
+ }
94
+
95
+ interface IndicatorPaneStackOptions {
96
+ theme?: 'dark' | 'light';
97
+ showGrid?: boolean;
98
+ config?: IndicatorConfig;
99
+ }
100
+ declare class IndicatorPaneStack {
101
+ private readonly root;
102
+ private readonly macdChart;
103
+ private readonly rsiChart;
104
+ private readonly kdjChart;
105
+ private readonly macdLine;
106
+ private readonly macdSignal;
107
+ private readonly macdHist;
108
+ private readonly rsiLine;
109
+ private readonly kdjK;
110
+ private readonly kdjD;
111
+ private readonly kdjJ;
112
+ private dark;
113
+ private showGrid;
114
+ private config;
115
+ private readonly macdWrap;
116
+ private readonly rsiWrap;
117
+ private readonly kdjWrap;
118
+ constructor(root: HTMLElement, bus: TimeScaleBus, opts?: IndicatorPaneStackOptions | 'dark' | 'light');
119
+ setConfig(config: IndicatorConfig): void;
120
+ private applyPaneVisibility;
121
+ clearBars(): void;
122
+ setBars(bars: Bar[]): void;
123
+ setTheme(theme: 'dark' | 'light'): void;
124
+ setShowGrid(show: boolean): void;
125
+ fitContent(): void;
126
+ scrollToRealtime(): void;
127
+ resize(): void;
128
+ destroy(): void;
129
+ private createPaneWrap;
130
+ private layoutForTheme;
131
+ }
132
+ declare function maOverlayLine(bars: Bar[], period?: number, source?: IndicatorConfig['source']): LineData<UTCTimestamp>[];
133
+ declare function volMaOverlayLine(bars: Bar[], period?: number): LineData<UTCTimestamp>[];
134
+
135
+ export { type CrosshairPayload, IndicatorPaneStack, type IndicatorPaneStackOptions, PaneOrchestrator, type PaneOrchestratorOptions, type ScaleMode, TimeScaleBus, type TransformState, maOverlayLine, volMaOverlayLine };
package/dist/index.js ADDED
@@ -0,0 +1,623 @@
1
+ // src/pane-orchestrator.ts
2
+ import {
3
+ CandlestickSeries,
4
+ ColorType as ColorType2,
5
+ createChart as createChart2,
6
+ HistogramSeries as HistogramSeries2,
7
+ LineSeries as LineSeries2
8
+ } from "lightweight-charts";
9
+ import { lodDecimateBars } from "@coderyo/series";
10
+
11
+ // src/chart-grid.ts
12
+ function gridOptions(showGrid, dark) {
13
+ const color = dark ? "#21262d" : "#d0d7de";
14
+ return {
15
+ vertLines: { visible: showGrid, color },
16
+ horzLines: { visible: showGrid, color }
17
+ };
18
+ }
19
+
20
+ // src/indicator-panes.ts
21
+ import {
22
+ ColorType,
23
+ createChart,
24
+ HistogramSeries,
25
+ LineSeries
26
+ } from "lightweight-charts";
27
+ import {
28
+ DEFAULT_INDICATOR_CONFIG,
29
+ kdj,
30
+ macd,
31
+ rsi,
32
+ sma
33
+ } from "@coderyo/indicators";
34
+ function barsForSource(bars, source) {
35
+ if (source === "close") return bars;
36
+ return bars.map((b) => ({ ...b, c: (b.h + b.l + b.c) / 3 }));
37
+ }
38
+ function toUtcSeconds(tMs) {
39
+ return Math.floor(tMs / 1e3);
40
+ }
41
+ function lineData(bars, values) {
42
+ const out = [];
43
+ for (let i = 0; i < bars.length; i++) {
44
+ const v = values[i];
45
+ if (v == null) continue;
46
+ out.push({ time: toUtcSeconds(bars[i].t), value: v });
47
+ }
48
+ return out;
49
+ }
50
+ function histData(bars, values) {
51
+ const out = [];
52
+ for (let i = 0; i < bars.length; i++) {
53
+ const v = values[i];
54
+ if (v == null) continue;
55
+ out.push({
56
+ time: toUtcSeconds(bars[i].t),
57
+ value: v,
58
+ color: v >= 0 ? "#26a69a88" : "#ef535088"
59
+ });
60
+ }
61
+ return out;
62
+ }
63
+ var IndicatorPaneStack = class {
64
+ constructor(root, bus, opts = "dark") {
65
+ this.root = root;
66
+ const o = typeof opts === "string" ? { theme: opts, showGrid: false } : opts;
67
+ this.dark = o.theme !== "light";
68
+ this.showGrid = o.showGrid ?? false;
69
+ this.config = o.config ?? DEFAULT_INDICATOR_CONFIG;
70
+ this.root.style.display = "flex";
71
+ this.root.style.flexDirection = "column";
72
+ this.root.style.flex = "2";
73
+ this.root.style.minHeight = "0";
74
+ this.root.style.overflow = "hidden";
75
+ const macdPane = this.createPaneWrap("MACD");
76
+ const rsiPane = this.createPaneWrap("RSI");
77
+ const kdjPane = this.createPaneWrap("KDJ");
78
+ this.macdWrap = macdPane.wrap;
79
+ this.rsiWrap = rsiPane.wrap;
80
+ this.kdjWrap = kdjPane.wrap;
81
+ this.root.append(macdPane.wrap, rsiPane.wrap, kdjPane.wrap);
82
+ this.applyPaneVisibility();
83
+ const layout = this.layoutForTheme(this.dark);
84
+ const grid = gridOptions(this.showGrid, this.dark);
85
+ this.macdChart = createChart(macdPane.el, { layout, grid, autoSize: true });
86
+ this.rsiChart = createChart(rsiPane.el, { layout, grid, autoSize: true });
87
+ this.kdjChart = createChart(kdjPane.el, { layout, grid, autoSize: true });
88
+ for (const c of [this.macdChart, this.rsiChart, this.kdjChart]) bus.register(c);
89
+ this.macdLine = this.macdChart.addSeries(LineSeries, { color: "#2962ff", lineWidth: 1 });
90
+ this.macdSignal = this.macdChart.addSeries(LineSeries, { color: "#ff9800", lineWidth: 1 });
91
+ this.macdHist = this.macdChart.addSeries(HistogramSeries, {
92
+ priceFormat: { type: "price", precision: 4, minMove: 1e-4 }
93
+ });
94
+ this.rsiLine = this.rsiChart.addSeries(LineSeries, { color: "#ab47bc", lineWidth: 1 });
95
+ this.kdjK = this.kdjChart.addSeries(LineSeries, { color: "#42a5f5", lineWidth: 1 });
96
+ this.kdjD = this.kdjChart.addSeries(LineSeries, { color: "#ffa726", lineWidth: 1 });
97
+ this.kdjJ = this.kdjChart.addSeries(LineSeries, { color: "#ef5350", lineWidth: 1 });
98
+ }
99
+ root;
100
+ macdChart;
101
+ rsiChart;
102
+ kdjChart;
103
+ macdLine;
104
+ macdSignal;
105
+ macdHist;
106
+ rsiLine;
107
+ kdjK;
108
+ kdjD;
109
+ kdjJ;
110
+ dark = true;
111
+ showGrid = false;
112
+ config = DEFAULT_INDICATOR_CONFIG;
113
+ macdWrap;
114
+ rsiWrap;
115
+ kdjWrap;
116
+ setConfig(config) {
117
+ this.config = config;
118
+ this.applyPaneVisibility();
119
+ }
120
+ applyPaneVisibility() {
121
+ this.macdWrap.style.display = this.config.showMacd ? "" : "none";
122
+ this.rsiWrap.style.display = this.config.showRsi ? "" : "none";
123
+ this.kdjWrap.style.display = this.config.showKdj ? "" : "none";
124
+ }
125
+ clearBars() {
126
+ this.macdLine.setData([]);
127
+ this.macdSignal.setData([]);
128
+ this.macdHist.setData([]);
129
+ this.rsiLine.setData([]);
130
+ this.kdjK.setData([]);
131
+ this.kdjD.setData([]);
132
+ this.kdjJ.setData([]);
133
+ }
134
+ setBars(bars) {
135
+ if (bars.length === 0) return;
136
+ const src = barsForSource(bars, this.config.source);
137
+ const m = macd(src, this.config.macdFast, this.config.macdSlow, this.config.macdSignal);
138
+ this.macdLine.setData(lineData(bars, m.macd));
139
+ this.macdSignal.setData(lineData(bars, m.signal));
140
+ this.macdHist.setData(histData(bars, m.histogram));
141
+ this.rsiLine.setData(lineData(bars, rsi(src, this.config.rsiPeriod)));
142
+ const k = kdj(src, this.config.kdjPeriod, this.config.kdjKSmooth, this.config.kdjDSmooth);
143
+ this.kdjK.setData(lineData(bars, k.k));
144
+ this.kdjD.setData(lineData(bars, k.d));
145
+ this.kdjJ.setData(lineData(bars, k.j));
146
+ this.macdChart.timeScale().fitContent();
147
+ this.rsiChart.timeScale().fitContent();
148
+ this.kdjChart.timeScale().fitContent();
149
+ this.resize();
150
+ }
151
+ setTheme(theme) {
152
+ this.dark = theme === "dark";
153
+ const layout = this.layoutForTheme(this.dark);
154
+ const grid = gridOptions(this.showGrid, this.dark);
155
+ this.macdChart.applyOptions({ layout, grid });
156
+ this.rsiChart.applyOptions({ layout, grid });
157
+ this.kdjChart.applyOptions({ layout, grid });
158
+ }
159
+ setShowGrid(show) {
160
+ this.showGrid = show;
161
+ const grid = gridOptions(show, this.dark);
162
+ this.macdChart.applyOptions({ grid });
163
+ this.rsiChart.applyOptions({ grid });
164
+ this.kdjChart.applyOptions({ grid });
165
+ }
166
+ fitContent() {
167
+ this.macdChart.timeScale().fitContent();
168
+ this.rsiChart.timeScale().fitContent();
169
+ this.kdjChart.timeScale().fitContent();
170
+ }
171
+ scrollToRealtime() {
172
+ this.macdChart.timeScale().scrollToRealTime();
173
+ this.rsiChart.timeScale().scrollToRealTime();
174
+ this.kdjChart.timeScale().scrollToRealTime();
175
+ }
176
+ resize() {
177
+ for (const { chart, el } of [
178
+ { chart: this.macdChart, el: this.macdChart.chartElement().parentElement },
179
+ { chart: this.rsiChart, el: this.rsiChart.chartElement().parentElement },
180
+ { chart: this.kdjChart, el: this.kdjChart.chartElement().parentElement }
181
+ ]) {
182
+ if (!el) continue;
183
+ const w = el.clientWidth;
184
+ const h = el.clientHeight;
185
+ if (w > 0 && h > 0) chart.resize(w, h);
186
+ }
187
+ }
188
+ destroy() {
189
+ this.macdChart.remove();
190
+ this.rsiChart.remove();
191
+ this.kdjChart.remove();
192
+ this.root.replaceChildren();
193
+ }
194
+ createPaneWrap(label) {
195
+ const wrap = document.createElement("div");
196
+ wrap.style.cssText = "flex:1;min-height:72px;width:100%;position:relative;border-top:1px solid #30363d;";
197
+ const tag = document.createElement("span");
198
+ tag.textContent = label;
199
+ tag.style.cssText = "position:absolute;left:6px;top:4px;z-index:2;font-size:10px;color:#8b949e;pointer-events:none;";
200
+ const el = document.createElement("div");
201
+ el.style.cssText = "width:100%;height:100%;";
202
+ wrap.append(tag, el);
203
+ return { wrap, el };
204
+ }
205
+ layoutForTheme(dark) {
206
+ return {
207
+ background: { type: ColorType.Solid, color: dark ? "#0d1117" : "#ffffff" },
208
+ textColor: dark ? "#e6edf3" : "#24292f"
209
+ };
210
+ }
211
+ };
212
+ function maOverlayLine(bars, period = 20, source = "close") {
213
+ const src = barsForSource(bars, source);
214
+ return lineData(bars, sma(src, period));
215
+ }
216
+ function volMaOverlayLine(bars, period = 5) {
217
+ const volBars = bars.map((b) => ({ ...b, c: b.v ?? 0 }));
218
+ return lineData(bars, sma(volBars, period, "close"));
219
+ }
220
+
221
+ // src/pane-resize.ts
222
+ function attachPaneResizer(topPane, bottomPane, opts = {}) {
223
+ const minTop = opts.minTopPx ?? 120;
224
+ const minBottom = opts.minBottomPx ?? 60;
225
+ const parent = topPane.parentElement;
226
+ if (!parent) return () => {
227
+ };
228
+ const handle = document.createElement("div");
229
+ handle.style.cssText = "height:4px;cursor:row-resize;background:#30363d;flex-shrink:0;touch-action:none;";
230
+ bottomPane.insertAdjacentElement("beforebegin", handle);
231
+ const saved = opts.storageKey ? localStorage.getItem(opts.storageKey) : null;
232
+ if (saved) {
233
+ const ratio = Number(saved);
234
+ if (Number.isFinite(ratio) && ratio > 0 && ratio < 1) {
235
+ topPane.style.flex = `${ratio * 10}`;
236
+ bottomPane.style.flex = `${(1 - ratio) * 10}`;
237
+ }
238
+ }
239
+ let dragging = false;
240
+ const onMove = (clientY) => {
241
+ const rect = parent.getBoundingClientRect();
242
+ const y = clientY - rect.top;
243
+ const ratio = Math.min(0.85, Math.max(0.15, y / rect.height));
244
+ const topPx = ratio * rect.height;
245
+ const bottomPx = rect.height - topPx - handle.offsetHeight;
246
+ if (topPx < minTop || bottomPx < minBottom) return;
247
+ topPane.style.flex = `${ratio * 10}`;
248
+ bottomPane.style.flex = `${(1 - ratio) * 10}`;
249
+ if (opts.storageKey) localStorage.setItem(opts.storageKey, String(ratio));
250
+ };
251
+ const stop = () => {
252
+ dragging = false;
253
+ document.body.style.cursor = "";
254
+ };
255
+ handle.addEventListener("pointerdown", (e) => {
256
+ dragging = true;
257
+ handle.setPointerCapture(e.pointerId);
258
+ document.body.style.cursor = "row-resize";
259
+ });
260
+ handle.addEventListener("pointermove", (e) => {
261
+ if (dragging) onMove(e.clientY);
262
+ });
263
+ handle.addEventListener("pointerup", stop);
264
+ handle.addEventListener("pointercancel", stop);
265
+ return () => handle.remove();
266
+ }
267
+
268
+ // src/time-scale-bus.ts
269
+ var TimeScaleBus = class {
270
+ charts = [];
271
+ listeners = /* @__PURE__ */ new Set();
272
+ syncing = false;
273
+ visibleFromMs = 0;
274
+ visibleToMs = 0;
275
+ register(chart) {
276
+ if (this.charts.includes(chart)) return;
277
+ this.charts.push(chart);
278
+ chart.timeScale().subscribeVisibleLogicalRangeChange((range) => {
279
+ if (this.syncing || !range) return;
280
+ const tr = chart.timeScale().getVisibleRange();
281
+ if (tr && typeof tr.from === "number" && typeof tr.to === "number") {
282
+ this.visibleFromMs = tr.from * 1e3;
283
+ this.visibleToMs = tr.to * 1e3;
284
+ }
285
+ this.syncFrom(chart, range);
286
+ });
287
+ }
288
+ subscribeTransform(listener) {
289
+ this.listeners.add(listener);
290
+ return () => this.listeners.delete(listener);
291
+ }
292
+ setBarsTimeRange(fromMs, toMs) {
293
+ this.visibleFromMs = fromMs;
294
+ this.visibleToMs = toMs;
295
+ this.emit();
296
+ }
297
+ syncFrom(source, range) {
298
+ this.syncing = true;
299
+ for (const chart of this.charts) {
300
+ if (chart !== source) {
301
+ chart.timeScale().setVisibleLogicalRange(range);
302
+ }
303
+ }
304
+ this.syncing = false;
305
+ this.emit();
306
+ }
307
+ emit() {
308
+ const state = {
309
+ visibleFromMs: this.visibleFromMs,
310
+ visibleToMs: this.visibleToMs,
311
+ width: 0,
312
+ height: 0
313
+ };
314
+ for (const l of this.listeners) l(state);
315
+ }
316
+ };
317
+
318
+ // src/pane-orchestrator.ts
319
+ function toUtcSeconds2(tMs) {
320
+ return Math.floor(tMs / 1e3);
321
+ }
322
+ function barToCandle(b) {
323
+ return { time: toUtcSeconds2(b.t), open: b.o, high: b.h, low: b.l, close: b.c };
324
+ }
325
+ function barToVolume(b) {
326
+ return { time: toUtcSeconds2(b.t), value: b.v ?? 0 };
327
+ }
328
+ var PaneOrchestrator = class {
329
+ bus = new TimeScaleBus();
330
+ mainChart;
331
+ volumeChart;
332
+ mainSeries;
333
+ volumeSeries;
334
+ maSeries;
335
+ volMaSeries;
336
+ indicatorRoot;
337
+ indicators;
338
+ overlayCanvas = null;
339
+ dark = true;
340
+ showGrid = false;
341
+ maxRenderPoints;
342
+ barByTime = /* @__PURE__ */ new Map();
343
+ didInitialFit = false;
344
+ indicatorConfig = null;
345
+ constructor(opts) {
346
+ this.maxRenderPoints = opts.maxRenderPoints ?? 4e3;
347
+ this.dark = opts.theme !== "light";
348
+ this.showGrid = opts.showGrid ?? false;
349
+ const layout = this.layoutForTheme(this.dark);
350
+ const grid = gridOptions(this.showGrid, this.dark);
351
+ const mainEl = document.createElement("div");
352
+ mainEl.style.cssText = "flex:7;min-height:120px;width:100%;position:relative;";
353
+ const volEl = document.createElement("div");
354
+ volEl.style.cssText = "flex:2;min-height:64px;width:100%;position:relative;";
355
+ opts.container.style.cssText = "display:flex;flex-direction:column;height:100%;width:100%;min-height:240px;overflow:hidden;";
356
+ opts.container.append(mainEl, volEl);
357
+ attachPaneResizer(mainEl, volEl, { storageKey: "tradview:pane:main-volume" });
358
+ this.mainChart = createChart2(mainEl, { layout, grid, autoSize: true });
359
+ this.volumeChart = createChart2(volEl, {
360
+ layout,
361
+ grid,
362
+ autoSize: true,
363
+ rightPriceScale: { scaleMargins: { top: 0.8, bottom: 0 } }
364
+ });
365
+ if (opts.scaleMode === "log") {
366
+ this.mainChart.priceScale("right").applyOptions({ mode: 1 });
367
+ }
368
+ this.mainSeries = this.mainChart.addSeries(CandlestickSeries, {
369
+ upColor: "#26a69a",
370
+ downColor: "#ef5350",
371
+ borderVisible: false,
372
+ wickUpColor: "#26a69a",
373
+ wickDownColor: "#ef5350"
374
+ });
375
+ this.maSeries = this.mainChart.addSeries(LineSeries2, {
376
+ color: "#f0b429",
377
+ lineWidth: 1,
378
+ title: "MA20"
379
+ });
380
+ this.volumeSeries = this.volumeChart.addSeries(HistogramSeries2, {
381
+ color: "#26a69a55",
382
+ priceFormat: { type: "volume" }
383
+ });
384
+ this.volMaSeries = this.volumeChart.addSeries(LineSeries2, {
385
+ color: "#58a6ff",
386
+ lineWidth: 1,
387
+ title: "VolMA5"
388
+ });
389
+ this.bus.register(this.mainChart);
390
+ this.bus.register(this.volumeChart);
391
+ this.indicatorRoot = opts.indicatorRoot;
392
+ this.indicatorConfig = opts.indicatorConfig ?? null;
393
+ this.indicators = this.createIndicatorStack();
394
+ this.initOverlay(mainEl);
395
+ }
396
+ setTheme(theme) {
397
+ this.dark = theme === "dark";
398
+ const layout = this.layoutForTheme(this.dark);
399
+ const grid = gridOptions(this.showGrid, this.dark);
400
+ this.mainChart.applyOptions({ layout, grid });
401
+ this.volumeChart.applyOptions({ layout, grid });
402
+ this.indicators?.setTheme(theme);
403
+ }
404
+ setIndicatorConfig(config) {
405
+ this.indicatorConfig = config;
406
+ if (!config) {
407
+ this.indicators = null;
408
+ this.maSeries.setData([]);
409
+ this.volMaSeries.setData([]);
410
+ return;
411
+ }
412
+ if (!this.indicators) this.indicators = this.createIndicatorStack();
413
+ const bars = [...this.barByTime.values()].sort((a, b) => a.t - b.t);
414
+ this.indicators?.setConfig(config);
415
+ if (bars.length > 0) {
416
+ this.maSeries.setData(maOverlayLine(bars, config.maPeriod, config.source));
417
+ this.volMaSeries.setData(volMaOverlayLine(bars, config.volMaPeriod));
418
+ this.indicators?.setBars(bars);
419
+ }
420
+ }
421
+ setShowGrid(show) {
422
+ this.showGrid = show;
423
+ const grid = gridOptions(show, this.dark);
424
+ this.mainChart.applyOptions({ grid });
425
+ this.volumeChart.applyOptions({ grid });
426
+ this.indicators?.setShowGrid(show);
427
+ }
428
+ setBars(bars, gaps) {
429
+ const renderBars = lodDecimateBars(bars, this.maxRenderPoints);
430
+ this.barByTime = new Map(renderBars.map((b) => [b.t, b]));
431
+ const candles = [];
432
+ const vols = [];
433
+ const gapSet = new Set(gaps ?? []);
434
+ const seenTimes = /* @__PURE__ */ new Set();
435
+ for (let i = 0; i < renderBars.length; i++) {
436
+ const b = renderBars[i];
437
+ const time = toUtcSeconds2(b.t);
438
+ if (seenTimes.has(time)) continue;
439
+ seenTimes.add(time);
440
+ if (i > 0 && gapSet.has(b.t)) {
441
+ }
442
+ candles.push(barToCandle(b));
443
+ vols.push(barToVolume(b));
444
+ }
445
+ this.mainSeries.setData(candles);
446
+ this.volumeSeries.setData(vols);
447
+ if (this.indicatorConfig) {
448
+ this.maSeries.setData(
449
+ maOverlayLine(renderBars, this.indicatorConfig.maPeriod, this.indicatorConfig.source)
450
+ );
451
+ this.volMaSeries.setData(volMaOverlayLine(renderBars, this.indicatorConfig.volMaPeriod));
452
+ this.indicators?.setBars(renderBars);
453
+ } else {
454
+ this.maSeries.setData([]);
455
+ this.volMaSeries.setData([]);
456
+ }
457
+ if (renderBars.length > 0) {
458
+ this.syncChartSize();
459
+ if (!this.didInitialFit) {
460
+ this.mainChart.timeScale().fitContent();
461
+ this.volumeChart.timeScale().fitContent();
462
+ this.indicators?.fitContent();
463
+ this.didInitialFit = true;
464
+ }
465
+ }
466
+ }
467
+ subscribeCrosshair(listener) {
468
+ const handler = (param) => {
469
+ if (param.time == null || !param.point) {
470
+ listener(null);
471
+ return;
472
+ }
473
+ const tMs = typeof param.time === "number" ? param.time * 1e3 : null;
474
+ if (tMs == null) {
475
+ listener(null);
476
+ return;
477
+ }
478
+ const price = this.mainSeries.coordinateToPrice(param.point.y) ?? null;
479
+ const bar = this.barByTime.get(tMs) ?? this.findNearestBar(tMs);
480
+ listener({
481
+ time: tMs,
482
+ price,
483
+ ohlcv: bar ? { o: bar.o, h: bar.h, l: bar.l, c: bar.c, v: bar.v } : null
484
+ });
485
+ };
486
+ this.mainChart.subscribeCrosshairMove(handler);
487
+ return () => this.mainChart.unsubscribeCrosshairMove(handler);
488
+ }
489
+ findNearestBar(tMs) {
490
+ let best = null;
491
+ let bestDt = Infinity;
492
+ for (const b of this.barByTime.values()) {
493
+ const dt = Math.abs(b.t - tMs);
494
+ if (dt < bestDt) {
495
+ bestDt = dt;
496
+ best = b;
497
+ }
498
+ }
499
+ return bestDt < 12e4 ? best : null;
500
+ }
501
+ createIndicatorStack() {
502
+ if (!this.indicatorRoot || !this.indicatorConfig) return null;
503
+ return new IndicatorPaneStack(this.indicatorRoot, this.bus, {
504
+ theme: this.dark ? "dark" : "light",
505
+ showGrid: this.showGrid,
506
+ config: this.indicatorConfig
507
+ });
508
+ }
509
+ resetViewState() {
510
+ this.didInitialFit = false;
511
+ this.bus.visibleFromMs = 0;
512
+ this.bus.visibleToMs = 0;
513
+ }
514
+ /** Clear series while symbol/interval data reloads (avoids overlapping candles). */
515
+ clearBars() {
516
+ this.barByTime = /* @__PURE__ */ new Map();
517
+ this.mainSeries.setData([]);
518
+ this.maSeries.setData([]);
519
+ this.volumeSeries.setData([]);
520
+ this.volMaSeries.setData([]);
521
+ this.indicators?.clearBars();
522
+ }
523
+ fitContent() {
524
+ this.mainChart.timeScale().fitContent();
525
+ this.volumeChart.timeScale().fitContent();
526
+ this.indicators?.fitContent();
527
+ this.didInitialFit = true;
528
+ }
529
+ scrollToRealtime() {
530
+ this.mainChart.timeScale().scrollToRealTime();
531
+ this.volumeChart.timeScale().scrollToRealTime();
532
+ this.indicators?.scrollToRealtime();
533
+ }
534
+ setLogScale(enabled) {
535
+ this.mainChart.priceScale("right").applyOptions({ mode: enabled ? 1 : 0 });
536
+ }
537
+ resize() {
538
+ this.syncChartSize();
539
+ this.syncOverlaySize();
540
+ this.indicators?.resize();
541
+ }
542
+ getOverlayCanvas() {
543
+ return this.overlayCanvas;
544
+ }
545
+ timeToX(tMs) {
546
+ const coord = this.mainChart.timeScale().timeToCoordinate(toUtcSeconds2(tMs));
547
+ if (coord == null) return null;
548
+ return coord * devicePixelRatio;
549
+ }
550
+ priceToY(price) {
551
+ const coord = this.mainSeries.priceToCoordinate(price);
552
+ if (coord == null) return null;
553
+ return coord * devicePixelRatio;
554
+ }
555
+ xToTime(x) {
556
+ const t = this.mainChart.timeScale().coordinateToTime(x / devicePixelRatio);
557
+ if (t == null) return null;
558
+ return Number(t) * 1e3;
559
+ }
560
+ yToPrice(y) {
561
+ const p = this.mainSeries.coordinateToPrice(y / devicePixelRatio);
562
+ return p ?? null;
563
+ }
564
+ destroy() {
565
+ this.mainChart.remove();
566
+ this.volumeChart.remove();
567
+ this.indicators?.destroy();
568
+ this.overlayCanvas?.remove();
569
+ }
570
+ initOverlay(parent) {
571
+ const canvas = document.createElement("canvas");
572
+ canvas.style.cssText = "position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:10;";
573
+ parent.style.position = "relative";
574
+ parent.appendChild(canvas);
575
+ this.overlayCanvas = canvas;
576
+ this.syncOverlaySize();
577
+ this.bus.subscribeTransform(() => {
578
+ this.syncOverlaySize();
579
+ });
580
+ }
581
+ /** Let drawing overlay receive clicks; cursor mode keeps pan/zoom on LWC. */
582
+ setOverlayPointerEvents(mode) {
583
+ if (this.overlayCanvas) this.overlayCanvas.style.pointerEvents = mode;
584
+ }
585
+ syncOverlaySize() {
586
+ if (!this.overlayCanvas?.parentElement) return;
587
+ const parent = this.overlayCanvas.parentElement;
588
+ if (parent.lastElementChild !== this.overlayCanvas) {
589
+ parent.appendChild(this.overlayCanvas);
590
+ }
591
+ const rect = parent.getBoundingClientRect();
592
+ this.overlayCanvas.width = rect.width * devicePixelRatio;
593
+ this.overlayCanvas.height = rect.height * devicePixelRatio;
594
+ }
595
+ syncChartSize() {
596
+ const mainEl = this.mainChart.chartElement().parentElement;
597
+ const volEl = this.volumeChart.chartElement().parentElement;
598
+ if (mainEl) {
599
+ const w = mainEl.clientWidth;
600
+ const h = mainEl.clientHeight;
601
+ if (w > 0 && h > 0) this.mainChart.resize(w, h);
602
+ }
603
+ if (volEl) {
604
+ const w = volEl.clientWidth;
605
+ const h = volEl.clientHeight;
606
+ if (w > 0 && h > 0) this.volumeChart.resize(w, h);
607
+ }
608
+ }
609
+ layoutForTheme(dark) {
610
+ return {
611
+ background: { type: ColorType2.Solid, color: dark ? "#0d1117" : "#ffffff" },
612
+ textColor: dark ? "#e6edf3" : "#24292f"
613
+ };
614
+ }
615
+ };
616
+ export {
617
+ IndicatorPaneStack,
618
+ PaneOrchestrator,
619
+ TimeScaleBus,
620
+ maOverlayLine,
621
+ volMaOverlayLine
622
+ };
623
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pane-orchestrator.ts","../src/chart-grid.ts","../src/indicator-panes.ts","../src/pane-resize.ts","../src/time-scale-bus.ts"],"sourcesContent":["import {\n CandlestickSeries,\n ColorType,\n createChart,\n HistogramSeries,\n LineSeries,\n type CandlestickData,\n type HistogramData,\n type IChartApi,\n type ISeriesApi,\n type MouseEventParams,\n type Time,\n type UTCTimestamp,\n} from 'lightweight-charts';\nimport type { Bar } from '@coderyo/data';\nimport { lodDecimateBars } from '@coderyo/series';\nimport { gridOptions } from './chart-grid.js';\nimport type { IndicatorConfig } from '@coderyo/indicators';\n\nimport { IndicatorPaneStack, maOverlayLine, volMaOverlayLine } from './indicator-panes.js';\nimport { attachPaneResizer } from './pane-resize.js';\nimport { TimeScaleBus } from './time-scale-bus.js';\n\nexport type ScaleMode = 'linear' | 'log';\n\nexport interface CrosshairPayload {\n time: number;\n price: number | null;\n ohlcv: { o: number; h: number; l: number; c: number; v?: number } | null;\n}\n\nexport interface PaneOrchestratorOptions {\n container: HTMLElement;\n indicatorRoot?: HTMLElement;\n theme?: 'dark' | 'light';\n scaleMode?: ScaleMode;\n maxRenderPoints?: number;\n /** Show chart grid lines (default false). */\n showGrid?: boolean;\n /** null = no MA overlays and no MACD/RSI/KDJ panes. */\n indicatorConfig?: IndicatorConfig | null;\n}\n\nfunction toUtcSeconds(tMs: number): UTCTimestamp {\n return Math.floor(tMs / 1000) as UTCTimestamp;\n}\n\nfunction barToCandle(b: Bar): CandlestickData {\n return { time: toUtcSeconds(b.t), open: b.o, high: b.h, low: b.l, close: b.c };\n}\n\nfunction barToVolume(b: Bar): HistogramData<UTCTimestamp> {\n return { time: toUtcSeconds(b.t), value: b.v ?? 0 };\n}\n\nexport class PaneOrchestrator {\n readonly bus = new TimeScaleBus();\n private readonly mainChart: IChartApi;\n private readonly volumeChart: IChartApi;\n private readonly mainSeries: ISeriesApi<'Candlestick'>;\n private readonly volumeSeries: ISeriesApi<'Histogram'>;\n private readonly maSeries: ISeriesApi<'Line'>;\n private readonly volMaSeries: ISeriesApi<'Line'>;\n private readonly indicatorRoot?: HTMLElement;\n private indicators: IndicatorPaneStack | null;\n private overlayCanvas: HTMLCanvasElement | null = null;\n private dark = true;\n private showGrid = false;\n private readonly maxRenderPoints: number;\n private barByTime = new Map<number, Bar>();\n private didInitialFit = false;\n private indicatorConfig: IndicatorConfig | null = null;\n\n constructor(opts: PaneOrchestratorOptions) {\n this.maxRenderPoints = opts.maxRenderPoints ?? 4000;\n this.dark = opts.theme !== 'light';\n this.showGrid = opts.showGrid ?? false;\n const layout = this.layoutForTheme(this.dark);\n const grid = gridOptions(this.showGrid, this.dark);\n\n const mainEl = document.createElement('div');\n mainEl.style.cssText = 'flex:7;min-height:120px;width:100%;position:relative;';\n const volEl = document.createElement('div');\n volEl.style.cssText = 'flex:2;min-height:64px;width:100%;position:relative;';\n\n opts.container.style.cssText =\n 'display:flex;flex-direction:column;height:100%;width:100%;min-height:240px;overflow:hidden;';\n opts.container.append(mainEl, volEl);\n attachPaneResizer(mainEl, volEl, { storageKey: 'tradview:pane:main-volume' });\n\n this.mainChart = createChart(mainEl, { layout, grid, autoSize: true });\n this.volumeChart = createChart(volEl, {\n layout,\n grid,\n autoSize: true,\n rightPriceScale: { scaleMargins: { top: 0.8, bottom: 0 } },\n });\n\n if (opts.scaleMode === 'log') {\n this.mainChart.priceScale('right').applyOptions({ mode: 1 });\n }\n\n this.mainSeries = this.mainChart.addSeries(CandlestickSeries, {\n upColor: '#26a69a',\n downColor: '#ef5350',\n borderVisible: false,\n wickUpColor: '#26a69a',\n wickDownColor: '#ef5350',\n });\n this.maSeries = this.mainChart.addSeries(LineSeries, {\n color: '#f0b429',\n lineWidth: 1,\n title: 'MA20',\n });\n this.volumeSeries = this.volumeChart.addSeries(HistogramSeries, {\n color: '#26a69a55',\n priceFormat: { type: 'volume' },\n });\n this.volMaSeries = this.volumeChart.addSeries(LineSeries, {\n color: '#58a6ff',\n lineWidth: 1,\n title: 'VolMA5',\n });\n\n this.bus.register(this.mainChart);\n this.bus.register(this.volumeChart);\n\n this.indicatorRoot = opts.indicatorRoot;\n this.indicatorConfig = opts.indicatorConfig ?? null;\n this.indicators = this.createIndicatorStack();\n\n this.initOverlay(mainEl);\n }\n\n setTheme(theme: 'dark' | 'light'): void {\n this.dark = theme === 'dark';\n const layout = this.layoutForTheme(this.dark);\n const grid = gridOptions(this.showGrid, this.dark);\n this.mainChart.applyOptions({ layout, grid });\n this.volumeChart.applyOptions({ layout, grid });\n this.indicators?.setTheme(theme);\n }\n\n setIndicatorConfig(config: IndicatorConfig | null): void {\n this.indicatorConfig = config;\n if (!config) {\n this.indicators = null;\n this.maSeries.setData([]);\n this.volMaSeries.setData([]);\n return;\n }\n if (!this.indicators) this.indicators = this.createIndicatorStack();\n const bars = [...this.barByTime.values()].sort((a, b) => a.t - b.t);\n this.indicators?.setConfig(config);\n if (bars.length > 0) {\n this.maSeries.setData(maOverlayLine(bars, config.maPeriod, config.source));\n this.volMaSeries.setData(volMaOverlayLine(bars, config.volMaPeriod));\n this.indicators?.setBars(bars);\n }\n }\n\n setShowGrid(show: boolean): void {\n this.showGrid = show;\n const grid = gridOptions(show, this.dark);\n this.mainChart.applyOptions({ grid });\n this.volumeChart.applyOptions({ grid });\n this.indicators?.setShowGrid(show);\n }\n\n setBars(bars: Bar[], gaps?: number[]): void {\n const renderBars = lodDecimateBars(bars, this.maxRenderPoints);\n this.barByTime = new Map(renderBars.map((b) => [b.t, b]));\n\n const candles: CandlestickData[] = [];\n const vols: HistogramData<UTCTimestamp>[] = [];\n const gapSet = new Set(gaps ?? []);\n const seenTimes = new Set<number>();\n\n for (let i = 0; i < renderBars.length; i++) {\n const b = renderBars[i]!;\n const time = toUtcSeconds(b.t);\n if (seenTimes.has(time)) continue;\n seenTimes.add(time);\n if (i > 0 && gapSet.has(b.t)) {\n // whitespace: skip connecting — LWC uses sparse times\n }\n candles.push(barToCandle(b));\n vols.push(barToVolume(b));\n }\n\n this.mainSeries.setData(candles);\n this.volumeSeries.setData(vols);\n if (this.indicatorConfig) {\n this.maSeries.setData(\n maOverlayLine(renderBars, this.indicatorConfig.maPeriod, this.indicatorConfig.source),\n );\n this.volMaSeries.setData(volMaOverlayLine(renderBars, this.indicatorConfig.volMaPeriod));\n this.indicators?.setBars(renderBars);\n } else {\n this.maSeries.setData([]);\n this.volMaSeries.setData([]);\n }\n\n if (renderBars.length > 0) {\n this.syncChartSize();\n if (!this.didInitialFit) {\n this.mainChart.timeScale().fitContent();\n this.volumeChart.timeScale().fitContent();\n this.indicators?.fitContent();\n this.didInitialFit = true;\n }\n }\n }\n\n subscribeCrosshair(listener: (payload: CrosshairPayload | null) => void): () => void {\n const handler = (param: MouseEventParams<Time>) => {\n if (param.time == null || !param.point) {\n listener(null);\n return;\n }\n const tMs = typeof param.time === 'number' ? param.time * 1000 : null;\n if (tMs == null) {\n listener(null);\n return;\n }\n const price = this.mainSeries.coordinateToPrice(param.point.y) ?? null;\n const bar = this.barByTime.get(tMs) ?? this.findNearestBar(tMs);\n listener({\n time: tMs,\n price,\n ohlcv: bar\n ? { o: bar.o, h: bar.h, l: bar.l, c: bar.c, v: bar.v }\n : null,\n });\n };\n this.mainChart.subscribeCrosshairMove(handler);\n return () => this.mainChart.unsubscribeCrosshairMove(handler);\n }\n\n private findNearestBar(tMs: number): Bar | null {\n let best: Bar | null = null;\n let bestDt = Infinity;\n for (const b of this.barByTime.values()) {\n const dt = Math.abs(b.t - tMs);\n if (dt < bestDt) {\n bestDt = dt;\n best = b;\n }\n }\n return bestDt < 120_000 ? best : null;\n }\n\n private createIndicatorStack(): IndicatorPaneStack | null {\n if (!this.indicatorRoot || !this.indicatorConfig) return null;\n return new IndicatorPaneStack(this.indicatorRoot, this.bus, {\n theme: this.dark ? 'dark' : 'light',\n showGrid: this.showGrid,\n config: this.indicatorConfig,\n });\n }\n\n resetViewState(): void {\n this.didInitialFit = false;\n this.bus.visibleFromMs = 0;\n this.bus.visibleToMs = 0;\n }\n\n /** Clear series while symbol/interval data reloads (avoids overlapping candles). */\n clearBars(): void {\n this.barByTime = new Map();\n this.mainSeries.setData([]);\n this.maSeries.setData([]);\n this.volumeSeries.setData([]);\n this.volMaSeries.setData([]);\n this.indicators?.clearBars();\n }\n\n fitContent(): void {\n this.mainChart.timeScale().fitContent();\n this.volumeChart.timeScale().fitContent();\n this.indicators?.fitContent();\n this.didInitialFit = true;\n }\n\n scrollToRealtime(): void {\n this.mainChart.timeScale().scrollToRealTime();\n this.volumeChart.timeScale().scrollToRealTime();\n this.indicators?.scrollToRealtime();\n }\n\n setLogScale(enabled: boolean): void {\n this.mainChart.priceScale('right').applyOptions({ mode: enabled ? 1 : 0 });\n }\n\n resize(): void {\n this.syncChartSize();\n this.syncOverlaySize();\n this.indicators?.resize();\n }\n\n getOverlayCanvas(): HTMLCanvasElement | null {\n return this.overlayCanvas;\n }\n\n timeToX(tMs: number): number | null {\n const coord = this.mainChart.timeScale().timeToCoordinate(toUtcSeconds(tMs));\n if (coord == null) return null;\n return coord * devicePixelRatio;\n }\n\n priceToY(price: number): number | null {\n const coord = this.mainSeries.priceToCoordinate(price);\n if (coord == null) return null;\n return coord * devicePixelRatio;\n }\n\n xToTime(x: number): number | null {\n const t = this.mainChart.timeScale().coordinateToTime(x / devicePixelRatio);\n if (t == null) return null;\n return Number(t) * 1000;\n }\n\n yToPrice(y: number): number | null {\n const p = this.mainSeries.coordinateToPrice(y / devicePixelRatio);\n return p ?? null;\n }\n\n destroy(): void {\n this.mainChart.remove();\n this.volumeChart.remove();\n this.indicators?.destroy();\n this.overlayCanvas?.remove();\n }\n\n private initOverlay(parent: HTMLElement) {\n const canvas = document.createElement('canvas');\n canvas.style.cssText =\n 'position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:10;';\n parent.style.position = 'relative';\n parent.appendChild(canvas);\n this.overlayCanvas = canvas;\n this.syncOverlaySize();\n this.bus.subscribeTransform(() => {\n this.syncOverlaySize();\n });\n }\n\n /** Let drawing overlay receive clicks; cursor mode keeps pan/zoom on LWC. */\n setOverlayPointerEvents(mode: 'auto' | 'none'): void {\n if (this.overlayCanvas) this.overlayCanvas.style.pointerEvents = mode;\n }\n\n private syncOverlaySize() {\n if (!this.overlayCanvas?.parentElement) return;\n const parent = this.overlayCanvas.parentElement;\n if (parent.lastElementChild !== this.overlayCanvas) {\n parent.appendChild(this.overlayCanvas);\n }\n const rect = parent.getBoundingClientRect();\n this.overlayCanvas.width = rect.width * devicePixelRatio;\n this.overlayCanvas.height = rect.height * devicePixelRatio;\n }\n\n private syncChartSize(): void {\n const mainEl = this.mainChart.chartElement().parentElement;\n const volEl = this.volumeChart.chartElement().parentElement;\n if (mainEl) {\n const w = mainEl.clientWidth;\n const h = mainEl.clientHeight;\n if (w > 0 && h > 0) this.mainChart.resize(w, h);\n }\n if (volEl) {\n const w = volEl.clientWidth;\n const h = volEl.clientHeight;\n if (w > 0 && h > 0) this.volumeChart.resize(w, h);\n }\n }\n\n private layoutForTheme(dark: boolean) {\n return {\n background: { type: ColorType.Solid, color: dark ? '#0d1117' : '#ffffff' },\n textColor: dark ? '#e6edf3' : '#24292f',\n };\n }\n}","/** LWC grid line options — default off per product spec. */\nexport function gridOptions(showGrid: boolean, dark: boolean) {\n const color = dark ? '#21262d' : '#d0d7de';\n return {\n vertLines: { visible: showGrid, color },\n horzLines: { visible: showGrid, color },\n };\n}","import {\n ColorType,\n createChart,\n HistogramSeries,\n LineSeries,\n type HistogramData,\n type IChartApi,\n type ISeriesApi,\n type LineData,\n type UTCTimestamp,\n} from 'lightweight-charts';\nimport type { Bar } from '@coderyo/data';\nimport {\n type IndicatorConfig,\n DEFAULT_INDICATOR_CONFIG,\n kdj,\n macd,\n rsi,\n sma,\n} from '@coderyo/indicators';\nimport { gridOptions } from './chart-grid.js';\nimport type { TimeScaleBus } from './time-scale-bus.js';\n\nexport interface IndicatorPaneStackOptions {\n theme?: 'dark' | 'light';\n showGrid?: boolean;\n config?: IndicatorConfig;\n}\n\nfunction barsForSource(bars: Bar[], source: IndicatorConfig['source']): Bar[] {\n if (source === 'close') return bars;\n return bars.map((b) => ({ ...b, c: (b.h + b.l + b.c) / 3 }));\n}\n\nfunction toUtcSeconds(tMs: number): UTCTimestamp {\n return Math.floor(tMs / 1000) as UTCTimestamp;\n}\n\nfunction lineData(bars: Bar[], values: (number | null)[]): LineData<UTCTimestamp>[] {\n const out: LineData<UTCTimestamp>[] = [];\n for (let i = 0; i < bars.length; i++) {\n const v = values[i];\n if (v == null) continue;\n out.push({ time: toUtcSeconds(bars[i]!.t), value: v });\n }\n return out;\n}\n\nfunction histData(bars: Bar[], values: (number | null)[]): HistogramData<UTCTimestamp>[] {\n const out: HistogramData<UTCTimestamp>[] = [];\n for (let i = 0; i < bars.length; i++) {\n const v = values[i];\n if (v == null) continue;\n out.push({\n time: toUtcSeconds(bars[i]!.t),\n value: v,\n color: v >= 0 ? '#26a69a88' : '#ef535088',\n });\n }\n return out;\n}\n\nexport class IndicatorPaneStack {\n private readonly macdChart: IChartApi;\n private readonly rsiChart: IChartApi;\n private readonly kdjChart: IChartApi;\n private readonly macdLine: ISeriesApi<'Line'>;\n private readonly macdSignal: ISeriesApi<'Line'>;\n private readonly macdHist: ISeriesApi<'Histogram'>;\n private readonly rsiLine: ISeriesApi<'Line'>;\n private readonly kdjK: ISeriesApi<'Line'>;\n private readonly kdjD: ISeriesApi<'Line'>;\n private readonly kdjJ: ISeriesApi<'Line'>;\n private dark = true;\n private showGrid = false;\n private config: IndicatorConfig = DEFAULT_INDICATOR_CONFIG;\n private readonly macdWrap: HTMLElement;\n private readonly rsiWrap: HTMLElement;\n private readonly kdjWrap: HTMLElement;\n\n constructor(\n private readonly root: HTMLElement,\n bus: TimeScaleBus,\n opts: IndicatorPaneStackOptions | 'dark' | 'light' = 'dark',\n ) {\n const o = typeof opts === 'string' ? { theme: opts, showGrid: false } : opts;\n this.dark = o.theme !== 'light';\n this.showGrid = o.showGrid ?? false;\n this.config = o.config ?? DEFAULT_INDICATOR_CONFIG;\n this.root.style.display = 'flex';\n this.root.style.flexDirection = 'column';\n this.root.style.flex = '2';\n this.root.style.minHeight = '0';\n this.root.style.overflow = 'hidden';\n\n const macdPane = this.createPaneWrap('MACD');\n const rsiPane = this.createPaneWrap('RSI');\n const kdjPane = this.createPaneWrap('KDJ');\n this.macdWrap = macdPane.wrap;\n this.rsiWrap = rsiPane.wrap;\n this.kdjWrap = kdjPane.wrap;\n this.root.append(macdPane.wrap, rsiPane.wrap, kdjPane.wrap);\n this.applyPaneVisibility();\n\n const layout = this.layoutForTheme(this.dark);\n const grid = gridOptions(this.showGrid, this.dark);\n this.macdChart = createChart(macdPane.el, { layout, grid, autoSize: true });\n this.rsiChart = createChart(rsiPane.el, { layout, grid, autoSize: true });\n this.kdjChart = createChart(kdjPane.el, { layout, grid, autoSize: true });\n\n for (const c of [this.macdChart, this.rsiChart, this.kdjChart]) bus.register(c);\n\n this.macdLine = this.macdChart.addSeries(LineSeries, { color: '#2962ff', lineWidth: 1 });\n this.macdSignal = this.macdChart.addSeries(LineSeries, { color: '#ff9800', lineWidth: 1 });\n this.macdHist = this.macdChart.addSeries(HistogramSeries, {\n priceFormat: { type: 'price', precision: 4, minMove: 0.0001 },\n });\n this.rsiLine = this.rsiChart.addSeries(LineSeries, { color: '#ab47bc', lineWidth: 1 });\n this.kdjK = this.kdjChart.addSeries(LineSeries, { color: '#42a5f5', lineWidth: 1 });\n this.kdjD = this.kdjChart.addSeries(LineSeries, { color: '#ffa726', lineWidth: 1 });\n this.kdjJ = this.kdjChart.addSeries(LineSeries, { color: '#ef5350', lineWidth: 1 });\n }\n\n setConfig(config: IndicatorConfig): void {\n this.config = config;\n this.applyPaneVisibility();\n }\n\n private applyPaneVisibility(): void {\n this.macdWrap.style.display = this.config.showMacd ? '' : 'none';\n this.rsiWrap.style.display = this.config.showRsi ? '' : 'none';\n this.kdjWrap.style.display = this.config.showKdj ? '' : 'none';\n }\n\n clearBars(): void {\n this.macdLine.setData([]);\n this.macdSignal.setData([]);\n this.macdHist.setData([]);\n this.rsiLine.setData([]);\n this.kdjK.setData([]);\n this.kdjD.setData([]);\n this.kdjJ.setData([]);\n }\n\n setBars(bars: Bar[]): void {\n if (bars.length === 0) return;\n const src = barsForSource(bars, this.config.source);\n const m = macd(src, this.config.macdFast, this.config.macdSlow, this.config.macdSignal);\n this.macdLine.setData(lineData(bars, m.macd));\n this.macdSignal.setData(lineData(bars, m.signal));\n this.macdHist.setData(histData(bars, m.histogram));\n\n this.rsiLine.setData(lineData(bars, rsi(src, this.config.rsiPeriod)));\n\n const k = kdj(src, this.config.kdjPeriod, this.config.kdjKSmooth, this.config.kdjDSmooth);\n this.kdjK.setData(lineData(bars, k.k));\n this.kdjD.setData(lineData(bars, k.d));\n this.kdjJ.setData(lineData(bars, k.j));\n\n this.macdChart.timeScale().fitContent();\n this.rsiChart.timeScale().fitContent();\n this.kdjChart.timeScale().fitContent();\n this.resize();\n }\n\n setTheme(theme: 'dark' | 'light'): void {\n this.dark = theme === 'dark';\n const layout = this.layoutForTheme(this.dark);\n const grid = gridOptions(this.showGrid, this.dark);\n this.macdChart.applyOptions({ layout, grid });\n this.rsiChart.applyOptions({ layout, grid });\n this.kdjChart.applyOptions({ layout, grid });\n }\n\n setShowGrid(show: boolean): void {\n this.showGrid = show;\n const grid = gridOptions(show, this.dark);\n this.macdChart.applyOptions({ grid });\n this.rsiChart.applyOptions({ grid });\n this.kdjChart.applyOptions({ grid });\n }\n\n fitContent(): void {\n this.macdChart.timeScale().fitContent();\n this.rsiChart.timeScale().fitContent();\n this.kdjChart.timeScale().fitContent();\n }\n\n scrollToRealtime(): void {\n this.macdChart.timeScale().scrollToRealTime();\n this.rsiChart.timeScale().scrollToRealTime();\n this.kdjChart.timeScale().scrollToRealTime();\n }\n\n resize(): void {\n for (const { chart, el } of [\n { chart: this.macdChart, el: this.macdChart.chartElement().parentElement },\n { chart: this.rsiChart, el: this.rsiChart.chartElement().parentElement },\n { chart: this.kdjChart, el: this.kdjChart.chartElement().parentElement },\n ]) {\n if (!el) continue;\n const w = el.clientWidth;\n const h = el.clientHeight;\n if (w > 0 && h > 0) chart.resize(w, h);\n }\n }\n\n destroy(): void {\n this.macdChart.remove();\n this.rsiChart.remove();\n this.kdjChart.remove();\n this.root.replaceChildren();\n }\n\n private createPaneWrap(label: string): { wrap: HTMLElement; el: HTMLElement } {\n const wrap = document.createElement('div');\n wrap.style.cssText =\n 'flex:1;min-height:72px;width:100%;position:relative;border-top:1px solid #30363d;';\n const tag = document.createElement('span');\n tag.textContent = label;\n tag.style.cssText =\n 'position:absolute;left:6px;top:4px;z-index:2;font-size:10px;color:#8b949e;pointer-events:none;';\n const el = document.createElement('div');\n el.style.cssText = 'width:100%;height:100%;';\n wrap.append(tag, el);\n return { wrap, el };\n }\n\n private layoutForTheme(dark: boolean) {\n return {\n background: { type: ColorType.Solid, color: dark ? '#0d1117' : '#ffffff' },\n textColor: dark ? '#e6edf3' : '#24292f',\n };\n }\n}\n\nexport function maOverlayLine(\n bars: Bar[],\n period = 20,\n source: IndicatorConfig['source'] = 'close',\n): LineData<UTCTimestamp>[] {\n const src = barsForSource(bars, source);\n return lineData(bars, sma(src, period));\n}\n\nexport function volMaOverlayLine(bars: Bar[], period = 5): LineData<UTCTimestamp>[] {\n const volBars = bars.map((b) => ({ ...b, c: b.v ?? 0 }));\n return lineData(bars, sma(volBars, period, 'close'));\n}","export function attachPaneResizer(\n topPane: HTMLElement,\n bottomPane: HTMLElement,\n opts: { minTopPx?: number; minBottomPx?: number; storageKey?: string } = {},\n): () => void {\n const minTop = opts.minTopPx ?? 120;\n const minBottom = opts.minBottomPx ?? 60;\n const parent = topPane.parentElement;\n if (!parent) return () => {};\n\n const handle = document.createElement('div');\n handle.style.cssText =\n 'height:4px;cursor:row-resize;background:#30363d;flex-shrink:0;touch-action:none;';\n bottomPane.insertAdjacentElement('beforebegin', handle);\n\n const saved = opts.storageKey ? localStorage.getItem(opts.storageKey) : null;\n if (saved) {\n const ratio = Number(saved);\n if (Number.isFinite(ratio) && ratio > 0 && ratio < 1) {\n topPane.style.flex = `${ratio * 10}`;\n bottomPane.style.flex = `${(1 - ratio) * 10}`;\n }\n }\n\n let dragging = false;\n\n const onMove = (clientY: number) => {\n const rect = parent.getBoundingClientRect();\n const y = clientY - rect.top;\n const ratio = Math.min(0.85, Math.max(0.15, y / rect.height));\n const topPx = ratio * rect.height;\n const bottomPx = rect.height - topPx - handle.offsetHeight;\n if (topPx < minTop || bottomPx < minBottom) return;\n topPane.style.flex = `${ratio * 10}`;\n bottomPane.style.flex = `${(1 - ratio) * 10}`;\n if (opts.storageKey) localStorage.setItem(opts.storageKey, String(ratio));\n };\n\n const stop = () => {\n dragging = false;\n document.body.style.cursor = '';\n };\n\n handle.addEventListener('pointerdown', (e) => {\n dragging = true;\n handle.setPointerCapture(e.pointerId);\n document.body.style.cursor = 'row-resize';\n });\n handle.addEventListener('pointermove', (e) => {\n if (dragging) onMove(e.clientY);\n });\n handle.addEventListener('pointerup', stop);\n handle.addEventListener('pointercancel', stop);\n\n return () => handle.remove();\n}","import type { IChartApi, LogicalRange } from 'lightweight-charts';\n\nexport interface TransformState {\n visibleFromMs: number;\n visibleToMs: number;\n width: number;\n height: number;\n}\n\ntype TransformListener = (state: TransformState) => void;\n\n/** Sync visible logical range across multiple LWC panes (§10.4.1). */\nexport class TimeScaleBus {\n private charts: IChartApi[] = [];\n private listeners = new Set<TransformListener>();\n private syncing = false;\n visibleFromMs = 0;\n visibleToMs = 0;\n\n register(chart: IChartApi): void {\n if (this.charts.includes(chart)) return;\n this.charts.push(chart);\n chart.timeScale().subscribeVisibleLogicalRangeChange((range) => {\n if (this.syncing || !range) return;\n const tr = chart.timeScale().getVisibleRange();\n if (tr && typeof tr.from === 'number' && typeof tr.to === 'number') {\n this.visibleFromMs = tr.from * 1000;\n this.visibleToMs = tr.to * 1000;\n }\n this.syncFrom(chart, range);\n });\n }\n\n subscribeTransform(listener: TransformListener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n setBarsTimeRange(fromMs: number, toMs: number): void {\n this.visibleFromMs = fromMs;\n this.visibleToMs = toMs;\n this.emit();\n }\n\n private syncFrom(source: IChartApi, range: LogicalRange) {\n this.syncing = true;\n for (const chart of this.charts) {\n if (chart !== source) {\n chart.timeScale().setVisibleLogicalRange(range);\n }\n }\n this.syncing = false;\n this.emit();\n }\n\n private emit(): void {\n const state: TransformState = {\n visibleFromMs: this.visibleFromMs,\n visibleToMs: this.visibleToMs,\n width: 0,\n height: 0,\n };\n for (const l of this.listeners) l(state);\n }\n}"],"mappings":"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","names":["ColorType","createChart","HistogramSeries","LineSeries","toUtcSeconds","createChart","LineSeries","HistogramSeries","ColorType"]}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@coderyo/renderer-lite",
3
+ "version": "1.0.0-rc.2",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js"
10
+ }
11
+ },
12
+ "dependencies": {
13
+ "lightweight-charts": "^5.0.7",
14
+ "@coderyo/data": "1.0.0-rc.2",
15
+ "@coderyo/indicators": "1.0.0-rc.2",
16
+ "@coderyo/series": "1.0.0-rc.2"
17
+ },
18
+ "devDependencies": {
19
+ "tsup": "^8.5.0",
20
+ "typescript": "^5.8.3",
21
+ "vitest": "^3.2.4",
22
+ "@coderyo/tsconfig": "0.0.0",
23
+ "@coderyo/eslint-config": "0.0.0"
24
+ },
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/CodeRyoStudio/tradview.git",
31
+ "directory": "packages/renderer-lite"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "scripts": {
37
+ "build": "tsup",
38
+ "test": "vitest run --passWithNoTests",
39
+ "lint": "eslint src",
40
+ "typecheck": "tsc --noEmit"
41
+ }
42
+ }