@363045841yyt/klinechart-core 0.8.5 → 0.8.6

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 (53) hide show
  1. package/dist/controllers/createChartController.js +1 -1
  2. package/dist/engine/controller/interaction.d.ts +6 -0
  3. package/dist/engine/controller/interaction.d.ts.map +1 -1
  4. package/dist/engine/controller/interaction.js +51 -8
  5. package/dist/engine/controller/interaction.js.map +1 -1
  6. package/dist/engine/indicators/calculators.d.ts.map +1 -1
  7. package/dist/engine/indicators/calculators.js +20 -3
  8. package/dist/engine/indicators/calculators.js.map +1 -1
  9. package/dist/engine/indicators/ichimokuState.d.ts +2 -0
  10. package/dist/engine/indicators/ichimokuState.d.ts.map +1 -1
  11. package/dist/engine/indicators/ichimokuState.js.map +1 -1
  12. package/dist/engine/indicators/visibleStateComposers.d.ts +14 -0
  13. package/dist/engine/indicators/visibleStateComposers.d.ts.map +1 -1
  14. package/dist/engine/indicators/visibleStateComposers.js +34 -0
  15. package/dist/engine/indicators/visibleStateComposers.js.map +1 -1
  16. package/dist/engine/renderers/Indicator/ichimoku.d.ts.map +1 -1
  17. package/dist/engine/renderers/Indicator/ichimoku.js +25 -3
  18. package/dist/engine/renderers/Indicator/ichimoku.js.map +1 -1
  19. package/dist/engine/renderers/Indicator/mainIndicatorLegend.d.ts.map +1 -1
  20. package/dist/engine/renderers/Indicator/mainIndicatorLegend.js +92 -0
  21. package/dist/engine/renderers/Indicator/mainIndicatorLegend.js.map +1 -1
  22. package/dist/engine/renderers/crosshair.js +1 -1
  23. package/dist/engine/renderers/crosshair.js.map +1 -1
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +1 -0
  27. package/dist/index.js.map +1 -1
  28. package/dist/mcp/chartBridge.d.ts.map +1 -1
  29. package/dist/mcp/chartBridge.js +2 -1
  30. package/dist/mcp/chartBridge.js.map +1 -1
  31. package/dist/tokens/theme-dark.js +1 -1
  32. package/dist/utils/uuid.d.ts +2 -0
  33. package/dist/utils/uuid.d.ts.map +1 -0
  34. package/dist/utils/uuid.js +10 -0
  35. package/dist/utils/uuid.js.map +1 -0
  36. package/dist/version.d.ts +1 -1
  37. package/dist/version.js +1 -1
  38. package/package.json +1 -1
  39. package/src/controllers/createChartController.ts +1 -1
  40. package/src/engine/controller/interaction.ts +56 -9
  41. package/src/engine/indicators/__tests__/ichimoku.test.ts +3 -3
  42. package/src/engine/indicators/calculators.ts +22 -3
  43. package/src/engine/indicators/ichimokuState.ts +2 -0
  44. package/src/engine/indicators/visibleStateComposers.ts +51 -0
  45. package/src/engine/renderers/Indicator/ichimoku.ts +23 -3
  46. package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +102 -0
  47. package/src/engine/renderers/crosshair.ts +1 -1
  48. package/src/index.ts +1 -0
  49. package/src/mcp/chartBridge.ts +2 -1
  50. package/src/tokens/__tests__/__snapshots__/baseline.test.ts.snap +1 -1
  51. package/src/tokens/theme-dark.ts +1 -1
  52. package/src/utils/uuid.ts +8 -0
  53. package/src/version.ts +1 -1
@@ -8,7 +8,8 @@ import { Indicator } from '../../indicators/indicatorDefinitionRegistry'
8
8
  import { resolveStateKey, type TitleInfo, type TitleValueItem, type GetTitleInfoFn } from '../../indicators/indicatorMetadata'
9
9
  import type { IndicatorScheduler, IchimokuSchedulerConfig } from '../../indicators/scheduler'
10
10
  import { calcIchimokuData } from '../../indicators/calculators'
11
- import { createValuePointVisibleStateComposer } from '../../indicators/visibleStateComposers'
11
+ import { createIchimokuVisibleStateComposer } from '../../indicators/visibleStateComposers'
12
+ import { getPhysicalKLineConfig } from '../../utils/klineConfig'
12
13
 
13
14
  const TENKAN_COLOR = '#dc2626'
14
15
  const KIJUN_COLOR = '#2563eb'
@@ -50,7 +51,7 @@ export function createIchimokuRendererPlugin(options: IchimokuRendererOptions =
50
51
  description: '一目均衡表渲染器(WebGL 线 + Canvas2D 云图)',
51
52
  debugName: 'Ichimoku',
52
53
  paneId,
53
- priority: RENDERER_PRIORITY.MAIN,
54
+ priority: RENDERER_PRIORITY.INDICATOR,
54
55
 
55
56
  onInstall(host: PluginHost) { pluginHost = host },
56
57
  getDeclaredNamespaces() { const key = resolveKey(); return key ? [key] : [] },
@@ -90,6 +91,25 @@ export function createIchimokuRendererPlugin(options: IchimokuRendererOptions =
90
91
  }
91
92
  }
92
93
 
94
+ // 未来云:在数据末尾延伸 displacement 根 spanA/spanB 线及云段
95
+ const dataLen = (context.data as unknown[]).length
96
+ if (dataLen < series.length) {
97
+ const physConfig = getPhysicalKLineConfig(context.kWidth, context.kGap, context.dpr)
98
+ const futureEnd = Math.min(dataLen + params.displacement, series.length)
99
+ for (let i = dataLen; i < futureEnd; i++) {
100
+ const p = series[i]
101
+ if (!p) continue
102
+ const leftPx = physConfig.startXPx + i * physConfig.unitPx
103
+ const wickXPx = leftPx + (physConfig.kWidthPx - 1) / 2
104
+ const centerX = wickXPx / context.dpr
105
+ if (params.showSpanA && p.spanA !== undefined) spanAPts.push({ x: centerX, y: toY(p.spanA) })
106
+ if (params.showSpanB && p.spanB !== undefined) spanBPts.push({ x: centerX, y: toY(p.spanB) })
107
+ if (params.showCloud && p.spanA !== undefined && p.spanB !== undefined) {
108
+ cloudSegs.push({ x: centerX, ya: toY(p.spanA), yb: toY(p.spanB), bull: p.spanA > p.spanB })
109
+ }
110
+ }
111
+ }
112
+
93
113
  // Cloud fill (Canvas2D only)
94
114
  if (params.showCloud && cloudSegs.length >= 2) {
95
115
  ctx.save()
@@ -209,7 +229,7 @@ export function getIchimokuTitleInfo(
209
229
  allowMainPane: true,
210
230
  mainPane: { rendererName: 'ichimoku_main', toActiveConfig: (params, active) => ({ ...params, showTenkan: active, showKijun: active, showSpanA: active, showSpanB: active, showChikou: active, showCloud: active }) },
211
231
  scale: { indicatorKey: 'ichimoku', label: 'Ichimoku', decimals: 2 },
212
- visibleState: { compose: createValuePointVisibleStateComposer('ichimoku', EMPTY_ICHIMOKU_STATE, ['tenkan', 'kijun', 'spanA', 'spanB', 'chikou']) },
232
+ visibleState: { compose: createIchimokuVisibleStateComposer('ichimoku', EMPTY_ICHIMOKU_STATE, ['tenkan', 'kijun', 'spanA', 'spanB', 'chikou']) },
213
233
  runtime: { defaultConfig:{tenkanPeriod:9,kijunPeriod:26,spanBPeriod:52,displacement:26,showTenkan:true,showKijun:true,showSpanA:true,showSpanB:true,showCloud:true,showChikou:true}, computeKey:'calcIchimokuData', compute:(data,c)=>calcIchimokuData(data,c.tenkanPeriod,c.kijunPeriod,c.spanBPeriod,c.displacement) },
214
234
  })
215
235
  class IchimokuDefinition {
@@ -81,6 +81,102 @@ export function createMainIndicatorLegendRendererPlugin(options: {
81
81
  const targetIndex = crosshairIndex ?? Math.min(range.end - 1, klineData.length - 1)
82
82
  const rows: Array<{ draw: (rowIndex: number) => void }> = []
83
83
 
84
+ if (typeof crosshairIndex === 'number') {
85
+ const k = klineData[targetIndex]
86
+ if (k) {
87
+ const isUp = k.close >= k.open
88
+ const volText = typeof k.volume === 'number' ? formatVolumeShort(k.volume) : null
89
+ const upColor = isUp ? colors.candleUpBody : colors.candleDownBody
90
+
91
+ if (context.paneWidth >= 400) {
92
+ rows.push({
93
+ draw: (rowIndex: number) => {
94
+ let x = legendX
95
+ const y = config.yPaddingPx / 2 + legendYOffset + rowIndex * lineHeight
96
+
97
+ overlayCtx.fillStyle = colors.text.primary
98
+ overlayCtx.fillText('O ', x, y)
99
+ x += measureTextWidth(overlayCtx, 'O ')
100
+ overlayCtx.fillStyle = upColor
101
+ overlayCtx.fillText(k.open.toFixed(2), x, y)
102
+ x += measureTextWidth(overlayCtx, k.open.toFixed(2)) + gap
103
+
104
+ overlayCtx.fillStyle = colors.text.primary
105
+ overlayCtx.fillText('H ', x, y)
106
+ x += measureTextWidth(overlayCtx, 'H ')
107
+ overlayCtx.fillText(k.high.toFixed(2), x, y)
108
+ x += measureTextWidth(overlayCtx, k.high.toFixed(2)) + gap
109
+
110
+ overlayCtx.fillText('L ', x, y)
111
+ x += measureTextWidth(overlayCtx, 'L ')
112
+ overlayCtx.fillText(k.low.toFixed(2), x, y)
113
+ x += measureTextWidth(overlayCtx, k.low.toFixed(2)) + gap
114
+
115
+ overlayCtx.fillStyle = colors.text.primary
116
+ overlayCtx.fillText('C ', x, y)
117
+ x += measureTextWidth(overlayCtx, 'C ')
118
+ overlayCtx.fillStyle = upColor
119
+ overlayCtx.fillText(k.close.toFixed(2), x, y)
120
+ x += measureTextWidth(overlayCtx, k.close.toFixed(2)) + gap
121
+
122
+ if (volText) {
123
+ overlayCtx.fillStyle = colors.text.tertiary
124
+ overlayCtx.fillText('Vol ', x, y)
125
+ x += measureTextWidth(overlayCtx, 'Vol ')
126
+ overlayCtx.fillStyle = colors.text.primary
127
+ overlayCtx.fillText(volText, x, y)
128
+ }
129
+ },
130
+ })
131
+ } else {
132
+ rows.push({
133
+ draw: (rowIndex: number) => {
134
+ let x = legendX
135
+ const y = config.yPaddingPx / 2 + legendYOffset + rowIndex * lineHeight
136
+
137
+ overlayCtx.fillStyle = colors.text.primary
138
+ overlayCtx.fillText('O ', x, y)
139
+ x += measureTextWidth(overlayCtx, 'O ')
140
+ overlayCtx.fillStyle = upColor
141
+ overlayCtx.fillText(k.open.toFixed(2), x, y)
142
+ x += measureTextWidth(overlayCtx, k.open.toFixed(2)) + gap
143
+
144
+ overlayCtx.fillStyle = colors.text.primary
145
+ overlayCtx.fillText('H ', x, y)
146
+ x += measureTextWidth(overlayCtx, 'H ')
147
+ overlayCtx.fillText(k.high.toFixed(2), x, y)
148
+ x += measureTextWidth(overlayCtx, k.high.toFixed(2)) + gap
149
+
150
+ overlayCtx.fillText('L ', x, y)
151
+ x += measureTextWidth(overlayCtx, 'L ')
152
+ overlayCtx.fillText(k.low.toFixed(2), x, y)
153
+ },
154
+ })
155
+ rows.push({
156
+ draw: (rowIndex: number) => {
157
+ let x = legendX
158
+ const y = config.yPaddingPx / 2 + legendYOffset + rowIndex * lineHeight
159
+
160
+ overlayCtx.fillStyle = colors.text.primary
161
+ overlayCtx.fillText('C ', x, y)
162
+ x += measureTextWidth(overlayCtx, 'C ')
163
+ overlayCtx.fillStyle = upColor
164
+ overlayCtx.fillText(k.close.toFixed(2), x, y)
165
+ x += measureTextWidth(overlayCtx, k.close.toFixed(2)) + gap
166
+
167
+ if (volText) {
168
+ overlayCtx.fillStyle = colors.text.tertiary
169
+ overlayCtx.fillText('Vol ', x, y)
170
+ x += measureTextWidth(overlayCtx, 'Vol ')
171
+ overlayCtx.fillStyle = colors.text.primary
172
+ overlayCtx.fillText(volText, x, y)
173
+ }
174
+ },
175
+ })
176
+ }
177
+ }
178
+ }
179
+
84
180
  const scheduler = pluginHost && typeof pluginHost.getService === 'function'
85
181
  ? pluginHost.getService<IndicatorScheduler>('indicatorScheduler')
86
182
  : undefined
@@ -237,3 +333,9 @@ function findBaselineByTimestamp(data: ReadonlyArray<KLineData>, timestamp: numb
237
333
  }
238
334
  return null
239
335
  }
336
+
337
+ function formatVolumeShort(v: number): string {
338
+ if (v >= 1e8) return (v / 1e8).toFixed(2) + '亿'
339
+ if (v >= 1e4) return (v / 1e4).toFixed(2) + '万'
340
+ return v.toFixed(2)
341
+ }
@@ -30,7 +30,7 @@ export function createCrosshairRendererPlugin(options: {
30
30
  const colors = resolveThemeColors(context.theme, context.isAsiaMarket, context.colorPresetSettings)
31
31
  const state = options.getCrosshairState()
32
32
 
33
- if (state.isDragging || !state.pos) return
33
+ if (!state.pos) return
34
34
 
35
35
  const { x } = state.pos
36
36
  const isActive = pane.id === state.activePaneId
package/src/index.ts CHANGED
@@ -4,3 +4,4 @@ export * from './mcp'
4
4
  export { VERSION } from './version'
5
5
  export * from './tokens'
6
6
  export { formatTimestamp } from './utils/dateFormat'
7
+ export { generateUUID } from './utils/uuid'
@@ -1,4 +1,5 @@
1
1
  import type { ToolCall, ToolResult, ControllerDescription, ToolCallHandler } from './types'
2
+ import { generateUUID } from '../utils/uuid'
2
3
 
3
4
  export interface ChartBridgeOptions {
4
5
  wsUrl: string
@@ -42,7 +43,7 @@ export class ChartBridge {
42
43
  onStateChange?: () => void
43
44
 
44
45
  constructor(options: ChartBridgeOptions) {
45
- this.sessionId = options.sessionId ?? crypto.randomUUID()
46
+ this.sessionId = options.sessionId ?? generateUUID()
46
47
  this.autoReconnect = options.autoReconnect ?? true
47
48
  this.reconnectDelay = options.reconnectDelay ?? 3000
48
49
  this.maxReconnectDelay = options.maxReconnectDelay ?? 30_000
@@ -64,7 +64,7 @@ exports[`theme baseline — dark > CSS declaration block (snapshot) 1`] = `
64
64
  --klc-color-tag-bg-transparent: transparent;
65
65
  --klc-color-tag-bg-active: #1890ff;
66
66
  --klc-color-tag-bg-active-hover: #40a9ff;
67
- --klc-color-tag-bg-hover: #3a3a4a;
67
+ --klc-color-tag-bg-hover: #262C36;
68
68
  --klc-color-border-dark: rgba(255, 255, 255, 0.15);
69
69
  --klc-color-border-medium: rgba(255, 255, 255, 0.12);
70
70
  --klc-color-border-light: rgba(255, 255, 255, 0.08);
@@ -105,7 +105,7 @@ export const darkTheme: Theme = {
105
105
  transparent: 'transparent',
106
106
  active: '#1890ff',
107
107
  activeHover: '#40a9ff',
108
- hover: '#3a3a4a',
108
+ hover: '#262C36',
109
109
  },
110
110
  border: {
111
111
  dark: 'rgba(255, 255, 255, 0.15)',
@@ -0,0 +1,8 @@
1
+ export function generateUUID(): string {
2
+ if (typeof crypto.randomUUID === 'function') return crypto.randomUUID()
3
+ const bytes = crypto.getRandomValues(new Uint8Array(16))
4
+ bytes[6] = (bytes[6]! & 0x0f) | 0x40
5
+ bytes[8] = (bytes[8]! & 0x3f) | 0x80
6
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0'))
7
+ return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex.slice(6, 8).join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10).join('')}`
8
+ }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.8.5"
1
+ export const VERSION = "0.8.6"