@baron1996/klinecharts-adapter 0.1.0

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.
Files changed (70) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +22 -0
  3. package/README.md +10 -0
  4. package/dist/adapter.d.ts +57 -0
  5. package/dist/adapter.d.ts.map +1 -0
  6. package/dist/adapter.js +262 -0
  7. package/dist/conversion/chart-options.d.ts +5 -0
  8. package/dist/conversion/chart-options.d.ts.map +1 -0
  9. package/dist/conversion/chart-options.js +123 -0
  10. package/dist/conversion/id-map.d.ts +12 -0
  11. package/dist/conversion/id-map.d.ts.map +1 -0
  12. package/dist/conversion/id-map.js +47 -0
  13. package/dist/conversion/indicators.d.ts +8 -0
  14. package/dist/conversion/indicators.d.ts.map +1 -0
  15. package/dist/conversion/indicators.js +62 -0
  16. package/dist/conversion/overlays.d.ts +33 -0
  17. package/dist/conversion/overlays.d.ts.map +1 -0
  18. package/dist/conversion/overlays.js +301 -0
  19. package/dist/conversion/panes.d.ts +6 -0
  20. package/dist/conversion/panes.d.ts.map +1 -0
  21. package/dist/conversion/panes.js +69 -0
  22. package/dist/conversion/viewport.d.ts +5 -0
  23. package/dist/conversion/viewport.d.ts.map +1 -0
  24. package/dist/conversion/viewport.js +6 -0
  25. package/dist/engine.d.ts +10 -0
  26. package/dist/engine.d.ts.map +1 -0
  27. package/dist/engine.js +37 -0
  28. package/dist/errors.d.ts +2 -0
  29. package/dist/errors.d.ts.map +1 -0
  30. package/dist/errors.js +1 -0
  31. package/dist/extensions/arrow.d.ts +6 -0
  32. package/dist/extensions/arrow.d.ts.map +1 -0
  33. package/dist/extensions/arrow.js +31 -0
  34. package/dist/extensions/callout.d.ts +6 -0
  35. package/dist/extensions/callout.d.ts.map +1 -0
  36. package/dist/extensions/callout.js +33 -0
  37. package/dist/extensions/cross-line.d.ts +6 -0
  38. package/dist/extensions/cross-line.d.ts.map +1 -0
  39. package/dist/extensions/cross-line.js +24 -0
  40. package/dist/extensions/rectangle.d.ts +6 -0
  41. package/dist/extensions/rectangle.d.ts.map +1 -0
  42. package/dist/extensions/rectangle.js +26 -0
  43. package/dist/extensions/register.d.ts +8 -0
  44. package/dist/extensions/register.d.ts.map +1 -0
  45. package/dist/extensions/register.js +26 -0
  46. package/dist/extensions/text.d.ts +6 -0
  47. package/dist/extensions/text.d.ts.map +1 -0
  48. package/dist/extensions/text.js +24 -0
  49. package/dist/index.d.ts +8 -0
  50. package/dist/index.d.ts.map +1 -0
  51. package/dist/index.js +7 -0
  52. package/dist/registry/indicators.d.ts +4 -0
  53. package/dist/registry/indicators.d.ts.map +1 -0
  54. package/dist/registry/indicators.js +33 -0
  55. package/dist/registry/overlays.d.ts +6 -0
  56. package/dist/registry/overlays.d.ts.map +1 -0
  57. package/dist/registry/overlays.js +33 -0
  58. package/dist/static-data-loader.d.ts +6 -0
  59. package/dist/static-data-loader.d.ts.map +1 -0
  60. package/dist/static-data-loader.js +12 -0
  61. package/dist/version.d.ts +7 -0
  62. package/dist/version.d.ts.map +1 -0
  63. package/dist/version.js +6 -0
  64. package/licenses/KLineCharts-LICENSE +201 -0
  65. package/licenses/KLineCharts-NOTICE +5 -0
  66. package/licenses/Noto-Sans-SC-OFL-1.1 +93 -0
  67. package/licenses/Tabler-Icons-LICENSE +21 -0
  68. package/licenses/TradingView-Lightweight-Charts-LICENSE +201 -0
  69. package/licenses/fflate-LICENSE +21 -0
  70. package/package.json +44 -0
@@ -0,0 +1,123 @@
1
+ function formatDate({ dateTimeFormat, timestamp }, template) {
2
+ const parts = new Map(dateTimeFormat
3
+ .formatToParts(timestamp)
4
+ .filter((part) => part.type !== 'literal')
5
+ .map((part) => [part.type, part.value]));
6
+ const date = `${parts.get('year')}-${parts.get('month')}-${parts.get('day')}`;
7
+ if (template === 'yyyy-MM-dd') {
8
+ return date;
9
+ }
10
+ const time = `${parts.get('hour')}:${parts.get('minute')}`;
11
+ if (template === 'yyyy-MM-dd HH:mm') {
12
+ return `${date} ${time}`;
13
+ }
14
+ return `${date} ${time}:${parts.get('second')}`;
15
+ }
16
+ function compact(value, divisor, suffix) {
17
+ const numeric = Number(value);
18
+ if (!Number.isFinite(numeric)) {
19
+ return String(value);
20
+ }
21
+ return `${Number((numeric / divisor).toFixed(3))}${suffix}`;
22
+ }
23
+ function formatLargeNumber(value, mode) {
24
+ const numeric = Number(value);
25
+ if (!Number.isFinite(numeric) || mode === 'plain') {
26
+ return String(value);
27
+ }
28
+ const absolute = Math.abs(numeric);
29
+ if (mode === 'chinese') {
30
+ if (absolute >= 100_000_000) {
31
+ return compact(value, 100_000_000, '亿');
32
+ }
33
+ if (absolute >= 10_000) {
34
+ return compact(value, 10_000, '万');
35
+ }
36
+ return String(value);
37
+ }
38
+ if (absolute >= 1_000_000_000) {
39
+ return compact(value, 1_000_000_000, 'B');
40
+ }
41
+ if (absolute >= 1_000_000) {
42
+ return compact(value, 1_000_000, 'M');
43
+ }
44
+ if (absolute >= 1_000) {
45
+ return compact(value, 1_000, 'K');
46
+ }
47
+ return String(value);
48
+ }
49
+ function chartStyles(chart) {
50
+ const text = {
51
+ color: chart.layout.textColor,
52
+ size: chart.layout.fontSize,
53
+ family: chart.layout.fontFamily,
54
+ };
55
+ return {
56
+ grid: {
57
+ horizontal: {
58
+ show: true,
59
+ color: chart.grid.horizontalColor,
60
+ },
61
+ vertical: {
62
+ show: true,
63
+ color: chart.grid.verticalColor,
64
+ },
65
+ },
66
+ candle: {
67
+ type: chart.candle.type,
68
+ bar: {
69
+ upColor: chart.candle.upColor,
70
+ downColor: chart.candle.downColor,
71
+ noChangeColor: chart.candle.noChangeColor,
72
+ upBorderColor: chart.candle.upBorderColor,
73
+ downBorderColor: chart.candle.downBorderColor,
74
+ noChangeBorderColor: chart.candle.noChangeBorderColor,
75
+ upWickColor: chart.candle.upWickColor,
76
+ downWickColor: chart.candle.downWickColor,
77
+ noChangeWickColor: chart.candle.noChangeWickColor,
78
+ },
79
+ area: {
80
+ point: {
81
+ animation: false,
82
+ },
83
+ },
84
+ },
85
+ xAxis: { tickText: text },
86
+ yAxis: { tickText: text },
87
+ };
88
+ }
89
+ /** 将纯数据 ChartConfig 转换为受控的 KLineCharts 初始化选项。 */
90
+ export function toKLineChartsOptions(chart) {
91
+ return {
92
+ locale: chart.locale,
93
+ timezone: chart.timezone,
94
+ styles: chartStyles(chart),
95
+ formatter: {
96
+ formatDate: (params) => formatDate(params, chart.dateFormat),
97
+ formatBigNumber: (value) => formatLargeNumber(value, chart.largeNumberFormat),
98
+ },
99
+ thousandsSeparator: {
100
+ sign: chart.thousandsSeparator,
101
+ },
102
+ decimalFold: {
103
+ threshold: chart.decimalFold.threshold,
104
+ format: chart.decimalFold.enabled
105
+ ? (value) => {
106
+ const source = String(value);
107
+ const expression = new RegExp(`\\.0{${chart.decimalFold.threshold},}[1-9][0-9]*$`);
108
+ if (!expression.test(source)) {
109
+ return source;
110
+ }
111
+ const [integer, decimal = ''] = source.split('.');
112
+ const zeroCount = decimal.match(/^0*/)?.[0].length ?? 0;
113
+ return `${integer}.0{${zeroCount}}${decimal.slice(zeroCount)}`;
114
+ }
115
+ : (value) => String(value),
116
+ },
117
+ zoomAnchor: chart.zoomAnchor === 'right' ? 'last_bar' : 'cursor',
118
+ hotkey: {
119
+ enabled: false,
120
+ exclude: [],
121
+ },
122
+ };
123
+ }
@@ -0,0 +1,12 @@
1
+ import type { ChartScene } from '@baron1996/kline-scene-schema';
2
+ import type { Chart } from 'klinecharts';
3
+ export interface EngineIdMap {
4
+ readonly paneToEngine: ReadonlyMap<string, string>;
5
+ readonly paneFromEngine: ReadonlyMap<string, string>;
6
+ readonly yAxisToEngine: ReadonlyMap<string, string>;
7
+ readonly yAxisFromEngine: ReadonlyMap<string, string>;
8
+ }
9
+ /** 依据场景数组位置建立稳定且双向的内部 ID 映射。 */
10
+ export declare function createEngineIdMap(scene: ChartScene, chart: Chart): EngineIdMap;
11
+ export declare function requireMappedId(map: ReadonlyMap<string, string>, sceneId: string, path: string, label: string): string;
12
+ //# sourceMappingURL=id-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id-map.d.ts","sourceRoot":"","sources":["../../src/conversion/id-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,KAAK,EAAE,KAAK,EAAS,MAAM,aAAa,CAAC;AAOhD,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrD,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpD,QAAQ,CAAC,eAAe,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtD;AAMD,gCAAgC;AAChC,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,WAAW,CAqC9E;AAED,wBAAgB,eAAe,CAC9B,GAAG,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACX,MAAM,CAMR"}
@@ -0,0 +1,47 @@
1
+ import { SceneError } from '@baron1996/kline-scene-schema';
2
+ function invert(source) {
3
+ return new Map(Array.from(source, ([sceneId, engineId]) => [engineId, sceneId]));
4
+ }
5
+ /** 依据场景数组位置建立稳定且双向的内部 ID 映射。 */
6
+ export function createEngineIdMap(scene, chart) {
7
+ const paneToEngine = new Map();
8
+ const yAxisToEngine = new Map();
9
+ for (let paneIndex = 0; paneIndex < scene.panes.length; paneIndex++) {
10
+ const pane = scene.panes[paneIndex];
11
+ if (pane === undefined) {
12
+ continue;
13
+ }
14
+ const enginePaneId = pane.kind === 'candle' ? 'candle_pane' : `baron_pane_${paneIndex}`;
15
+ paneToEngine.set(pane.id, enginePaneId);
16
+ for (let axisIndex = 0; axisIndex < pane.yAxes.length; axisIndex++) {
17
+ const axis = pane.yAxes[axisIndex];
18
+ if (axis === undefined) {
19
+ continue;
20
+ }
21
+ if (pane.kind === 'candle' && axis.role === 'primary') {
22
+ const actual = chart.getYAxes({ paneId: enginePaneId });
23
+ const defaultAxis = actual[0];
24
+ if (defaultAxis === undefined) {
25
+ throw new SceneError('RUNTIME_INIT_FAILED', `/panes/${paneIndex}/yAxes/${axisIndex}`, 'KLineCharts did not create the candle Pane primary Y-axis.');
26
+ }
27
+ yAxisToEngine.set(axis.id, defaultAxis.id);
28
+ }
29
+ else {
30
+ yAxisToEngine.set(axis.id, `baron_y_${paneIndex}_${axisIndex}`);
31
+ }
32
+ }
33
+ }
34
+ return {
35
+ paneToEngine,
36
+ paneFromEngine: invert(paneToEngine),
37
+ yAxisToEngine,
38
+ yAxisFromEngine: invert(yAxisToEngine),
39
+ };
40
+ }
41
+ export function requireMappedId(map, sceneId, path, label) {
42
+ const engineId = map.get(sceneId);
43
+ if (engineId === undefined) {
44
+ throw new SceneError('INVALID_REFERENCE', path, `${label} ${sceneId} is not mapped.`);
45
+ }
46
+ return engineId;
47
+ }
@@ -0,0 +1,8 @@
1
+ import type { SceneIndicator, ScenePane } from '@baron1996/kline-scene-schema';
2
+ import type { Chart, DeepPartial, IndicatorCreate, IndicatorStyle } from 'klinecharts';
3
+ import type { EngineIdMap } from './id-map.js';
4
+ /** 将协议内指标样式映射为 KLineCharts 的受控样式子集。 */
5
+ export declare function toKLineChartsIndicatorStyles(styles: SceneIndicator['styles']): DeepPartial<IndicatorStyle>;
6
+ export declare function toIndicatorCreate(indicator: SceneIndicator, idMap: EngineIdMap, path: string): IndicatorCreate;
7
+ export declare function createPaneIndicators(chart: Chart, pane: ScenePane, paneIndex: number, idMap: EngineIdMap): void;
8
+ //# sourceMappingURL=indicators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indicators.d.ts","sourceRoot":"","sources":["../../src/conversion/indicators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,cAAc,EACd,SAAS,EACT,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EACX,KAAK,EACL,WAAW,EACX,eAAe,EACf,cAAc,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAc/C,uCAAuC;AACvC,wBAAgB,4BAA4B,CAC3C,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,GAC9B,WAAW,CAAC,cAAc,CAAC,CAiB7B;AAED,wBAAgB,iBAAiB,CAChC,SAAS,EAAE,cAAc,EACzB,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,GACV,eAAe,CAejB;AAED,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,SAAS,EACf,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,WAAW,GAChB,IAAI,CAqBN"}
@@ -0,0 +1,62 @@
1
+ import { SceneError } from '@baron1996/kline-scene-schema';
2
+ import { requireMappedId } from './id-map.js';
3
+ import { isSupportedIndicator } from '../registry/indicators.js';
4
+ function lineStyle(style) {
5
+ return {
6
+ color: style.color,
7
+ size: style.size,
8
+ style: style.style === 'solid' ? 'solid' : 'dashed',
9
+ dashedValue: style.style === 'dotted' ? [1, 2] : [4, 4],
10
+ smooth: false,
11
+ };
12
+ }
13
+ /** 将协议内指标样式映射为 KLineCharts 的受控样式子集。 */
14
+ export function toKLineChartsIndicatorStyles(styles) {
15
+ return {
16
+ lines: styles.lines.map(lineStyle),
17
+ bars: styles.bars.map((style) => ({
18
+ style: 'fill',
19
+ upColor: style.upColor,
20
+ downColor: style.downColor,
21
+ noChangeColor: style.noChangeColor,
22
+ })),
23
+ circles: styles.circles.map((style) => ({
24
+ style: 'fill',
25
+ upColor: style.color,
26
+ downColor: style.color,
27
+ noChangeColor: style.color,
28
+ borderRadius: style.radius,
29
+ })),
30
+ };
31
+ }
32
+ export function toIndicatorCreate(indicator, idMap, path) {
33
+ if (!isSupportedIndicator(indicator.name)) {
34
+ throw new SceneError('UNKNOWN_INDICATOR', `${path}/name`, `Unsupported Indicator: ${indicator.name}`);
35
+ }
36
+ return {
37
+ id: indicator.id,
38
+ name: indicator.name,
39
+ paneId: requireMappedId(idMap.paneToEngine, indicator.paneId, `${path}/paneId`, 'Pane'),
40
+ yAxisId: requireMappedId(idMap.yAxisToEngine, indicator.yAxisId, `${path}/yAxisId`, 'Y-axis'),
41
+ calcParams: [...indicator.calcParams],
42
+ precision: indicator.precision,
43
+ visible: indicator.visible,
44
+ zLevel: indicator.zLevel,
45
+ styles: toKLineChartsIndicatorStyles(indicator.styles),
46
+ };
47
+ }
48
+ export function createPaneIndicators(chart, pane, paneIndex, idMap) {
49
+ const primaryAxisId = pane.yAxes.find((axis) => axis.role === 'primary')?.id;
50
+ const ordered = [...pane.indicators].sort((left, right) => {
51
+ const leftPrimary = left.yAxisId === primaryAxisId ? 0 : 1;
52
+ const rightPrimary = right.yAxisId === primaryAxisId ? 0 : 1;
53
+ return leftPrimary - rightPrimary;
54
+ });
55
+ for (const indicator of ordered) {
56
+ const originalIndex = pane.indicators.findIndex((candidate) => candidate.id === indicator.id);
57
+ const id = chart.createIndicator(toIndicatorCreate(indicator, idMap, `/panes/${paneIndex}/indicators/${originalIndex}`), true);
58
+ if (id === null || id !== indicator.id) {
59
+ throw new SceneError('RUNTIME_INIT_FAILED', `/panes/${paneIndex}/indicators/${originalIndex}`, `KLineCharts failed to create Indicator ${indicator.id}.`);
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,33 @@
1
+ import type { ChartScene, SceneOverlay } from '@baron1996/kline-scene-schema';
2
+ import type { Overlay, OverlayCreate } from 'klinecharts';
3
+ import type { EngineIdMap } from './id-map.js';
4
+ export interface OverlaySourceSnapshot {
5
+ readonly id: string;
6
+ readonly type: SceneOverlay['type'];
7
+ readonly paneId: string;
8
+ readonly groupId?: string;
9
+ readonly visible: boolean;
10
+ readonly locked: boolean;
11
+ readonly zLevel: number;
12
+ readonly mode: SceneOverlay['mode'];
13
+ readonly styles: SceneOverlay['styles'];
14
+ readonly metadata?: SceneOverlay['metadata'];
15
+ }
16
+ export interface OverlayDrawingSource extends OverlaySourceSnapshot {
17
+ readonly text?: string;
18
+ }
19
+ export interface EngineOverlayCallbacks {
20
+ readonly onDrawEnd?: NonNullable<OverlayCreate['onDrawEnd']>;
21
+ readonly onPressedMoveEnd?: NonNullable<OverlayCreate['onPressedMoveEnd']>;
22
+ readonly onRemoved?: NonNullable<OverlayCreate['onRemoved']>;
23
+ readonly onSelected?: NonNullable<OverlayCreate['onSelected']>;
24
+ }
25
+ export declare function toEngineOverlay(overlay: SceneOverlay, idMap: EngineIdMap, path: string, callbacks?: EngineOverlayCallbacks): OverlayCreate;
26
+ /** 创建尚无几何点的交互式 Overlay,不把临时状态写入 Scene。 */
27
+ export declare function toEngineOverlayDrawing(source: OverlayDrawingSource, idMap: EngineIdMap, callbacks: EngineOverlayCallbacks): OverlayCreate;
28
+ export declare function fromEngineOverlay(engine: Overlay, source: OverlaySourceSnapshot, idMap: EngineIdMap, path: string): SceneOverlay;
29
+ /** 在引擎内创建场景的全部标注。 */
30
+ export declare function createSceneOverlays(scene: ChartScene, chart: {
31
+ createOverlay(value: OverlayCreate): string | null | Array<string | null>;
32
+ }, idMap: EngineIdMap, callbacks?: (overlay: SceneOverlay) => EngineOverlayCallbacks): void;
33
+ //# sourceMappingURL=overlays.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../../src/conversion/overlays.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,UAAU,EACV,YAAY,EACZ,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAEX,OAAO,EACP,aAAa,EAGb,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;IAClE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;IAC7D,QAAQ,CAAC,gBAAgB,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC3E,QAAQ,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;IAC7D,QAAQ,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC;CAC/D;AAmGD,wBAAgB,eAAe,CAC9B,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,sBAA2B,GACpC,aAAa,CAwBf;AAED,0CAA0C;AAC1C,wBAAgB,sBAAsB,CACrC,MAAM,EAAE,oBAAoB,EAC5B,KAAK,EAAE,WAAW,EAClB,SAAS,EAAE,sBAAsB,GAC/B,aAAa,CAsBf;AA8DD,wBAAgB,iBAAiB,CAChC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,GACV,YAAY,CAoGd;AAED,qBAAqB;AACrB,wBAAgB,mBAAmB,CAClC,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE;IAAE,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;CAAE,EACpF,KAAK,EAAE,WAAW,EAClB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,sBAAsB,GAC3D,IAAI,CAiBN"}
@@ -0,0 +1,301 @@
1
+ import { SceneError } from '@baron1996/kline-scene-schema';
2
+ import { requireMappedId } from './id-map.js';
3
+ import { isSupportedOverlay } from '../registry/overlays.js';
4
+ function toLineStyle(style) {
5
+ return {
6
+ color: style.color,
7
+ size: style.size,
8
+ style: style.style === 'solid' ? 'solid' : 'dashed',
9
+ dashedValue: style.style === 'dotted' ? [1, 2] : [4, 4],
10
+ smooth: false,
11
+ };
12
+ }
13
+ function toOverlayStyles(styles) {
14
+ const borderStyle = styles.line.style === 'solid' ? 'solid' : 'dashed';
15
+ const borderDashedValue = styles.line.style === 'dotted' ? [1, 2] : [4, 4];
16
+ return {
17
+ line: toLineStyle(styles.line),
18
+ rect: {
19
+ style: 'stroke_fill',
20
+ color: styles.fill.color,
21
+ borderColor: styles.line.color,
22
+ borderSize: styles.line.size,
23
+ borderStyle,
24
+ borderDashedValue,
25
+ },
26
+ polygon: {
27
+ style: 'stroke_fill',
28
+ color: styles.fill.color,
29
+ borderColor: styles.line.color,
30
+ borderSize: styles.line.size,
31
+ borderStyle,
32
+ borderDashedValue,
33
+ },
34
+ text: {
35
+ style: 'stroke_fill',
36
+ color: styles.text.color,
37
+ size: styles.text.size,
38
+ family: styles.text.family,
39
+ weight: styles.text.weight,
40
+ backgroundColor: styles.text.backgroundColor,
41
+ borderColor: styles.text.borderColor,
42
+ borderSize: styles.line.size,
43
+ borderStyle,
44
+ borderDashedValue,
45
+ },
46
+ };
47
+ }
48
+ function toPoints(overlay) {
49
+ switch (overlay.type) {
50
+ case 'horizontalStraightLine':
51
+ case 'priceLine':
52
+ case 'simpleTag':
53
+ return [{ value: overlay.anchor.value }];
54
+ case 'verticalStraightLine':
55
+ return [{ timestamp: overlay.anchor.timestamp }];
56
+ case 'horizontalRayLine':
57
+ case 'horizontalSegment':
58
+ return [
59
+ { timestamp: overlay.startTimestamp, value: overlay.value },
60
+ { timestamp: overlay.endTimestamp, value: overlay.value },
61
+ ];
62
+ case 'verticalRayLine':
63
+ case 'verticalSegment':
64
+ return [
65
+ { timestamp: overlay.timestamp, value: overlay.startValue },
66
+ { timestamp: overlay.timestamp, value: overlay.endValue },
67
+ ];
68
+ case 'rayLine':
69
+ case 'segment':
70
+ case 'straightLine':
71
+ case 'fibonacciLine':
72
+ case 'priceChannelLine':
73
+ case 'parallelStraightLine':
74
+ case 'brush':
75
+ return structuredClone(overlay.points ?? []);
76
+ case 'simpleAnnotation':
77
+ case 'crossLine':
78
+ case 'callout':
79
+ case 'text':
80
+ return [structuredClone(overlay.point ?? {})];
81
+ case 'rectangle':
82
+ case 'arrow':
83
+ return [structuredClone(overlay.start ?? {}), structuredClone(overlay.end ?? {})];
84
+ }
85
+ }
86
+ function overlayText(overlay) {
87
+ switch (overlay.type) {
88
+ case 'simpleAnnotation':
89
+ case 'simpleTag':
90
+ case 'callout':
91
+ case 'text':
92
+ return overlay.text;
93
+ default:
94
+ return undefined;
95
+ }
96
+ }
97
+ export function toEngineOverlay(overlay, idMap, path, callbacks = {}) {
98
+ if (!isSupportedOverlay(overlay.type)) {
99
+ throw new SceneError('UNKNOWN_OVERLAY', `${path}/type`, `Unsupported Overlay: ${overlay.type}`);
100
+ }
101
+ const value = {
102
+ id: overlay.id,
103
+ name: overlay.type,
104
+ paneId: requireMappedId(idMap.paneToEngine, overlay.paneId, `${path}/paneId`, 'Pane'),
105
+ lock: overlay.locked,
106
+ visible: overlay.visible,
107
+ zLevel: overlay.zLevel,
108
+ mode: overlay.mode,
109
+ points: toPoints(overlay),
110
+ styles: toOverlayStyles(overlay.styles),
111
+ ...callbacks,
112
+ };
113
+ if (overlay.groupId !== undefined) {
114
+ value.groupId = overlay.groupId;
115
+ }
116
+ const text = overlayText(overlay);
117
+ if (text !== undefined) {
118
+ value.extendData = text;
119
+ }
120
+ return value;
121
+ }
122
+ /** 创建尚无几何点的交互式 Overlay,不把临时状态写入 Scene。 */
123
+ export function toEngineOverlayDrawing(source, idMap, callbacks) {
124
+ if (!isSupportedOverlay(source.type)) {
125
+ throw new SceneError('UNKNOWN_OVERLAY', '/overlays/type', `Unsupported Overlay: ${source.type}`);
126
+ }
127
+ const value = {
128
+ id: source.id,
129
+ name: source.type,
130
+ paneId: requireMappedId(idMap.paneToEngine, source.paneId, '/overlays/paneId', 'Pane'),
131
+ lock: source.locked,
132
+ visible: source.visible,
133
+ zLevel: source.zLevel,
134
+ mode: source.mode,
135
+ styles: toOverlayStyles(source.styles),
136
+ ...callbacks,
137
+ };
138
+ if (source.groupId !== undefined) {
139
+ value.groupId = source.groupId;
140
+ }
141
+ if (source.text !== undefined) {
142
+ value.extendData = source.text;
143
+ }
144
+ return value;
145
+ }
146
+ function requirePoint(points, index, requireTimestamp, requireValue, path) {
147
+ const point = points[index];
148
+ if (point === undefined ||
149
+ (requireTimestamp && !Number.isSafeInteger(point.timestamp)) ||
150
+ (requireValue && !Number.isFinite(point.value))) {
151
+ throw new SceneError('EXPORT_INVALID', path, 'KLineCharts returned an incomplete Overlay point.');
152
+ }
153
+ return {
154
+ timestamp: point.timestamp ?? 0,
155
+ value: point.value ?? 0,
156
+ };
157
+ }
158
+ function requireText(overlay, path) {
159
+ if (typeof overlay.extendData !== 'string') {
160
+ throw new SceneError('EXPORT_INVALID', path, 'KLineCharts returned an Overlay without text.');
161
+ }
162
+ return overlay.extendData;
163
+ }
164
+ function baseFromEngine(engine, source, idMap, path) {
165
+ const scenePaneId = idMap.paneFromEngine.get(engine.paneId);
166
+ if (scenePaneId === undefined) {
167
+ throw new SceneError('EXPORT_INVALID', `${path}/paneId`, 'Overlay uses an unmapped engine Pane.');
168
+ }
169
+ if (!isSupportedOverlay(engine.name) || engine.name !== source.type) {
170
+ throw new SceneError('EXPORT_INVALID', `${path}/type`, 'Overlay type changed outside the registry.');
171
+ }
172
+ const base = {
173
+ id: engine.id,
174
+ type: engine.name,
175
+ paneId: scenePaneId,
176
+ visible: source.visible,
177
+ locked: source.locked,
178
+ zLevel: source.zLevel,
179
+ mode: source.mode,
180
+ styles: structuredClone(source.styles),
181
+ };
182
+ if (source.groupId !== undefined) {
183
+ base.groupId = source.groupId;
184
+ }
185
+ if (source.metadata !== undefined) {
186
+ base.metadata = structuredClone(source.metadata);
187
+ }
188
+ return base;
189
+ }
190
+ export function fromEngineOverlay(engine, source, idMap, path) {
191
+ const base = baseFromEngine(engine, source, idMap, path);
192
+ const points = engine.points;
193
+ switch (base.type) {
194
+ case 'horizontalStraightLine':
195
+ case 'priceLine': {
196
+ const point = requirePoint(points, 0, false, true, `${path}/anchor`);
197
+ return { ...base, anchor: { value: point.value } };
198
+ }
199
+ case 'verticalStraightLine': {
200
+ const point = requirePoint(points, 0, true, false, `${path}/anchor`);
201
+ return { ...base, anchor: { timestamp: point.timestamp } };
202
+ }
203
+ case 'horizontalRayLine':
204
+ case 'horizontalSegment': {
205
+ const start = requirePoint(points, 0, true, true, `${path}/points/0`);
206
+ const end = requirePoint(points, 1, true, true, `${path}/points/1`);
207
+ return {
208
+ ...base,
209
+ value: start.value,
210
+ startTimestamp: start.timestamp,
211
+ endTimestamp: end.timestamp,
212
+ };
213
+ }
214
+ case 'verticalRayLine':
215
+ case 'verticalSegment': {
216
+ const start = requirePoint(points, 0, true, true, `${path}/points/0`);
217
+ const end = requirePoint(points, 1, true, true, `${path}/points/1`);
218
+ return {
219
+ ...base,
220
+ timestamp: start.timestamp,
221
+ startValue: start.value,
222
+ endValue: end.value,
223
+ };
224
+ }
225
+ case 'rayLine':
226
+ case 'segment':
227
+ case 'straightLine':
228
+ case 'fibonacciLine': {
229
+ return {
230
+ ...base,
231
+ points: [
232
+ requirePoint(points, 0, true, true, `${path}/points/0`),
233
+ requirePoint(points, 1, true, true, `${path}/points/1`),
234
+ ],
235
+ };
236
+ }
237
+ case 'priceChannelLine':
238
+ case 'parallelStraightLine': {
239
+ return {
240
+ ...base,
241
+ points: [
242
+ requirePoint(points, 0, true, true, `${path}/points/0`),
243
+ requirePoint(points, 1, true, true, `${path}/points/1`),
244
+ requirePoint(points, 2, true, true, `${path}/points/2`),
245
+ ],
246
+ };
247
+ }
248
+ case 'brush': {
249
+ const converted = points.map((_point, index) => requirePoint(points, index, true, true, `${path}/points/${index}`));
250
+ const first = converted[0];
251
+ if (first === undefined) {
252
+ throw new SceneError('EXPORT_INVALID', `${path}/points`, 'Brush has no points.');
253
+ }
254
+ return {
255
+ ...base,
256
+ points: [first, ...converted.slice(1)],
257
+ };
258
+ }
259
+ case 'simpleTag': {
260
+ const point = requirePoint(points, 0, false, true, `${path}/anchor`);
261
+ return {
262
+ ...base,
263
+ anchor: { value: point.value },
264
+ text: requireText(engine, `${path}/text`),
265
+ };
266
+ }
267
+ case 'simpleAnnotation':
268
+ case 'callout':
269
+ case 'text':
270
+ return {
271
+ ...base,
272
+ point: requirePoint(points, 0, true, true, `${path}/point`),
273
+ text: requireText(engine, `${path}/text`),
274
+ };
275
+ case 'rectangle':
276
+ case 'arrow':
277
+ return {
278
+ ...base,
279
+ start: requirePoint(points, 0, true, true, `${path}/start`),
280
+ end: requirePoint(points, 1, true, true, `${path}/end`),
281
+ };
282
+ case 'crossLine':
283
+ return {
284
+ ...base,
285
+ point: requirePoint(points, 0, true, true, `${path}/point`),
286
+ };
287
+ }
288
+ }
289
+ /** 在引擎内创建场景的全部标注。 */
290
+ export function createSceneOverlays(scene, chart, idMap, callbacks) {
291
+ for (let index = 0; index < scene.overlays.length; index++) {
292
+ const overlay = scene.overlays[index];
293
+ if (overlay === undefined) {
294
+ continue;
295
+ }
296
+ const result = chart.createOverlay(toEngineOverlay(overlay, idMap, `/overlays/${index}`, callbacks?.(overlay)));
297
+ if (result !== overlay.id) {
298
+ throw new SceneError('RUNTIME_INIT_FAILED', `/overlays/${index}`, `KLineCharts failed to create Overlay ${overlay.id}.`);
299
+ }
300
+ }
301
+ }
@@ -0,0 +1,6 @@
1
+ import type { ChartScene } from '@baron1996/kline-scene-schema';
2
+ import type { Chart } from 'klinecharts';
3
+ import type { EngineIdMap } from './id-map.js';
4
+ /** 按 Scene 顺序创建 Pane、Y 轴和指标,并核对每个映射。 */
5
+ export declare function applyPanes(scene: ChartScene, chart: Chart, idMap: EngineIdMap): void;
6
+ //# sourceMappingURL=panes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panes.d.ts","sourceRoot":"","sources":["../../src/conversion/panes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAuB,MAAM,+BAA+B,CAAC;AAErF,OAAO,KAAK,EAAE,KAAK,EAAS,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA8B/C,wCAAwC;AACxC,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAuEpF"}