@363045841yyt/klinechart-core 0.8.11-alpha.0 → 0.8.12

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 (578) hide show
  1. package/dist/alerts/createAlertController.d.ts +30 -0
  2. package/dist/alerts/createAlertController.d.ts.map +1 -0
  3. package/dist/alerts/createAlertController.js +261 -0
  4. package/dist/alerts/createAlertController.js.map +1 -0
  5. package/dist/alerts/index.d.ts +5 -0
  6. package/dist/alerts/index.d.ts.map +1 -0
  7. package/dist/alerts/index.js +4 -0
  8. package/dist/alerts/index.js.map +1 -0
  9. package/dist/alerts/predicates.d.ts +28 -0
  10. package/dist/alerts/predicates.d.ts.map +1 -0
  11. package/dist/alerts/predicates.js +211 -0
  12. package/dist/alerts/predicates.js.map +1 -0
  13. package/dist/alerts/rollingVolume.d.ts +15 -0
  14. package/dist/alerts/rollingVolume.d.ts.map +1 -0
  15. package/dist/alerts/rollingVolume.js +42 -0
  16. package/dist/alerts/rollingVolume.js.map +1 -0
  17. package/dist/alerts/ruleSchema.d.ts +42 -0
  18. package/dist/alerts/ruleSchema.d.ts.map +1 -0
  19. package/dist/alerts/ruleSchema.js +240 -0
  20. package/dist/alerts/ruleSchema.js.map +1 -0
  21. package/dist/alerts/types.d.ts +169 -0
  22. package/dist/alerts/types.d.ts.map +1 -0
  23. package/dist/alerts/types.js +24 -0
  24. package/dist/alerts/types.js.map +1 -0
  25. package/dist/chartTypes/heikinAshi.d.ts +49 -0
  26. package/dist/chartTypes/heikinAshi.d.ts.map +1 -0
  27. package/dist/chartTypes/heikinAshi.js +94 -0
  28. package/dist/chartTypes/heikinAshi.js.map +1 -0
  29. package/dist/chartTypes/index.d.ts +6 -0
  30. package/dist/chartTypes/index.d.ts.map +1 -0
  31. package/dist/chartTypes/index.js +5 -0
  32. package/dist/chartTypes/index.js.map +1 -0
  33. package/dist/chartTypes/pointAndFigure.d.ts +83 -0
  34. package/dist/chartTypes/pointAndFigure.d.ts.map +1 -0
  35. package/dist/chartTypes/pointAndFigure.js +159 -0
  36. package/dist/chartTypes/pointAndFigure.js.map +1 -0
  37. package/dist/chartTypes/rangeBars.d.ts +61 -0
  38. package/dist/chartTypes/rangeBars.d.ts.map +1 -0
  39. package/dist/chartTypes/rangeBars.js +181 -0
  40. package/dist/chartTypes/rangeBars.js.map +1 -0
  41. package/dist/chartTypes/renko.d.ts +94 -0
  42. package/dist/chartTypes/renko.d.ts.map +1 -0
  43. package/dist/chartTypes/renko.js +207 -0
  44. package/dist/chartTypes/renko.js.map +1 -0
  45. package/dist/chartTypes/types.d.ts +97 -0
  46. package/dist/chartTypes/types.d.ts.map +1 -0
  47. package/dist/chartTypes/types.js +39 -0
  48. package/dist/chartTypes/types.js.map +1 -0
  49. package/dist/components/anchoredVwap/computeAnchoredVwap.d.ts +76 -0
  50. package/dist/components/anchoredVwap/computeAnchoredVwap.d.ts.map +1 -0
  51. package/dist/components/anchoredVwap/computeAnchoredVwap.js +155 -0
  52. package/dist/components/anchoredVwap/computeAnchoredVwap.js.map +1 -0
  53. package/dist/components/anchoredVwap/createAnchoredVwapController.d.ts +28 -0
  54. package/dist/components/anchoredVwap/createAnchoredVwapController.d.ts.map +1 -0
  55. package/dist/components/anchoredVwap/createAnchoredVwapController.js +282 -0
  56. package/dist/components/anchoredVwap/createAnchoredVwapController.js.map +1 -0
  57. package/dist/components/anchoredVwap/index.d.ts +11 -0
  58. package/dist/components/anchoredVwap/index.d.ts.map +1 -0
  59. package/dist/components/anchoredVwap/index.js +10 -0
  60. package/dist/components/anchoredVwap/index.js.map +1 -0
  61. package/dist/components/anchoredVwap/types.d.ts +171 -0
  62. package/dist/components/anchoredVwap/types.d.ts.map +1 -0
  63. package/dist/components/anchoredVwap/types.js +25 -0
  64. package/dist/components/anchoredVwap/types.js.map +1 -0
  65. package/dist/components/crosshairSync/createCrosshairSync.d.ts +94 -0
  66. package/dist/components/crosshairSync/createCrosshairSync.d.ts.map +1 -0
  67. package/dist/components/crosshairSync/createCrosshairSync.js +118 -0
  68. package/dist/components/crosshairSync/createCrosshairSync.js.map +1 -0
  69. package/dist/components/crosshairSync/index.d.ts +2 -0
  70. package/dist/components/crosshairSync/index.d.ts.map +1 -0
  71. package/dist/components/crosshairSync/index.js +2 -0
  72. package/dist/components/crosshairSync/index.js.map +1 -0
  73. package/dist/components/footprint/aggressor.d.ts +89 -0
  74. package/dist/components/footprint/aggressor.d.ts.map +1 -0
  75. package/dist/components/footprint/aggressor.js +106 -0
  76. package/dist/components/footprint/aggressor.js.map +1 -0
  77. package/dist/components/footprint/createFootprintController.d.ts +29 -0
  78. package/dist/components/footprint/createFootprintController.d.ts.map +1 -0
  79. package/dist/components/footprint/createFootprintController.js +264 -0
  80. package/dist/components/footprint/createFootprintController.js.map +1 -0
  81. package/dist/components/footprint/index.d.ts +7 -0
  82. package/dist/components/footprint/index.d.ts.map +1 -0
  83. package/dist/components/footprint/index.js +4 -0
  84. package/dist/components/footprint/index.js.map +1 -0
  85. package/dist/components/footprint/perBarStats.d.ts +63 -0
  86. package/dist/components/footprint/perBarStats.d.ts.map +1 -0
  87. package/dist/components/footprint/perBarStats.js +123 -0
  88. package/dist/components/footprint/perBarStats.js.map +1 -0
  89. package/dist/components/footprint/types.d.ts +197 -0
  90. package/dist/components/footprint/types.d.ts.map +1 -0
  91. package/dist/components/footprint/types.js +23 -0
  92. package/dist/components/footprint/types.js.map +1 -0
  93. package/dist/components/mtfOverlay/alignToBaseIndex.d.ts +45 -0
  94. package/dist/components/mtfOverlay/alignToBaseIndex.d.ts.map +1 -0
  95. package/dist/components/mtfOverlay/alignToBaseIndex.js +92 -0
  96. package/dist/components/mtfOverlay/alignToBaseIndex.js.map +1 -0
  97. package/dist/components/mtfOverlay/createMtfController.d.ts +7 -0
  98. package/dist/components/mtfOverlay/createMtfController.d.ts.map +1 -0
  99. package/dist/components/mtfOverlay/createMtfController.js +134 -0
  100. package/dist/components/mtfOverlay/createMtfController.js.map +1 -0
  101. package/dist/components/mtfOverlay/index.d.ts +19 -0
  102. package/dist/components/mtfOverlay/index.d.ts.map +1 -0
  103. package/dist/components/mtfOverlay/index.js +18 -0
  104. package/dist/components/mtfOverlay/index.js.map +1 -0
  105. package/dist/components/mtfOverlay/resampleBars.d.ts +37 -0
  106. package/dist/components/mtfOverlay/resampleBars.d.ts.map +1 -0
  107. package/dist/components/mtfOverlay/resampleBars.js +93 -0
  108. package/dist/components/mtfOverlay/resampleBars.js.map +1 -0
  109. package/dist/components/mtfOverlay/types.d.ts +132 -0
  110. package/dist/components/mtfOverlay/types.d.ts.map +1 -0
  111. package/dist/components/mtfOverlay/types.js +22 -0
  112. package/dist/components/mtfOverlay/types.js.map +1 -0
  113. package/dist/components/orderBookHeatmap/createHeatmapController.d.ts +21 -0
  114. package/dist/components/orderBookHeatmap/createHeatmapController.d.ts.map +1 -0
  115. package/dist/components/orderBookHeatmap/createHeatmapController.js +234 -0
  116. package/dist/components/orderBookHeatmap/createHeatmapController.js.map +1 -0
  117. package/dist/components/orderBookHeatmap/createOrderBookState.d.ts +18 -0
  118. package/dist/components/orderBookHeatmap/createOrderBookState.d.ts.map +1 -0
  119. package/dist/components/orderBookHeatmap/createOrderBookState.js +102 -0
  120. package/dist/components/orderBookHeatmap/createOrderBookState.js.map +1 -0
  121. package/dist/components/orderBookHeatmap/deltaArchive.d.ts +25 -0
  122. package/dist/components/orderBookHeatmap/deltaArchive.d.ts.map +1 -0
  123. package/dist/components/orderBookHeatmap/deltaArchive.js +106 -0
  124. package/dist/components/orderBookHeatmap/deltaArchive.js.map +1 -0
  125. package/dist/components/orderBookHeatmap/index.d.ts +13 -0
  126. package/dist/components/orderBookHeatmap/index.d.ts.map +1 -0
  127. package/dist/components/orderBookHeatmap/index.js +12 -0
  128. package/dist/components/orderBookHeatmap/index.js.map +1 -0
  129. package/dist/components/orderBookHeatmap/logColorScale.d.ts +20 -0
  130. package/dist/components/orderBookHeatmap/logColorScale.d.ts.map +1 -0
  131. package/dist/components/orderBookHeatmap/logColorScale.js +71 -0
  132. package/dist/components/orderBookHeatmap/logColorScale.js.map +1 -0
  133. package/dist/components/orderBookHeatmap/snapshotRing.d.ts +16 -0
  134. package/dist/components/orderBookHeatmap/snapshotRing.d.ts.map +1 -0
  135. package/dist/components/orderBookHeatmap/snapshotRing.js +61 -0
  136. package/dist/components/orderBookHeatmap/snapshotRing.js.map +1 -0
  137. package/dist/components/orderBookHeatmap/types.d.ts +128 -0
  138. package/dist/components/orderBookHeatmap/types.d.ts.map +1 -0
  139. package/dist/components/orderBookHeatmap/types.js +16 -0
  140. package/dist/components/orderBookHeatmap/types.js.map +1 -0
  141. package/dist/components/volumeProfile/binning.d.ts +54 -0
  142. package/dist/components/volumeProfile/binning.d.ts.map +1 -0
  143. package/dist/components/volumeProfile/binning.js +123 -0
  144. package/dist/components/volumeProfile/binning.js.map +1 -0
  145. package/dist/components/volumeProfile/createVolumeProfileController.d.ts +31 -0
  146. package/dist/components/volumeProfile/createVolumeProfileController.d.ts.map +1 -0
  147. package/dist/components/volumeProfile/createVolumeProfileController.js +179 -0
  148. package/dist/components/volumeProfile/createVolumeProfileController.js.map +1 -0
  149. package/dist/components/volumeProfile/index.d.ts +6 -0
  150. package/dist/components/volumeProfile/index.d.ts.map +1 -0
  151. package/dist/components/volumeProfile/index.js +5 -0
  152. package/dist/components/volumeProfile/index.js.map +1 -0
  153. package/dist/components/volumeProfile/poc.d.ts +35 -0
  154. package/dist/components/volumeProfile/poc.d.ts.map +1 -0
  155. package/dist/components/volumeProfile/poc.js +51 -0
  156. package/dist/components/volumeProfile/poc.js.map +1 -0
  157. package/dist/components/volumeProfile/types.d.ts +138 -0
  158. package/dist/components/volumeProfile/types.d.ts.map +1 -0
  159. package/dist/components/volumeProfile/types.js +19 -0
  160. package/dist/components/volumeProfile/types.js.map +1 -0
  161. package/dist/components/volumeProfile/valueArea.d.ts +58 -0
  162. package/dist/components/volumeProfile/valueArea.d.ts.map +1 -0
  163. package/dist/components/volumeProfile/valueArea.js +172 -0
  164. package/dist/components/volumeProfile/valueArea.js.map +1 -0
  165. package/dist/controllers/createChartController.d.ts.map +1 -1
  166. package/dist/controllers/createChartController.js +22 -11
  167. package/dist/controllers/createChartController.js.map +1 -1
  168. package/dist/controllers/types.d.ts +3 -1
  169. package/dist/controllers/types.d.ts.map +1 -1
  170. package/dist/data-fetchers/baostock.d.ts.map +1 -1
  171. package/dist/data-fetchers/baostock.js +2 -1
  172. package/dist/data-fetchers/baostock.js.map +1 -1
  173. package/dist/data-fetchers/dataBuffer.effects.d.ts.map +1 -1
  174. package/dist/data-fetchers/dataBuffer.effects.js +2 -1
  175. package/dist/data-fetchers/dataBuffer.effects.js.map +1 -1
  176. package/dist/data-fetchers/fetcherDefinitionRegistry.d.ts.map +1 -1
  177. package/dist/data-fetchers/fetcherDefinitionRegistry.js +2 -1
  178. package/dist/data-fetchers/fetcherDefinitionRegistry.js.map +1 -1
  179. package/dist/data-fetchers/gotdx.js +4 -3
  180. package/dist/data-fetchers/gotdx.js.map +1 -1
  181. package/dist/data-fetchers/router.d.ts.map +1 -1
  182. package/dist/data-fetchers/router.js +4 -3
  183. package/dist/data-fetchers/router.js.map +1 -1
  184. package/dist/data-fetchers/tradingview.js +3 -2
  185. package/dist/data-fetchers/tradingview.js.map +1 -1
  186. package/dist/engine/chart.d.ts +13 -0
  187. package/dist/engine/chart.d.ts.map +1 -1
  188. package/dist/engine/chart.js +109 -0
  189. package/dist/engine/chart.js.map +1 -1
  190. package/dist/engine/data/chartDataManager.d.ts +1 -0
  191. package/dist/engine/data/chartDataManager.d.ts.map +1 -1
  192. package/dist/engine/data/chartDataManager.js +27 -4
  193. package/dist/engine/data/chartDataManager.js.map +1 -1
  194. package/dist/engine/drawing/toolConfig.d.ts.map +1 -1
  195. package/dist/engine/drawing/toolConfig.js +2 -1
  196. package/dist/engine/drawing/toolConfig.js.map +1 -1
  197. package/dist/engine/indicators/chartIndicatorManager.d.ts.map +1 -1
  198. package/dist/engine/indicators/chartIndicatorManager.js +2 -1
  199. package/dist/engine/indicators/chartIndicatorManager.js.map +1 -1
  200. package/dist/engine/indicators/indicatorDefinitionRegistry.d.ts.map +1 -1
  201. package/dist/engine/indicators/indicatorDefinitionRegistry.js +2 -1
  202. package/dist/engine/indicators/indicatorDefinitionRegistry.js.map +1 -1
  203. package/dist/engine/indicators/indicatorMetadata.d.ts.map +1 -1
  204. package/dist/engine/indicators/indicatorMetadata.js +2 -1
  205. package/dist/engine/indicators/indicatorMetadata.js.map +1 -1
  206. package/dist/engine/indicators/indicatorRegistry.d.ts.map +1 -1
  207. package/dist/engine/indicators/indicatorRegistry.js +6 -5
  208. package/dist/engine/indicators/indicatorRegistry.js.map +1 -1
  209. package/dist/engine/indicators/registerBuiltins.d.ts.map +1 -1
  210. package/dist/engine/indicators/registerBuiltins.js +2 -1
  211. package/dist/engine/indicators/registerBuiltins.js.map +1 -1
  212. package/dist/engine/indicators/scheduler.d.ts +6 -0
  213. package/dist/engine/indicators/scheduler.d.ts.map +1 -1
  214. package/dist/engine/indicators/scheduler.js +12 -0
  215. package/dist/engine/indicators/scheduler.js.map +1 -1
  216. package/dist/engine/indicators/soa.d.ts.map +1 -1
  217. package/dist/engine/indicators/soa.js +2 -1
  218. package/dist/engine/indicators/soa.js.map +1 -1
  219. package/dist/engine/indicators/stateComposer.d.ts.map +1 -1
  220. package/dist/engine/indicators/stateComposer.js +2 -1
  221. package/dist/engine/indicators/stateComposer.js.map +1 -1
  222. package/dist/engine/subPaneManager.d.ts.map +1 -1
  223. package/dist/engine/subPaneManager.js +2 -1
  224. package/dist/engine/subPaneManager.js.map +1 -1
  225. package/dist/engine/utils/chartZoomController.d.ts +2 -0
  226. package/dist/engine/utils/chartZoomController.d.ts.map +1 -1
  227. package/dist/engine/utils/chartZoomController.js +5 -1
  228. package/dist/engine/utils/chartZoomController.js.map +1 -1
  229. package/dist/errors-help.d.ts +72 -0
  230. package/dist/errors-help.d.ts.map +1 -0
  231. package/dist/errors-help.js +149 -0
  232. package/dist/errors-help.js.map +1 -0
  233. package/dist/errors.d.ts +64 -0
  234. package/dist/errors.d.ts.map +1 -0
  235. package/dist/errors.js +61 -0
  236. package/dist/errors.js.map +1 -0
  237. package/dist/index.d.ts +18 -0
  238. package/dist/index.d.ts.map +1 -1
  239. package/dist/index.js +22 -0
  240. package/dist/index.js.map +1 -1
  241. package/dist/indicators/alma.d.ts +27 -0
  242. package/dist/indicators/alma.d.ts.map +1 -0
  243. package/dist/indicators/alma.js +36 -0
  244. package/dist/indicators/alma.js.map +1 -0
  245. package/dist/indicators/awesomeOscillator.d.ts +19 -0
  246. package/dist/indicators/awesomeOscillator.d.ts.map +1 -0
  247. package/dist/indicators/awesomeOscillator.js +36 -0
  248. package/dist/indicators/awesomeOscillator.js.map +1 -0
  249. package/dist/indicators/dpo.d.ts +17 -0
  250. package/dist/indicators/dpo.d.ts.map +1 -0
  251. package/dist/indicators/dpo.js +24 -0
  252. package/dist/indicators/dpo.js.map +1 -0
  253. package/dist/indicators/fisherTransform.d.ts +26 -0
  254. package/dist/indicators/fisherTransform.d.ts.map +1 -0
  255. package/dist/indicators/fisherTransform.js +44 -0
  256. package/dist/indicators/fisherTransform.js.map +1 -0
  257. package/dist/indicators/frama.d.ts +25 -0
  258. package/dist/indicators/frama.d.ts.map +1 -0
  259. package/dist/indicators/frama.js +61 -0
  260. package/dist/indicators/frama.js.map +1 -0
  261. package/dist/indicators/index.d.ts +24 -0
  262. package/dist/indicators/index.d.ts.map +1 -0
  263. package/dist/indicators/index.js +26 -0
  264. package/dist/indicators/index.js.map +1 -0
  265. package/dist/indicators/lsma.d.ts +21 -0
  266. package/dist/indicators/lsma.d.ts.map +1 -0
  267. package/dist/indicators/lsma.js +37 -0
  268. package/dist/indicators/lsma.js.map +1 -0
  269. package/dist/indicators/schaffTrendCycle.d.ts +24 -0
  270. package/dist/indicators/schaffTrendCycle.d.ts.map +1 -0
  271. package/dist/indicators/schaffTrendCycle.js +70 -0
  272. package/dist/indicators/schaffTrendCycle.js.map +1 -0
  273. package/dist/indicators/stochRSI.d.ts +24 -0
  274. package/dist/indicators/stochRSI.d.ts.map +1 -0
  275. package/dist/indicators/stochRSI.js +86 -0
  276. package/dist/indicators/stochRSI.js.map +1 -0
  277. package/dist/indicators/t3.d.ts +31 -0
  278. package/dist/indicators/t3.d.ts.map +1 -0
  279. package/dist/indicators/t3.js +63 -0
  280. package/dist/indicators/t3.js.map +1 -0
  281. package/dist/indicators/ultimateOscillator.d.ts +26 -0
  282. package/dist/indicators/ultimateOscillator.d.ts.map +1 -0
  283. package/dist/indicators/ultimateOscillator.js +59 -0
  284. package/dist/indicators/ultimateOscillator.js.map +1 -0
  285. package/dist/indicators/vidya.d.ts +24 -0
  286. package/dist/indicators/vidya.d.ts.map +1 -0
  287. package/dist/indicators/vidya.js +54 -0
  288. package/dist/indicators/vidya.js.map +1 -0
  289. package/dist/indicators/zlema.d.ts +16 -0
  290. package/dist/indicators/zlema.d.ts.map +1 -0
  291. package/dist/indicators/zlema.js +26 -0
  292. package/dist/indicators/zlema.js.map +1 -0
  293. package/dist/input/gesture.d.ts +125 -0
  294. package/dist/input/gesture.d.ts.map +1 -0
  295. package/dist/input/gesture.js +249 -0
  296. package/dist/input/gesture.js.map +1 -0
  297. package/dist/input/index.d.ts +9 -0
  298. package/dist/input/index.d.ts.map +1 -0
  299. package/dist/input/index.js +9 -0
  300. package/dist/input/index.js.map +1 -0
  301. package/dist/input/keyboard.d.ts +140 -0
  302. package/dist/input/keyboard.d.ts.map +1 -0
  303. package/dist/input/keyboard.js +260 -0
  304. package/dist/input/keyboard.js.map +1 -0
  305. package/dist/mcp/chartBridge.d.ts +2 -2
  306. package/dist/mcp/chartBridge.d.ts.map +1 -1
  307. package/dist/mcp/chartBridge.js +27 -21
  308. package/dist/mcp/chartBridge.js.map +1 -1
  309. package/dist/plugin/PluginHost.d.ts +0 -3
  310. package/dist/plugin/PluginHost.d.ts.map +1 -1
  311. package/dist/plugin/PluginHost.js +7 -3
  312. package/dist/plugin/PluginHost.js.map +1 -1
  313. package/dist/plugin/PluginRegistry.d.ts +0 -3
  314. package/dist/plugin/PluginRegistry.d.ts.map +1 -1
  315. package/dist/plugin/PluginRegistry.js +5 -1
  316. package/dist/plugin/PluginRegistry.js.map +1 -1
  317. package/dist/render/Renderer.d.ts +116 -0
  318. package/dist/render/Renderer.d.ts.map +1 -0
  319. package/dist/render/Renderer.js +31 -0
  320. package/dist/render/Renderer.js.map +1 -0
  321. package/dist/render/SurfaceBackend.d.ts +83 -0
  322. package/dist/render/SurfaceBackend.d.ts.map +1 -0
  323. package/dist/render/SurfaceBackend.js +27 -0
  324. package/dist/render/SurfaceBackend.js.map +1 -0
  325. package/dist/render/index.d.ts +12 -0
  326. package/dist/render/index.d.ts.map +1 -0
  327. package/dist/render/index.js +11 -0
  328. package/dist/render/index.js.map +1 -0
  329. package/dist/renderer-tier/detectRendererTier.d.ts +57 -0
  330. package/dist/renderer-tier/detectRendererTier.d.ts.map +1 -0
  331. package/dist/renderer-tier/detectRendererTier.js +143 -0
  332. package/dist/renderer-tier/detectRendererTier.js.map +1 -0
  333. package/dist/renderer-tier/index.d.ts +12 -0
  334. package/dist/renderer-tier/index.d.ts.map +1 -0
  335. package/dist/renderer-tier/index.js +12 -0
  336. package/dist/renderer-tier/index.js.map +1 -0
  337. package/dist/renderer-tier/selectBackend.d.ts +106 -0
  338. package/dist/renderer-tier/selectBackend.d.ts.map +1 -0
  339. package/dist/renderer-tier/selectBackend.js +113 -0
  340. package/dist/renderer-tier/selectBackend.js.map +1 -0
  341. package/dist/renderer-tier/types.d.ts +50 -0
  342. package/dist/renderer-tier/types.d.ts.map +1 -0
  343. package/dist/renderer-tier/types.js +24 -0
  344. package/dist/renderer-tier/types.js.map +1 -0
  345. package/dist/replay/createReplayController.d.ts +3 -0
  346. package/dist/replay/createReplayController.d.ts.map +1 -0
  347. package/dist/replay/createReplayController.js +251 -0
  348. package/dist/replay/createReplayController.js.map +1 -0
  349. package/dist/replay/index.d.ts +5 -0
  350. package/dist/replay/index.d.ts.map +1 -0
  351. package/dist/replay/index.js +3 -0
  352. package/dist/replay/index.js.map +1 -0
  353. package/dist/replay/timeline.d.ts +50 -0
  354. package/dist/replay/timeline.d.ts.map +1 -0
  355. package/dist/replay/timeline.js +104 -0
  356. package/dist/replay/timeline.js.map +1 -0
  357. package/dist/replay/types.d.ts +94 -0
  358. package/dist/replay/types.d.ts.map +1 -0
  359. package/dist/replay/types.js +21 -0
  360. package/dist/replay/types.js.map +1 -0
  361. package/dist/scale/anchoredZoom.d.ts +64 -0
  362. package/dist/scale/anchoredZoom.d.ts.map +1 -0
  363. package/dist/scale/anchoredZoom.js +67 -0
  364. package/dist/scale/anchoredZoom.js.map +1 -0
  365. package/dist/scale/createPriceScale.d.ts +49 -0
  366. package/dist/scale/createPriceScale.d.ts.map +1 -0
  367. package/dist/scale/createPriceScale.js +175 -0
  368. package/dist/scale/createPriceScale.js.map +1 -0
  369. package/dist/scale/createTimeScale.d.ts +27 -0
  370. package/dist/scale/createTimeScale.d.ts.map +1 -0
  371. package/dist/scale/createTimeScale.js +139 -0
  372. package/dist/scale/createTimeScale.js.map +1 -0
  373. package/dist/scale/index.d.ts +6 -0
  374. package/dist/scale/index.d.ts.map +1 -0
  375. package/dist/scale/index.js +5 -0
  376. package/dist/scale/index.js.map +1 -0
  377. package/dist/scale/originShift.d.ts +69 -0
  378. package/dist/scale/originShift.d.ts.map +1 -0
  379. package/dist/scale/originShift.js +41 -0
  380. package/dist/scale/originShift.js.map +1 -0
  381. package/dist/scale/types.d.ts +90 -0
  382. package/dist/scale/types.d.ts.map +1 -0
  383. package/dist/scale/types.js +17 -0
  384. package/dist/scale/types.js.map +1 -0
  385. package/dist/scene/createScene.d.ts +22 -0
  386. package/dist/scene/createScene.d.ts.map +1 -0
  387. package/dist/scene/createScene.js +114 -0
  388. package/dist/scene/createScene.js.map +1 -0
  389. package/dist/scene/index.d.ts +13 -0
  390. package/dist/scene/index.d.ts.map +1 -0
  391. package/dist/scene/index.js +11 -0
  392. package/dist/scene/index.js.map +1 -0
  393. package/dist/scene/layerRegistry.d.ts +83 -0
  394. package/dist/scene/layerRegistry.d.ts.map +1 -0
  395. package/dist/scene/layerRegistry.js +43 -0
  396. package/dist/scene/layerRegistry.js.map +1 -0
  397. package/dist/scene/types.d.ts +128 -0
  398. package/dist/scene/types.d.ts.map +1 -0
  399. package/dist/scene/types.js +21 -0
  400. package/dist/scene/types.js.map +1 -0
  401. package/dist/scheduler/createFrameBudget.d.ts +91 -0
  402. package/dist/scheduler/createFrameBudget.d.ts.map +1 -0
  403. package/dist/scheduler/createFrameBudget.js +232 -0
  404. package/dist/scheduler/createFrameBudget.js.map +1 -0
  405. package/dist/scheduler/index.d.ts +2 -0
  406. package/dist/scheduler/index.d.ts.map +1 -0
  407. package/dist/scheduler/index.js +2 -0
  408. package/dist/scheduler/index.js.map +1 -0
  409. package/dist/tokens/themeToCssVars.d.ts.map +1 -1
  410. package/dist/tokens/themeToCssVars.js +2 -28
  411. package/dist/tokens/themeToCssVars.js.map +1 -1
  412. package/dist/version.d.ts +1 -1
  413. package/dist/version.d.ts.map +1 -1
  414. package/dist/version.js +1 -1
  415. package/dist/version.js.map +1 -1
  416. package/package.json +1 -1
  417. package/src/__bench__/indicators.bench.ts +215 -0
  418. package/src/__bench__/orderBookHeatmap.bench.ts +68 -0
  419. package/src/__bench__/scale.bench.ts +80 -0
  420. package/src/__bench__/signal.bench.ts +41 -0
  421. package/src/__bench__/volumeProfile.bench.ts +66 -0
  422. package/src/__tests__/errors-help.test.ts +184 -0
  423. package/src/__tests__/errors.test.ts +187 -0
  424. package/src/alerts/__tests__/controller.test.ts +231 -0
  425. package/src/alerts/__tests__/predicates.test.ts +374 -0
  426. package/src/alerts/__tests__/ruleSchema.test.ts +180 -0
  427. package/src/alerts/createAlertController.ts +312 -0
  428. package/src/alerts/index.ts +18 -0
  429. package/src/alerts/predicates.ts +231 -0
  430. package/src/alerts/rollingVolume.ts +51 -0
  431. package/src/alerts/ruleSchema.ts +278 -0
  432. package/src/alerts/types.ts +177 -0
  433. package/src/chartTypes/__tests__/heikinAshi.test.ts +122 -0
  434. package/src/chartTypes/__tests__/pointAndFigure.test.ts +167 -0
  435. package/src/chartTypes/__tests__/rangeBars.test.ts +146 -0
  436. package/src/chartTypes/__tests__/renko.test.ts +160 -0
  437. package/src/chartTypes/heikinAshi.ts +116 -0
  438. package/src/chartTypes/index.ts +5 -0
  439. package/src/chartTypes/pointAndFigure.ts +278 -0
  440. package/src/chartTypes/rangeBars.ts +303 -0
  441. package/src/chartTypes/renko.ts +311 -0
  442. package/src/chartTypes/types.ts +99 -0
  443. package/src/components/anchoredVwap/__tests__/computeAnchoredVwap.test.ts +331 -0
  444. package/src/components/anchoredVwap/__tests__/controller.test.ts +430 -0
  445. package/src/components/anchoredVwap/computeAnchoredVwap.ts +174 -0
  446. package/src/components/anchoredVwap/createAnchoredVwapController.ts +358 -0
  447. package/src/components/anchoredVwap/index.ts +17 -0
  448. package/src/components/anchoredVwap/types.ts +187 -0
  449. package/src/components/crosshairSync/__tests__/crosshairSync.test.ts +261 -0
  450. package/src/components/crosshairSync/createCrosshairSync.ts +187 -0
  451. package/src/components/crosshairSync/index.ts +5 -0
  452. package/src/components/footprint/__tests__/aggressor.test.ts +127 -0
  453. package/src/components/footprint/__tests__/controller.test.ts +130 -0
  454. package/src/components/footprint/__tests__/perBarStats.test.ts +114 -0
  455. package/src/components/footprint/aggressor.ts +165 -0
  456. package/src/components/footprint/createFootprintController.ts +338 -0
  457. package/src/components/footprint/index.ts +21 -0
  458. package/src/components/footprint/perBarStats.ts +137 -0
  459. package/src/components/footprint/types.ts +232 -0
  460. package/src/components/mtfOverlay/__tests__/alignToBaseIndex.test.ts +103 -0
  461. package/src/components/mtfOverlay/__tests__/controller.test.ts +172 -0
  462. package/src/components/mtfOverlay/__tests__/resampleBars.test.ts +106 -0
  463. package/src/components/mtfOverlay/alignToBaseIndex.ts +108 -0
  464. package/src/components/mtfOverlay/createMtfController.ts +180 -0
  465. package/src/components/mtfOverlay/index.ts +26 -0
  466. package/src/components/mtfOverlay/resampleBars.ts +134 -0
  467. package/src/components/mtfOverlay/types.ts +148 -0
  468. package/src/components/orderBookHeatmap/__tests__/controller.test.ts +237 -0
  469. package/src/components/orderBookHeatmap/__tests__/deltaArchive.test.ts +88 -0
  470. package/src/components/orderBookHeatmap/__tests__/logColorScale.test.ts +69 -0
  471. package/src/components/orderBookHeatmap/__tests__/orderBookState.test.ts +113 -0
  472. package/src/components/orderBookHeatmap/__tests__/snapshotRing.test.ts +53 -0
  473. package/src/components/orderBookHeatmap/computeShader.wgsl.md +130 -0
  474. package/src/components/orderBookHeatmap/createHeatmapController.ts +273 -0
  475. package/src/components/orderBookHeatmap/createOrderBookState.ts +116 -0
  476. package/src/components/orderBookHeatmap/deltaArchive.ts +113 -0
  477. package/src/components/orderBookHeatmap/index.ts +25 -0
  478. package/src/components/orderBookHeatmap/logColorScale.ts +77 -0
  479. package/src/components/orderBookHeatmap/snapshotRing.ts +65 -0
  480. package/src/components/orderBookHeatmap/types.ts +168 -0
  481. package/src/components/volumeProfile/__tests__/binning.test.ts +133 -0
  482. package/src/components/volumeProfile/__tests__/poc.test.ts +44 -0
  483. package/src/components/volumeProfile/__tests__/valueArea.test.ts +177 -0
  484. package/src/components/volumeProfile/binning.ts +133 -0
  485. package/src/components/volumeProfile/createVolumeProfileController.ts +213 -0
  486. package/src/components/volumeProfile/index.ts +12 -0
  487. package/src/components/volumeProfile/poc.ts +53 -0
  488. package/src/components/volumeProfile/types.ts +148 -0
  489. package/src/components/volumeProfile/valueArea.ts +187 -0
  490. package/src/controllers/createChartController.ts +23 -10
  491. package/src/controllers/types.ts +5 -1
  492. package/src/data-fetchers/baostock.ts +2 -1
  493. package/src/data-fetchers/dataBuffer.effects.ts +3 -1
  494. package/src/data-fetchers/fetcherDefinitionRegistry.ts +2 -1
  495. package/src/data-fetchers/gotdx.ts +4 -3
  496. package/src/data-fetchers/router.ts +6 -3
  497. package/src/data-fetchers/tradingview.ts +3 -2
  498. package/src/engine/chart.ts +126 -3
  499. package/src/engine/data/chartDataManager.ts +26 -4
  500. package/src/engine/drawing/toolConfig.ts +2 -1
  501. package/src/engine/indicators/chartIndicatorManager.ts +2 -1
  502. package/src/engine/indicators/indicatorDefinitionRegistry.ts +2 -1
  503. package/src/engine/indicators/indicatorMetadata.ts +2 -1
  504. package/src/engine/indicators/indicatorRegistry.ts +6 -5
  505. package/src/engine/indicators/registerBuiltins.ts +2 -1
  506. package/src/engine/indicators/scheduler.ts +15 -0
  507. package/src/engine/indicators/soa.ts +2 -1
  508. package/src/engine/indicators/stateComposer.ts +2 -1
  509. package/src/engine/subPaneManager.ts +2 -1
  510. package/src/engine/utils/chartZoomController.ts +7 -1
  511. package/src/errors-help.ts +218 -0
  512. package/src/errors.ts +128 -0
  513. package/src/index.ts +37 -0
  514. package/src/indicators/__tests__/maFamily.test.ts +159 -0
  515. package/src/indicators/__tests__/oscillators.test.ts +150 -0
  516. package/src/indicators/alma.ts +61 -0
  517. package/src/indicators/awesomeOscillator.ts +54 -0
  518. package/src/indicators/dpo.ts +37 -0
  519. package/src/indicators/fisherTransform.ts +63 -0
  520. package/src/indicators/frama.ts +87 -0
  521. package/src/indicators/index.ts +39 -0
  522. package/src/indicators/lsma.ts +58 -0
  523. package/src/indicators/schaffTrendCycle.ts +92 -0
  524. package/src/indicators/stochRSI.ts +108 -0
  525. package/src/indicators/t3.ts +96 -0
  526. package/src/indicators/ultimateOscillator.ts +83 -0
  527. package/src/indicators/vidya.ts +80 -0
  528. package/src/indicators/zlema.ts +41 -0
  529. package/src/input/__tests__/gesture.test.ts +271 -0
  530. package/src/input/__tests__/keyboard.test.ts +317 -0
  531. package/src/input/gesture.ts +357 -0
  532. package/src/input/index.ts +26 -0
  533. package/src/input/keyboard.ts +373 -0
  534. package/src/mcp/chartBridge.ts +98 -72
  535. package/src/plugin/PluginHost.ts +4 -3
  536. package/src/plugin/PluginRegistry.ts +2 -1
  537. package/src/render/Renderer.ts +127 -0
  538. package/src/render/SurfaceBackend.ts +94 -0
  539. package/src/render/__tests__/contract.test.ts +176 -0
  540. package/src/render/index.ts +27 -0
  541. package/src/renderer-tier/__tests__/detectRendererTier.test.ts +180 -0
  542. package/src/renderer-tier/__tests__/selectBackend.test.ts +253 -0
  543. package/src/renderer-tier/detectRendererTier.ts +168 -0
  544. package/src/renderer-tier/index.ts +30 -0
  545. package/src/renderer-tier/selectBackend.ts +201 -0
  546. package/src/renderer-tier/types.ts +59 -0
  547. package/src/replay/__tests__/controller.test.ts +309 -0
  548. package/src/replay/__tests__/timeline.test.ts +99 -0
  549. package/src/replay/createReplayController.ts +302 -0
  550. package/src/replay/index.ts +15 -0
  551. package/src/replay/timeline.ts +106 -0
  552. package/src/replay/types.ts +107 -0
  553. package/src/scale/__tests__/anchoredZoom.test.ts +125 -0
  554. package/src/scale/__tests__/originShift.test.ts +97 -0
  555. package/src/scale/__tests__/priceScale.test.ts +219 -0
  556. package/src/scale/__tests__/timeScale.test.ts +119 -0
  557. package/src/scale/anchoredZoom.ts +104 -0
  558. package/src/scale/createPriceScale.ts +226 -0
  559. package/src/scale/createTimeScale.ts +150 -0
  560. package/src/scale/index.ts +9 -0
  561. package/src/scale/originShift.ts +111 -0
  562. package/src/scale/types.ts +100 -0
  563. package/src/scene/__tests__/layerRegistry.test.ts +207 -0
  564. package/src/scene/__tests__/scene.test.ts +411 -0
  565. package/src/scene/createScene.ts +117 -0
  566. package/src/scene/index.ts +29 -0
  567. package/src/scene/layerRegistry.ts +117 -0
  568. package/src/scene/types.ts +143 -0
  569. package/src/scheduler/__tests__/createFrameBudget.test.ts +315 -0
  570. package/src/scheduler/createFrameBudget.ts +316 -0
  571. package/src/scheduler/index.ts +7 -0
  572. package/src/tokens/themeToCssVars.ts +3 -1
  573. package/src/version.ts +1 -1
  574. package/dist/data-fetchers/gotdx/gotdx.d.ts +0 -7
  575. package/dist/data-fetchers/gotdx/gotdx.d.ts.map +0 -1
  576. package/dist/data-fetchers/gotdx/gotdx.js +0 -7
  577. package/dist/data-fetchers/gotdx/gotdx.js.map +0 -1
  578. package/src/data-fetchers/gotdx/gotdx.ts +0 -6
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Tests for `createShortcutRegistry` + the parser helpers.
3
+ *
4
+ * Coverage:
5
+ * 1. parseCombo — happy paths, all modifier aliases, errors on
6
+ * malformed inputs (empty, no key, multi-key).
7
+ * 2. canonicalCombo — canonical order, modifier-order independence,
8
+ * single-letter uppercasing, named-key preservation.
9
+ * 3. register / unregister / findByCombo — basic CRUD.
10
+ * 4. Conflict detection — same combo, different ids throws.
11
+ * 5. Re-binding same id to a new combo drops the old binding.
12
+ * 6. findByKeyboardEvent — happy path + missing modifier + extra
13
+ * modifier + non-matching key.
14
+ * 7. Platform Mod resolution — mac → metaKey, other → ctrlKey.
15
+ * 8. `when` predicate gates lookup.
16
+ * 9. dispose() makes everything a no-op.
17
+ */
18
+
19
+ import { describe, it, expect } from 'vitest'
20
+
21
+ import {
22
+ createShortcutRegistry,
23
+ parseCombo,
24
+ canonicalCombo,
25
+ type KeyboardEventLike,
26
+ } from '..'
27
+ import { isKLineChartError } from '../../errors'
28
+
29
+ function evt(over: Partial<KeyboardEventLike>): KeyboardEventLike {
30
+ return {
31
+ key: '',
32
+ ctrlKey: false,
33
+ metaKey: false,
34
+ altKey: false,
35
+ shiftKey: false,
36
+ ...over,
37
+ }
38
+ }
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // parseCombo
42
+ // ---------------------------------------------------------------------------
43
+
44
+ describe('parseCombo', () => {
45
+ it('parses a simple letter', () => {
46
+ const p = parseCombo('k')
47
+ expect(p.key).toBe('k')
48
+ expect(p.requiresMod).toBe(false)
49
+ expect(p.ctrlKey).toBe(false)
50
+ })
51
+
52
+ it.each([
53
+ ['Mod+K', { requiresMod: true, key: 'K' }],
54
+ ['Ctrl+K', { ctrlKey: true, key: 'K' }],
55
+ ['Control+K', { ctrlKey: true, key: 'K' }],
56
+ ['Meta+K', { metaKey: true, key: 'K' }],
57
+ ['Cmd+K', { metaKey: true, key: 'K' }],
58
+ ['Command+K', { metaKey: true, key: 'K' }],
59
+ ['Alt+K', { altKey: true, key: 'K' }],
60
+ ['Option+K', { altKey: true, key: 'K' }],
61
+ ['Shift+K', { shiftKey: true, key: 'K' }],
62
+ ] as const)('%s parses modifier correctly', (src, expected) => {
63
+ const p = parseCombo(src)
64
+ for (const [k, v] of Object.entries(expected)) {
65
+ expect((p as unknown as Record<string, unknown>)[k]).toBe(v)
66
+ }
67
+ })
68
+
69
+ it('parses named keys', () => {
70
+ expect(parseCombo('ArrowRight').key).toBe('ArrowRight')
71
+ expect(parseCombo('Shift+ArrowRight').shiftKey).toBe(true)
72
+ expect(parseCombo('F5').key).toBe('F5')
73
+ })
74
+
75
+ it('throws on empty input', () => {
76
+ try {
77
+ parseCombo('')
78
+ expect.fail('expected throw')
79
+ } catch (e) {
80
+ expect(isKLineChartError(e, 'INVALID_PARAM')).toBe(true)
81
+ }
82
+ })
83
+
84
+ it('throws on only modifiers, no key', () => {
85
+ try {
86
+ parseCombo('Shift+Mod')
87
+ expect.fail('expected throw')
88
+ } catch (e) {
89
+ expect(isKLineChartError(e, 'INVALID_PARAM')).toBe(true)
90
+ }
91
+ })
92
+
93
+ it('throws on multiple non-modifier keys', () => {
94
+ try {
95
+ parseCombo('K+J')
96
+ expect.fail('expected throw')
97
+ } catch (e) {
98
+ expect(isKLineChartError(e, 'INVALID_PARAM')).toBe(true)
99
+ }
100
+ })
101
+
102
+ it('ignores extra whitespace', () => {
103
+ expect(parseCombo(' Mod + K ').key).toBe('K')
104
+ expect(parseCombo(' Mod + K ').requiresMod).toBe(true)
105
+ })
106
+ })
107
+
108
+ // ---------------------------------------------------------------------------
109
+ // canonicalCombo
110
+ // ---------------------------------------------------------------------------
111
+
112
+ describe('canonicalCombo', () => {
113
+ it('sorts modifiers into canonical order', () => {
114
+ expect(canonicalCombo('Shift+Mod+K')).toBe('Mod+Shift+K')
115
+ expect(canonicalCombo('Alt+Shift+Mod+K')).toBe('Mod+Alt+Shift+K')
116
+ })
117
+
118
+ it('uppercases single-letter keys', () => {
119
+ expect(canonicalCombo('mod+k')).toBe('Mod+K')
120
+ })
121
+
122
+ it('preserves named-key casing', () => {
123
+ expect(canonicalCombo('shift+arrowright')).toBe('Shift+arrowright')
124
+ expect(canonicalCombo('Shift+ArrowRight')).toBe('Shift+ArrowRight')
125
+ })
126
+
127
+ it('is idempotent', () => {
128
+ const c = canonicalCombo('alt+shift+mod+k')
129
+ expect(canonicalCombo(c)).toBe(c)
130
+ })
131
+ })
132
+
133
+ // ---------------------------------------------------------------------------
134
+ // register / unregister / findByCombo
135
+ // ---------------------------------------------------------------------------
136
+
137
+ describe('register / unregister / findByCombo', () => {
138
+ it('round-trips a registered shortcut by canonical combo', () => {
139
+ const r = createShortcutRegistry({ platform: 'win' })
140
+ r.register({ id: 'pan.left', label: 'Pan left', combo: 'ArrowLeft', command: 'pan' })
141
+ expect(r.findByCombo('ArrowLeft')?.id).toBe('pan.left')
142
+ expect(r.shortcuts.peek()).toHaveLength(1)
143
+ })
144
+
145
+ it('canonicalises during lookup', () => {
146
+ const r = createShortcutRegistry({ platform: 'win' })
147
+ r.register({ id: 'open', label: 'Open', combo: 'Mod+K', command: 'open' })
148
+ expect(r.findByCombo('Shift+Mod+K')).toBeNull()
149
+ expect(r.findByCombo('Mod+K')?.id).toBe('open')
150
+ })
151
+
152
+ it('unregister removes the binding', () => {
153
+ const r = createShortcutRegistry({ platform: 'win' })
154
+ r.register({ id: 'open', label: 'Open', combo: 'Mod+K', command: 'open' })
155
+ r.unregister('open')
156
+ expect(r.findByCombo('Mod+K')).toBeNull()
157
+ expect(r.shortcuts.peek()).toHaveLength(0)
158
+ })
159
+
160
+ it('unregistering an unknown id is a no-op', () => {
161
+ const r = createShortcutRegistry({ platform: 'win' })
162
+ expect(() => r.unregister('nope')).not.toThrow()
163
+ })
164
+
165
+ it('throws on empty id', () => {
166
+ const r = createShortcutRegistry({ platform: 'win' })
167
+ try {
168
+ r.register({ id: '', label: '', combo: 'K', command: 'x' })
169
+ expect.fail('expected throw')
170
+ } catch (e) {
171
+ expect(isKLineChartError(e, 'INVALID_PARAM')).toBe(true)
172
+ }
173
+ })
174
+ })
175
+
176
+ // ---------------------------------------------------------------------------
177
+ // Conflict detection
178
+ // ---------------------------------------------------------------------------
179
+
180
+ describe('conflict detection', () => {
181
+ it('throws when a second id registers the same canonical combo', () => {
182
+ const r = createShortcutRegistry({ platform: 'win' })
183
+ r.register({ id: 'a', label: 'A', combo: 'Mod+K', command: 'aCmd' })
184
+ try {
185
+ r.register({ id: 'b', label: 'B', combo: 'Shift+Mod+K', command: 'bCmd' })
186
+ // Note: Shift+Mod+K canonicalises differently, so this is the
187
+ // POSITIVE path — the test below covers the conflict.
188
+ } catch {
189
+ expect.fail('Shift+Mod+K is a different combo and must not conflict')
190
+ }
191
+ try {
192
+ r.register({ id: 'c', label: 'C', combo: 'Mod+K', command: 'cCmd' })
193
+ expect.fail('expected conflict throw')
194
+ } catch (e) {
195
+ expect(isKLineChartError(e, 'INVALID_PARAM')).toBe(true)
196
+ expect((e as Error).message).toContain('a')
197
+ }
198
+ })
199
+
200
+ it('re-registering the same id with the same combo is a no-op', () => {
201
+ const r = createShortcutRegistry({ platform: 'win' })
202
+ r.register({ id: 'a', label: 'A', combo: 'Mod+K', command: 'aCmd' })
203
+ expect(() =>
204
+ r.register({ id: 'a', label: 'A-renamed', combo: 'Mod+K', command: 'aCmd' }),
205
+ ).not.toThrow()
206
+ expect(r.findByCombo('Mod+K')?.label).toBe('A-renamed')
207
+ })
208
+
209
+ it('re-binding same id to a different combo drops the old binding', () => {
210
+ const r = createShortcutRegistry({ platform: 'win' })
211
+ r.register({ id: 'a', label: 'A', combo: 'Mod+K', command: 'aCmd' })
212
+ r.register({ id: 'a', label: 'A', combo: 'Mod+J', command: 'aCmd' })
213
+ expect(r.findByCombo('Mod+K')).toBeNull()
214
+ expect(r.findByCombo('Mod+J')?.id).toBe('a')
215
+ })
216
+ })
217
+
218
+ // ---------------------------------------------------------------------------
219
+ // findByKeyboardEvent
220
+ // ---------------------------------------------------------------------------
221
+
222
+ describe('findByKeyboardEvent', () => {
223
+ it('matches a basic letter', () => {
224
+ const r = createShortcutRegistry({ platform: 'win' })
225
+ r.register({ id: 'r', label: 'Reset', combo: 'R', command: 'reset' })
226
+ expect(r.findByKeyboardEvent(evt({ key: 'r' }))?.id).toBe('r')
227
+ expect(r.findByKeyboardEvent(evt({ key: 'R' }))?.id).toBe('r')
228
+ })
229
+
230
+ it('rejects when modifier missing', () => {
231
+ const r = createShortcutRegistry({ platform: 'win' })
232
+ r.register({ id: 'open', label: 'Open', combo: 'Mod+K', command: 'open' })
233
+ expect(r.findByKeyboardEvent(evt({ key: 'k' }))).toBeNull()
234
+ })
235
+
236
+ it('rejects when extra modifier present', () => {
237
+ const r = createShortcutRegistry({ platform: 'win' })
238
+ r.register({ id: 'r', label: 'Reset', combo: 'R', command: 'reset' })
239
+ expect(r.findByKeyboardEvent(evt({ key: 'r', shiftKey: true }))).toBeNull()
240
+ })
241
+
242
+ it('rejects when key differs', () => {
243
+ const r = createShortcutRegistry({ platform: 'win' })
244
+ r.register({ id: 'open', label: 'Open', combo: 'Mod+K', command: 'open' })
245
+ expect(r.findByKeyboardEvent(evt({ key: 'j', ctrlKey: true }))).toBeNull()
246
+ })
247
+
248
+ it('matches named keys', () => {
249
+ const r = createShortcutRegistry({ platform: 'win' })
250
+ r.register({ id: 'panL', label: 'Pan left', combo: 'ArrowLeft', command: 'pan' })
251
+ expect(r.findByKeyboardEvent(evt({ key: 'ArrowLeft' }))?.id).toBe('panL')
252
+ })
253
+ })
254
+
255
+ // ---------------------------------------------------------------------------
256
+ // Platform Mod resolution
257
+ // ---------------------------------------------------------------------------
258
+
259
+ describe('platform Mod resolution', () => {
260
+ it('on mac: Mod matches metaKey, not ctrlKey', () => {
261
+ const r = createShortcutRegistry({ platform: 'mac' })
262
+ r.register({ id: 'open', label: 'Open', combo: 'Mod+K', command: 'open' })
263
+ expect(r.findByKeyboardEvent(evt({ key: 'k', metaKey: true }))?.id).toBe('open')
264
+ expect(r.findByKeyboardEvent(evt({ key: 'k', ctrlKey: true }))).toBeNull()
265
+ })
266
+
267
+ it('on win: Mod matches ctrlKey, not metaKey', () => {
268
+ const r = createShortcutRegistry({ platform: 'win' })
269
+ r.register({ id: 'open', label: 'Open', combo: 'Mod+K', command: 'open' })
270
+ expect(r.findByKeyboardEvent(evt({ key: 'k', ctrlKey: true }))?.id).toBe('open')
271
+ expect(r.findByKeyboardEvent(evt({ key: 'k', metaKey: true }))).toBeNull()
272
+ })
273
+
274
+ it('explicit Ctrl + Meta still bind on both platforms', () => {
275
+ const r = createShortcutRegistry({ platform: 'mac' })
276
+ r.register({ id: 'a', label: 'A', combo: 'Ctrl+K', command: 'a' })
277
+ expect(r.findByKeyboardEvent(evt({ key: 'k', ctrlKey: true }))?.id).toBe('a')
278
+ })
279
+ })
280
+
281
+ // ---------------------------------------------------------------------------
282
+ // `when` predicate
283
+ // ---------------------------------------------------------------------------
284
+
285
+ describe('when predicate', () => {
286
+ it('false predicate skips the shortcut', () => {
287
+ const r = createShortcutRegistry({ platform: 'win' })
288
+ let active = false
289
+ r.register({
290
+ id: 'esc',
291
+ label: 'Cancel tool',
292
+ combo: 'Escape',
293
+ command: 'cancel',
294
+ when: () => active,
295
+ })
296
+ expect(r.findByKeyboardEvent(evt({ key: 'Escape' }))).toBeNull()
297
+ active = true
298
+ expect(r.findByKeyboardEvent(evt({ key: 'Escape' }))?.id).toBe('esc')
299
+ })
300
+ })
301
+
302
+ // ---------------------------------------------------------------------------
303
+ // dispose
304
+ // ---------------------------------------------------------------------------
305
+
306
+ describe('dispose', () => {
307
+ it('makes register / unregister / findByCombo no-ops', () => {
308
+ const r = createShortcutRegistry({ platform: 'win' })
309
+ r.register({ id: 'a', label: 'A', combo: 'K', command: 'a' })
310
+ r.dispose()
311
+ expect(r.findByCombo('K')).toBeNull()
312
+ expect(() =>
313
+ r.register({ id: 'b', label: 'B', combo: 'J', command: 'b' }),
314
+ ).not.toThrow()
315
+ expect(r.findByCombo('J')).toBeNull()
316
+ })
317
+ })
@@ -0,0 +1,357 @@
1
+ /**
2
+ * Pointer gesture recognizer — 1-pointer pan/swipe + 2-pointer pinch.
3
+ *
4
+ * Pure-data state machine: no DOM. Consumers feed `PointerEventLike`
5
+ * records via `onPointerDown` / `onPointerMove` / `onPointerUp`, and
6
+ * read recognized gestures off the `events` signal.
7
+ *
8
+ * The recognizer is **framework-agnostic** for the same reason as the
9
+ * shortcut registry: every consumer touches a different event surface
10
+ * (`PointerEvent`, `MouseEvent`, `TouchEvent`, synthetic events from a
11
+ * Vue composable). Defining the minimal `PointerEventLike` interface
12
+ * means none of them have to adapt to ours — they adapt to themselves
13
+ * and hand us the four fields we need.
14
+ *
15
+ * State diagram (simplified):
16
+ *
17
+ * idle ──[1 down]──▶ tracking (1 pointer)
18
+ * ▲ │ ├──[move past deadzone]──▶ pan
19
+ * │ │ ├──[up + velocity > min]──▶ swipe → idle
20
+ * │ │ └──[up + small move]──▶ tap → idle
21
+ * │ │
22
+ * │ └─[2nd down]──▶ pinch (2 pointers)
23
+ * │ │
24
+ * └─[all up / cancel]────────────────────┘
25
+ *
26
+ * Pan / pinch emit continuous deltas. Tap and swipe are terminal.
27
+ *
28
+ * Why no long-press / double-tap in v1: those are heuristic-laden and
29
+ * usually better delegated to the host platform. The four gestures
30
+ * above cover 95 % of finance-chart interactions (pan to scroll
31
+ * history, pinch to zoom, swipe to flick-scroll, tap to inspect).
32
+ */
33
+
34
+ import { createSignal, type Signal } from '../reactivity'
35
+ import { KLineChartError } from '../errors'
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // Event surface
39
+ // ---------------------------------------------------------------------------
40
+
41
+ export interface PointerEventLike {
42
+ /** Identifier consistent across down/move/up for the same finger/pen. */
43
+ readonly pointerId: number
44
+ /** Client-space x. */
45
+ readonly x: number
46
+ /** Client-space y. */
47
+ readonly y: number
48
+ /** Event timestamp in ms (event.timeStamp from browsers, perf.now() OK). */
49
+ readonly timestamp: number
50
+ }
51
+
52
+ export type GestureEvent =
53
+ | { type: 'tap'; x: number; y: number; timestamp: number }
54
+ | { type: 'panStart'; x: number; y: number; timestamp: number }
55
+ | { type: 'pan'; x: number; y: number; dx: number; dy: number; timestamp: number }
56
+ | { type: 'panEnd'; x: number; y: number; timestamp: number }
57
+ | {
58
+ type: 'swipe'
59
+ x: number
60
+ y: number
61
+ vx: number
62
+ vy: number
63
+ timestamp: number
64
+ }
65
+ | {
66
+ type: 'pinchStart'
67
+ cx: number
68
+ cy: number
69
+ distance: number
70
+ timestamp: number
71
+ }
72
+ | {
73
+ type: 'pinch'
74
+ cx: number
75
+ cy: number
76
+ distance: number
77
+ /** Multiplicative scale relative to the pinchStart distance. */
78
+ scale: number
79
+ timestamp: number
80
+ }
81
+ | { type: 'pinchEnd'; cx: number; cy: number; timestamp: number }
82
+
83
+ export type GestureState = 'idle' | 'tracking' | 'pan' | 'pinch'
84
+
85
+ export interface GestureRecognizerOptions {
86
+ /**
87
+ * Pixel radius the primary pointer must escape before tracking turns
88
+ * into a pan. Below this, the gesture is interpreted as a tap on
89
+ * release. Default 6 — matches the click-vs-drag heuristic UI
90
+ * frameworks settle on.
91
+ */
92
+ readonly panDeadzone?: number
93
+ /**
94
+ * Minimum velocity (px/ms) on release to classify a movement as
95
+ * a swipe rather than a pan-end. Default 0.5 — the median swipe
96
+ * velocity on mid-range mobile devices.
97
+ */
98
+ readonly swipeMinVelocity?: number
99
+ /**
100
+ * Window in ms over which the swipe velocity is averaged.
101
+ * Default 100ms. Larger smooths jitter; smaller catches flicks.
102
+ */
103
+ readonly swipeVelocityWindowMs?: number
104
+ }
105
+
106
+ export interface GestureRecognizer {
107
+ readonly state: Signal<GestureState>
108
+ readonly events: Signal<GestureEvent | null>
109
+ onPointerDown(e: PointerEventLike): void
110
+ onPointerMove(e: PointerEventLike): void
111
+ onPointerUp(e: PointerEventLike): void
112
+ /** Resets to idle without emitting. Use on tab blur, etc. */
113
+ cancel(): void
114
+ dispose(): void
115
+ }
116
+
117
+ // ---------------------------------------------------------------------------
118
+ // Implementation
119
+ // ---------------------------------------------------------------------------
120
+
121
+ const DEFAULT_OPTS: Required<GestureRecognizerOptions> = {
122
+ panDeadzone: 6,
123
+ swipeMinVelocity: 0.5,
124
+ swipeVelocityWindowMs: 100,
125
+ }
126
+
127
+ interface ActivePointer {
128
+ pointerId: number
129
+ startX: number
130
+ startY: number
131
+ startTs: number
132
+ lastX: number
133
+ lastY: number
134
+ lastTs: number
135
+ // small ring buffer for velocity smoothing
136
+ history: Array<{ x: number; y: number; ts: number }>
137
+ }
138
+
139
+ export function createGestureRecognizer(
140
+ opts: GestureRecognizerOptions = {},
141
+ ): GestureRecognizer {
142
+ if (opts.panDeadzone !== undefined && opts.panDeadzone < 0) {
143
+ throw new KLineChartError(
144
+ 'INVALID_PARAM',
145
+ `createGestureRecognizer: panDeadzone must be >= 0, got ${opts.panDeadzone}`,
146
+ )
147
+ }
148
+ if (opts.swipeMinVelocity !== undefined && opts.swipeMinVelocity < 0) {
149
+ throw new KLineChartError(
150
+ 'INVALID_PARAM',
151
+ `createGestureRecognizer: swipeMinVelocity must be >= 0, got ${opts.swipeMinVelocity}`,
152
+ )
153
+ }
154
+ const cfg = { ...DEFAULT_OPTS, ...opts }
155
+
156
+ const state = createSignal<GestureState>('idle')
157
+ const events = createSignal<GestureEvent | null>(null)
158
+ let disposed = false
159
+
160
+ const active = new Map<number, ActivePointer>()
161
+ // pinchStartDistance captured when transitioning into 'pinch'
162
+ let pinchStartDistance = 0
163
+
164
+ function emit(event: GestureEvent): void {
165
+ if (disposed) return
166
+ events.set(event)
167
+ }
168
+
169
+ function dist(a: ActivePointer, b: ActivePointer): number {
170
+ const dx = a.lastX - b.lastX
171
+ const dy = a.lastY - b.lastY
172
+ return Math.sqrt(dx * dx + dy * dy)
173
+ }
174
+
175
+ function centroid(a: ActivePointer, b: ActivePointer): { cx: number; cy: number } {
176
+ return { cx: (a.lastX + b.lastX) / 2, cy: (a.lastY + b.lastY) / 2 }
177
+ }
178
+
179
+ function pushHistory(p: ActivePointer, e: PointerEventLike): void {
180
+ p.history.push({ x: e.x, y: e.y, ts: e.timestamp })
181
+ // Trim history older than the velocity window from the latest
182
+ const cutoff = e.timestamp - cfg.swipeVelocityWindowMs
183
+ while (p.history.length > 0 && p.history[0]!.ts < cutoff) {
184
+ p.history.shift()
185
+ }
186
+ }
187
+
188
+ function recentVelocity(p: ActivePointer): { vx: number; vy: number } {
189
+ if (p.history.length < 2) return { vx: 0, vy: 0 }
190
+ const first = p.history[0]!
191
+ const last = p.history[p.history.length - 1]!
192
+ const dt = last.ts - first.ts
193
+ if (dt <= 0) return { vx: 0, vy: 0 }
194
+ return { vx: (last.x - first.x) / dt, vy: (last.y - first.y) / dt }
195
+ }
196
+
197
+ function onPointerDown(e: PointerEventLike): void {
198
+ if (disposed) return
199
+ const p: ActivePointer = {
200
+ pointerId: e.pointerId,
201
+ startX: e.x,
202
+ startY: e.y,
203
+ startTs: e.timestamp,
204
+ lastX: e.x,
205
+ lastY: e.y,
206
+ lastTs: e.timestamp,
207
+ history: [{ x: e.x, y: e.y, ts: e.timestamp }],
208
+ }
209
+ active.set(e.pointerId, p)
210
+ if (active.size === 1) {
211
+ state.set('tracking')
212
+ } else if (active.size === 2) {
213
+ const [a, b] = [...active.values()] as [ActivePointer, ActivePointer]
214
+ const c = centroid(a, b)
215
+ pinchStartDistance = Math.max(dist(a, b), 1e-9)
216
+ state.set('pinch')
217
+ emit({
218
+ type: 'pinchStart',
219
+ cx: c.cx,
220
+ cy: c.cy,
221
+ distance: pinchStartDistance,
222
+ timestamp: e.timestamp,
223
+ })
224
+ }
225
+ // Extra pointers (3+) are ignored — we don't recognise 3-finger
226
+ // gestures in v1.
227
+ }
228
+
229
+ function onPointerMove(e: PointerEventLike): void {
230
+ if (disposed) return
231
+ const p = active.get(e.pointerId)
232
+ if (p === undefined) return
233
+ const dx = e.x - p.lastX
234
+ const dy = e.y - p.lastY
235
+ p.lastX = e.x
236
+ p.lastY = e.y
237
+ p.lastTs = e.timestamp
238
+ pushHistory(p, e)
239
+
240
+ if (state.peek() === 'pinch' && active.size >= 2) {
241
+ const [a, b] = [...active.values()] as [ActivePointer, ActivePointer]
242
+ const c = centroid(a, b)
243
+ const d = dist(a, b)
244
+ emit({
245
+ type: 'pinch',
246
+ cx: c.cx,
247
+ cy: c.cy,
248
+ distance: d,
249
+ scale: d / pinchStartDistance,
250
+ timestamp: e.timestamp,
251
+ })
252
+ return
253
+ }
254
+
255
+ // Single-pointer path: maybe promote tracking → pan
256
+ if (state.peek() === 'tracking') {
257
+ const ddx = e.x - p.startX
258
+ const ddy = e.y - p.startY
259
+ if (Math.sqrt(ddx * ddx + ddy * ddy) > cfg.panDeadzone) {
260
+ state.set('pan')
261
+ emit({ type: 'panStart', x: e.x, y: e.y, timestamp: e.timestamp })
262
+ // Also emit the first pan event right away. dx/dy carry
263
+ // the full delta from the down position so the consumer
264
+ // doesn't have to wait for a second move to react.
265
+ emit({
266
+ type: 'pan',
267
+ x: e.x,
268
+ y: e.y,
269
+ dx: ddx,
270
+ dy: ddy,
271
+ timestamp: e.timestamp,
272
+ })
273
+ }
274
+ return
275
+ }
276
+ if (state.peek() === 'pan') {
277
+ emit({ type: 'pan', x: e.x, y: e.y, dx, dy, timestamp: e.timestamp })
278
+ }
279
+ }
280
+
281
+ function onPointerUp(e: PointerEventLike): void {
282
+ if (disposed) return
283
+ const p = active.get(e.pointerId)
284
+ if (p === undefined) return
285
+
286
+ if (state.peek() === 'pinch') {
287
+ // Decommit pinch; if exactly one pointer remains, drop back into
288
+ // 'tracking' on it (the remaining finger can still pan).
289
+ active.delete(e.pointerId)
290
+ const remaining = active.values().next().value as ActivePointer | undefined
291
+ const c = remaining
292
+ ? { cx: remaining.lastX, cy: remaining.lastY }
293
+ : { cx: e.x, cy: e.y }
294
+ emit({ type: 'pinchEnd', cx: c.cx, cy: c.cy, timestamp: e.timestamp })
295
+ if (active.size === 1 && remaining !== undefined) {
296
+ // Re-baseline the remaining pointer so panDeadzone applies
297
+ // afresh and we don't immediately fall into a pan-tracking
298
+ // weirdness using the original press position.
299
+ remaining.startX = remaining.lastX
300
+ remaining.startY = remaining.lastY
301
+ remaining.startTs = e.timestamp
302
+ remaining.history = [
303
+ { x: remaining.lastX, y: remaining.lastY, ts: e.timestamp },
304
+ ]
305
+ state.set('tracking')
306
+ } else {
307
+ state.set('idle')
308
+ }
309
+ return
310
+ }
311
+
312
+ // Single-pointer end paths: tap, swipe, panEnd, or just clean up.
313
+ active.delete(e.pointerId)
314
+ const ddx = p.lastX - p.startX
315
+ const ddy = p.lastY - p.startY
316
+ const distMoved = Math.sqrt(ddx * ddx + ddy * ddy)
317
+
318
+ if (state.peek() === 'pan') {
319
+ const { vx, vy } = recentVelocity(p)
320
+ const speed = Math.sqrt(vx * vx + vy * vy)
321
+ if (speed >= cfg.swipeMinVelocity) {
322
+ emit({
323
+ type: 'swipe',
324
+ x: e.x,
325
+ y: e.y,
326
+ vx,
327
+ vy,
328
+ timestamp: e.timestamp,
329
+ })
330
+ } else {
331
+ emit({ type: 'panEnd', x: e.x, y: e.y, timestamp: e.timestamp })
332
+ }
333
+ } else if (state.peek() === 'tracking') {
334
+ if (distMoved <= cfg.panDeadzone) {
335
+ emit({ type: 'tap', x: e.x, y: e.y, timestamp: e.timestamp })
336
+ }
337
+ // else: aborted gesture, no emit (movement was below deadzone
338
+ // forever; nothing meaningful happened)
339
+ }
340
+
341
+ state.set(active.size === 0 ? 'idle' : 'tracking')
342
+ }
343
+
344
+ function cancel(): void {
345
+ if (disposed) return
346
+ active.clear()
347
+ state.set('idle')
348
+ }
349
+
350
+ function dispose(): void {
351
+ if (disposed) return
352
+ disposed = true
353
+ active.clear()
354
+ }
355
+
356
+ return { state, events, onPointerDown, onPointerMove, onPointerUp, cancel, dispose }
357
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @klinechart-quant/core/input — framework-agnostic input layer.
3
+ *
4
+ * Shipping module: {@link createShortcutRegistry}. See `./keyboard.ts`
5
+ * for the design notes.
6
+ */
7
+
8
+ export {
9
+ createShortcutRegistry,
10
+ parseCombo,
11
+ canonicalCombo,
12
+ type ShortcutRegistry,
13
+ type ShortcutRegistryOptions,
14
+ type ShortcutDef,
15
+ type ParsedCombo,
16
+ type ModifierState,
17
+ type KeyboardEventLike,
18
+ } from './keyboard'
19
+ export {
20
+ createGestureRecognizer,
21
+ type GestureRecognizer,
22
+ type GestureRecognizerOptions,
23
+ type GestureEvent,
24
+ type GestureState,
25
+ type PointerEventLike,
26
+ } from './gesture'