@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.
- package/dist/index.d.ts +135 -0
- package/dist/index.js +623 -0
- package/dist/index.js.map +1 -0
- package/package.json +42 -0
package/dist/index.d.ts
ADDED
|
@@ -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":";AAAA;AAAA,EACE;AAAA,EACA,aAAAA;AAAA,EACA,eAAAC;AAAA,EACA,mBAAAC;AAAA,EACA,cAAAC;AAAA,OAQK;AAEP,SAAS,uBAAuB;;;ACdzB,SAAS,YAAY,UAAmB,MAAe;AAC5D,QAAM,QAAQ,OAAO,YAAY;AACjC,SAAO;AAAA,IACL,WAAW,EAAE,SAAS,UAAU,MAAM;AAAA,IACtC,WAAW,EAAE,SAAS,UAAU,MAAM;AAAA,EACxC;AACF;;;ACPA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAMK;AAEP;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAUP,SAAS,cAAc,MAAa,QAA0C;AAC5E,MAAI,WAAW,QAAS,QAAO;AAC/B,SAAO,KAAK,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;AAC7D;AAEA,SAAS,aAAa,KAA2B;AAC/C,SAAO,KAAK,MAAM,MAAM,GAAI;AAC9B;AAEA,SAAS,SAAS,MAAa,QAAqD;AAClF,QAAM,MAAgC,CAAC;AACvC,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,IAAI,OAAO,CAAC;AAClB,QAAI,KAAK,KAAM;AACf,QAAI,KAAK,EAAE,MAAM,aAAa,KAAK,CAAC,EAAG,CAAC,GAAG,OAAO,EAAE,CAAC;AAAA,EACvD;AACA,SAAO;AACT;AAEA,SAAS,SAAS,MAAa,QAA0D;AACvF,QAAM,MAAqC,CAAC;AAC5C,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,IAAI,OAAO,CAAC;AAClB,QAAI,KAAK,KAAM;AACf,QAAI,KAAK;AAAA,MACP,MAAM,aAAa,KAAK,CAAC,EAAG,CAAC;AAAA,MAC7B,OAAO;AAAA,MACP,OAAO,KAAK,IAAI,cAAc;AAAA,IAChC,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEO,IAAM,qBAAN,MAAyB;AAAA,EAkB9B,YACmB,MACjB,KACA,OAAqD,QACrD;AAHiB;AAIjB,UAAM,IAAI,OAAO,SAAS,WAAW,EAAE,OAAO,MAAM,UAAU,MAAM,IAAI;AACxE,SAAK,OAAO,EAAE,UAAU;AACxB,SAAK,WAAW,EAAE,YAAY;AAC9B,SAAK,SAAS,EAAE,UAAU;AAC1B,SAAK,KAAK,MAAM,UAAU;AAC1B,SAAK,KAAK,MAAM,gBAAgB;AAChC,SAAK,KAAK,MAAM,OAAO;AACvB,SAAK,KAAK,MAAM,YAAY;AAC5B,SAAK,KAAK,MAAM,WAAW;AAE3B,UAAM,WAAW,KAAK,eAAe,MAAM;AAC3C,UAAM,UAAU,KAAK,eAAe,KAAK;AACzC,UAAM,UAAU,KAAK,eAAe,KAAK;AACzC,SAAK,WAAW,SAAS;AACzB,SAAK,UAAU,QAAQ;AACvB,SAAK,UAAU,QAAQ;AACvB,SAAK,KAAK,OAAO,SAAS,MAAM,QAAQ,MAAM,QAAQ,IAAI;AAC1D,SAAK,oBAAoB;AAEzB,UAAM,SAAS,KAAK,eAAe,KAAK,IAAI;AAC5C,UAAM,OAAO,YAAY,KAAK,UAAU,KAAK,IAAI;AACjD,SAAK,YAAY,YAAY,SAAS,IAAI,EAAE,QAAQ,MAAM,UAAU,KAAK,CAAC;AAC1E,SAAK,WAAW,YAAY,QAAQ,IAAI,EAAE,QAAQ,MAAM,UAAU,KAAK,CAAC;AACxE,SAAK,WAAW,YAAY,QAAQ,IAAI,EAAE,QAAQ,MAAM,UAAU,KAAK,CAAC;AAExE,eAAW,KAAK,CAAC,KAAK,WAAW,KAAK,UAAU,KAAK,QAAQ,EAAG,KAAI,SAAS,CAAC;AAE9E,SAAK,WAAW,KAAK,UAAU,UAAU,YAAY,EAAE,OAAO,WAAW,WAAW,EAAE,CAAC;AACvF,SAAK,aAAa,KAAK,UAAU,UAAU,YAAY,EAAE,OAAO,WAAW,WAAW,EAAE,CAAC;AACzF,SAAK,WAAW,KAAK,UAAU,UAAU,iBAAiB;AAAA,MACxD,aAAa,EAAE,MAAM,SAAS,WAAW,GAAG,SAAS,KAAO;AAAA,IAC9D,CAAC;AACD,SAAK,UAAU,KAAK,SAAS,UAAU,YAAY,EAAE,OAAO,WAAW,WAAW,EAAE,CAAC;AACrF,SAAK,OAAO,KAAK,SAAS,UAAU,YAAY,EAAE,OAAO,WAAW,WAAW,EAAE,CAAC;AAClF,SAAK,OAAO,KAAK,SAAS,UAAU,YAAY,EAAE,OAAO,WAAW,WAAW,EAAE,CAAC;AAClF,SAAK,OAAO,KAAK,SAAS,UAAU,YAAY,EAAE,OAAO,WAAW,WAAW,EAAE,CAAC;AAAA,EACpF;AAAA,EAxCmB;AAAA,EAlBF;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT,OAAO;AAAA,EACP,WAAW;AAAA,EACX,SAA0B;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EA6CjB,UAAU,QAA+B;AACvC,SAAK,SAAS;AACd,SAAK,oBAAoB;AAAA,EAC3B;AAAA,EAEQ,sBAA4B;AAClC,SAAK,SAAS,MAAM,UAAU,KAAK,OAAO,WAAW,KAAK;AAC1D,SAAK,QAAQ,MAAM,UAAU,KAAK,OAAO,UAAU,KAAK;AACxD,SAAK,QAAQ,MAAM,UAAU,KAAK,OAAO,UAAU,KAAK;AAAA,EAC1D;AAAA,EAEA,YAAkB;AAChB,SAAK,SAAS,QAAQ,CAAC,CAAC;AACxB,SAAK,WAAW,QAAQ,CAAC,CAAC;AAC1B,SAAK,SAAS,QAAQ,CAAC,CAAC;AACxB,SAAK,QAAQ,QAAQ,CAAC,CAAC;AACvB,SAAK,KAAK,QAAQ,CAAC,CAAC;AACpB,SAAK,KAAK,QAAQ,CAAC,CAAC;AACpB,SAAK,KAAK,QAAQ,CAAC,CAAC;AAAA,EACtB;AAAA,EAEA,QAAQ,MAAmB;AACzB,QAAI,KAAK,WAAW,EAAG;AACvB,UAAM,MAAM,cAAc,MAAM,KAAK,OAAO,MAAM;AAClD,UAAM,IAAI,KAAK,KAAK,KAAK,OAAO,UAAU,KAAK,OAAO,UAAU,KAAK,OAAO,UAAU;AACtF,SAAK,SAAS,QAAQ,SAAS,MAAM,EAAE,IAAI,CAAC;AAC5C,SAAK,WAAW,QAAQ,SAAS,MAAM,EAAE,MAAM,CAAC;AAChD,SAAK,SAAS,QAAQ,SAAS,MAAM,EAAE,SAAS,CAAC;AAEjD,SAAK,QAAQ,QAAQ,SAAS,MAAM,IAAI,KAAK,KAAK,OAAO,SAAS,CAAC,CAAC;AAEpE,UAAM,IAAI,IAAI,KAAK,KAAK,OAAO,WAAW,KAAK,OAAO,YAAY,KAAK,OAAO,UAAU;AACxF,SAAK,KAAK,QAAQ,SAAS,MAAM,EAAE,CAAC,CAAC;AACrC,SAAK,KAAK,QAAQ,SAAS,MAAM,EAAE,CAAC,CAAC;AACrC,SAAK,KAAK,QAAQ,SAAS,MAAM,EAAE,CAAC,CAAC;AAErC,SAAK,UAAU,UAAU,EAAE,WAAW;AACtC,SAAK,SAAS,UAAU,EAAE,WAAW;AACrC,SAAK,SAAS,UAAU,EAAE,WAAW;AACrC,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,SAAS,OAA+B;AACtC,SAAK,OAAO,UAAU;AACtB,UAAM,SAAS,KAAK,eAAe,KAAK,IAAI;AAC5C,UAAM,OAAO,YAAY,KAAK,UAAU,KAAK,IAAI;AACjD,SAAK,UAAU,aAAa,EAAE,QAAQ,KAAK,CAAC;AAC5C,SAAK,SAAS,aAAa,EAAE,QAAQ,KAAK,CAAC;AAC3C,SAAK,SAAS,aAAa,EAAE,QAAQ,KAAK,CAAC;AAAA,EAC7C;AAAA,EAEA,YAAY,MAAqB;AAC/B,SAAK,WAAW;AAChB,UAAM,OAAO,YAAY,MAAM,KAAK,IAAI;AACxC,SAAK,UAAU,aAAa,EAAE,KAAK,CAAC;AACpC,SAAK,SAAS,aAAa,EAAE,KAAK,CAAC;AACnC,SAAK,SAAS,aAAa,EAAE,KAAK,CAAC;AAAA,EACrC;AAAA,EAEA,aAAmB;AACjB,SAAK,UAAU,UAAU,EAAE,WAAW;AACtC,SAAK,SAAS,UAAU,EAAE,WAAW;AACrC,SAAK,SAAS,UAAU,EAAE,WAAW;AAAA,EACvC;AAAA,EAEA,mBAAyB;AACvB,SAAK,UAAU,UAAU,EAAE,iBAAiB;AAC5C,SAAK,SAAS,UAAU,EAAE,iBAAiB;AAC3C,SAAK,SAAS,UAAU,EAAE,iBAAiB;AAAA,EAC7C;AAAA,EAEA,SAAe;AACb,eAAW,EAAE,OAAO,GAAG,KAAK;AAAA,MAC1B,EAAE,OAAO,KAAK,WAAW,IAAI,KAAK,UAAU,aAAa,EAAE,cAAc;AAAA,MACzE,EAAE,OAAO,KAAK,UAAU,IAAI,KAAK,SAAS,aAAa,EAAE,cAAc;AAAA,MACvE,EAAE,OAAO,KAAK,UAAU,IAAI,KAAK,SAAS,aAAa,EAAE,cAAc;AAAA,IACzE,GAAG;AACD,UAAI,CAAC,GAAI;AACT,YAAM,IAAI,GAAG;AACb,YAAM,IAAI,GAAG;AACb,UAAI,IAAI,KAAK,IAAI,EAAG,OAAM,OAAO,GAAG,CAAC;AAAA,IACvC;AAAA,EACF;AAAA,EAEA,UAAgB;AACd,SAAK,UAAU,OAAO;AACtB,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS,OAAO;AACrB,SAAK,KAAK,gBAAgB;AAAA,EAC5B;AAAA,EAEQ,eAAe,OAAuD;AAC5E,UAAM,OAAO,SAAS,cAAc,KAAK;AACzC,SAAK,MAAM,UACT;AACF,UAAM,MAAM,SAAS,cAAc,MAAM;AACzC,QAAI,cAAc;AAClB,QAAI,MAAM,UACR;AACF,UAAM,KAAK,SAAS,cAAc,KAAK;AACvC,OAAG,MAAM,UAAU;AACnB,SAAK,OAAO,KAAK,EAAE;AACnB,WAAO,EAAE,MAAM,GAAG;AAAA,EACpB;AAAA,EAEQ,eAAe,MAAe;AACpC,WAAO;AAAA,MACL,YAAY,EAAE,MAAM,UAAU,OAAO,OAAO,OAAO,YAAY,UAAU;AAAA,MACzE,WAAW,OAAO,YAAY;AAAA,IAChC;AAAA,EACF;AACF;AAEO,SAAS,cACd,MACA,SAAS,IACT,SAAoC,SACV;AAC1B,QAAM,MAAM,cAAc,MAAM,MAAM;AACtC,SAAO,SAAS,MAAM,IAAI,KAAK,MAAM,CAAC;AACxC;AAEO,SAAS,iBAAiB,MAAa,SAAS,GAA6B;AAClF,QAAM,UAAU,KAAK,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,GAAG,EAAE,KAAK,EAAE,EAAE;AACvD,SAAO,SAAS,MAAM,IAAI,SAAS,QAAQ,OAAO,CAAC;AACrD;;;ACxPO,SAAS,kBACd,SACA,YACA,OAAyE,CAAC,GAC9D;AACZ,QAAM,SAAS,KAAK,YAAY;AAChC,QAAM,YAAY,KAAK,eAAe;AACtC,QAAM,SAAS,QAAQ;AACvB,MAAI,CAAC,OAAQ,QAAO,MAAM;AAAA,EAAC;AAE3B,QAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,SAAO,MAAM,UACX;AACF,aAAW,sBAAsB,eAAe,MAAM;AAEtD,QAAM,QAAQ,KAAK,aAAa,aAAa,QAAQ,KAAK,UAAU,IAAI;AACxE,MAAI,OAAO;AACT,UAAM,QAAQ,OAAO,KAAK;AAC1B,QAAI,OAAO,SAAS,KAAK,KAAK,QAAQ,KAAK,QAAQ,GAAG;AACpD,cAAQ,MAAM,OAAO,GAAG,QAAQ,EAAE;AAClC,iBAAW,MAAM,OAAO,IAAI,IAAI,SAAS,EAAE;AAAA,IAC7C;AAAA,EACF;AAEA,MAAI,WAAW;AAEf,QAAM,SAAS,CAAC,YAAoB;AAClC,UAAM,OAAO,OAAO,sBAAsB;AAC1C,UAAM,IAAI,UAAU,KAAK;AACzB,UAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,IAAI,MAAM,IAAI,KAAK,MAAM,CAAC;AAC5D,UAAM,QAAQ,QAAQ,KAAK;AAC3B,UAAM,WAAW,KAAK,SAAS,QAAQ,OAAO;AAC9C,QAAI,QAAQ,UAAU,WAAW,UAAW;AAC5C,YAAQ,MAAM,OAAO,GAAG,QAAQ,EAAE;AAClC,eAAW,MAAM,OAAO,IAAI,IAAI,SAAS,EAAE;AAC3C,QAAI,KAAK,WAAY,cAAa,QAAQ,KAAK,YAAY,OAAO,KAAK,CAAC;AAAA,EAC1E;AAEA,QAAM,OAAO,MAAM;AACjB,eAAW;AACX,aAAS,KAAK,MAAM,SAAS;AAAA,EAC/B;AAEA,SAAO,iBAAiB,eAAe,CAAC,MAAM;AAC5C,eAAW;AACX,WAAO,kBAAkB,EAAE,SAAS;AACpC,aAAS,KAAK,MAAM,SAAS;AAAA,EAC/B,CAAC;AACD,SAAO,iBAAiB,eAAe,CAAC,MAAM;AAC5C,QAAI,SAAU,QAAO,EAAE,OAAO;AAAA,EAChC,CAAC;AACD,SAAO,iBAAiB,aAAa,IAAI;AACzC,SAAO,iBAAiB,iBAAiB,IAAI;AAE7C,SAAO,MAAM,OAAO,OAAO;AAC7B;;;AC3CO,IAAM,eAAN,MAAmB;AAAA,EAChB,SAAsB,CAAC;AAAA,EACvB,YAAY,oBAAI,IAAuB;AAAA,EACvC,UAAU;AAAA,EAClB,gBAAgB;AAAA,EAChB,cAAc;AAAA,EAEd,SAAS,OAAwB;AAC/B,QAAI,KAAK,OAAO,SAAS,KAAK,EAAG;AACjC,SAAK,OAAO,KAAK,KAAK;AACtB,UAAM,UAAU,EAAE,mCAAmC,CAAC,UAAU;AAC9D,UAAI,KAAK,WAAW,CAAC,MAAO;AAC5B,YAAM,KAAK,MAAM,UAAU,EAAE,gBAAgB;AAC7C,UAAI,MAAM,OAAO,GAAG,SAAS,YAAY,OAAO,GAAG,OAAO,UAAU;AAClE,aAAK,gBAAgB,GAAG,OAAO;AAC/B,aAAK,cAAc,GAAG,KAAK;AAAA,MAC7B;AACA,WAAK,SAAS,OAAO,KAAK;AAAA,IAC5B,CAAC;AAAA,EACH;AAAA,EAEA,mBAAmB,UAAyC;AAC1D,SAAK,UAAU,IAAI,QAAQ;AAC3B,WAAO,MAAM,KAAK,UAAU,OAAO,QAAQ;AAAA,EAC7C;AAAA,EAEA,iBAAiB,QAAgB,MAAoB;AACnD,SAAK,gBAAgB;AACrB,SAAK,cAAc;AACnB,SAAK,KAAK;AAAA,EACZ;AAAA,EAEQ,SAAS,QAAmB,OAAqB;AACvD,SAAK,UAAU;AACf,eAAW,SAAS,KAAK,QAAQ;AAC/B,UAAI,UAAU,QAAQ;AACpB,cAAM,UAAU,EAAE,uBAAuB,KAAK;AAAA,MAChD;AAAA,IACF;AACA,SAAK,UAAU;AACf,SAAK,KAAK;AAAA,EACZ;AAAA,EAEQ,OAAa;AACnB,UAAM,QAAwB;AAAA,MAC5B,eAAe,KAAK;AAAA,MACpB,aAAa,KAAK;AAAA,MAClB,OAAO;AAAA,MACP,QAAQ;AAAA,IACV;AACA,eAAW,KAAK,KAAK,UAAW,GAAE,KAAK;AAAA,EACzC;AACF;;;AJrBA,SAASC,cAAa,KAA2B;AAC/C,SAAO,KAAK,MAAM,MAAM,GAAI;AAC9B;AAEA,SAAS,YAAY,GAAyB;AAC5C,SAAO,EAAE,MAAMA,cAAa,EAAE,CAAC,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,KAAK,EAAE,GAAG,OAAO,EAAE,EAAE;AAC/E;AAEA,SAAS,YAAY,GAAqC;AACxD,SAAO,EAAE,MAAMA,cAAa,EAAE,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE;AACpD;AAEO,IAAM,mBAAN,MAAuB;AAAA,EACnB,MAAM,IAAI,aAAa;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT;AAAA,EACA,gBAA0C;AAAA,EAC1C,OAAO;AAAA,EACP,WAAW;AAAA,EACF;AAAA,EACT,YAAY,oBAAI,IAAiB;AAAA,EACjC,gBAAgB;AAAA,EAChB,kBAA0C;AAAA,EAElD,YAAY,MAA+B;AACzC,SAAK,kBAAkB,KAAK,mBAAmB;AAC/C,SAAK,OAAO,KAAK,UAAU;AAC3B,SAAK,WAAW,KAAK,YAAY;AACjC,UAAM,SAAS,KAAK,eAAe,KAAK,IAAI;AAC5C,UAAM,OAAO,YAAY,KAAK,UAAU,KAAK,IAAI;AAEjD,UAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,WAAO,MAAM,UAAU;AACvB,UAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,UAAM,MAAM,UAAU;AAEtB,SAAK,UAAU,MAAM,UACnB;AACF,SAAK,UAAU,OAAO,QAAQ,KAAK;AACnC,sBAAkB,QAAQ,OAAO,EAAE,YAAY,4BAA4B,CAAC;AAE5E,SAAK,YAAYC,aAAY,QAAQ,EAAE,QAAQ,MAAM,UAAU,KAAK,CAAC;AACrE,SAAK,cAAcA,aAAY,OAAO;AAAA,MACpC;AAAA,MACA;AAAA,MACA,UAAU;AAAA,MACV,iBAAiB,EAAE,cAAc,EAAE,KAAK,KAAK,QAAQ,EAAE,EAAE;AAAA,IAC3D,CAAC;AAED,QAAI,KAAK,cAAc,OAAO;AAC5B,WAAK,UAAU,WAAW,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC;AAAA,IAC7D;AAEA,SAAK,aAAa,KAAK,UAAU,UAAU,mBAAmB;AAAA,MAC5D,SAAS;AAAA,MACT,WAAW;AAAA,MACX,eAAe;AAAA,MACf,aAAa;AAAA,MACb,eAAe;AAAA,IACjB,CAAC;AACD,SAAK,WAAW,KAAK,UAAU,UAAUC,aAAY;AAAA,MACnD,OAAO;AAAA,MACP,WAAW;AAAA,MACX,OAAO;AAAA,IACT,CAAC;AACD,SAAK,eAAe,KAAK,YAAY,UAAUC,kBAAiB;AAAA,MAC9D,OAAO;AAAA,MACP,aAAa,EAAE,MAAM,SAAS;AAAA,IAChC,CAAC;AACD,SAAK,cAAc,KAAK,YAAY,UAAUD,aAAY;AAAA,MACxD,OAAO;AAAA,MACP,WAAW;AAAA,MACX,OAAO;AAAA,IACT,CAAC;AAED,SAAK,IAAI,SAAS,KAAK,SAAS;AAChC,SAAK,IAAI,SAAS,KAAK,WAAW;AAElC,SAAK,gBAAgB,KAAK;AAC1B,SAAK,kBAAkB,KAAK,mBAAmB;AAC/C,SAAK,aAAa,KAAK,qBAAqB;AAE5C,SAAK,YAAY,MAAM;AAAA,EACzB;AAAA,EAEA,SAAS,OAA+B;AACtC,SAAK,OAAO,UAAU;AACtB,UAAM,SAAS,KAAK,eAAe,KAAK,IAAI;AAC5C,UAAM,OAAO,YAAY,KAAK,UAAU,KAAK,IAAI;AACjD,SAAK,UAAU,aAAa,EAAE,QAAQ,KAAK,CAAC;AAC5C,SAAK,YAAY,aAAa,EAAE,QAAQ,KAAK,CAAC;AAC9C,SAAK,YAAY,SAAS,KAAK;AAAA,EACjC;AAAA,EAEA,mBAAmB,QAAsC;AACvD,SAAK,kBAAkB;AACvB,QAAI,CAAC,QAAQ;AACX,WAAK,aAAa;AAClB,WAAK,SAAS,QAAQ,CAAC,CAAC;AACxB,WAAK,YAAY,QAAQ,CAAC,CAAC;AAC3B;AAAA,IACF;AACA,QAAI,CAAC,KAAK,WAAY,MAAK,aAAa,KAAK,qBAAqB;AAClE,UAAM,OAAO,CAAC,GAAG,KAAK,UAAU,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC;AAClE,SAAK,YAAY,UAAU,MAAM;AACjC,QAAI,KAAK,SAAS,GAAG;AACnB,WAAK,SAAS,QAAQ,cAAc,MAAM,OAAO,UAAU,OAAO,MAAM,CAAC;AACzE,WAAK,YAAY,QAAQ,iBAAiB,MAAM,OAAO,WAAW,CAAC;AACnE,WAAK,YAAY,QAAQ,IAAI;AAAA,IAC/B;AAAA,EACF;AAAA,EAEA,YAAY,MAAqB;AAC/B,SAAK,WAAW;AAChB,UAAM,OAAO,YAAY,MAAM,KAAK,IAAI;AACxC,SAAK,UAAU,aAAa,EAAE,KAAK,CAAC;AACpC,SAAK,YAAY,aAAa,EAAE,KAAK,CAAC;AACtC,SAAK,YAAY,YAAY,IAAI;AAAA,EACnC;AAAA,EAEA,QAAQ,MAAa,MAAuB;AAC1C,UAAM,aAAa,gBAAgB,MAAM,KAAK,eAAe;AAC7D,SAAK,YAAY,IAAI,IAAI,WAAW,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;AAExD,UAAM,UAA6B,CAAC;AACpC,UAAM,OAAsC,CAAC;AAC7C,UAAM,SAAS,IAAI,IAAI,QAAQ,CAAC,CAAC;AACjC,UAAM,YAAY,oBAAI,IAAY;AAElC,aAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,YAAM,IAAI,WAAW,CAAC;AACtB,YAAM,OAAOF,cAAa,EAAE,CAAC;AAC7B,UAAI,UAAU,IAAI,IAAI,EAAG;AACzB,gBAAU,IAAI,IAAI;AAClB,UAAI,IAAI,KAAK,OAAO,IAAI,EAAE,CAAC,GAAG;AAAA,MAE9B;AACA,cAAQ,KAAK,YAAY,CAAC,CAAC;AAC3B,WAAK,KAAK,YAAY,CAAC,CAAC;AAAA,IAC1B;AAEA,SAAK,WAAW,QAAQ,OAAO;AAC/B,SAAK,aAAa,QAAQ,IAAI;AAC9B,QAAI,KAAK,iBAAiB;AACxB,WAAK,SAAS;AAAA,QACZ,cAAc,YAAY,KAAK,gBAAgB,UAAU,KAAK,gBAAgB,MAAM;AAAA,MACtF;AACA,WAAK,YAAY,QAAQ,iBAAiB,YAAY,KAAK,gBAAgB,WAAW,CAAC;AACvF,WAAK,YAAY,QAAQ,UAAU;AAAA,IACrC,OAAO;AACL,WAAK,SAAS,QAAQ,CAAC,CAAC;AACxB,WAAK,YAAY,QAAQ,CAAC,CAAC;AAAA,IAC7B;AAEA,QAAI,WAAW,SAAS,GAAG;AACzB,WAAK,cAAc;AACnB,UAAI,CAAC,KAAK,eAAe;AACvB,aAAK,UAAU,UAAU,EAAE,WAAW;AACtC,aAAK,YAAY,UAAU,EAAE,WAAW;AACxC,aAAK,YAAY,WAAW;AAC5B,aAAK,gBAAgB;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,mBAAmB,UAAkE;AACnF,UAAM,UAAU,CAAC,UAAkC;AACjD,UAAI,MAAM,QAAQ,QAAQ,CAAC,MAAM,OAAO;AACtC,iBAAS,IAAI;AACb;AAAA,MACF;AACA,YAAM,MAAM,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,MAAO;AACjE,UAAI,OAAO,MAAM;AACf,iBAAS,IAAI;AACb;AAAA,MACF;AACA,YAAM,QAAQ,KAAK,WAAW,kBAAkB,MAAM,MAAM,CAAC,KAAK;AAClE,YAAM,MAAM,KAAK,UAAU,IAAI,GAAG,KAAK,KAAK,eAAe,GAAG;AAC9D,eAAS;AAAA,QACP,MAAM;AAAA,QACN;AAAA,QACA,OAAO,MACH,EAAE,GAAG,IAAI,GAAG,GAAG,IAAI,GAAG,GAAG,IAAI,GAAG,GAAG,IAAI,GAAG,GAAG,IAAI,EAAE,IACnD;AAAA,MACN,CAAC;AAAA,IACH;AACA,SAAK,UAAU,uBAAuB,OAAO;AAC7C,WAAO,MAAM,KAAK,UAAU,yBAAyB,OAAO;AAAA,EAC9D;AAAA,EAEQ,eAAe,KAAyB;AAC9C,QAAI,OAAmB;AACvB,QAAI,SAAS;AACb,eAAW,KAAK,KAAK,UAAU,OAAO,GAAG;AACvC,YAAM,KAAK,KAAK,IAAI,EAAE,IAAI,GAAG;AAC7B,UAAI,KAAK,QAAQ;AACf,iBAAS;AACT,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO,SAAS,OAAU,OAAO;AAAA,EACnC;AAAA,EAEQ,uBAAkD;AACxD,QAAI,CAAC,KAAK,iBAAiB,CAAC,KAAK,gBAAiB,QAAO;AACzD,WAAO,IAAI,mBAAmB,KAAK,eAAe,KAAK,KAAK;AAAA,MAC1D,OAAO,KAAK,OAAO,SAAS;AAAA,MAC5B,UAAU,KAAK;AAAA,MACf,QAAQ,KAAK;AAAA,IACf,CAAC;AAAA,EACH;AAAA,EAEA,iBAAuB;AACrB,SAAK,gBAAgB;AACrB,SAAK,IAAI,gBAAgB;AACzB,SAAK,IAAI,cAAc;AAAA,EACzB;AAAA;AAAA,EAGA,YAAkB;AAChB,SAAK,YAAY,oBAAI,IAAI;AACzB,SAAK,WAAW,QAAQ,CAAC,CAAC;AAC1B,SAAK,SAAS,QAAQ,CAAC,CAAC;AACxB,SAAK,aAAa,QAAQ,CAAC,CAAC;AAC5B,SAAK,YAAY,QAAQ,CAAC,CAAC;AAC3B,SAAK,YAAY,UAAU;AAAA,EAC7B;AAAA,EAEA,aAAmB;AACjB,SAAK,UAAU,UAAU,EAAE,WAAW;AACtC,SAAK,YAAY,UAAU,EAAE,WAAW;AACxC,SAAK,YAAY,WAAW;AAC5B,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,mBAAyB;AACvB,SAAK,UAAU,UAAU,EAAE,iBAAiB;AAC5C,SAAK,YAAY,UAAU,EAAE,iBAAiB;AAC9C,SAAK,YAAY,iBAAiB;AAAA,EACpC;AAAA,EAEA,YAAY,SAAwB;AAClC,SAAK,UAAU,WAAW,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,IAAI,EAAE,CAAC;AAAA,EAC3E;AAAA,EAEA,SAAe;AACb,SAAK,cAAc;AACnB,SAAK,gBAAgB;AACrB,SAAK,YAAY,OAAO;AAAA,EAC1B;AAAA,EAEA,mBAA6C;AAC3C,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,QAAQ,KAA4B;AAClC,UAAM,QAAQ,KAAK,UAAU,UAAU,EAAE,iBAAiBA,cAAa,GAAG,CAAC;AAC3E,QAAI,SAAS,KAAM,QAAO;AAC1B,WAAO,QAAQ;AAAA,EACjB;AAAA,EAEA,SAAS,OAA8B;AACrC,UAAM,QAAQ,KAAK,WAAW,kBAAkB,KAAK;AACrD,QAAI,SAAS,KAAM,QAAO;AAC1B,WAAO,QAAQ;AAAA,EACjB;AAAA,EAEA,QAAQ,GAA0B;AAChC,UAAM,IAAI,KAAK,UAAU,UAAU,EAAE,iBAAiB,IAAI,gBAAgB;AAC1E,QAAI,KAAK,KAAM,QAAO;AACtB,WAAO,OAAO,CAAC,IAAI;AAAA,EACrB;AAAA,EAEA,SAAS,GAA0B;AACjC,UAAM,IAAI,KAAK,WAAW,kBAAkB,IAAI,gBAAgB;AAChE,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,UAAgB;AACd,SAAK,UAAU,OAAO;AACtB,SAAK,YAAY,OAAO;AACxB,SAAK,YAAY,QAAQ;AACzB,SAAK,eAAe,OAAO;AAAA,EAC7B;AAAA,EAEQ,YAAY,QAAqB;AACvC,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,MAAM,UACX;AACF,WAAO,MAAM,WAAW;AACxB,WAAO,YAAY,MAAM;AACzB,SAAK,gBAAgB;AACrB,SAAK,gBAAgB;AACrB,SAAK,IAAI,mBAAmB,MAAM;AAChC,WAAK,gBAAgB;AAAA,IACvB,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,wBAAwB,MAA6B;AACnD,QAAI,KAAK,cAAe,MAAK,cAAc,MAAM,gBAAgB;AAAA,EACnE;AAAA,EAEQ,kBAAkB;AACxB,QAAI,CAAC,KAAK,eAAe,cAAe;AACxC,UAAM,SAAS,KAAK,cAAc;AAClC,QAAI,OAAO,qBAAqB,KAAK,eAAe;AAClD,aAAO,YAAY,KAAK,aAAa;AAAA,IACvC;AACA,UAAM,OAAO,OAAO,sBAAsB;AAC1C,SAAK,cAAc,QAAQ,KAAK,QAAQ;AACxC,SAAK,cAAc,SAAS,KAAK,SAAS;AAAA,EAC5C;AAAA,EAEQ,gBAAsB;AAC5B,UAAM,SAAS,KAAK,UAAU,aAAa,EAAE;AAC7C,UAAM,QAAQ,KAAK,YAAY,aAAa,EAAE;AAC9C,QAAI,QAAQ;AACV,YAAM,IAAI,OAAO;AACjB,YAAM,IAAI,OAAO;AACjB,UAAI,IAAI,KAAK,IAAI,EAAG,MAAK,UAAU,OAAO,GAAG,CAAC;AAAA,IAChD;AACA,QAAI,OAAO;AACT,YAAM,IAAI,MAAM;AAChB,YAAM,IAAI,MAAM;AAChB,UAAI,IAAI,KAAK,IAAI,EAAG,MAAK,YAAY,OAAO,GAAG,CAAC;AAAA,IAClD;AAAA,EACF;AAAA,EAEQ,eAAe,MAAe;AACpC,WAAO;AAAA,MACL,YAAY,EAAE,MAAMI,WAAU,OAAO,OAAO,OAAO,YAAY,UAAU;AAAA,MACzE,WAAW,OAAO,YAAY;AAAA,IAChC;AAAA,EACF;AACF;","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
|
+
}
|