-
-
- {{ i18n('view_home_hero_title', lang) }}
-
+
+
{{ i18n('view_home_hero_desc', lang) }}
@@ -39,9 +37,11 @@ const { lang } = useData()
display: flex;
flex-direction: column;
align-items: center;
+ justify-content: center;
margin-top: calc((var(--vp-nav-height) + var(--vp-layout-top-height, 0px)) * -1);
- padding: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 48px) 24px 48px;
- /* background: var(--vp-home-hero-bg); */
+ padding: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 60px) 24px 80px 24px;
+ background: var(--vp-home-hero-bg);
+ transition: background .3s ease;
}
.container {
@@ -53,20 +53,6 @@ const { lang } = useData()
max-width: 1080px;
}
-.name {
- height: 22px;
-}
-
-.text {
- max-width: 460px;
- line-height: 36px;
- font-size: 30px;
- font-weight: 700;
- padding: 10px 0;
- white-space: pre-wrap;
- max-width: 80%;
-}
-
.tagline {
max-width: 392px;
line-height: 26px;
@@ -75,6 +61,8 @@ const { lang } = useData()
white-space: pre-wrap;
color: var(--vp-c-text-2);
max-width: 100%;
+ opacity: 0;
+ animation: toTop .6s ease forwards;
}
.actions {
@@ -84,61 +72,24 @@ const { lang } = useData()
gap: 12px;
padding-top: 32px;
font-weight: 500;
+ opacity: 0;
+ animation: toTop .6s ease forwards 0.6s;
}
-.quick-start-action {
- display: flex;
- flex-direction: row;
- align-items: center;
- border-radius: 99999px;
- cursor: pointer;
- border: solid 1px var(--vp-c-indigo-1);
- background-color: var(--vp-c-indigo-1);
- color: #fff;
- padding: 0 16px;
- height: 36px;
- font-size: 14px;
- transition: all 0.3s ease;
-}
-
-.quick-start-action:hover {
- background-color: var(--vp-c-indigo-2);
- border-color: var(--vp-c-indigo-2);
-}
-
-.quick-start-action svg {
- width: 12px;
- height: 12px;;
- fill: currentColor;
- transition: all 0.3s ease;
- margin-inline-start: 6px;
-}
-
-.quick-start-action:hover svg {
- animation: move 0.45s ease infinite alternate;
-}
-
-@keyframes move {
- 0% {
- transform: translateX(-1px);
+@keyframes toTop {
+ from {
+ opacity: 0;
+ transform: translateY(50px);
}
- 100% {
- transform: translateX(1px);
+ to {
+ opacity: 1;
+ transform: translateY(0);
}
}
@media (min-width: 640px) {
.hero {
- padding: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 80px) 48px 64px;
- }
- .name {
- height: 34px;
- }
- .text {
- line-height: 50px;
- font-size: 44px;
- padding: 14px 0 26px 0;
- max-width: 70%;
+ padding: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 100px) 48px 110px 48px;
}
.tagline {
max-width: 576px;
@@ -148,30 +99,11 @@ const { lang } = useData()
gap: 20px;
padding-top: 40px;
}
- .quick-start-action {
- height: 40px;
- font-size: 16px;
- padding: 0 20px;
- }
- .quick-start-action svg {
- width: 14px;
- height: 14px;
- margin-inline-start: 8px;
- }
}
@media (min-width: 960px) {
.hero {
- padding: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 100px) 64px 100px 64px;
- }
- .name {
- height: 40px;
- }
- .text {
- line-height: 60px;
- padding: 20px 0 34px 0;
- font-size: 54px;
- max-width: 700px;
+ padding: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 120px) 64px 120px 64px;
}
.tagline {
font-size: 24px;
diff --git a/docs/@views/home/hero/slogan/DropText.vue b/docs/@views/home/hero/slogan/DropText.vue
new file mode 100644
index 000000000..74243c324
--- /dev/null
+++ b/docs/@views/home/hero/slogan/DropText.vue
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+ {{ char }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/@views/home/hero/slogan/index.vue b/docs/@views/home/hero/slogan/index.vue
new file mode 100644
index 000000000..66e90f6bd
--- /dev/null
+++ b/docs/@views/home/hero/slogan/index.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/@views/styles/config.md b/docs/@views/styles/config.md
index 42ea74eac..cfa6d84bd 100644
--- a/docs/@views/styles/config.md
+++ b/docs/@views/styles/config.md
@@ -18,7 +18,7 @@ const styles = {
}
},
candle: {
- // 'candle_solid' | 'candle_stroke' | 'candle_up_stroke' | 'candle_down_stroke' | 'ohlc' | 'area'
+ // 'candle_solid' | 'candle_stroke' | 'candle_up_stroke' | 'candle_down_stroke' | 'ohlc' | 'area' | 'heikin_ashi'
type: 'candle_solid',
bar: {
// 'current_open' | 'previous_close'
diff --git a/docs/api/chart/init.md b/docs/api/chart/init.md
index 92e615e19..cefa5b044 100644
--- a/docs/api/chart/init.md
+++ b/docs/api/chart/init.md
@@ -2,12 +2,29 @@
outline: deep
---
+
+
# init(ds, options?)
`init` 用于初始化一个图表。
-::: tip 提示
-调用时,需要等待容器 `dom` 准备完成之后。
-:::
+
## 参考 {#reference}
@@ -54,23 +71,6 @@ outline: deep
`init` 返回一个图表实例对象 `Chart`。
## 用法 {#usage}
-
### 基本使用 {#basic}
diff --git a/docs/api/instance/createOverlay.md b/docs/api/instance/createOverlay.md
index 51e50d999..580bd49e3 100644
--- a/docs/api/instance/createOverlay.md
+++ b/docs/api/instance/createOverlay.md
@@ -49,6 +49,7 @@ import CreateOverlayBasic from '../../@views/api/samples/createOverlay-basic/ind
import CreateOverlayExtension from '../../@views/api/samples/custom-figure-custom-overlay/index.vue'
import CreateOverlayPoints from '../../@views/api/samples/createOverlay-points/index.vue'
import CreateOverlayBatch from '../../@views/api/samples/createOverlay-batch/index.vue'
+import CreateOverlayFreePath from '../../@views/api/samples/createOverlay-freePath/index.vue'
### 基本使用 {#basic}
@@ -62,3 +63,6 @@ import CreateOverlayBatch from '../../@views/api/samples/createOverlay-batch/ind
### 批量创建 {#batch}
+
+### 自由路径绘制(双击并拖动可绘制无限次){#freePath}
+
diff --git a/docs/api/instance/getBarSpaceLimit.md b/docs/api/instance/getBarSpaceLimit.md
new file mode 100644
index 000000000..f4cccaacd
--- /dev/null
+++ b/docs/api/instance/getBarSpaceLimit.md
@@ -0,0 +1,23 @@
+---
+outline: deep
+---
+
+# getBarSpaceLimit()
+`getBarSpaceLimit` 获取图表单根蜡烛柱所占空间最大值/最小值。
+
+## 参考 {#reference}
+
+
+### 参数 {#parameters}
+`getBarSpaceLimit` 不接收任何参数。
+
+### 返回值 {#returns}
+`getBarSpaceLimit` 返回一个包含单根蜡烛柱所占空间信息的对象 `BarSpaceLimit` 。
+
+## 用法 {#usage}
+
+
+### 基本使用 {#basic}
+
\ No newline at end of file
diff --git a/docs/api/instance/getZoomAnchor.md b/docs/api/instance/getZoomAnchor.md
index 58aef6a07..c9faafa3c 100644
--- a/docs/api/instance/getZoomAnchor.md
+++ b/docs/api/instance/getZoomAnchor.md
@@ -12,7 +12,7 @@ outline: deep
`getZoomAnchor` 不接受任何参数。
### 返回值 {#returns}
-`getZoomAnchor` 回报 `ZoomAnchor` 。
+`getZoomAnchor` 返回对象 `ZoomAnchor` 。
## 用法 {#usage}
+
# overrideIndicator(indicator)
`overrideIndicator` 覆盖指标属性。
@@ -38,18 +44,12 @@ outline: deep
- `draw` 自定义绘制方法,如果返回值是 `true` ,则会覆盖默认的绘制。
- `onDataStateChange` 数据变化回调通知。
-::: tip 提示
-其中 `id`,`paneId` 和 `name` 是索引,如果入参包含这三个参数,会根据这三个参数查找符合条件的指标来覆盖。
-:::
+
### 返回值 {#returns}
`overrideIndicator` 返回 `undefined` 。
## 用法 {#usage}
-
### 基本使用 {#basic}
diff --git a/docs/api/instance/resize.md b/docs/api/instance/resize.md
index 807d93d89..1b63f7fb4 100644
--- a/docs/api/instance/resize.md
+++ b/docs/api/instance/resize.md
@@ -2,12 +2,15 @@
outline: deep
---
+
+
# resize()
`resize` 调整图表尺寸。
-::: warning 注意
-总是会填充容器大小,此方法会重新计算整个图表各个模块的大小,频繁调用可能会影响到性能,调用请谨慎。
-:::
+
## 参考 {#reference}
@@ -19,9 +22,6 @@ outline: deep
`resize` 返回 `undefined` 。
## 用法 {#usage}
-
### 基本用法 {#basic}
\ No newline at end of file
diff --git a/docs/api/instance/setBarSpace.md b/docs/api/instance/setBarSpace.md
index f11a66ce0..fbb55116c 100644
--- a/docs/api/instance/setBarSpace.md
+++ b/docs/api/instance/setBarSpace.md
@@ -9,7 +9,7 @@ outline: deep
### 参数 {#parameters}
-- `space` 空间大小,范围在 1 到 50 之间。
+- `space` 空间大小,默认范围在 1 到 50 之间,可以通过`setBarSpaceLimit`设置。
### 返回值 {#returns}
`setBarSpace` 返回 `undefined` 。
diff --git a/docs/api/instance/setBarSpaceLimit.md b/docs/api/instance/setBarSpaceLimit.md
new file mode 100644
index 000000000..e37a7ff69
--- /dev/null
+++ b/docs/api/instance/setBarSpaceLimit.md
@@ -0,0 +1,25 @@
+---
+outline: deep
+---
+
+# setBarSpaceLimit(limit)
+`setBarSpaceLimit` 设置图表单根蜡烛柱所占的空间最大值和最小值
+
+## 参考 {#reference}
+
+
+### 参数 {#parameters}
+- `limit` 空间大小限制。
+ - `min` 最小值。
+ - `max` 最大值。
+
+### 返回值 {#returns}
+`setBarSpaceLimit` 返回 `undefined` 。
+
+## 用法 {#usage}
+
+
+### 基本用法 {#basic}
+
\ No newline at end of file
diff --git a/docs/api/instance/setZoomAnchor.md b/docs/api/instance/setZoomAnchor.md
index 7e6653568..2fa8367e2 100644
--- a/docs/api/instance/setZoomAnchor.md
+++ b/docs/api/instance/setZoomAnchor.md
@@ -9,10 +9,10 @@ outline: deep
### 参数 {#parameters}
-- `anchor: ZoomAnchor` 主窗格和 x 轴窗格的缩放锚点类型。
+- `anchor`: 主窗格和 x 轴窗格的缩放锚点类型, 支持 `cursor` , `last_bar` 和 `{ main: 'cursor' | 'last_bar', xAxis: 'cursor' | 'last_bar' }`。
### 返回值 {#returns}
-`setZoomAnchor` 返回 `void` 。
+`setZoomAnchor` 返回 `undefined` 。
## 用法 {#usage}
+
# init(ds, options?)
`init` used to initialize a chart.
-::: tip Tip
-When calling, you need to wait until the container `dom` is ready.
-:::
+
## Reference {#reference}
@@ -48,29 +65,12 @@ When calling, you need to wait until the container `dom` is ready.
- `decimalFold` Decimal 0 folds the configuration.
- `threshold` Fold threshold.
- `format` Custom formatting method.
- - `zoomAnchor` Set the anchor position when zooming the chart to `last_bar` or `cursor_point`
+ - `zoomAnchor` Set the anchor position when zooming the chart to `last_bar` or `cursor`
### Returns {#returns}
`init` returns an object `Chart`。
## Usage {#usage}
-
### Basic usage {#basic}
diff --git a/docs/en-US/api/instance/createOverlay.md b/docs/en-US/api/instance/createOverlay.md
index 1849b4775..3d81fad05 100644
--- a/docs/en-US/api/instance/createOverlay.md
+++ b/docs/en-US/api/instance/createOverlay.md
@@ -49,6 +49,7 @@ import CreateOverlayBasic from '../../../@views/api/samples/createOverlay-basic/
import CreateOverlayExtension from '../../../@views/api/samples/custom-figure-custom-overlay/index.vue'
import CreateOverlayPoints from '../../../@views/api/samples/createOverlay-points/index.vue'
import CreateOverlayBatch from '../../../@views/api/samples/createOverlay-batch/index.vue'
+import CreateOverlayFreePath from '../../../@views/api/samples/createOverlay-freePath/index.vue'
### Basic usage {#basic}
@@ -62,3 +63,6 @@ import CreateOverlayBatch from '../../../@views/api/samples/createOverlay-batch/
### Batch create {#batch}
+
+### Free path drawing (double click and drag to draw unlimited times) {#freePath}
+
diff --git a/docs/en-US/api/instance/getBarSpaceLimit.md b/docs/en-US/api/instance/getBarSpaceLimit.md
new file mode 100644
index 000000000..6b9414b51
--- /dev/null
+++ b/docs/en-US/api/instance/getBarSpaceLimit.md
@@ -0,0 +1,23 @@
+---
+outline: deep
+---
+
+# getBarSpaceLimit()
+`getBarSpaceLimit` get limit size about the space occupied by a single candlestick on the chart.
+
+## Reference {#reference}
+
+
+### Parameters {#parameters}
+`getBarSpaceLimit` does not accept any parameters.
+
+### Returns {#returns}
+`getBarSpaceLimit` returns an object containing information about the space occupied by a single candlestick `BarSpaceLimit`.
+
+## Usage {#usage}
+
+
+### Basic usage {#basic}
+
\ No newline at end of file
diff --git a/docs/en-US/api/instance/overrideIndicator.md b/docs/en-US/api/instance/overrideIndicator.md
index a59191924..f7eca2280 100644
--- a/docs/en-US/api/instance/overrideIndicator.md
+++ b/docs/en-US/api/instance/overrideIndicator.md
@@ -2,6 +2,12 @@
outline: deep
---
+
+
# overrideIndicator(indicator)
`overrideIndicator` override indicator attrs.
@@ -37,19 +43,14 @@ outline: deep
- `createTooltipDataSource` Create custom prompts.
- `draw` Custom drawing method, if the return value is `true`, it will override the default drawing.
- `onDataStateChange` Data change callback notification.
-
-::: tip Tip
-Among them, `id`, `paneId` and `name` are indexes. If the input parameters contain these three parameters, the indicators that meet the conditions will be searched for overwriting based on these three parameters.
-:::
+
+
+
### Returns {#returns}
`overrideIndicator` returns `undefined` .
## Usage {#usage}
-
### Basic usage {#basic}
diff --git a/docs/en-US/api/instance/resize.md b/docs/en-US/api/instance/resize.md
index 745ca8a8f..905cd2284 100644
--- a/docs/en-US/api/instance/resize.md
+++ b/docs/en-US/api/instance/resize.md
@@ -2,12 +2,19 @@
outline: deep
---
+
+
# resize()
`resize` resize the chart.
-::: warning Warning
-The container size will always be filled. This method will recalculate the size of each module of the entire chart. Frequent calls may affect performance, so please call with caution.
-:::
+
+
## Reference {#reference}
@@ -19,9 +26,6 @@ The container size will always be filled. This method will recalculate the size
`resize` returns `undefined` .
## Usage {#usage}
-
### Basic usage {#basic}
\ No newline at end of file
diff --git a/docs/en-US/api/instance/setBarSpaceLimit.md b/docs/en-US/api/instance/setBarSpaceLimit.md
new file mode 100644
index 000000000..a9d18da6f
--- /dev/null
+++ b/docs/en-US/api/instance/setBarSpaceLimit.md
@@ -0,0 +1,25 @@
+---
+outline: deep
+---
+
+# setBarSpaceLimit(limit)
+`setBarSpaceLimit` set the limit size of the space that a single candlestick on the chart should occupy.
+
+## Reference {#reference}
+
+
+### Parameters {#parameters}
+- `limit` The limit size of the space
+ - `min` min value of the space
+ - `max` max value of the space
+
+### Returns {#returns}
+`setBarSpaceLimit` returns `undefined` 。
+
+## Usage {#usage}
+
+
+### Basic usage {#basic}
+
\ No newline at end of file
diff --git a/docs/en-US/api/instance/setZoomAnchor.md b/docs/en-US/api/instance/setZoomAnchor.md
index 604e5dcb1..d2644a8e3 100644
--- a/docs/en-US/api/instance/setZoomAnchor.md
+++ b/docs/en-US/api/instance/setZoomAnchor.md
@@ -9,10 +9,10 @@ outline: deep
### Parameters {#parameters}
-- `anchor: ZoomAnchor` zoom anchor point type for main and xAxis pane.
+- `anchor: ZoomAnchor` zoom anchor point type for main and xAxis pane, supports `cursor` , `last_bar` 和 `{ main: 'cursor' | 'last_bar', xAxis: 'cursor' | 'last_bar' }`.
### Returns {#returns}
-`setZoomAnchor` returns `void` .
+`setZoomAnchor` returns `undefined` .
## Usage {#usage}
+
# Figure
Figure are an important part of the chart. All elements on the chart are composed of figures. If you need to make complex custom technical indicators and overlays, it is recommended to read carefully. This document describes the built-in figures and how to customize a figure. The basic figure can be obtained through the chart method `klinecharts.getFigureClass(name)`.
## Example of use
-::: warning Note
-It needs to be used when there is a canvas context.
-:::
+
```javascript
// Get the figure class
diff --git a/docs/en-US/guide/indicator.md b/docs/en-US/guide/indicator.md
index 7aa47d9d9..1a710859d 100644
--- a/docs/en-US/guide/indicator.md
+++ b/docs/en-US/guide/indicator.md
@@ -1,3 +1,7 @@
+
+
# Technical indicator
This document introduces the built-in technical indicators in the chart and how to customize a technical indicator.
@@ -14,9 +18,7 @@ This document introduces the built-in technical indicators in the chart and how
| KDJ | [9, 3, 3] | TRIX | [12, 20] | PVT | None |
| RSI | [6, 12, 24] | OBV | [30] | AVP | None |
-::: tip Tip
-Some indicators can be overlaid on the candlestick using `chart.createIndicator('MA', true, { id:'candle_pane' })` and some cannot. Compatible indicators are: BBI, BOLL, EMA, MA, SAR, SMA. You can use custom drawing of custom indicators to draw the indicator on the candlestick chart to make it compatible with the candlestick.
-:::
+
## Custom Indicators
diff --git a/docs/en-US/guide/local-development.md b/docs/en-US/guide/local-development.md
index 02d190dd6..b50d126a3 100644
--- a/docs/en-US/guide/local-development.md
+++ b/docs/en-US/guide/local-development.md
@@ -1,4 +1,4 @@
-# Local Development
+# 👨💻 Local Development
## Introduction
If you see this, you may be interested in improving the KLineChart core. Thank you [@fish2016](https://github.com/fish2016) This document was written.
diff --git a/docs/en-US/guide/overlay.md b/docs/en-US/guide/overlay.md
index 4364e65a3..2c703dda4 100644
--- a/docs/en-US/guide/overlay.md
+++ b/docs/en-US/guide/overlay.md
@@ -2,7 +2,7 @@
This document introduces the built-in overlays in the chart and how to customize a overlay.
## Built-in overlay types
-`horizontalRayLine`, `horizontalSegment`, `horizontalStraightLine`, `verticalRayLine`, `verticalSegment`, `verticalStraightLine`, `rayLine`, `segment`, `straightLine`, `priceLine`, `priceChannelLine`, `parallelL`filineLine`, ci `, `simpleAnnotation`, `simpleTag`
+`horizontalRayLine`, `horizontalSegment`, `horizontalStraightLine`, `verticalRayLine`, `verticalSegment`, `verticalStraightLine`, `rayLine`, `segment`, `straightLine`, `priceLine`, `priceChannelLine`, `parallelStraightLine`, `fibonacciLine`, `freePath`, `simpleAnnotation`, `simpleTag`
## Custom overlays
To create custom an overlay, then add it globally via [registerOverlay](/api/chart/registerOverlay) and add it to the chart to use it just like a built-in overlay. For more examples, refer to the files under [https://github.com/klinecharts/KLineChart/tree/main/src/extension/overlay](https://github.com/klinecharts/KLineChart/tree/main/src/extension/overlay) .
\ No newline at end of file
diff --git a/docs/guide/changelog.md b/docs/guide/changelog.md
index c3de3e2a1..5a3ba23f1 100644
--- a/docs/guide/changelog.md
+++ b/docs/guide/changelog.md
@@ -1,87 +1,35 @@
# 📠 更新日志
-## 10.0.0-alpha9
-`2025-09-02`
-+ 👉 指标方法 `calc` 返回值由数组变更为以时间戳为key的对象。
-+ 💄 优化指标计算任务执行。
-+ 🐞 修复实例api `setSymbol` , `setPeriod` 和 `setDataLoader` 不重置Y轴问题。
-
-## 10.0.0-alpha8
-`2025-06-14`
-+ 🐞 修复 typescript 引用错误。
-
-## 10.0.0-alpha7
-`2025-06-14`
-+ 👉 图表api `init(ds, options)` 中的 `options.customApi` 变更为 `options.formatter` , `formatDate` 参数变更为对象。
-+ 👉 实例api `setCustomApi` 变更为 `setFormatter` , `getCustomApi` 变更为 `getFormatter`。
-+ 🆕 样式配置新增 `candle.priceMark.last.extendTexts` , `candle.tooltip.title` , `candle.tooltip.legend` , `indicator.tooltip.title` , `indicator.tooltip.legend` , 和 `crosshair.horizontal.features` 。
-+ 🆕 实例方法新增 `setDataLoader` , `setSymbol` , `getSymbol` , `setPeriod` , `getPeriod` 和 `resetData` 。
-+ 🆕 实例api的 `subscribeAction` 和 `unsubscribeAction` , 入参 `type` 新增 `onIndicatorTooltipFeatureClick` 和 `onCrosshairFeatureClick` 。
-+ 🗑 样式配置删除 `candle.tooltip.defaultValue` , `candle.tooltip.custom` 请替换为 `candle.tooltip.legend` ,删除 `candle.tooltip.text` ,删除 `indicator.tooltip.showName` , `indicator.tooltip.showParams` ,请用 `indicator.tooltip.title` ,删除 `indicator.tooltip.defaultValue` , 请替换为 `indicator.tooltip.legend` , 删除 `indicator.tooltip.text` 。
-+ 🗑 实例api删除 `setLoadMoreData` , `applyNewData` , `updateData` , 请替换为 `setDataLoader` , 删除 `clearData` , `setPrecision` 和 `getPrecision`。
-+ 🐞 修复覆盖物 `onSelected` 和 `onDeselected` 响应错乱问题。
-+ 🐞 修复样式配置 `candle.type` 是 `ohlc` 时的显示问题。
-+ 💄 优化覆盖物事件默认事件响应。
-+ 💄 优化x轴显示。
-
-## 10.0.0-alpha6
-`2025-06-12`
-+ 错误发布
-
-## 10.0.0-alpha5
-`2025-03-09`
-+ 👉 样式配置 `candle.tooltip.icons` 变更为 `candle.tooltip.features` , `indicator.tooltip.icons` 变更为 `indicator.tooltip.features` 。
-+ 👉 指标中的 `createTooltipDataSource` 方法返回值中的 `icons` 变更为 `features` 。
-+ 👉 实例api `subscribeAction` 和 `unsubscribeAction` 入参 `onTooltipIconClick` 变更为 `onCandleTooltipFeatureClick` ,指标的事件用 `indicator.onClick` 代替。
-+ 🐞 修复移动端特定情况下无法滚动问题。
-+ 💄 优化覆盖物事件响应显示。
-
-## 10.0.0-alpha4
-`2025-02-23`
-+ 🐞 修复实例方法 `applyNewData` 入参 `more.backward` 不对问题。
-+ 🐞 修复单条数据导致图表出错问题。
-
-
-## 10.0.0-alpha3
-`2025-02-19`
-+ 👉 实例api `createIndicator` 返回值变更为返回指标id。
-+ 👉 实例api `overlayIndicator` 入参 `paneId` 合并到入参 `indicator` 中。
-+ 👉 实例api `getIndicators` 返回值变更为返回数组。
-+ 👉 实例api `getOverlays` 返回值变更为返回数组。
-+ 🆕 样式配置新增 `candle.bar.compareRule` , `indicator.ohlc.compareRule` 和 `candle.priceMark.last.compareRule` 。
-+ 🆕 支持在移动端y轴拖动。
-+ 🆕 支持在同一窗口上创建多个相同名称的指标。
-+ 💄 优化 `overlay` 模版中的 `figure` 忽略事件类型,事件名和 `overlay` 中的事件名称一致。
-+ 🐞 修复指标自定义提示信息可能出错问题。
-+ 🐞 修复正在绘制中的覆盖物可能不正确删除问题。
-+ 🐞 修复api `createOverlay` 在指定 `points` 时,可能不能正确创建问题。
-+ 🐞 修复api `executeAction` 可能导致 `subscribeAction` 无限触发问题。
-
-## 10.0.0-alpha2
-`2024-12-20`
-+ 🆕 x轴支持显示未来时间。
-+ 🐞 修复 `subscribeAction` 类型是 `ActionType.OnCandleBarClick` 不生效问题。
-
-## 10.0.0-alpha1
-`2024-12-01`
+## 10.0.0-beta1
+`2025-11-21`
+ 🆕 新特性
+ 支持千分符,小数折叠自定义。
+ + x轴支持显示未来时间。
+ + 支持在移动端y轴拖动。
+ + 支持在同一窗口上创建多个相同名称的指标。
+ 重写坐标轴模块,自定义y轴支持设置范围。
- + 新增实例方法 `setPrecision(precision)` , `getPrecision()` , `setThousandsSeparator(thousandsSeparator)` , `getThousandsSeparator()` , `setDecimalFold(decimalFold)` , `getDecimalFold()` 和 `setLoadMoreDataCallback(cb)` 。
+ + 图表方法 `init(dom, options)` 中的 `options` 新增 `zoomAnchor` 。
+ + 实例新增方法 `setZoomAnchor(anchor)` , `getZoomAnchor()` , `setDataLoader(loader)` , `setSymbol(symbol)` , `getSymbol()` , `setPeriod(period)` , `getPeriod()` , `resetData()` , `setThousandsSeparator(thousandsSeparator)` , `getThousandsSeparator()` , `setDecimalFold(decimalFold)` , `getDecimalFold()` , `getIndicators()` 和 `getOverlays()` 。
+ + 样式配置新增 `candle.priceMark.last.extendTexts` , `candle.tooltip.title` , `candle.tooltip.legend` , `indicator.tooltip.title` , `indicator.tooltip.legend` , `crosshair.horizontal.features` , `candle.bar.compareRule` , `indicator.ohlc.compareRule` 和 `candle.priceMark.last.compareRule` 。
+ + 实例方法 `subscribeAction` 和 `unsubscribeAction` , 入参 `type` 新增 `onIndicatorTooltipFeatureClick` 和 `onCrosshairFeatureClick` 。
+ 👉 变更
- + 图表方法 `init(dcs, options)` , `options.layout` 子项中的 `position` 变更为 `order` , `options.customApi` 中的 `formatDate(dateTimeFormat, timestamp, format, type)` 变更为 `formatDate(timestamp, format, type)` , `options.thousandsSeparator` 变更为对象 `{ sign, format }` , `options.decimalFoldThreshold` 变更为 `options.decimalFold` 。
- + 实例方法 `applyNewData(dataList, more, callback)` 变更为 `applyNewData(dataList, more)` 。
- + 实例方法 `updateData(data, callback)` 变更为 `updateData(data)` 。
- + 实例方法 `getBarSpace()` 返回值变更为对象。
- + 自定义指标 `createTooltipDataSource` 方法返回值 `values` 变更为 `legends` 。
+ + 图表方法 `init(dcs, options)` , `options.layout` 子项中的 `position` 变更为 `order` , `options.thousandsSeparator` 变更为对象 `{ sign, format }` , `options.decimalFoldThreshold` 变更为 `options.decimalFold` , `options.customApi` 变更为 `options.formatter` , `formatDate` 参数变更为对象。
+ + 实例方法 `setCustomApi` 变更为 `setFormatter` , `getCustomApi` 变更为 `getFormatter` , `getBarSpace()` 返回值变更为对象 , `createIndicator` 返回值变更为返回指标id , `overlayIndicator` 入参 `paneId` 合并到入参 `indicator` 中, 。
+ + 自定义指标 `createTooltipDataSource` 方法返回值 `values` 变更为 `legends` , `icons` 变更为 `features` 。
+ + 样式配置 `candle.tooltip.icons` 变更为 `candle.tooltip.features` , `indicator.tooltip.icons` 变更为 `indicator.tooltip.features` 。
++ 💄 优化
+ + 优化覆盖物模版中的 `figure` 忽略事件类型,事件名和 `overlay` 中的事件名称一致。
+ + 优化指标计算任务执行。
+ + 优化移动端滚动事件触发。
+ 🗑 废弃
+ 图表方法删除 `utils.drawArc(ctx, arc, styles)` ,`utils.drawCircle(ctx, circle, styles)` , `utils.drawLine(ctx, line, styles)` ,`utils.drawPolygon(ctx, polygon, styles)` , `utils.drawRect(ctx, rect, styles)` ,`utils.drawText(ctx, text, styles)` , `utils.drawRectText(ctx, rectText, styles)`,请使用 `getFigureClass(name)` 代替。
+ 实例方法删除 `setPriceVolumePrecision(pricePrecision, volumePrecision)` ,请使用 `setPrecision(precision)` 代替。
- + 实例方法删除 `applyMoreData(dataList, more, callback)` , `setLoadDataCallback(cb)` 和 `loadMore(cb)` ,请使用 `setLoadMoreDataCallback(cb)` 代替。
+ + 实例api删除 `setLoadMoreData` , `applyNewData` , `updateData` , 请替换为 `setDataLoader` , 删除 `clearData` , `setPrecision` 和 `getPrecision`。
+ 实例方法删除 `getIndicatorByPaneId(paneId, name)` ,请使用 `getIndicators(filter)` 代替。
+ 实例方法删除 `getOverlayById(id)` ,请使用 `getOverlays(filter)` 代替。
+ + 实例方法 `subscribeAction` 和 `unsubscribeAction` 删除入参 `onTooltipIconClick` ,请使用 `onCandleTooltipFeatureClick` 和 `onIndicatorTooltipFeatureClick` 代替。
+ 样式配置删除 `yAxis.position` , `yAxis.type` , `yAxis.inside` 和 `yAxis.inside` ,请使用窗口配置 `axis` 中的属性代替。详情参阅图表API [init(dcs, options)](/api/chart/init#parameters) ,实例API [createIndicator(value, isStack, paneOptions)](/api/instance/createIndicator#parameters) 和 [setPaneOptions(options)](/api/instance/setPaneOptions#parameters) 。
- + 样式配置删除 `overlay.rectText` 。
+ + 样式配置删除 `candle.tooltip.defaultValue` , `candle.tooltip.custom` 请替换为 `candle.tooltip.legend` ,删除 `candle.tooltip.text` ,删除 `indicator.tooltip.showName` , `indicator.tooltip.showParams` ,请用 `indicator.tooltip.title` ,删除 `indicator.tooltip.defaultValue` , 请替换为 `indicator.tooltip.legend` , 删除 `indicator.tooltip.text` , 删除 `overlay.rectText` 。
+ 内置的基础图形删除 `rectText` ,请使用 `text` 代替。
## 9.x
diff --git a/docs/guide/figure.md b/docs/guide/figure.md
index 4eb49d171..e7638f6f8 100644
--- a/docs/guide/figure.md
+++ b/docs/guide/figure.md
@@ -1,10 +1,13 @@
+
+
# 基础图形
基础图形是图表重要的组成部分,图表上所有的元素都是由基础图形组成,如果需要制作复杂的自定义技术指标和覆盖物,建议仔细阅读。这篇文档介绍了内置的基本图形和如何自定义一个基础图形。基础图形可以通过图表方法 `klinecharts.getFigureClass(name)` 获取。
## 使用示例
-::: warning 注意
-需要在有画布上下文的情况下使用。
-:::
+
+
```javascript
// 获取基础图形实例
diff --git a/docs/guide/indicator.md b/docs/guide/indicator.md
index e21a9d855..f49bd867d 100644
--- a/docs/guide/indicator.md
+++ b/docs/guide/indicator.md
@@ -1,3 +1,7 @@
+
+
# 技术指标
本文档介绍了图表内置的技术指标和如何自定义一个技术指标。
@@ -14,10 +18,7 @@
| KDJ | [9, 3, 3] | TRIX | [12, 20] | PVT | 无 |
| RSI | [6, 12, 24] | OBV | [30] | AVP | 无 |
-::: tip 提示
-一些指标可以使用 `chart.createIndicator('MA', true, { id:'candle_pane' })` 叠加在蜡烛图上,而有些则不能。与蜡烛图兼容的指标有:BBI、BOLL、EMA、MA、SAR、SMA。另外也可以使用自定义指标的自定义绘制,将指标绘制在蜡烛图上,使其能够和蜡烛图兼容。
-:::
-
+
## 自定义技术指标
创建一个自定义技术指标,只需要生成一个技术指标信息,然后通过图表API [registerIndicator](/api/chart/registerIndicator) 全局添加,添加到图表即可和内置技术指标一样去使用。更多示例可参考 [https://github.com/klinecharts/KLineChart/tree/main/src/extension/indicator](https://github.com/klinecharts/KLineChart/tree/main/src/extension/indicator) 下的文件。
diff --git a/docs/guide/local-development.md b/docs/guide/local-development.md
index d3921529c..9c3cfdbf1 100644
--- a/docs/guide/local-development.md
+++ b/docs/guide/local-development.md
@@ -1,4 +1,4 @@
-# 本地开发
+# 👨💻 本地开发
## 摘要
如果你看到此处,你可能会对改进 KLineChart 核心感兴趣。感谢 [@fish2016](https://github.com/fish2016) 编写的此文档。
diff --git a/docs/guide/overlay.md b/docs/guide/overlay.md
index 758a0baab..e6433dc50 100644
--- a/docs/guide/overlay.md
+++ b/docs/guide/overlay.md
@@ -2,7 +2,7 @@
本文档介绍了图表内置的覆盖物和如何自定义一个覆盖物。
## 内置覆盖物类型
-`horizontalRayLine` , `horizontalSegment` , `horizontalStraightLine` , `verticalRayLine` , `verticalSegment` , `verticalStraightLine` , `rayLine` , `segment` , `straightLine` , `priceLine` , `priceChannelLine` , `parallelStraightLine` , `fibonacciLine` , `simpleAnnotation` , `simpleTag`
+`horizontalRayLine` , `horizontalSegment` , `horizontalStraightLine` , `verticalRayLine` , `verticalSegment` , `verticalStraightLine` , `rayLine` , `segment` , `straightLine` , `priceLine` , `priceChannelLine` , `parallelStraightLine` , `fibonacciLine` , `freePath` , `simpleAnnotation` , `simpleTag`
## 自定义覆盖物
自定义一个覆盖物,然后通过 [registerOverlay](/api/chart/registerOverlay) 全局添加,添加到图表即可和内置覆盖物一样去使用。更多示例可参考 [https://github.com/klinecharts/KLineChart/tree/main/src/extension/overlay](https://github.com/klinecharts/KLineChart/tree/main/src/extension/overlay) 下的文件。
diff --git a/docs/public/images/box_dark.png b/docs/public/images/box_dark.png
index 03e897358..d754c47c0 100644
Binary files a/docs/public/images/box_dark.png and b/docs/public/images/box_dark.png differ
diff --git a/docs/public/images/box_light.png b/docs/public/images/box_light.png
index 3defd75dd..37accc822 100644
Binary files a/docs/public/images/box_light.png and b/docs/public/images/box_light.png differ
diff --git a/docs/public/images/expand_dark.png b/docs/public/images/expand_dark.png
index 93be33f50..d72c282f2 100644
Binary files a/docs/public/images/expand_dark.png and b/docs/public/images/expand_dark.png differ
diff --git a/docs/public/images/expand_light.png b/docs/public/images/expand_light.png
index 75d8ab510..51dfeef0a 100644
Binary files a/docs/public/images/expand_light.png and b/docs/public/images/expand_light.png differ
diff --git a/docs/public/images/fav.png b/docs/public/images/fav.png
new file mode 100644
index 000000000..74f325415
Binary files /dev/null and b/docs/public/images/fav.png differ
diff --git a/docs/public/images/logo-nav.svg b/docs/public/images/logo-nav.svg
index 0cb38c28d..f9375696a 100644
--- a/docs/public/images/logo-nav.svg
+++ b/docs/public/images/logo-nav.svg
@@ -1 +1,65 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/public/images/logo.svg b/docs/public/images/logo.svg
deleted file mode 100644
index f58763c60..000000000
--- a/docs/public/images/logo.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/public/images/mobile_dark.png b/docs/public/images/mobile_dark.png
index 1967230e9..ac514301d 100644
Binary files a/docs/public/images/mobile_dark.png and b/docs/public/images/mobile_dark.png differ
diff --git a/docs/public/images/mobile_light.png b/docs/public/images/mobile_light.png
index b6bc6f3ae..aa0009a49 100644
Binary files a/docs/public/images/mobile_light.png and b/docs/public/images/mobile_light.png differ
diff --git a/docs/public/images/power_dark.png b/docs/public/images/power_dark.png
index b8fcac7d7..ef9095420 100644
Binary files a/docs/public/images/power_dark.png and b/docs/public/images/power_dark.png differ
diff --git a/docs/public/images/power_light.png b/docs/public/images/power_light.png
index df2c11567..f0f44f697 100644
Binary files a/docs/public/images/power_light.png and b/docs/public/images/power_light.png differ
diff --git a/docs/public/images/rocket_dark.png b/docs/public/images/rocket_dark.png
index 8bc2470a0..42fddb8b0 100644
Binary files a/docs/public/images/rocket_dark.png and b/docs/public/images/rocket_dark.png differ
diff --git a/docs/public/images/rocket_light.png b/docs/public/images/rocket_light.png
index 106b2898f..ee7bbe090 100644
Binary files a/docs/public/images/rocket_light.png and b/docs/public/images/rocket_light.png differ
diff --git a/docs/public/images/typescript_dark.png b/docs/public/images/typescript_dark.png
index f46daf95d..1d2b1f7ba 100644
Binary files a/docs/public/images/typescript_dark.png and b/docs/public/images/typescript_dark.png differ
diff --git a/docs/public/images/typescript_light.png b/docs/public/images/typescript_light.png
index 3c9f4378c..d6f412745 100644
Binary files a/docs/public/images/typescript_light.png and b/docs/public/images/typescript_light.png differ
diff --git a/package.json b/package.json
index 7158de09c..9737dae7c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "klinecharts",
- "version": "10.0.0-alpha9",
+ "version": "10.0.0-beta1",
"description": "Lightweight k-line chart built with html5 canvas",
"type": "module",
"main": "./dist/index.cjs",
@@ -104,7 +104,7 @@
"shiki": "^2.4.2",
"tslib": "^2.8.1",
"typescript": "^5.8.3",
- "vitepress": "2.0.0-alpha.9"
+ "vitepress": "2.0.0-alpha.15"
},
"pnpm": {
"overrides": {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2e2c5d70d..dbd7311c4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -64,7 +64,7 @@ importers:
version: 20.17.30
'@vueuse/core':
specifier: ^11.1.0
- version: 11.3.0(vue@3.5.18(typescript@5.8.3))
+ version: 11.3.0(vue@3.5.13(typescript@5.8.3))
codesandbox:
specifier: ^2.2.3
version: 2.2.3
@@ -123,8 +123,8 @@ importers:
specifier: ^5.8.3
version: 5.8.3
vitepress:
- specifier: 2.0.0-alpha.9
- version: 2.0.0-alpha.9(@types/node@20.17.30)(jiti@2.4.2)(postcss@8.5.6)(terser@5.39.0)(typescript@5.8.3)
+ specifier: 2.0.0-alpha.15
+ version: 2.0.0-alpha.15(@types/node@20.17.30)(jiti@2.4.2)(postcss@8.5.6)(terser@5.39.0)(typescript@5.8.3)
packages:
@@ -148,8 +148,8 @@ packages:
resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
engines: {node: '>=6.9.0'}
- '@babel/helper-validator-identifier@7.27.1':
- resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+ '@babel/helper-validator-identifier@7.28.5':
+ resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
engines: {node: '>=6.9.0'}
'@babel/parser@7.27.0':
@@ -157,8 +157,8 @@ packages:
engines: {node: '>=6.0.0'}
hasBin: true
- '@babel/parser@7.28.0':
- resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==}
+ '@babel/parser@7.28.5':
+ resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==}
engines: {node: '>=6.0.0'}
hasBin: true
@@ -182,8 +182,8 @@ packages:
resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==}
engines: {node: '>=6.9.0'}
- '@babel/types@7.28.2':
- resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
+ '@babel/types@7.28.5':
+ resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
engines: {node: '>=6.9.0'}
'@commitlint/cli@19.8.0':
@@ -255,11 +255,11 @@ packages:
resolution: {integrity: sha512-LRjP623jPyf3Poyfb0ohMj8I3ORyBDOwXAgxxVPbSD0unJuW2mJWeiRfaQinjtccMqC5Wy1HOMfa4btKjbNxbg==}
engines: {node: '>=v18'}
- '@docsearch/css@4.0.0-beta.5':
- resolution: {integrity: sha512-bZy+gIXRZch0KNPC7MLoj4wkEcNVeEHBXOKJtonoJ2EaLw2vbO1PLGIXxtPgW7Ab7TvI0StkrmGuEQqE2q/1QA==}
+ '@docsearch/css@4.3.2':
+ resolution: {integrity: sha512-K3Yhay9MgkBjJJ0WEL5MxnACModX9xuNt3UlQQkDEDZJZ0+aeWKtOkxHNndMRkMBnHdYvQjxkm6mdlneOtU1IQ==}
- '@docsearch/js@4.0.0-beta.5':
- resolution: {integrity: sha512-FEtkwdblZDrTkd0mYwmfR94Vo/jgkXVIbS6vD2FcKazK/L5RmgNb7KAUDUWW11V/fIcS5XHvHprIxEnoB9gllQ==}
+ '@docsearch/js@4.3.2':
+ resolution: {integrity: sha512-xdfpPXMgKRY9EW7U1vtY7gLKbLZFa9ed+t0Dacquq8zXBqAlH9HlUf0h4Mhxm0xatsVeMaIR2wr/u6g0GsZyQw==}
'@esbuild/aix-ppc64@0.25.2':
resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==}
@@ -485,8 +485,8 @@ packages:
resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==}
engines: {node: '>=18.18'}
- '@iconify-json/simple-icons@1.2.46':
- resolution: {integrity: sha512-MJfKQDhOMQD5Fc8PcTtCdFX0oBf/nKVfp69ScdEKIXW0JXELX5V2Ld45EsjShi8aJ6DNhdDtSDZvKuDnkDiKnw==}
+ '@iconify-json/simple-icons@1.2.59':
+ resolution: {integrity: sha512-fYx/InyQsWFW4wVxWka3CGDJ6m/fXoTqWBSl+oA3FBXO5RhPAb6S3Y5bRgCPnrYevErH8VjAL0TZevIqlN2PhQ==}
'@iconify/types@2.0.0':
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
@@ -513,6 +513,9 @@ packages:
'@jridgewell/sourcemap-codec@1.5.0':
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
@@ -566,8 +569,8 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
- '@rolldown/pluginutils@1.0.0-beta.19':
- resolution: {integrity: sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==}
+ '@rolldown/pluginutils@1.0.0-beta.29':
+ resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==}
'@rollup/plugin-commonjs@25.0.8':
resolution: {integrity: sha512-ZEZWTK5n6Qde0to4vS9Mr5x/0UZoqCxPVR9KRUjU4kA2sO7GEUn1fop0DAwpO6z0Nw/kJON9bDmSxdWxO/TT1A==}
@@ -742,50 +745,50 @@ packages:
'@shikijs/core@2.4.2':
resolution: {integrity: sha512-V0kXYB/70xA3CO+b2Pz9kcSThaOUfObOEkGeHsKSFqV6rultaWPfeyZPpBlKHMUXO9Bt1ZGINDCctN90pQvnTg==}
+ '@shikijs/core@3.15.0':
+ resolution: {integrity: sha512-8TOG6yG557q+fMsSVa8nkEDOZNTSxjbbR8l6lF2gyr6Np+jrPlslqDxQkN6rMXCECQ3isNPZAGszAfYoJOPGlg==}
+
'@shikijs/core@3.2.2':
resolution: {integrity: sha512-yvlSKVMLjddAGBa2Yu+vUZxuu3sClOWW1AG+UtJkvejYuGM5BVL35s6Ijiwb75O9QdEx6IkMxinHZSi8ZyrBaA==}
- '@shikijs/core@3.9.2':
- resolution: {integrity: sha512-3q/mzmw09B2B6PgFNeiaN8pkNOixWS726IHmJEpjDAcneDPMQmUg2cweT9cWXY4XcyQS3i6mOOUgQz9RRUP6HA==}
-
'@shikijs/engine-javascript@2.4.2':
resolution: {integrity: sha512-WRg63Lfta+5RJ0y0/ns1e1NqSxo+jSQclMf9kBHvtchLhR/x3R/E3PSNFiCM+t7oo+d9/VCCp1kURqsSVTHWJg==}
+ '@shikijs/engine-javascript@3.15.0':
+ resolution: {integrity: sha512-ZedbOFpopibdLmvTz2sJPJgns8Xvyabe2QbmqMTz07kt1pTzfEvKZc5IqPVO/XFiEbbNyaOpjPBkkr1vlwS+qg==}
+
'@shikijs/engine-javascript@3.2.2':
resolution: {integrity: sha512-tlDKfhWpF4jKLUyVAnmL+ggIC+0VyteNsUpBzh1iwWLZu4i+PelIRr0TNur6pRRo5UZIv3ss/PLMuwahg9S2hg==}
- '@shikijs/engine-javascript@3.9.2':
- resolution: {integrity: sha512-kUTRVKPsB/28H5Ko6qEsyudBiWEDLst+Sfi+hwr59E0GLHV0h8RfgbQU7fdN5Lt9A8R1ulRiZyTvAizkROjwDA==}
-
'@shikijs/engine-oniguruma@2.4.2':
resolution: {integrity: sha512-YmvW7XcvT2f2pf1r1IvKd48fFYcsZRMMISRr2nY1fE2uOF4xcm+84R7+yg4jNAblrFcXU9tDrkllJKH2uD3mBQ==}
+ '@shikijs/engine-oniguruma@3.15.0':
+ resolution: {integrity: sha512-HnqFsV11skAHvOArMZdLBZZApRSYS4LSztk2K3016Y9VCyZISnlYUYsL2hzlS7tPqKHvNqmI5JSUJZprXloMvA==}
+
'@shikijs/engine-oniguruma@3.2.2':
resolution: {integrity: sha512-vyXRnWVCSvokwbaUD/8uPn6Gqsf5Hv7XwcW4AgiU4Z2qwy19sdr6VGzMdheKKN58tJOOe5MIKiNb901bgcUXYQ==}
- '@shikijs/engine-oniguruma@3.9.2':
- resolution: {integrity: sha512-Vn/w5oyQ6TUgTVDIC/BrpXwIlfK6V6kGWDVVz2eRkF2v13YoENUvaNwxMsQU/t6oCuZKzqp9vqtEtEzKl9VegA==}
-
'@shikijs/langs@2.4.2':
resolution: {integrity: sha512-USwSIDIxalwON4FSE2IFMGmAvM250RNdWjOf79zj2JjV2fsNJWn0vvEE9gh1WtvPp2l5BXXhdybFYA6ek7ogFQ==}
+ '@shikijs/langs@3.15.0':
+ resolution: {integrity: sha512-WpRvEFvkVvO65uKYW4Rzxs+IG0gToyM8SARQMtGGsH4GDMNZrr60qdggXrFOsdfOVssG/QQGEl3FnJ3EZ+8w8A==}
+
'@shikijs/langs@3.2.2':
resolution: {integrity: sha512-NY0Urg2dV9ETt3JIOWoMPuoDNwte3geLZ4M1nrPHbkDS8dWMpKcEwlqiEIGqtwZNmt5gKyWpR26ln2Bg2ecPgw==}
- '@shikijs/langs@3.9.2':
- resolution: {integrity: sha512-X1Q6wRRQXY7HqAuX3I8WjMscjeGjqXCg/Sve7J2GWFORXkSrXud23UECqTBIdCSNKJioFtmUGJQNKtlMMZMn0w==}
-
'@shikijs/themes@2.4.2':
resolution: {integrity: sha512-W6uxyv91JWI6udgBpsSRCdmIp8WPxOq5Ys9Nj9royB+Or8sYmvnEBHLw6f+dZB9DIlFgvRPw5VnlwUx5ofKMKA==}
+ '@shikijs/themes@3.15.0':
+ resolution: {integrity: sha512-8ow2zWb1IDvCKjYb0KiLNrK4offFdkfNVPXb1OZykpLCzRU6j+efkY+Y7VQjNlNFXonSw+4AOdGYtmqykDbRiQ==}
+
'@shikijs/themes@3.2.2':
resolution: {integrity: sha512-Zuq4lgAxVKkb0FFdhHSdDkALuRpsj1so1JdihjKNQfgM78EHxV2JhO10qPsMrm01FkE3mDRTdF68wfmsqjt6HA==}
- '@shikijs/themes@3.9.2':
- resolution: {integrity: sha512-6z5lBPBMRfLyyEsgf6uJDHPa6NAGVzFJqH4EAZ+03+7sedYir2yJBRu2uPZOKmj43GyhVHWHvyduLDAwJQfDjA==}
-
- '@shikijs/transformers@3.9.2':
- resolution: {integrity: sha512-MW5hT4TyUp6bNAgTExRYLk1NNasVQMTCw1kgbxHcEC0O5cbepPWaB+1k+JzW9r3SP2/R8kiens8/3E6hGKfgsA==}
+ '@shikijs/transformers@3.15.0':
+ resolution: {integrity: sha512-Hmwip5ovvSkg+Kc41JTvSHHVfCYF+C8Cp1omb5AJj4Xvd+y9IXz2rKJwmFRGsuN0vpHxywcXJ1+Y4B9S7EG1/A==}
'@shikijs/twoslash@3.2.2':
resolution: {integrity: sha512-rqi1qQiQI3tYVciYVsbm2llQNGCslVWPcgzGSaapXPaSs3r9CfwmRHat9B7Vs6gT6likZN+qMxqIOqSqwCoJtQ==}
@@ -795,12 +798,12 @@ packages:
'@shikijs/types@2.4.2':
resolution: {integrity: sha512-e28aFDPwVgK8H2nPrEA5CexLa5yumBvb5aF6nN4SlmqaBFOuGQdxX/Cfh8rwRFALepJtlj0P3wvJ4oL+ndxgSA==}
+ '@shikijs/types@3.15.0':
+ resolution: {integrity: sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw==}
+
'@shikijs/types@3.2.2':
resolution: {integrity: sha512-a5TiHk7EH5Lso8sHcLHbVNNhWKP0Wi3yVnXnu73g86n3WoDgEra7n3KszyeCGuyoagspQ2fzvy4cpSc8pKhb0A==}
- '@shikijs/types@3.9.2':
- resolution: {integrity: sha512-/M5L0Uc2ljyn2jKvj4Yiah7ow/W+DJSglVafvWAJ/b8AZDeeRAdMu3c2riDzB7N42VD+jSnWxeP9AKtd4TfYVw==}
-
'@shikijs/vitepress-twoslash@3.2.2':
resolution: {integrity: sha512-90nEoOBWgAjsvIBzJKRih89VTYCExWgF72BeesWJsxDpZasYyEZ/MpNaaDTSRHnEHXwfsmezZheiHOUmBi5G5g==}
@@ -859,9 +862,18 @@ packages:
'@types/keyv@3.1.4':
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
+ '@types/linkify-it@5.0.0':
+ resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
+
+ '@types/markdown-it@14.1.2':
+ resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==}
+
'@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+ '@types/mdurl@2.0.0':
+ resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
+
'@types/ms@2.1.0':
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
@@ -938,8 +950,8 @@ packages:
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
- '@vitejs/plugin-vue@6.0.0':
- resolution: {integrity: sha512-iAliE72WsdhjzTOp2DtvKThq1VBC4REhwRcaA+zPAAph6I+OQhUXv+Xu2KS7ElxYtb7Zc/3R30Hwv1DxEo7NXQ==}
+ '@vitejs/plugin-vue@6.0.1':
+ resolution: {integrity: sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==}
engines: {node: ^20.19.0 || >=22.12.0}
peerDependencies:
vite: ^5.0.0 || ^6.0.0 || ^7.0.0
@@ -954,38 +966,38 @@ packages:
'@vue/compiler-core@3.5.13':
resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==}
- '@vue/compiler-core@3.5.18':
- resolution: {integrity: sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==}
+ '@vue/compiler-core@3.5.24':
+ resolution: {integrity: sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==}
'@vue/compiler-dom@3.5.13':
resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==}
- '@vue/compiler-dom@3.5.18':
- resolution: {integrity: sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==}
+ '@vue/compiler-dom@3.5.24':
+ resolution: {integrity: sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==}
'@vue/compiler-sfc@3.5.13':
resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==}
- '@vue/compiler-sfc@3.5.18':
- resolution: {integrity: sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==}
+ '@vue/compiler-sfc@3.5.24':
+ resolution: {integrity: sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==}
'@vue/compiler-ssr@3.5.13':
resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==}
- '@vue/compiler-ssr@3.5.18':
- resolution: {integrity: sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==}
+ '@vue/compiler-ssr@3.5.24':
+ resolution: {integrity: sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==}
'@vue/compiler-vue2@2.7.16':
resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
- '@vue/devtools-api@7.7.7':
- resolution: {integrity: sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==}
+ '@vue/devtools-api@8.0.5':
+ resolution: {integrity: sha512-DgVcW8H/Nral7LgZEecYFFYXnAvGuN9C3L3DtWekAncFBedBczpNW8iHKExfaM559Zm8wQWrwtYZ9lXthEHtDw==}
- '@vue/devtools-kit@7.7.7':
- resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==}
+ '@vue/devtools-kit@8.0.5':
+ resolution: {integrity: sha512-q2VV6x1U3KJMTQPUlRMyWEKVbcHuxhqJdSr6Jtjz5uAThAIrfJ6WVZdGZm5cuO63ZnSUz0RCsVwiUUb0mDV0Yg==}
- '@vue/devtools-shared@7.7.7':
- resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==}
+ '@vue/devtools-shared@8.0.5':
+ resolution: {integrity: sha512-bRLn6/spxpmgLk+iwOrR29KrYnJjG9DGpHGkDFG82UM21ZpJ39ztUT9OXX3g+usW7/b2z+h46I9ZiYyB07XMXg==}
'@vue/language-core@2.2.4':
resolution: {integrity: sha512-eGGdw7eWUwdIn9Fy/irJ7uavCGfgemuHQABgJ/hU1UgZFnbTg9VWeXvHQdhY+2SPQZWJqWXvRWIg67t4iWEa+Q==}
@@ -998,47 +1010,47 @@ packages:
'@vue/reactivity@3.5.13':
resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==}
- '@vue/reactivity@3.5.18':
- resolution: {integrity: sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==}
+ '@vue/reactivity@3.5.24':
+ resolution: {integrity: sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==}
'@vue/runtime-core@3.5.13':
resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==}
- '@vue/runtime-core@3.5.18':
- resolution: {integrity: sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==}
+ '@vue/runtime-core@3.5.24':
+ resolution: {integrity: sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==}
'@vue/runtime-dom@3.5.13':
resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==}
- '@vue/runtime-dom@3.5.18':
- resolution: {integrity: sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==}
+ '@vue/runtime-dom@3.5.24':
+ resolution: {integrity: sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==}
'@vue/server-renderer@3.5.13':
resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==}
peerDependencies:
vue: 3.5.13
- '@vue/server-renderer@3.5.18':
- resolution: {integrity: sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==}
+ '@vue/server-renderer@3.5.24':
+ resolution: {integrity: sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==}
peerDependencies:
- vue: 3.5.18
+ vue: 3.5.24
'@vue/shared@3.5.13':
resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
- '@vue/shared@3.5.18':
- resolution: {integrity: sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==}
+ '@vue/shared@3.5.24':
+ resolution: {integrity: sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==}
'@vueuse/core@11.3.0':
resolution: {integrity: sha512-7OC4Rl1f9G8IT6rUfi9JrKiXy4bfmHhZ5x2Ceojy0jnd3mHNEvV4JaRygH362ror6/NZ+Nl+n13LPzGiPN8cKA==}
- '@vueuse/core@13.5.0':
- resolution: {integrity: sha512-wV7z0eUpifKmvmN78UBZX8T7lMW53Nrk6JP5+6hbzrB9+cJ3jr//hUlhl9TZO/03bUkMK6gGkQpqOPWoabr72g==}
+ '@vueuse/core@14.0.0':
+ resolution: {integrity: sha512-d6tKRWkZE8IQElX2aHBxXOMD478fHIYV+Dzm2y9Ag122ICBpNKtGICiXKOhWU3L1kKdttDD9dCMS4bGP3jhCTQ==}
peerDependencies:
vue: ^3.5.0
- '@vueuse/integrations@13.5.0':
- resolution: {integrity: sha512-7RACJySnlpl0MkSzxbtadioNGSX4TL5/Wl2cUy4nDq/XkeHwPYvVM880HJUSiap/FXhVEup9VKTM9y/n5UspAw==}
+ '@vueuse/integrations@14.0.0':
+ resolution: {integrity: sha512-5A0X7q9qyLtM3xyghq5nK/NEESf7cpcZlkQgXTMuW4JWiAMYxc1ImdhhGrk4negFBsq3ejvAlRmLdNrkcTzk1Q==}
peerDependencies:
async-validator: ^4
axios: ^1
@@ -1082,14 +1094,14 @@ packages:
'@vueuse/metadata@11.3.0':
resolution: {integrity: sha512-pwDnDspTqtTo2HwfLw4Rp6yywuuBdYnPYDq+mO38ZYKGebCUQC/nVj/PXSiK9HX5otxLz8Fn7ECPbjiRz2CC3g==}
- '@vueuse/metadata@13.5.0':
- resolution: {integrity: sha512-euhItU3b0SqXxSy8u1XHxUCdQ8M++bsRs+TYhOLDU/OykS7KvJnyIFfep0XM5WjIFry9uAPlVSjmVHiqeshmkw==}
+ '@vueuse/metadata@14.0.0':
+ resolution: {integrity: sha512-6yoGqbJcMldVCevkFiHDBTB1V5Hq+G/haPlGIuaFZHpXC0HADB0EN1ryQAAceiW+ryS3niUwvdFbGiqHqBrfVA==}
'@vueuse/shared@11.3.0':
resolution: {integrity: sha512-P8gSSWQeucH5821ek2mn/ciCk+MS/zoRKqdQIM3bHq6p7GXDAJLmnRRKmF5F65sAVJIfzQlwR3aDzwCn10s8hA==}
- '@vueuse/shared@13.5.0':
- resolution: {integrity: sha512-K7GrQIxJ/ANtucxIXbQlUHdB0TPA8c+q5i+zbrjxuhJCnJ9GtBg75sBSnvmLSxHKPg2Yo8w62PWksl9kwH0Q8g==}
+ '@vueuse/shared@14.0.0':
+ resolution: {integrity: sha512-mTCA0uczBgurRlwVaQHfG0Ja7UdGe4g9mwffiJmvLiTtp1G4AQyIjej6si/k8c8pUwTfVpNufck+23gXptPAkw==}
peerDependencies:
vue: ^3.5.0
@@ -1244,8 +1256,8 @@ packages:
resolution: {integrity: sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg==}
engines: {node: '>=0.8'}
- birpc@2.3.0:
- resolution: {integrity: sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g==}
+ birpc@2.8.0:
+ resolution: {integrity: sha512-Bz2a4qD/5GRhiHSwj30c/8kC8QGj12nNDwz3D4ErQ4Xhy35dsSDvF+RA/tWpjyU0pdGtSDiEk6B5fBGE1qNVhw==}
bl@1.2.3:
resolution: {integrity: sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==}
@@ -1917,8 +1929,9 @@ packages:
fastq@1.19.1:
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
- fdir@6.4.6:
- resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
@@ -2000,8 +2013,8 @@ packages:
flush-write-stream@1.1.1:
resolution: {integrity: sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==}
- focus-trap@7.6.5:
- resolution: {integrity: sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg==}
+ focus-trap@7.6.6:
+ resolution: {integrity: sha512-v/Z8bvMCajtx4mEXmOo7QEsIzlIOqRXTIwgUfsFOF9gEsespdbD0AkPIka1bSXZ8Y8oZ+2IVDQZePkTfEHZl7Q==}
follow-redirects@1.5.10:
resolution: {integrity: sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==}
@@ -2252,6 +2265,9 @@ packages:
resolution: {integrity: sha512-HVJyzUrLIL1c0QmviVh5E8VGyUS7xCFPS6yydaVd1UegW+ibV/CohqTH9MkOLDp5o+rb82DMo77PTuc9F/8GKw==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ htm@3.1.1:
+ resolution: {integrity: sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==}
+
html-void-elements@3.0.0:
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
@@ -2698,6 +2714,9 @@ packages:
magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
make-dir@1.3.0:
resolution: {integrity: sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==}
engines: {node: '>=4'}
@@ -2900,8 +2919,8 @@ packages:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
- minisearch@7.1.2:
- resolution: {integrity: sha512-R1Pd9eF+MD5JYDDSPAp/q1ougKglm14uEkPMvQ/05RGmx6G9wvmLTrTI/Q5iPNJLYqNdsDQ7qTGIcNWR+FrHmA==}
+ minisearch@7.2.0:
+ resolution: {integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==}
minizlib@2.1.2:
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
@@ -3207,8 +3226,8 @@ packages:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
- perfect-debounce@1.0.0:
- resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
+ perfect-debounce@2.0.0:
+ resolution: {integrity: sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -3519,12 +3538,12 @@ packages:
shiki@2.4.2:
resolution: {integrity: sha512-kPOa6plKRlylb23/qOtO+iBI3HYO84IgMix9oc7oet9WcsnuGHCPK4s/v7635nkUSmv+F6s6xmaDreNs5z6v+w==}
+ shiki@3.15.0:
+ resolution: {integrity: sha512-kLdkY6iV3dYbtPwS9KXU7mjfmDm25f5m0IPNFnaXO7TBPcvbUOY72PYXSuSqDzwp+vlH/d7MXpHlKO/x+QoLXw==}
+
shiki@3.2.2:
resolution: {integrity: sha512-0qWBkM2t/0NXPRcVgtLhtHv6Ak3Q5yI4K/ggMqcgLRKm4+pCs3namgZlhlat/7u2CuqNtlShNs9lENOG6n7UaQ==}
- shiki@3.9.2:
- resolution: {integrity: sha512-t6NKl5e/zGTvw/IyftLcumolgOczhuroqwXngDeMqJ3h3EQiTY/7wmfgPlsmloD8oYfqkEDqxiaH37Pjm1zUhQ==}
-
shortid@2.2.17:
resolution: {integrity: sha512-GpbM3gLF1UUXZvQw6MCyulHkWbRseNO4cyBEZresZRorwl1+SLu1ZdqgVtuwqz8mB6RpwPkm541mYSqrKyJSaA==}
@@ -3729,8 +3748,8 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
- tabbable@6.2.0:
- resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
+ tabbable@6.3.0:
+ resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==}
tapable@2.2.1:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
@@ -3777,8 +3796,8 @@ packages:
tinyexec@0.3.2:
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
- tinyglobby@0.2.14:
- resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
+ tinyglobby@0.2.15:
+ resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
tmp@0.0.33:
@@ -3964,8 +3983,8 @@ packages:
vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
- vite@7.0.6:
- resolution: {integrity: sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==}
+ vite@7.2.4:
+ resolution: {integrity: sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -4004,12 +4023,12 @@ packages:
yaml:
optional: true
- vitepress@2.0.0-alpha.9:
- resolution: {integrity: sha512-oUdZiT8ZCLf80Nw02Ha+v25aaabwik6iSMTEBXg46bMypNS/5i6AfMgFqpTuR5l3qG9XfNmau/SLT0sRiks2Zg==}
+ vitepress@2.0.0-alpha.15:
+ resolution: {integrity: sha512-jhjSYd10Z6RZiKOa7jy0xMVf5NB5oSc/lS3bD/QoUc6V8PrvQR5JhC9104NEt6+oTGY/ftieVWxY9v7YI+1IjA==}
hasBin: true
peerDependencies:
markdown-it-mathjax3: ^4
- oxc-minify: ^0.78.0
+ oxc-minify: '*'
postcss: ^8
peerDependenciesMeta:
markdown-it-mathjax3:
@@ -4043,8 +4062,8 @@ packages:
typescript:
optional: true
- vue@3.5.18:
- resolution: {integrity: sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==}
+ vue@3.5.24:
+ resolution: {integrity: sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
@@ -4175,15 +4194,15 @@ snapshots:
'@babel/helper-validator-identifier@7.25.9': {}
- '@babel/helper-validator-identifier@7.27.1': {}
+ '@babel/helper-validator-identifier@7.28.5': {}
'@babel/parser@7.27.0':
dependencies:
'@babel/types': 7.27.0
- '@babel/parser@7.28.0':
+ '@babel/parser@7.28.5':
dependencies:
- '@babel/types': 7.28.2
+ '@babel/types': 7.28.5
'@babel/runtime@7.27.0':
dependencies:
@@ -4214,10 +4233,10 @@ snapshots:
'@babel/helper-string-parser': 7.25.9
'@babel/helper-validator-identifier': 7.25.9
- '@babel/types@7.28.2':
+ '@babel/types@7.28.5':
dependencies:
'@babel/helper-string-parser': 7.27.1
- '@babel/helper-validator-identifier': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
'@commitlint/cli@19.8.0(@types/node@20.17.30)(typescript@5.8.3)':
dependencies:
@@ -4329,9 +4348,11 @@ snapshots:
'@types/conventional-commits-parser': 5.0.1
chalk: 5.4.1
- '@docsearch/css@4.0.0-beta.5': {}
+ '@docsearch/css@4.3.2': {}
- '@docsearch/js@4.0.0-beta.5': {}
+ '@docsearch/js@4.3.2':
+ dependencies:
+ htm: 3.1.1
'@esbuild/aix-ppc64@0.25.2':
optional: true
@@ -4481,7 +4502,7 @@ snapshots:
'@humanwhocodes/retry@0.4.2': {}
- '@iconify-json/simple-icons@1.2.46':
+ '@iconify-json/simple-icons@1.2.59':
dependencies:
'@iconify/types': 2.0.0
@@ -4513,6 +4534,8 @@ snapshots:
'@jridgewell/sourcemap-codec@1.5.0': {}
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
'@jridgewell/trace-mapping@0.3.25':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
@@ -4582,7 +4605,7 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
- '@rolldown/pluginutils@1.0.0-beta.19': {}
+ '@rolldown/pluginutils@1.0.0-beta.29': {}
'@rollup/plugin-commonjs@25.0.8(rollup@4.43.0)':
dependencies:
@@ -4718,16 +4741,16 @@ snapshots:
'@types/hast': 3.0.4
hast-util-to-html: 9.0.5
- '@shikijs/core@3.2.2':
+ '@shikijs/core@3.15.0':
dependencies:
- '@shikijs/types': 3.2.2
+ '@shikijs/types': 3.15.0
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
hast-util-to-html: 9.0.5
- '@shikijs/core@3.9.2':
+ '@shikijs/core@3.2.2':
dependencies:
- '@shikijs/types': 3.9.2
+ '@shikijs/types': 3.2.2
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
hast-util-to-html: 9.0.5
@@ -4738,61 +4761,61 @@ snapshots:
'@shikijs/vscode-textmate': 10.0.2
oniguruma-to-es: 3.1.1
- '@shikijs/engine-javascript@3.2.2':
+ '@shikijs/engine-javascript@3.15.0':
dependencies:
- '@shikijs/types': 3.2.2
+ '@shikijs/types': 3.15.0
'@shikijs/vscode-textmate': 10.0.2
- oniguruma-to-es: 4.1.0
+ oniguruma-to-es: 4.3.3
- '@shikijs/engine-javascript@3.9.2':
+ '@shikijs/engine-javascript@3.2.2':
dependencies:
- '@shikijs/types': 3.9.2
+ '@shikijs/types': 3.2.2
'@shikijs/vscode-textmate': 10.0.2
- oniguruma-to-es: 4.3.3
+ oniguruma-to-es: 4.1.0
'@shikijs/engine-oniguruma@2.4.2':
dependencies:
'@shikijs/types': 2.4.2
'@shikijs/vscode-textmate': 10.0.2
- '@shikijs/engine-oniguruma@3.2.2':
+ '@shikijs/engine-oniguruma@3.15.0':
dependencies:
- '@shikijs/types': 3.2.2
+ '@shikijs/types': 3.15.0
'@shikijs/vscode-textmate': 10.0.2
- '@shikijs/engine-oniguruma@3.9.2':
+ '@shikijs/engine-oniguruma@3.2.2':
dependencies:
- '@shikijs/types': 3.9.2
+ '@shikijs/types': 3.2.2
'@shikijs/vscode-textmate': 10.0.2
'@shikijs/langs@2.4.2':
dependencies:
'@shikijs/types': 2.4.2
- '@shikijs/langs@3.2.2':
+ '@shikijs/langs@3.15.0':
dependencies:
- '@shikijs/types': 3.2.2
+ '@shikijs/types': 3.15.0
- '@shikijs/langs@3.9.2':
+ '@shikijs/langs@3.2.2':
dependencies:
- '@shikijs/types': 3.9.2
+ '@shikijs/types': 3.2.2
'@shikijs/themes@2.4.2':
dependencies:
'@shikijs/types': 2.4.2
- '@shikijs/themes@3.2.2':
+ '@shikijs/themes@3.15.0':
dependencies:
- '@shikijs/types': 3.2.2
+ '@shikijs/types': 3.15.0
- '@shikijs/themes@3.9.2':
+ '@shikijs/themes@3.2.2':
dependencies:
- '@shikijs/types': 3.9.2
+ '@shikijs/types': 3.2.2
- '@shikijs/transformers@3.9.2':
+ '@shikijs/transformers@3.15.0':
dependencies:
- '@shikijs/core': 3.9.2
- '@shikijs/types': 3.9.2
+ '@shikijs/core': 3.15.0
+ '@shikijs/types': 3.15.0
'@shikijs/twoslash@3.2.2(typescript@5.8.3)':
dependencies:
@@ -4808,12 +4831,12 @@ snapshots:
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
- '@shikijs/types@3.2.2':
+ '@shikijs/types@3.15.0':
dependencies:
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
- '@shikijs/types@3.9.2':
+ '@shikijs/types@3.2.2':
dependencies:
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
@@ -4890,10 +4913,19 @@ snapshots:
dependencies:
'@types/node': 20.17.30
+ '@types/linkify-it@5.0.0': {}
+
+ '@types/markdown-it@14.1.2':
+ dependencies:
+ '@types/linkify-it': 5.0.0
+ '@types/mdurl': 2.0.0
+
'@types/mdast@4.0.4':
dependencies:
'@types/unist': 3.0.3
+ '@types/mdurl@2.0.0': {}
+
'@types/ms@2.1.0': {}
'@types/node@20.17.30':
@@ -4998,11 +5030,11 @@ snapshots:
'@ungap/structured-clone@1.3.0': {}
- '@vitejs/plugin-vue@6.0.0(vite@7.0.6(@types/node@20.17.30)(jiti@2.4.2)(terser@5.39.0))(vue@3.5.18(typescript@5.8.3))':
+ '@vitejs/plugin-vue@6.0.1(vite@7.2.4(@types/node@20.17.30)(jiti@2.4.2)(terser@5.39.0))(vue@3.5.24(typescript@5.8.3))':
dependencies:
- '@rolldown/pluginutils': 1.0.0-beta.19
- vite: 7.0.6(@types/node@20.17.30)(jiti@2.4.2)(terser@5.39.0)
- vue: 3.5.18(typescript@5.8.3)
+ '@rolldown/pluginutils': 1.0.0-beta.29
+ vite: 7.2.4(@types/node@20.17.30)(jiti@2.4.2)(terser@5.39.0)
+ vue: 3.5.24(typescript@5.8.3)
'@volar/language-core@2.4.12':
dependencies:
@@ -5018,10 +5050,10 @@ snapshots:
estree-walker: 2.0.2
source-map-js: 1.2.1
- '@vue/compiler-core@3.5.18':
+ '@vue/compiler-core@3.5.24':
dependencies:
- '@babel/parser': 7.28.0
- '@vue/shared': 3.5.18
+ '@babel/parser': 7.28.5
+ '@vue/shared': 3.5.24
entities: 4.5.0
estree-walker: 2.0.2
source-map-js: 1.2.1
@@ -5031,10 +5063,10 @@ snapshots:
'@vue/compiler-core': 3.5.13
'@vue/shared': 3.5.13
- '@vue/compiler-dom@3.5.18':
+ '@vue/compiler-dom@3.5.24':
dependencies:
- '@vue/compiler-core': 3.5.18
- '@vue/shared': 3.5.18
+ '@vue/compiler-core': 3.5.24
+ '@vue/shared': 3.5.24
'@vue/compiler-sfc@3.5.13':
dependencies:
@@ -5048,15 +5080,15 @@ snapshots:
postcss: 8.5.3
source-map-js: 1.2.1
- '@vue/compiler-sfc@3.5.18':
+ '@vue/compiler-sfc@3.5.24':
dependencies:
- '@babel/parser': 7.28.0
- '@vue/compiler-core': 3.5.18
- '@vue/compiler-dom': 3.5.18
- '@vue/compiler-ssr': 3.5.18
- '@vue/shared': 3.5.18
+ '@babel/parser': 7.28.5
+ '@vue/compiler-core': 3.5.24
+ '@vue/compiler-dom': 3.5.24
+ '@vue/compiler-ssr': 3.5.24
+ '@vue/shared': 3.5.24
estree-walker: 2.0.2
- magic-string: 0.30.17
+ magic-string: 0.30.21
postcss: 8.5.6
source-map-js: 1.2.1
@@ -5065,31 +5097,31 @@ snapshots:
'@vue/compiler-dom': 3.5.13
'@vue/shared': 3.5.13
- '@vue/compiler-ssr@3.5.18':
+ '@vue/compiler-ssr@3.5.24':
dependencies:
- '@vue/compiler-dom': 3.5.18
- '@vue/shared': 3.5.18
+ '@vue/compiler-dom': 3.5.24
+ '@vue/shared': 3.5.24
'@vue/compiler-vue2@2.7.16':
dependencies:
de-indent: 1.0.2
he: 1.2.0
- '@vue/devtools-api@7.7.7':
+ '@vue/devtools-api@8.0.5':
dependencies:
- '@vue/devtools-kit': 7.7.7
+ '@vue/devtools-kit': 8.0.5
- '@vue/devtools-kit@7.7.7':
+ '@vue/devtools-kit@8.0.5':
dependencies:
- '@vue/devtools-shared': 7.7.7
- birpc: 2.3.0
+ '@vue/devtools-shared': 8.0.5
+ birpc: 2.8.0
hookable: 5.5.3
mitt: 3.0.1
- perfect-debounce: 1.0.0
+ perfect-debounce: 2.0.0
speakingurl: 14.0.1
superjson: 2.2.2
- '@vue/devtools-shared@7.7.7':
+ '@vue/devtools-shared@8.0.5':
dependencies:
rfdc: 1.4.1
@@ -5110,19 +5142,19 @@ snapshots:
dependencies:
'@vue/shared': 3.5.13
- '@vue/reactivity@3.5.18':
+ '@vue/reactivity@3.5.24':
dependencies:
- '@vue/shared': 3.5.18
+ '@vue/shared': 3.5.24
'@vue/runtime-core@3.5.13':
dependencies:
'@vue/reactivity': 3.5.13
'@vue/shared': 3.5.13
- '@vue/runtime-core@3.5.18':
+ '@vue/runtime-core@3.5.24':
dependencies:
- '@vue/reactivity': 3.5.18
- '@vue/shared': 3.5.18
+ '@vue/reactivity': 3.5.24
+ '@vue/shared': 3.5.24
'@vue/runtime-dom@3.5.13':
dependencies:
@@ -5131,11 +5163,11 @@ snapshots:
'@vue/shared': 3.5.13
csstype: 3.1.3
- '@vue/runtime-dom@3.5.18':
+ '@vue/runtime-dom@3.5.24':
dependencies:
- '@vue/reactivity': 3.5.18
- '@vue/runtime-core': 3.5.18
- '@vue/shared': 3.5.18
+ '@vue/reactivity': 3.5.24
+ '@vue/runtime-core': 3.5.24
+ '@vue/shared': 3.5.24
csstype: 3.1.3
'@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.8.3))':
@@ -5144,55 +5176,55 @@ snapshots:
'@vue/shared': 3.5.13
vue: 3.5.13(typescript@5.8.3)
- '@vue/server-renderer@3.5.18(vue@3.5.18(typescript@5.8.3))':
+ '@vue/server-renderer@3.5.24(vue@3.5.24(typescript@5.8.3))':
dependencies:
- '@vue/compiler-ssr': 3.5.18
- '@vue/shared': 3.5.18
- vue: 3.5.18(typescript@5.8.3)
+ '@vue/compiler-ssr': 3.5.24
+ '@vue/shared': 3.5.24
+ vue: 3.5.24(typescript@5.8.3)
'@vue/shared@3.5.13': {}
- '@vue/shared@3.5.18': {}
+ '@vue/shared@3.5.24': {}
- '@vueuse/core@11.3.0(vue@3.5.18(typescript@5.8.3))':
+ '@vueuse/core@11.3.0(vue@3.5.13(typescript@5.8.3))':
dependencies:
'@types/web-bluetooth': 0.0.20
'@vueuse/metadata': 11.3.0
- '@vueuse/shared': 11.3.0(vue@3.5.18(typescript@5.8.3))
- vue-demi: 0.14.10(vue@3.5.18(typescript@5.8.3))
+ '@vueuse/shared': 11.3.0(vue@3.5.13(typescript@5.8.3))
+ vue-demi: 0.14.10(vue@3.5.13(typescript@5.8.3))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
- '@vueuse/core@13.5.0(vue@3.5.18(typescript@5.8.3))':
+ '@vueuse/core@14.0.0(vue@3.5.24(typescript@5.8.3))':
dependencies:
'@types/web-bluetooth': 0.0.21
- '@vueuse/metadata': 13.5.0
- '@vueuse/shared': 13.5.0(vue@3.5.18(typescript@5.8.3))
- vue: 3.5.18(typescript@5.8.3)
+ '@vueuse/metadata': 14.0.0
+ '@vueuse/shared': 14.0.0(vue@3.5.24(typescript@5.8.3))
+ vue: 3.5.24(typescript@5.8.3)
- '@vueuse/integrations@13.5.0(focus-trap@7.6.5)(vue@3.5.18(typescript@5.8.3))':
+ '@vueuse/integrations@14.0.0(focus-trap@7.6.6)(vue@3.5.24(typescript@5.8.3))':
dependencies:
- '@vueuse/core': 13.5.0(vue@3.5.18(typescript@5.8.3))
- '@vueuse/shared': 13.5.0(vue@3.5.18(typescript@5.8.3))
- vue: 3.5.18(typescript@5.8.3)
+ '@vueuse/core': 14.0.0(vue@3.5.24(typescript@5.8.3))
+ '@vueuse/shared': 14.0.0(vue@3.5.24(typescript@5.8.3))
+ vue: 3.5.24(typescript@5.8.3)
optionalDependencies:
- focus-trap: 7.6.5
+ focus-trap: 7.6.6
'@vueuse/metadata@11.3.0': {}
- '@vueuse/metadata@13.5.0': {}
+ '@vueuse/metadata@14.0.0': {}
- '@vueuse/shared@11.3.0(vue@3.5.18(typescript@5.8.3))':
+ '@vueuse/shared@11.3.0(vue@3.5.13(typescript@5.8.3))':
dependencies:
- vue-demi: 0.14.10(vue@3.5.18(typescript@5.8.3))
+ vue-demi: 0.14.10(vue@3.5.13(typescript@5.8.3))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
- '@vueuse/shared@13.5.0(vue@3.5.18(typescript@5.8.3))':
+ '@vueuse/shared@14.0.0(vue@3.5.24(typescript@5.8.3))':
dependencies:
- vue: 3.5.18(typescript@5.8.3)
+ vue: 3.5.24(typescript@5.8.3)
JSONStream@1.3.5:
dependencies:
@@ -5356,7 +5388,7 @@ snapshots:
binaryextensions@2.3.0: {}
- birpc@2.3.0: {}
+ birpc@2.8.0: {}
bl@1.2.3:
dependencies:
@@ -6219,7 +6251,7 @@ snapshots:
dependencies:
reusify: 1.1.0
- fdir@6.4.6(picomatch@4.0.3):
+ fdir@6.5.0(picomatch@4.0.3):
optionalDependencies:
picomatch: 4.0.3
@@ -6298,9 +6330,9 @@ snapshots:
inherits: 2.0.4
readable-stream: 2.3.8
- focus-trap@7.6.5:
+ focus-trap@7.6.6:
dependencies:
- tabbable: 6.2.0
+ tabbable: 6.3.0
follow-redirects@1.5.10:
dependencies:
@@ -6609,6 +6641,8 @@ snapshots:
dependencies:
lru-cache: 7.18.3
+ htm@3.1.1: {}
+
html-void-elements@3.0.0: {}
http-cache-semantics@3.8.1: {}
@@ -7017,6 +7051,10 @@ snapshots:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
make-dir@1.3.0:
dependencies:
pify: 3.0.0
@@ -7408,7 +7446,7 @@ snapshots:
minipass@7.1.2: {}
- minisearch@7.1.2: {}
+ minisearch@7.2.0: {}
minizlib@2.1.2:
dependencies:
@@ -7823,7 +7861,7 @@ snapshots:
path-type@4.0.0: {}
- perfect-debounce@1.0.0: {}
+ perfect-debounce@2.0.0: {}
picocolors@1.1.1: {}
@@ -8179,6 +8217,17 @@ snapshots:
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
+ shiki@3.15.0:
+ dependencies:
+ '@shikijs/core': 3.15.0
+ '@shikijs/engine-javascript': 3.15.0
+ '@shikijs/engine-oniguruma': 3.15.0
+ '@shikijs/langs': 3.15.0
+ '@shikijs/themes': 3.15.0
+ '@shikijs/types': 3.15.0
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+
shiki@3.2.2:
dependencies:
'@shikijs/core': 3.2.2
@@ -8190,17 +8239,6 @@ snapshots:
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
- shiki@3.9.2:
- dependencies:
- '@shikijs/core': 3.9.2
- '@shikijs/engine-javascript': 3.9.2
- '@shikijs/engine-oniguruma': 3.9.2
- '@shikijs/langs': 3.9.2
- '@shikijs/themes': 3.9.2
- '@shikijs/types': 3.9.2
- '@shikijs/vscode-textmate': 10.0.2
- '@types/hast': 3.0.4
-
shortid@2.2.17:
dependencies:
nanoid: 3.3.11
@@ -8427,7 +8465,7 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
- tabbable@6.2.0: {}
+ tabbable@6.3.0: {}
tapable@2.2.1: {}
@@ -8483,9 +8521,9 @@ snapshots:
tinyexec@0.3.2: {}
- tinyglobby@0.2.14:
+ tinyglobby@0.2.15:
dependencies:
- fdir: 6.4.6(picomatch@4.0.3)
+ fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
tmp@0.0.33:
@@ -8711,39 +8749,40 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.2
- vite@7.0.6(@types/node@20.17.30)(jiti@2.4.2)(terser@5.39.0):
+ vite@7.2.4(@types/node@20.17.30)(jiti@2.4.2)(terser@5.39.0):
dependencies:
esbuild: 0.25.2
- fdir: 6.4.6(picomatch@4.0.3)
+ fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.6
rollup: 4.43.0
- tinyglobby: 0.2.14
+ tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 20.17.30
fsevents: 2.3.3
jiti: 2.4.2
terser: 5.39.0
- vitepress@2.0.0-alpha.9(@types/node@20.17.30)(jiti@2.4.2)(postcss@8.5.6)(terser@5.39.0)(typescript@5.8.3):
- dependencies:
- '@docsearch/css': 4.0.0-beta.5
- '@docsearch/js': 4.0.0-beta.5
- '@iconify-json/simple-icons': 1.2.46
- '@shikijs/core': 3.9.2
- '@shikijs/transformers': 3.9.2
- '@shikijs/types': 3.9.2
- '@vitejs/plugin-vue': 6.0.0(vite@7.0.6(@types/node@20.17.30)(jiti@2.4.2)(terser@5.39.0))(vue@3.5.18(typescript@5.8.3))
- '@vue/devtools-api': 7.7.7
- '@vue/shared': 3.5.18
- '@vueuse/core': 13.5.0(vue@3.5.18(typescript@5.8.3))
- '@vueuse/integrations': 13.5.0(focus-trap@7.6.5)(vue@3.5.18(typescript@5.8.3))
- focus-trap: 7.6.5
+ vitepress@2.0.0-alpha.15(@types/node@20.17.30)(jiti@2.4.2)(postcss@8.5.6)(terser@5.39.0)(typescript@5.8.3):
+ dependencies:
+ '@docsearch/css': 4.3.2
+ '@docsearch/js': 4.3.2
+ '@iconify-json/simple-icons': 1.2.59
+ '@shikijs/core': 3.15.0
+ '@shikijs/transformers': 3.15.0
+ '@shikijs/types': 3.15.0
+ '@types/markdown-it': 14.1.2
+ '@vitejs/plugin-vue': 6.0.1(vite@7.2.4(@types/node@20.17.30)(jiti@2.4.2)(terser@5.39.0))(vue@3.5.24(typescript@5.8.3))
+ '@vue/devtools-api': 8.0.5
+ '@vue/shared': 3.5.24
+ '@vueuse/core': 14.0.0(vue@3.5.24(typescript@5.8.3))
+ '@vueuse/integrations': 14.0.0(focus-trap@7.6.6)(vue@3.5.24(typescript@5.8.3))
+ focus-trap: 7.6.6
mark.js: 8.11.1
- minisearch: 7.1.2
- shiki: 3.9.2
- vite: 7.0.6(@types/node@20.17.30)(jiti@2.4.2)(terser@5.39.0)
- vue: 3.5.18(typescript@5.8.3)
+ minisearch: 7.2.0
+ shiki: 3.15.0
+ vite: 7.2.4(@types/node@20.17.30)(jiti@2.4.2)(terser@5.39.0)
+ vue: 3.5.24(typescript@5.8.3)
optionalDependencies:
postcss: 8.5.6
transitivePeerDependencies:
@@ -8771,9 +8810,9 @@ snapshots:
- universal-cookie
- yaml
- vue-demi@0.14.10(vue@3.5.18(typescript@5.8.3)):
+ vue-demi@0.14.10(vue@3.5.13(typescript@5.8.3)):
dependencies:
- vue: 3.5.18(typescript@5.8.3)
+ vue: 3.5.13(typescript@5.8.3)
vue-resize@2.0.0-alpha.1(vue@3.5.13(typescript@5.8.3)):
dependencies:
@@ -8789,13 +8828,13 @@ snapshots:
optionalDependencies:
typescript: 5.8.3
- vue@3.5.18(typescript@5.8.3):
+ vue@3.5.24(typescript@5.8.3):
dependencies:
- '@vue/compiler-dom': 3.5.18
- '@vue/compiler-sfc': 3.5.18
- '@vue/runtime-dom': 3.5.18
- '@vue/server-renderer': 3.5.18(vue@3.5.18(typescript@5.8.3))
- '@vue/shared': 3.5.18
+ '@vue/compiler-dom': 3.5.24
+ '@vue/compiler-sfc': 3.5.24
+ '@vue/runtime-dom': 3.5.24
+ '@vue/server-renderer': 3.5.24(vue@3.5.24(typescript@5.8.3))
+ '@vue/shared': 3.5.24
optionalDependencies:
typescript: 5.8.3
diff --git a/src/Chart.ts b/src/Chart.ts
index 05b30a60c..ebd065e1c 100644
--- a/src/Chart.ts
+++ b/src/Chart.ts
@@ -25,7 +25,7 @@ import type Crosshair from './common/Crosshair'
import type { ActionType, ActionCallback } from './common/Action'
import type { DataLoader } from './common/DataLoader'
import type VisibleRange from './common/VisibleRange'
-import type { Formatter, DecimalFold, LayoutChild, Options, ThousandsSeparator, ZoomAnchor } from './Options'
+import type { Formatter, DecimalFold, LayoutChild, Options, ThousandsSeparator, BarSpaceLimit, ZoomAnchor } from './Options'
import Animation from './common/Animation'
import { createId } from './common/utils/id'
import { createDom } from './common/utils/dom'
@@ -686,6 +686,14 @@ export default class ChartImp implements Chart {
return this._chartStore.getBarSpace()
}
+ setBarSpaceLimit (limit: Partial
): void {
+ this._chartStore.setBarSpaceLimit(limit)
+ }
+
+ getBarSpaceLimit (): BarSpaceLimit {
+ return this._chartStore.getBarSpaceLimit()
+ }
+
getVisibleRange (): VisibleRange {
return this._chartStore.getVisibleRange()
}
@@ -694,8 +702,8 @@ export default class ChartImp implements Chart {
this._chartStore.resetData()
}
- getDataList (): KLineData[] {
- return this._chartStore.getDataList()
+ getDataList (mutateToCandleType = false): KLineData[] {
+ return this._chartStore.getDataList(mutateToCandleType)
}
setDataLoader (dataLoader: DataLoader): void {
@@ -973,8 +981,8 @@ export default class ChartImp implements Chart {
this._chartStore.setZoomAnchor(anchor)
}
- zoomAnchor (): ZoomAnchor {
- return this._chartStore.zoomAnchor()
+ getZoomAnchor (): ZoomAnchor {
+ return this._chartStore.getZoomAnchor()
}
setScrollEnabled (enabled: boolean): void {
@@ -1030,12 +1038,12 @@ export default class ChartImp implements Chart {
animation.doFrame(frameTime => {
const progressBarSpace = difSpace * (frameTime / duration)
const scale = (progressBarSpace - prevProgressBarSpace) / this._chartStore.getBarSpace().bar * SCALE_MULTIPLIER
- this._chartStore.zoom(scale, coordinate)
+ this._chartStore.zoom(scale, coordinate ?? null, 'main')
prevProgressBarSpace = progressBarSpace
})
animation.start()
} else {
- this._chartStore.zoom(difSpace / barSpace * SCALE_MULTIPLIER, coordinate)
+ this._chartStore.zoom(difSpace / barSpace * SCALE_MULTIPLIER, coordinate ?? null, 'main')
}
}
@@ -1111,11 +1119,14 @@ export default class ChartImp implements Chart {
return isArray(coordinates) ? points : (points[0] ?? {})
}
- executeAction (type: ActionType, data: Crosshair): void {
+ executeAction (type: ActionType, data: Nullable): void {
switch (type) {
case 'onCrosshairChange': {
- const crosshair: Crosshair = { ...data }
- crosshair.paneId ??= PaneIdConstants.CANDLE
+ let crosshair: Nullable = null
+ if (isValid(data)) {
+ crosshair = { ...data }
+ crosshair.paneId ??= PaneIdConstants.CANDLE
+ }
this._chartStore.setCrosshair(crosshair, { notExecuteAction: true })
break
}
diff --git a/src/Event.ts b/src/Event.ts
index 1cb484d07..1e3ad9827 100644
--- a/src/Event.ts
+++ b/src/Event.ts
@@ -72,11 +72,11 @@ export default class Event implements EventHandler {
if (event.shiftKey) {
switch (event.code) {
case 'Equal': {
- this._chart.getChartStore().zoom(0.5, this._chart.getChartStore().zoomAnchor().main === 'last_bar' ? this.lastDataCoordinate() : undefined)
+ this._chart.getChartStore().zoom(0.5, null, 'main')
break
}
case 'Minus': {
- this._chart.getChartStore().zoom(-0.5, this._chart.getChartStore().zoomAnchor().main === 'last_bar' ? this.lastDataCoordinate() : undefined)
+ this._chart.getChartStore().zoom(-0.5, null, 'main')
break
}
case 'ArrowLeft': {
@@ -108,11 +108,6 @@ export default class Event implements EventHandler {
container.addEventListener('keydown', this._boundKeyBoardDownEvent)
}
- private lastDataCoordinate (): Partial {
- const store = this._chart.getChartStore()
- return { x: store.dataIndexToCoordinate(store.getDataList().length - 1) }
- }
-
pinchStartEvent (): boolean {
this._touchZoomed = true
this._pinchScale = 1
@@ -125,7 +120,7 @@ export default class Event implements EventHandler {
const event = this._makeWidgetEvent(e, widget)
const zoomScale = (scale - this._pinchScale) * 5
this._pinchScale = scale
- this._chart.getChartStore().zoom(zoomScale, this._chart.getChartStore().zoomAnchor().main === 'last_bar' ? this.lastDataCoordinate() : { x: event.x, y: event.y })
+ this._chart.getChartStore().zoom(zoomScale, { x: event.x, y: event.y }, 'main')
return true
}
return false
@@ -143,7 +138,7 @@ export default class Event implements EventHandler {
const event = this._makeWidgetEvent(e, widget)
const name = widget?.getName()
if (name === WidgetNameConstants.MAIN) {
- this._chart.getChartStore().zoom(scale, this._chart.getChartStore().zoomAnchor().main === 'last_bar' ? this.lastDataCoordinate() : { x: event.x, y: event.y })
+ this._chart.getChartStore().zoom(scale, { x: event.x, y: event.y }, 'main')
return true
}
return false
@@ -160,14 +155,19 @@ export default class Event implements EventHandler {
return widget.dispatchEvent('mouseDownEvent', event)
}
case WidgetNameConstants.MAIN: {
- const yAxis = (pane as DrawPane).getAxisComponent()
- if (!yAxis.getAutoCalcTickFlag()) {
- const range = yAxis.getRange()
- this._prevYAxisRange = { ...range }
+ // Dispatch event first to allow overlays (e.g., continuous drawing) to consume it
+ const consumed = widget.dispatchEvent('mouseDownEvent', event)
+ // Only start scrolling if the event was not consumed by an overlay
+ if (!consumed) {
+ const yAxis = (pane as DrawPane).getAxisComponent()
+ if (!yAxis.getAutoCalcTickFlag()) {
+ const range = yAxis.getRange()
+ this._prevYAxisRange = { ...range }
+ }
+ this._startScrollCoordinate = { x: event.x, y: event.y }
+ this._chart.getChartStore().startScroll()
}
- this._startScrollCoordinate = { x: event.x, y: event.y }
- this._chart.getChartStore().startScroll()
- return widget.dispatchEvent('mouseDownEvent', event)
+ return consumed
}
case WidgetNameConstants.X_AXIS: {
return this._processXAxisScrollStartEvent(widget, event)
@@ -239,6 +239,9 @@ export default class Event implements EventHandler {
const consumed = widget.dispatchEvent('pressedMouseMoveEvent', event)
if (!consumed) {
this._processMainScrollingEvent(widget as Widget>, event)
+ } else {
+ // Explicitly update overlay when event was consumed (e.g., continuous drawing)
+ this._chart.updatePane(UpdateLevel.Overlay)
}
if (!consumed || widget.getForceCursor() === 'pointer') {
crosshair = { x: event.x, y: event.y, paneId: pane?.getId() }
@@ -407,17 +410,18 @@ export default class Event implements EventHandler {
const { pane, widget } = this._findWidgetByEvent(e)
if (widget !== null) {
const event = this._makeWidgetEvent(e, widget)
- event.preventDefault?.()
const name = widget.getName()
const chartStore = this._chart.getChartStore()
switch (name) {
case WidgetNameConstants.MAIN: {
if (widget.dispatchEvent('pressedMouseMoveEvent', event)) {
+ event.preventDefault?.()
chartStore.setCrosshair(undefined, { notInvalidate: true })
this._chart.updatePane(UpdateLevel.Overlay)
return true
}
if (this._touchCoordinate !== null) {
+ event.preventDefault?.()
chartStore.setCrosshair({ x: event.x, y: event.y, paneId: pane?.getId() })
} else {
this._processMainScrollingEvent(widget as Widget>, event)
@@ -425,6 +429,7 @@ export default class Event implements EventHandler {
return true
}
case WidgetNameConstants.X_AXIS: {
+ event.preventDefault?.()
return this._processXAxisScrollingEvent(widget as Widget>, event)
}
case WidgetNameConstants.Y_AXIS: {
@@ -536,6 +541,7 @@ export default class Event implements EventHandler {
if (this._startScrollCoordinate !== null) {
const yAxis = widget.getPane().getAxisComponent()
if (this._prevYAxisRange !== null && !yAxis.getAutoCalcTickFlag() && yAxis.scrollZoomEnabled) {
+ event.preventDefault?.()
const { from, to, range } = this._prevYAxisRange
let distance = 0
if (yAxis.reverse) {
@@ -588,7 +594,7 @@ export default class Event implements EventHandler {
if (Number.isFinite(scale)) {
const zoomScale = (scale - this._xAxisScale) * 10
this._xAxisScale = scale
- this._chart.getChartStore().zoom(zoomScale, this._chart.getChartStore().zoomAnchor().xAxis === 'last_bar' ? this.lastDataCoordinate() : (this._xAxisStartScaleCoordinate ?? undefined))
+ this._chart.getChartStore().zoom(zoomScale, this._xAxisStartScaleCoordinate, 'xAxis')
}
}
} else {
@@ -613,6 +619,7 @@ export default class Event implements EventHandler {
if (!consumed) {
const yAxis = widget.getPane().getAxisComponent()
if (this._prevYAxisRange !== null && yAxis.scrollZoomEnabled && this._yAxisStartScaleDistance !== 0) {
+ event.preventDefault?.()
const { from, to, range } = this._prevYAxisRange
const scale = event.pageY / this._yAxisStartScaleDistance
const newRange = range * scale
diff --git a/src/Options.ts b/src/Options.ts
index a64712e17..84b364eff 100644
--- a/src/Options.ts
+++ b/src/Options.ts
@@ -85,9 +85,16 @@ export interface ThousandsSeparator {
format: (value: string | number) => string
}
+export type ZoomAnchorType = 'cursor' | 'last_bar'
+
export interface ZoomAnchor {
- main: 'cursor_point' | 'last_bar'
- xAxis: 'cursor_point' | 'last_bar'
+ main: ZoomAnchorType
+ xAxis: ZoomAnchorType
+}
+
+export interface BarSpaceLimit {
+ min: number
+ max: number
}
export interface Options {
@@ -99,4 +106,5 @@ export interface Options {
decimalFold?: Partial
layout?: LayoutChild[]
zoomAnchor?: Partial
+ barSpaceLimit?: Partial
}
diff --git a/src/Store.ts b/src/Store.ts
index bbd5d4a50..a34a65fea 100644
--- a/src/Store.ts
+++ b/src/Store.ts
@@ -20,7 +20,7 @@ import type { KLineData, VisibleRangeData } from './common/Data'
import type VisibleRange from './common/VisibleRange'
import type Coordinate from './common/Coordinate'
import { getDefaultVisibleRange } from './common/VisibleRange'
-import TaskScheduler, { generateTaskId } from './common/TaskScheduler'
+import TaskScheduler from './common/TaskScheduler'
import type Crosshair from './common/Crosshair'
import type BarSpace from './common/BarSpace'
import type { Period } from './common/Period'
@@ -37,7 +37,7 @@ import { logWarn } from './common/utils/logger'
import { UpdateLevel } from './common/Updater'
import type { DataLoader, DataLoaderGetBarsParams, DataLoadMore, DataLoadType } from './common/DataLoader'
-import type { Options, Formatter, ThousandsSeparator, DecimalFold, FormatDateType, FormatDateParams, FormatBigNumber, FormatExtendText, FormatExtendTextParams, ZoomAnchor } from './Options'
+import type { Options, Formatter, ThousandsSeparator, DecimalFold, BarSpaceLimit, FormatDateType, FormatDateParams, FormatBigNumber, FormatExtendText, FormatExtendTextParams, ZoomAnchor, ZoomAnchorType } from './Options'
import type { IndicatorOverride, IndicatorCreate, IndicatorFilter } from './component/Indicator'
import type IndicatorImp from './component/Indicator'
@@ -53,7 +53,7 @@ import { PaneIdConstants } from './pane/types'
import type Chart from './Chart'
-const BarSpaceLimitConstants = {
+const DEFAULT_BAR_SPACE_LIMIT = {
MIN: 1,
MAX: 50
}
@@ -105,7 +105,7 @@ export interface Store {
getSymbol: () => Nullable
setPeriod: (period: Period) => void
getPeriod: () => Nullable
- getDataList: () => KLineData[]
+ getDataList: (mutateToCandleType?: boolean) => KLineData[]
setOffsetRightDistance: (distance: number) => void
getOffsetRightDistance: () => number
setMaxOffsetLeftDistance: (distance: number) => void
@@ -114,6 +114,8 @@ export interface Store {
setRightMinVisibleBarCount: (barCount: number) => void
setBarSpace: (space: number) => void
getBarSpace: () => BarSpace
+ setBarSpaceLimit: (limit: Partial) => void
+ getBarSpaceLimit: () => BarSpaceLimit
getVisibleRange: () => VisibleRange
setDataLoader: (dataLoader: DataLoader) => void
overrideIndicator: (override: IndicatorCreate) => boolean
@@ -123,7 +125,7 @@ export interface Store {
setZoomEnabled: (enabled: boolean) => void
isZoomEnabled: () => boolean
setZoomAnchor: (behavior: ZoomAnchor) => void
- zoomAnchor: () => ZoomAnchor
+ getZoomAnchor: () => ZoomAnchor
setScrollEnabled: (enabled: boolean) => void
isScrollEnabled: () => boolean
resetData: () => void
@@ -228,8 +230,8 @@ export default class StoreImp implements Store {
* Zoom anchor point flag
*/
private readonly _zoomAnchor: ZoomAnchor = {
- main: 'cursor_point',
- xAxis: 'cursor_point'
+ main: 'cursor',
+ xAxis: 'cursor'
}
/**
@@ -247,6 +249,14 @@ export default class StoreImp implements Store {
*/
private _barSpace = DEFAULT_BAR_SPACE
+ /**
+ * Bar space limit
+ */
+ private readonly _barSpaceLimit: BarSpaceLimit = {
+ min: DEFAULT_BAR_SPACE_LIMIT.MIN,
+ max: DEFAULT_BAR_SPACE_LIMIT.MAX
+ }
+
/**
* The space of the draw bar
*/
@@ -318,7 +328,7 @@ export default class StoreImp implements Store {
/**
* Task scheduler
*/
- private readonly _taskScheduler = new TaskScheduler()
+ private readonly _taskScheduler: TaskScheduler
/**
* Overlay
@@ -369,7 +379,7 @@ export default class StoreImp implements Store {
this._chart = chart
this._calcOptimalBarSpace()
this._lastBarRightSideDiffBarCount = this._offsetRightDistance / this._barSpace
- const { styles, locale, timezone, formatter, thousandsSeparator, decimalFold, zoomAnchor } = options ?? {}
+ const { styles, locale, timezone, formatter, thousandsSeparator, decimalFold, barSpaceLimit, zoomAnchor } = options ?? {}
if (isValid(styles)) {
this.setStyles(styles)
}
@@ -389,6 +399,16 @@ export default class StoreImp implements Store {
if (isValid(zoomAnchor)) {
this.setZoomAnchor(zoomAnchor)
}
+ if (isValid(barSpaceLimit)) {
+ this.setBarSpaceLimit(barSpaceLimit)
+ }
+ this._taskScheduler = new TaskScheduler(() => {
+ this._chart.layout({
+ measureWidth: true,
+ update: true,
+ buildYAxisTick: true
+ })
+ })
}
setStyles (value: string | DeepPartial): void {
@@ -512,18 +532,94 @@ export default class StoreImp implements Store {
return this._period
}
- getDataList (): KLineData[] {
- return this._dataList
+ getDataList (mutateToCandleType = false): KLineData[] {
+ if (!mutateToCandleType || this._styles.candle.type !== 'heikin_ashi') {
+ return this._dataList
+ }
+
+ let prevHaBar: Nullable = null
+ return this._dataList.map(value => {
+ const data = this.ohlcvToHeikinAshi(value, prevHaBar)
+ prevHaBar = data
+ return data
+ })
}
- getVisibleRangeDataList (): VisibleRangeData[] {
- return this._visibleRangeDataList
+ getVisibleRangeDataList (mutateToCandleType = false): VisibleRangeData[] {
+ if (!mutateToCandleType || this._styles.candle.type !== 'heikin_ashi') {
+ return this._visibleRangeDataList
+ }
+
+ const result: VisibleRangeData[] = []
+ let prevHaBar: Nullable = null
+ const list = this._visibleRangeDataList
+ const len = list.length
+
+ for (let i = 0; i < len; i++) {
+ const curOrig: Nullable = list[i].data.current ?? null
+ if (curOrig === null) {
+ continue
+ }
+ const curHa = this.ohlcvToHeikinAshi(curOrig, prevHaBar)
+ const prevHa = prevHaBar
+
+ let nextHa: Nullable = null
+ if (i + 1 < len) {
+ const nextOrig: Nullable = list[i + 1].data.current ?? null
+ nextHa = nextOrig === null ? null : this.ohlcvToHeikinAshi(nextOrig, curHa)
+ } else {
+ nextHa = null
+ }
+
+ result.push({
+ dataIndex: list[i].dataIndex,
+ x: list[i].x,
+ data: {
+ prev: prevHa,
+ current: curHa,
+ next: nextHa
+ }
+ })
+
+ prevHaBar = curHa
+ }
+
+ return result
}
getVisibleRangeHighLowPrice (): Array<{ price: number; x: number }> {
return this._visibleRangeHighLowPrice
}
+ private ohlcvToHeikinAshi (currentBar: KLineData, prevHaBar: Nullable): KLineData {
+ if (prevHaBar === null) {
+ const firstHaBar: KLineData = {
+ timestamp: currentBar.timestamp,
+ open: (currentBar.open + currentBar.close) / 2, // Some methods use currentBar.o, but averaging is more common
+ high: currentBar.high,
+ low: currentBar.low,
+ close: (currentBar.open + currentBar.high + currentBar.low + currentBar.close) / 4
+ }
+
+ return firstHaBar
+ }
+
+ const haClose = (currentBar.open + currentBar.high + currentBar.low + currentBar.close) / 4
+ const haOpen = (prevHaBar.open + prevHaBar.close) / 2
+ const haHigh = Math.max(currentBar.high, haOpen, haClose)
+ const haLow = Math.min(currentBar.low, haOpen, haClose)
+
+ const newHaBar: KLineData = {
+ timestamp: currentBar.timestamp,
+ open: haOpen,
+ high: haHigh,
+ low: haLow,
+ close: haClose
+ }
+
+ return newHaBar
+ }
+
private _addData (
data: KLineData | KLineData[],
type: DataLoadType,
@@ -590,14 +686,13 @@ export default class StoreImp implements Store {
adjustFlag = true
}
}
- if (success) {
- if (adjustFlag) {
- this._adjustVisibleRange()
- this.setCrosshair(this._crosshair, { notInvalidate: true })
- const filterIndicators = this.getIndicatorsByFilter({})
- filterIndicators.forEach(indicator => {
- this._addIndicatorCalcTask(indicator, type)
- })
+ if (success && adjustFlag) {
+ this._adjustVisibleRange()
+ this.setCrosshair(this._crosshair, { notInvalidate: true })
+ const filterIndicators = this.getIndicatorsByFilter({})
+ if (filterIndicators.length > 0) {
+ this._calcIndicator(filterIndicators)
+ } else {
this._chart.layout({
measureWidth: true,
update: true,
@@ -712,7 +807,7 @@ export default class StoreImp implements Store {
symbol: this._symbol,
period: this._period,
timestamp: null,
- callback: (data: KLineData[], more?: boolean) => {
+ callback: (data: KLineData[], more?: DataLoadMore) => {
this._loading = false
this._addData(data, type, more)
if (type === 'init') {
@@ -768,8 +863,21 @@ export default class StoreImp implements Store {
}
}
+ setBarSpaceLimit (limit: Partial): void {
+ if (isValid(limit.min) && isNumber(limit.min)) {
+ this._barSpaceLimit.min = Math.max(0, limit.min)
+ }
+ if (isValid(limit.max) && isNumber(limit.max)) {
+ this._barSpaceLimit.max = Math.max(this._barSpaceLimit.min, limit.max)
+ }
+ }
+
+ getBarSpaceLimit (): BarSpaceLimit {
+ return { ...this._barSpaceLimit }
+ }
+
setBarSpace (barSpace: number, adjustBeforeFunc?: () => void): void {
- if (barSpace < BarSpaceLimitConstants.MIN || barSpace > BarSpaceLimitConstants.MAX || this._barSpace === barSpace) {
+ if (barSpace < this._barSpaceLimit.min || barSpace > this._barSpaceLimit.max || this._barSpace === barSpace) {
return
}
this._barSpace = barSpace
@@ -1019,13 +1127,154 @@ export default class StoreImp implements Store {
return Math.ceil(this.coordinateToFloatIndex(x)) - 1
}
- zoom (scale: number, coordinate?: Partial): void {
+ /**
+ * Converts a float data index to an interpolated timestamp.
+ * This allows sub-bar precision for smooth freehand drawings.
+ * Supports extrapolation beyond the data range (drawing in the "future").
+ * @param floatIndex - A floating point index (e.g., 42.75)
+ * @returns An interpolated timestamp between two bars
+ */
+ floatIndexToTimestamp (floatIndex: number): Nullable {
+ const length = this._dataList.length
+ if (length === 0) {
+ return null
+ }
+
+ const lastIndex = length - 1
+
+ // Handle float index beyond the last bar (extrapolate into the future)
+ if (floatIndex > lastIndex && length >= 2) {
+ const lastTimestamp = this._dataList[lastIndex].timestamp
+ const secondLastTimestamp = this._dataList[lastIndex - 1].timestamp
+ const barDuration = lastTimestamp - secondLastTimestamp
+ if (barDuration > 0) {
+ const barsBeyondLast = floatIndex - lastIndex
+ return Math.round(lastTimestamp + barsBeyondLast * barDuration)
+ }
+ }
+
+ // Handle float index before the first bar (extrapolate into the past)
+ if (floatIndex < 0 && length >= 2) {
+ const firstTimestamp = this._dataList[0].timestamp
+ const secondTimestamp = this._dataList[1].timestamp
+ const barDuration = secondTimestamp - firstTimestamp
+ if (barDuration > 0) {
+ return Math.round(firstTimestamp + floatIndex * barDuration)
+ }
+ }
+
+ // Normal case: interpolate between two bars within the data range
+ const intIndex = Math.floor(floatIndex)
+ const fraction = floatIndex - intIndex
+
+ // Get timestamp at the integer index
+ const timestampAtInt = this.dataIndexToTimestamp(intIndex)
+
+ // If no fractional part, return the integer timestamp
+ if (fraction === 0 || !isNumber(timestampAtInt)) {
+ return timestampAtInt
+ }
+
+ // Get timestamp at the next index for interpolation
+ const timestampAtNext = this.dataIndexToTimestamp(intIndex + 1)
+
+ if (isNumber(timestampAtNext)) {
+ // Linear interpolation between the two timestamps
+ return Math.round(timestampAtInt + (timestampAtNext - timestampAtInt) * fraction)
+ }
+
+ return timestampAtInt
+ }
+
+ /**
+ * Converts a precise timestamp to a float data index.
+ * This preserves sub-bar precision for smooth freehand drawings across timeframe changes.
+ * Supports extrapolation beyond the data range (drawing in the "future").
+ * @param timestamp - A precise timestamp (possibly between or beyond bars)
+ * @returns A floating point index representing the exact position
+ */
+ timestampToFloatIndex (timestamp: number): number {
+ const length = this._dataList.length
+ if (length === 0) {
+ return 0
+ }
+
+ const firstTimestamp = this._dataList[0].timestamp
+ const lastTimestamp = this._dataList[length - 1].timestamp
+
+ // Handle timestamp beyond the last bar (drawing in the "future")
+ if (timestamp > lastTimestamp && length >= 2) {
+ // Calculate average bar duration from the last two bars
+ const secondLastTimestamp = this._dataList[length - 2].timestamp
+ const barDuration = lastTimestamp - secondLastTimestamp
+ if (barDuration > 0) {
+ const timeBeyondLast = timestamp - lastTimestamp
+ const barsBeyond = timeBeyondLast / barDuration
+ return length - 1 + barsBeyond
+ }
+ }
+
+ // Handle timestamp before the first bar
+ if (timestamp < firstTimestamp && length >= 2) {
+ const secondTimestamp = this._dataList[1].timestamp
+ const barDuration = secondTimestamp - firstTimestamp
+ if (barDuration > 0) {
+ const timeBeforeFirst = firstTimestamp - timestamp
+ const barsBefore = timeBeforeFirst / barDuration
+ return -barsBefore
+ }
+ }
+
+ // Find the floor bar index using binary search
+ // We need the bar where barTimestamp <= timestamp < nextBarTimestamp
+ let left = 0
+ let right = length - 1
+ let floorIndex = 0
+
+ while (left <= right) {
+ const mid = Math.floor((left + right) / 2)
+ const midTimestamp = this._dataList[mid].timestamp
+
+ if (midTimestamp <= timestamp) {
+ floorIndex = mid
+ left = mid + 1
+ } else {
+ right = mid - 1
+ }
+ }
+
+ // Get the floor bar and the next bar for interpolation
+ const dataAtFloor = this._dataList[floorIndex]
+ const dataAtNext = floorIndex + 1 < length ? this._dataList[floorIndex + 1] : null
+
+ if (isValid(dataAtFloor) && isValid(dataAtNext)) {
+ const timestampAtFloor = dataAtFloor.timestamp
+ const timestampAtNext = dataAtNext.timestamp
+
+ // Calculate fractional position between the two bars
+ if (timestamp >= timestampAtFloor && timestampAtNext > timestampAtFloor) {
+ const fraction = (timestamp - timestampAtFloor) / (timestampAtNext - timestampAtFloor)
+ return floorIndex + Math.min(fraction, 1) // Clamp to max 1
+ }
+ }
+
+ return floorIndex
+ }
+
+ zoom (scale: number, coordinate: Nullable>, position: 'main' | 'xAxis'): void {
if (!this._zoomEnabled) {
return
}
- let zoomCoordinate: Nullable> = coordinate ?? null
- if (!isNumber(zoomCoordinate?.x)) {
- zoomCoordinate = { x: this._crosshair.x ?? this._totalBarSpace / 2 }
+ const zoomCoordinate: Partial = coordinate ?? { x: this._crosshair.x ?? this._totalBarSpace / 2 }
+
+ if (position === 'xAxis') {
+ if (this._zoomAnchor.xAxis === 'last_bar') {
+ zoomCoordinate.x = this.dataIndexToCoordinate(this._dataList.length - 1)
+ }
+ } else {
+ if (this._zoomAnchor.main === 'last_bar') {
+ zoomCoordinate.x = this.dataIndexToCoordinate(this._dataList.length - 1)
+ }
}
const x = zoomCoordinate.x!
const floatIndex = this.coordinateToFloatIndex(x)
@@ -1048,16 +1297,21 @@ export default class StoreImp implements Store {
return this._zoomEnabled
}
- setZoomAnchor (anchor: Partial): void {
- if (isValid(anchor.main) && isString(anchor.main)) {
- this._zoomAnchor.main = anchor.main
- }
- if (isValid(anchor.xAxis) && isString(anchor.xAxis)) {
- this._zoomAnchor.xAxis = anchor.xAxis
+ setZoomAnchor (anchor: ZoomAnchorType | Partial): void {
+ if (isString(anchor)) {
+ this._zoomAnchor.main = anchor
+ this._zoomAnchor.xAxis = anchor
+ } else {
+ if (isString(anchor.main)) {
+ this._zoomAnchor.main = anchor.main
+ }
+ if (isString(anchor.xAxis)) {
+ this._zoomAnchor.xAxis = anchor.xAxis
+ }
}
}
- zoomAnchor (): ZoomAnchor {
+ getZoomAnchor (): ZoomAnchor {
return { ...this._zoomAnchor }
}
@@ -1070,7 +1324,7 @@ export default class StoreImp implements Store {
}
setCrosshair (
- crosshair?: Crosshair,
+ crosshair?: Nullable,
options?: { notInvalidate?: boolean, notExecuteAction?: boolean, forceInvalidate?: boolean }
): void {
const { notInvalidate, notExecuteAction, forceInvalidate } = options ?? {}
@@ -1149,38 +1403,16 @@ export default class StoreImp implements Store {
}
}
- private _addIndicatorCalcTask (indicator: IndicatorImp, dataLoadType: DataLoadType): void {
- indicator.onDataStateChange?.({
- state: 'loading',
- type: dataLoadType,
- indicator
- })
- void this._taskScheduler.add({
- id: generateTaskId(indicator.id),
- handler: async () => await indicator.calcImp(this._dataList).then(result => result)
- }).then(result => {
- if (result) {
- this._chart.layout({
- measureWidth: true,
- update: true,
- buildYAxisTick: true,
- cacheYAxisWidth: dataLoadType !== 'init'
- })
- indicator.onDataStateChange?.({
- state: 'ready',
- type: dataLoadType,
- indicator
- })
- }
- }).catch((e: unknown) => {
- if (e !== 'canceled') {
- indicator.onDataStateChange?.({
- state: 'error',
- type: dataLoadType,
- indicator
- })
- }
- })
+ private _calcIndicator (data: IndicatorImp | IndicatorImp[]): void {
+ let indicators: IndicatorImp[] = []
+ indicators = indicators.concat(data)
+ if (indicators.length > 0) {
+ const tasks: Record> = {}
+ indicators.forEach(indicator => {
+ tasks[indicator.id] = indicator.calcImp(this._dataList)
+ })
+ this._taskScheduler.add(tasks)
+ }
}
addIndicator (create: PickRequired, paneId: string, isStack: boolean): boolean {
@@ -1203,7 +1435,7 @@ export default class StoreImp implements Store {
paneIndicators.push(indicator)
this._indicators.set(paneId, paneIndicators)
this._sortIndicators(paneId)
- this._addIndicatorCalcTask(indicator, 'init')
+ this._calcIndicator(indicator)
return true
}
@@ -1237,7 +1469,6 @@ export default class StoreImp implements Store {
const paneIndicators = this.getIndicatorsByPaneId(indicator.paneId)
const index = paneIndicators.findIndex(ins => ins.id === indicator.id)
if (index > -1) {
- this._taskScheduler.remove(generateTaskId(indicator.id))
paneIndicators.splice(index, 1)
removed = true
}
@@ -1295,7 +1526,7 @@ export default class StoreImp implements Store {
sortFlag = true
}
if (calc) {
- this._addIndicatorCalcTask(indicator, 'update')
+ this._calcIndicator(indicator)
} else {
if (draw) {
updateFlag = true
@@ -1598,7 +1829,11 @@ export default class StoreImp implements Store {
}
isOverlayDrawing (): boolean {
- return this._progressOverlayInfo?.overlay.isDrawing() ?? false
+ const info = this._progressOverlayInfo
+ if (info !== null) {
+ return info.overlay.isDrawing()
+ }
+ return false
}
private _clearLastPriceMarkExtendTextUpdateTimer (): void {
diff --git a/src/common/DataLoader.ts b/src/common/DataLoader.ts
index feef65371..28968c200 100644
--- a/src/common/DataLoader.ts
+++ b/src/common/DataLoader.ts
@@ -20,8 +20,8 @@ import type { Period } from './Period'
export type DataLoadType = 'init' | 'forward' | 'backward' | 'update'
export type DataLoadMore = boolean | {
- backward: boolean
- forward: boolean
+ backward?: boolean
+ forward?: boolean
}
export interface DataLoaderGetBarsParams {
diff --git a/src/common/Styles.ts b/src/common/Styles.ts
index 82a341aa2..8b045c6b6 100644
--- a/src/common/Styles.ts
+++ b/src/common/Styles.ts
@@ -237,7 +237,7 @@ export interface CandleTooltipStyle extends TooltipStyle {
rect: CandleTooltipRectStyle
}
-export type CandleType = 'candle_solid' | 'candle_stroke' | 'candle_up_stroke' | 'candle_down_stroke' | 'ohlc' | 'area'
+export type CandleType = 'candle_solid' | 'candle_stroke' | 'candle_up_stroke' | 'candle_down_stroke' | 'ohlc' | 'area' | 'heikin_ashi'
export type CandleColorCompareRule = 'current_open' | 'previous_close'
diff --git a/src/common/TaskScheduler.ts b/src/common/TaskScheduler.ts
index 3a00ae0e5..7d920d37b 100644
--- a/src/common/TaskScheduler.ts
+++ b/src/common/TaskScheduler.ts
@@ -12,101 +12,53 @@
* limitations under the License.
*/
+import type Nullable from './Nullable'
import { isValid } from './utils/typeChecks'
-export function generateTaskId (...params: string[]): string {
- return params.join('_')
-}
-
-interface Task {
- id: string
- handler: () => Promise
-}
-
-type TaskRun = () => void
-
-export type TaskRejectReason = 'canceled' | 'failed'
-
-interface TaskWrapper {
- run: TaskRun
- reject: (reason?: TaskRejectReason) => void
-}
-
-const DEFAULT_TASK_LIMIT = 5
+type TaskFinishedCallback = () => void
export default class TaskScheduler {
- private readonly _limit: number
- private _running = 0
- private _queue: TaskRun[] = []
- private readonly _taskMap = new Map()
+ private _holdingTasks: Nullable>> = null
- constructor (limit?: number) {
- this._limit = limit ?? DEFAULT_TASK_LIMIT
- }
+ private _running = false
- async add (task: Task): Promise {
- return await new Promise((resolve, reject) => {
- const { id, handler } = task
- const run: TaskRun = () => {
- this._running++
- handler().then(
- result => {
- resolve(result)
- }
- ).catch(
- () => {
- // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors, prefer-promise-reject-errors -- ignore
- reject('failed')
- }
- ).finally(() => {
- this._running--
- this._taskMap.delete(id)
- this._next()
- })
- }
+ private readonly _callback: Nullable
- const oldTaskWrapper = this._taskMap.get(id)
- if (isValid(oldTaskWrapper)) {
- oldTaskWrapper.reject('canceled')
+ constructor (callback: TaskFinishedCallback) {
+ this._callback = callback
+ }
- const index = this._queue.indexOf(oldTaskWrapper.run)
- if (index > -1) {
- this._queue.splice(index, 1)
+ add (tasks: Record>): void {
+ if (!this._running) {
+ void this._runTask(tasks)
+ } else {
+ if (isValid(this._holdingTasks)) {
+ this._holdingTasks = {
+ ...this._holdingTasks,
+ ...tasks
}
+ } else {
+ this._holdingTasks = tasks
}
-
- this._taskMap.set(id, { run, reject })
- this._queue.push(run)
- this._next()
- })
- }
-
- private _next (): void {
- while (this._running < this._limit && this._queue.length > 0) {
- const fn = this._queue.shift()
- fn?.()
}
}
- remove (id: string): void {
- const taskWrapper = this._taskMap.get(id)
- if (isValid(taskWrapper)) {
- this._taskMap.delete(id)
-
- const index = this._queue.indexOf(taskWrapper.run)
- if (index > -1) {
- this._queue.splice(index, 1)
+ private async _runTask (tasks: Record>): Promise {
+ this._running = true
+ try {
+ await Promise.all(Object.values(tasks))
+ } finally {
+ this._running = false
+ this._callback?.()
+ if (isValid(this._holdingTasks)) {
+ const next = this._holdingTasks
+ void this._runTask(next)
+ this._holdingTasks = null
}
-
- taskWrapper.reject('canceled')
}
}
clear (): void {
- for (const [, { reject }] of this._taskMap) {
- reject('canceled')
- }
- this._taskMap.clear()
- this._queue = []
+ this._holdingTasks = null
}
}
diff --git a/src/common/utils/typeChecks.ts b/src/common/utils/typeChecks.ts
index c5b06fbac..b83b2706d 100644
--- a/src/common/utils/typeChecks.ts
+++ b/src/common/utils/typeChecks.ts
@@ -29,11 +29,8 @@ export function merge (target: any, source: any): void {
) {
merge(targetProp, sourceProp)
} else {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- ignore
- if (isValid(source[key])) {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access -- ignore
- target[key] = clone(source[key])
- }
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access -- ignore
+ target[key] = clone(sourceProp)
}
}
}
diff --git a/src/component/Indicator.ts b/src/component/Indicator.ts
index a4caf1d10..ff7776d6d 100644
--- a/src/component/Indicator.ts
+++ b/src/component/Indicator.ts
@@ -15,7 +15,7 @@
import type Nullable from '../common/Nullable'
import type DeepPartial from '../common/DeepPartial'
import type ExcludePickPartial from '../common/ExcludePickPartial'
-import type { KLineData, NeighborData, Timestamp } from '../common/Data'
+import type { KLineData, NeighborData } from '../common/Data'
import type Bounding from '../common/Bounding'
import type BarSpace from '../common/BarSpace'
import type Crosshair from '../common/Crosshair'
@@ -99,7 +99,7 @@ export interface IndicatorDrawParams {
export type IndicatorDrawCallback = (params: IndicatorDrawParams) => boolean
-export type IndicatorCalcCallback = (dataList: KLineData[], indicator: Indicator) => Promise> | Record
+export type IndicatorCalcCallback = (dataList: KLineData[], indicator: Indicator) => Promise | D[]
export type IndicatorShouldUpdateCallback = (prev: Indicator, current: Indicator) => (boolean | { calc: boolean, draw: boolean })
@@ -111,7 +111,6 @@ export interface IndicatorOnDataStateChangeParams {
indicator: Indicator
}
-export type IndicatorOnDataStateChangeCallback = (params: IndicatorOnDataStateChangeParams) => void
export interface Indicator {
/**
@@ -219,15 +218,10 @@ export interface Indicator {
*/
draw: Nullable>
- /**
- * Data state change
- */
- onDataStateChange: Nullable>
-
/**
* Calculation result
*/
- result: Record
+ result: D[]
}
export type IndicatorTemplate = ExcludePickPartial, 'result' | 'paneId'>, 'name' | 'calc'>
@@ -244,7 +238,7 @@ export type EachFigureCallback = (figure: IndicatorFigure, figureStyles: I
export function eachFigures (
indicator: Indicator,
- timestamps: NeighborData>,
+ dataIndex: number,
defaultStyles: IndicatorStyle,
eachFigureCallback: EachFigureCallback
): void {
@@ -295,9 +289,9 @@ export function eachFigures (
if (isValid(figure.type)) {
const ss = figure.styles?.({
data: {
- prev: result[timestamps.prev ?? ''],
- current: result[timestamps.current ?? ''],
- next: result[timestamps.next ?? '']
+ prev: result[dataIndex - 1],
+ current: result[dataIndex],
+ next: result[dataIndex + 1]
},
indicator,
defaultStyles
@@ -347,14 +341,12 @@ export default class IndicatorImp impleme
return { calc, draw }
}
- calc: IndicatorCalcCallback = () => ({})
+ calc: IndicatorCalcCallback = () => []
regenerateFigures: Nullable> = null
createTooltipDataSource: Nullable> = null
draw: Nullable> = null
- onDataStateChange: Nullable> = null
-
- result: Record = {}
+ result: D[] = []
private _prevIndicator: Indicator
private _lockSeriesPrecision = false
diff --git a/src/component/Overlay.ts b/src/component/Overlay.ts
index eed315947..683ade093 100644
--- a/src/component/Overlay.ts
+++ b/src/component/Overlay.ts
@@ -37,6 +37,13 @@ export interface OverlayPerformEventParams {
performPoint: Partial
}
+/**
+ * Drawing mode for overlays
+ * - 'step': Traditional click-based drawing (default)
+ * - 'continuous': Freehand drawing with mouse down, move, up
+ */
+export type OverlayDrawingMode = 'step' | 'continuous'
+
export interface OverlayEventCollection {
onDrawStart: Nullable>
onDrawing: Nullable>
@@ -124,6 +131,11 @@ export interface Overlay extends OverlayEventCollection {
*/
currentStep: number
+ /**
+ * Drawing mode: 'step' for click-based, 'continuous' for freehand
+ */
+ drawingMode: OverlayDrawingMode
+
/**
* Whether it is locked. When it is true, it will not respond to events
*/
@@ -229,6 +241,7 @@ export default class OverlayImp implements Overlay {
name: string
totalStep = 1
currentStep = OVERLAY_DRAW_STEP_START
+ drawingMode: OverlayDrawingMode = 'step'
lock = false
visible = true
zLevel = 0
@@ -237,7 +250,7 @@ export default class OverlayImp implements Overlay {
needDefaultYAxisFigure = false
mode: OverlayMode = 'normal'
modeSensitivity = 8
- points: Array>> = []
+ points: Array> = []
extendData: E
styles: Nullable> = null
createPointFigures: Nullable> = null
@@ -273,7 +286,10 @@ export default class OverlayImp implements Overlay {
}
override (overlay: Partial>): void {
- this._prevOverlay = clone(this)
+ this._prevOverlay = clone({
+ ...this,
+ _prevOverlay: null
+ })
const {
id,
@@ -369,6 +385,44 @@ export default class OverlayImp implements Overlay {
return this.currentStep === OVERLAY_DRAW_STEP_START
}
+ isContinuousDrawing (): boolean {
+ return this.drawingMode === 'continuous'
+ }
+
+ /**
+ * Add a point during continuous drawing mode
+ */
+ addPointForContinuousDrawing (point: Partial): boolean {
+ const newPoint: Partial = {}
+ if (isNumber(point.timestamp)) {
+ newPoint.timestamp = point.timestamp
+ }
+ if (isNumber(point.dataIndex)) {
+ newPoint.dataIndex = point.dataIndex
+ }
+ if (isNumber(point.value)) {
+ newPoint.value = point.value
+ }
+ this.points.push(newPoint)
+ return true
+ }
+
+ /**
+ * Start continuous drawing - set first point
+ */
+ startContinuousDrawing (point: Partial): void {
+ this.points = []
+ this.addPointForContinuousDrawing(point)
+ this.currentStep = 2 // Mark as actively drawing
+ }
+
+ /**
+ * Complete continuous drawing
+ */
+ completeContinuousDrawing (): void {
+ this.currentStep = OVERLAY_DRAW_STEP_FINISHED
+ }
+
eventMoveForDrawing (point: Partial): void {
const pointIndex = this.currentStep - 1
const newPoint: Partial = {}
diff --git a/src/component/YAxis.ts b/src/component/YAxis.ts
index b84a69202..1e7482843 100644
--- a/src/component/YAxis.ts
+++ b/src/component/YAxis.ts
@@ -120,31 +120,32 @@ export default abstract class YAxisImp extends AxisImp implements YAxis {
const areaValueKey = candleStyles.area.value
const shouldCompareHighLow = (inCandle && !isArea) || (!inCandle && shouldOhlc)
visibleRangeDataList.forEach((visibleData) => {
- const kLineData = visibleData.data.current
- if (isValid(kLineData)) {
+ const dataIndex = visibleData.dataIndex
+ const data = visibleData.data.current
+ if (isValid(data)) {
if (shouldCompareHighLow) {
- min = Math.min(min, kLineData.low)
- max = Math.max(max, kLineData.high)
+ min = Math.min(min, data.low)
+ max = Math.max(max, data.high)
}
if (inCandle && isArea) {
- const value = kLineData[areaValueKey]
+ const value = data[areaValueKey]
if (isNumber(value)) {
min = Math.min(min, value)
max = Math.max(max, value)
}
}
- indicators.forEach(({ result, figures }) => {
- const data = result[kLineData.timestamp] ?? {}
- figures.forEach(figure => {
+ }
+ indicators.forEach(({ result, figures }) => {
+ const data = result[dataIndex] ?? {}
+ figures.forEach(figure => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ignore
- const value = data[figure.key]
- if (isNumber(value)) {
- min = Math.min(min, value)
- max = Math.max(max, value)
- }
- })
+ const value = data[figure.key]
+ if (isNumber(value)) {
+ min = Math.min(min, value)
+ max = Math.max(max, value)
+ }
})
- }
+ })
})
if (min !== Number.MAX_SAFE_INTEGER && max !== Number.MIN_SAFE_INTEGER) {
diff --git a/src/extension/figure/line.ts b/src/extension/figure/line.ts
index 6acf55ca5..2dae01de4 100644
--- a/src/extension/figure/line.ts
+++ b/src/extension/figure/line.ts
@@ -138,12 +138,27 @@ export function lineTo (ctx: CanvasRenderingContext2D, coordinates: Coordinate[]
}
}
-export function drawLine (ctx: CanvasRenderingContext2D, attrs: LineAttrs | LineAttrs[], styles: Partial): void {
+export function drawLine (ctx: CanvasRenderingContext2D, attrs: LineAttrs | LineAttrs[], styles: Partial & { lineCap?: CanvasLineCap, lineJoin?: CanvasLineJoin }): void {
let lines: LineAttrs[] = []
lines = lines.concat(attrs)
- const { style = 'solid', smooth = false, size = 1, color = 'currentColor', dashedValue = [2, 2] } = styles
+ const { style = 'solid', smooth = false, size = 1, color = 'currentColor', dashedValue = [2, 2], lineCap, lineJoin } = styles
ctx.lineWidth = size
ctx.strokeStyle = color
+ // Use explicit lineCap/lineJoin if provided, otherwise default based on smooth
+ if (lineCap !== undefined) {
+ ctx.lineCap = lineCap
+ } else if (smooth !== false && smooth !== 0) {
+ ctx.lineCap = 'round'
+ } else {
+ ctx.lineCap = 'butt'
+ }
+ if (lineJoin !== undefined) {
+ ctx.lineJoin = lineJoin
+ } else if (smooth !== false && smooth !== 0) {
+ ctx.lineJoin = 'round'
+ } else {
+ ctx.lineJoin = 'miter'
+ }
if (style === 'dashed') {
ctx.setLineDash(dashedValue)
} else {
diff --git a/src/extension/indicator/averagePrice.ts b/src/extension/indicator/averagePrice.ts
index e0cbd061c..a68a56c38 100644
--- a/src/extension/indicator/averagePrice.ts
+++ b/src/extension/indicator/averagePrice.ts
@@ -32,7 +32,7 @@ const averagePrice: IndicatorTemplate = {
calc: (dataList) => {
let totalTurnover = 0
let totalVolume = 0
- return dataList.reduce((prev, kLineData) => {
+ return dataList.map((kLineData) => {
const avp: Avp = {}
const turnover = kLineData.turnover ?? 0
const volume = kLineData.volume ?? 0
@@ -41,9 +41,8 @@ const averagePrice: IndicatorTemplate = {
if (totalVolume !== 0) {
avp.avp = totalTurnover / totalVolume
}
- prev[kLineData.timestamp] = avp
- return prev
- }, {})
+ return avp
+ })
}
}
diff --git a/src/extension/indicator/awesomeOscillator.ts b/src/extension/indicator/awesomeOscillator.ts
index eb803d578..0df8e941e 100644
--- a/src/extension/indicator/awesomeOscillator.ts
+++ b/src/extension/indicator/awesomeOscillator.ts
@@ -50,7 +50,7 @@ const awesomeOscillator: IndicatorTemplate = {
let longSum = 0
let short = 0
let long = 0
- return dataList.reduce((prev, kLineData, i) => {
+ return dataList.map((kLineData, i) => {
const ao: Ao = {}
const middle = (kLineData.low + kLineData.high) / 2
shortSum += middle
@@ -68,9 +68,8 @@ const awesomeOscillator: IndicatorTemplate = {
if (i >= maxPeriod - 1) {
ao.ao = short - long
}
- prev[kLineData.timestamp] = ao
- return prev
- }, {})
+ return ao
+ })
}
}
diff --git a/src/extension/indicator/bias.ts b/src/extension/indicator/bias.ts
index c1d0c7a26..658e71989 100644
--- a/src/extension/indicator/bias.ts
+++ b/src/extension/indicator/bias.ts
@@ -37,7 +37,7 @@ const bias: IndicatorTemplate = {
calc: (dataList, indicator) => {
const { calcParams: params, figures } = indicator
const closeSums: number[] = []
- return dataList.reduce((prev, kLineData, i) => {
+ return dataList.map((kLineData, i) => {
const bias: Bias = {}
const close = kLineData.close
params.forEach((p, index) => {
@@ -49,9 +49,8 @@ const bias: IndicatorTemplate = {
closeSums[index] -= dataList[i - (p - 1)].close
}
})
- prev[kLineData.timestamp] = bias
- return prev
- }, {})
+ return bias
+ })
}
}
diff --git a/src/extension/indicator/bollingerBands.ts b/src/extension/indicator/bollingerBands.ts
index c9dadc102..1f8d569a4 100644
--- a/src/extension/indicator/bollingerBands.ts
+++ b/src/extension/indicator/bollingerBands.ts
@@ -57,7 +57,7 @@ const bollingerBands: IndicatorTemplate = {
const params = indicator.calcParams
const p = params[0] - 1
let closeSum = 0
- return dataList.reduce((prev, kLineData, i) => {
+ return dataList.map((kLineData, i) => {
const close = kLineData.close
const boll: Boll = {}
closeSum += close
@@ -68,9 +68,8 @@ const bollingerBands: IndicatorTemplate = {
boll.dn = boll.mid - params[1] * md
closeSum -= dataList[i - p].close
}
- prev[kLineData.timestamp] = boll
- return prev
- }, {})
+ return boll
+ })
}
}
diff --git a/src/extension/indicator/brar.ts b/src/extension/indicator/brar.ts
index 666557f40..9254e57b0 100644
--- a/src/extension/indicator/brar.ts
+++ b/src/extension/indicator/brar.ts
@@ -42,7 +42,7 @@ const brar: IndicatorTemplate = {
let cyl = 0
let ho = 0
let ol = 0
- return dataList.reduce((prev, kLineData, i) => {
+ return dataList.map((kLineData, i) => {
const brar: Brar = {}
const high = kLineData.high
const low = kLineData.low
@@ -73,9 +73,8 @@ const brar: IndicatorTemplate = {
ho -= (agoHigh - agoOpen)
ol -= (agoOpen - agoLow)
}
- prev[kLineData.timestamp] = brar
- return prev
- }, {})
+ return brar
+ })
}
}
diff --git a/src/extension/indicator/bullAndBearIndex.ts b/src/extension/indicator/bullAndBearIndex.ts
index 25daec74e..1c228c537 100644
--- a/src/extension/indicator/bullAndBearIndex.ts
+++ b/src/extension/indicator/bullAndBearIndex.ts
@@ -37,7 +37,7 @@ const bullAndBearIndex: IndicatorTemplate = {
const maxPeriod = Math.max(...params)
const closeSums: number[] = []
const mas: number[] = []
- return dataList.reduce((prev, kLineData, i) => {
+ return dataList.map((kLineData, i) => {
const bbi: Bbi = {}
const close = kLineData.close
params.forEach((p, index) => {
@@ -54,9 +54,8 @@ const bullAndBearIndex: IndicatorTemplate = {
})
bbi.bbi = maSum / 4
}
- prev[kLineData.timestamp] = bbi
- return prev
- }, {})
+ return bbi
+ })
}
}
diff --git a/src/extension/indicator/commodityChannelIndex.ts b/src/extension/indicator/commodityChannelIndex.ts
index 47345c836..699ee6613 100644
--- a/src/extension/indicator/commodityChannelIndex.ts
+++ b/src/extension/indicator/commodityChannelIndex.ts
@@ -38,7 +38,7 @@ const commodityChannelIndex: IndicatorTemplate = {
const p = params[0] - 1
let tpSum = 0
const tpList: number[] = []
- return dataList.reduce((prev, kLineData, i) => {
+ return dataList.map((kLineData, i) => {
const cci: Cci = {}
const tp = (kLineData.high + kLineData.low + kLineData.close) / 3
tpSum += tp
@@ -55,9 +55,8 @@ const commodityChannelIndex: IndicatorTemplate = {
const agoTp = (dataList[i - p].high + dataList[i - p].low + dataList[i - p].close) / 3
tpSum -= agoTp
}
- prev[kLineData.timestamp] = cci
- return prev
- }, {})
+ return cci
+ })
}
}
diff --git a/src/extension/indicator/currentRatio.ts b/src/extension/indicator/currentRatio.ts
index a3df1fb8e..bb4e3ad37 100644
--- a/src/extension/indicator/currentRatio.ts
+++ b/src/extension/indicator/currentRatio.ts
@@ -63,8 +63,7 @@ const currentRatio: IndicatorTemplate = {
const ma3List: number[] = []
let ma4Sum = 0
const ma4List: number[] = []
- const crList: Cr[] = []
- const result: Record = {}
+ const result: Cr[] = []
dataList.forEach((kLineData, i) => {
const cr: Cr = {}
const prevData = dataList[i - 1] ?? kLineData
@@ -89,32 +88,31 @@ const currentRatio: IndicatorTemplate = {
if (i >= params[0] + params[1] + ma1ForwardPeriod - 3) {
cr.ma1 = ma1List[ma1List.length - 1 - ma1ForwardPeriod]
}
- ma1Sum -= (crList[i - (params[1] - 1)].cr ?? 0)
+ ma1Sum -= (result[i - (params[1] - 1)].cr ?? 0)
}
if (i >= params[0] + params[2] - 2) {
ma2List.push(ma2Sum / params[2])
if (i >= params[0] + params[2] + ma2ForwardPeriod - 3) {
cr.ma2 = ma2List[ma2List.length - 1 - ma2ForwardPeriod]
}
- ma2Sum -= (crList[i - (params[2] - 1)].cr ?? 0)
+ ma2Sum -= (result[i - (params[2] - 1)].cr ?? 0)
}
if (i >= params[0] + params[3] - 2) {
ma3List.push(ma3Sum / params[3])
if (i >= params[0] + params[3] + ma3ForwardPeriod - 3) {
cr.ma3 = ma3List[ma3List.length - 1 - ma3ForwardPeriod]
}
- ma3Sum -= (crList[i - (params[3] - 1)].cr ?? 0)
+ ma3Sum -= (result[i - (params[3] - 1)].cr ?? 0)
}
if (i >= params[0] + params[4] - 2) {
ma4List.push(ma4Sum / params[4])
if (i >= params[0] + params[4] + ma4ForwardPeriod - 3) {
cr.ma4 = ma4List[ma4List.length - 1 - ma4ForwardPeriod]
}
- ma4Sum -= (crList[i - (params[4] - 1)].cr ?? 0)
+ ma4Sum -= (result[i - (params[4] - 1)].cr ?? 0)
}
}
- crList.push(cr)
- result[kLineData.timestamp] = cr
+ result.push(cr)
})
return result
}
diff --git a/src/extension/indicator/differentOfMovingAverage.ts b/src/extension/indicator/differentOfMovingAverage.ts
index 88b8005f1..6b0cecc97 100644
--- a/src/extension/indicator/differentOfMovingAverage.ts
+++ b/src/extension/indicator/differentOfMovingAverage.ts
@@ -37,8 +37,7 @@ const differentOfMovingAverage: IndicatorTemplate = {
let closeSum1 = 0
let closeSum2 = 0
let dmaSum = 0
- const dmaList: Dma[] = []
- const result: Record = {}
+ const result: Dma[] = []
dataList.forEach((kLineData, i) => {
const dma: Dma = {}
const close = kLineData.close
@@ -61,11 +60,10 @@ const differentOfMovingAverage: IndicatorTemplate = {
dmaSum += dif
if (i >= maxPeriod + params[2] - 2) {
dma.ama = dmaSum / params[2]
- dmaSum -= (dmaList[i - (params[2] - 1)].dma ?? 0)
+ dmaSum -= (result[i - (params[2] - 1)].dma ?? 0)
}
}
- dmaList.push(dma)
- result[kLineData.timestamp] = dma
+ result.push(dma)
})
return result
}
diff --git a/src/extension/indicator/directionalMovementIndex.ts b/src/extension/indicator/directionalMovementIndex.ts
index 9b2bfc310..a370c735f 100644
--- a/src/extension/indicator/directionalMovementIndex.ts
+++ b/src/extension/indicator/directionalMovementIndex.ts
@@ -66,8 +66,7 @@ const directionalMovementIndex: IndicatorTemplate = {
let dmm = 0
let dxSum = 0
let adx = 0
- const dmiList: Dmi[] = []
- const result: Record = {}
+ const result: Dmi[] = []
dataList.forEach((kLineData, i) => {
const dmi: Dmi = {}
const prevKLineData = dataList[i - 1] ?? kLineData
@@ -116,12 +115,11 @@ const directionalMovementIndex: IndicatorTemplate = {
}
dmi.adx = adx
if (i >= params[0] * 2 + params[1] - 3) {
- dmi.adxr = ((dmiList[i - (params[1] - 1)].adx ?? 0) + adx) / 2
+ dmi.adxr = ((result[i - (params[1] - 1)].adx ?? 0) + adx) / 2
}
}
}
- dmiList.push(dmi)
- result[kLineData.timestamp] = dmi
+ result.push(dmi)
})
return result
}
diff --git a/src/extension/indicator/easeOfMovementValue.ts b/src/extension/indicator/easeOfMovementValue.ts
index c9f3d4434..7cd631501 100644
--- a/src/extension/indicator/easeOfMovementValue.ts
+++ b/src/extension/indicator/easeOfMovementValue.ts
@@ -43,7 +43,7 @@ const easeOfMovementValue: IndicatorTemplate = {
const params = indicator.calcParams
let emvValueSum = 0
const emvValueList: number[] = []
- return dataList.reduce((prev, kLineData, i) => {
+ return dataList.map((kLineData, i) => {
const emv: Emv = {}
if (i > 0) {
const prevKLineData = dataList[i - 1]
@@ -65,9 +65,8 @@ const easeOfMovementValue: IndicatorTemplate = {
emvValueSum -= emvValueList[i - params[0]]
}
}
- prev[kLineData.timestamp] = emv
- return prev
- }, {})
+ return emv
+ })
}
}
diff --git a/src/extension/indicator/exponentialMovingAverage.ts b/src/extension/indicator/exponentialMovingAverage.ts
index 238421ea5..2c058a80c 100644
--- a/src/extension/indicator/exponentialMovingAverage.ts
+++ b/src/extension/indicator/exponentialMovingAverage.ts
@@ -40,7 +40,7 @@ const exponentialMovingAverage: IndicatorTemplate = {
const { calcParams: params, figures } = indicator
let closeSum = 0
const emaValues: number[] = []
- return dataList.reduce((prev, kLineData, i) => {
+ return dataList.map((kLineData, i) => {
const ema = {}
const close = kLineData.close
closeSum += close
@@ -54,9 +54,8 @@ const exponentialMovingAverage: IndicatorTemplate = {
ema[figures[index].key] = emaValues[index]
}
})
- prev[kLineData.timestamp] = ema
- return prev
- }, {})
+ return ema
+ })
}
}
diff --git a/src/extension/indicator/momentum.ts b/src/extension/indicator/momentum.ts
index e614974be..b428d7dd7 100644
--- a/src/extension/indicator/momentum.ts
+++ b/src/extension/indicator/momentum.ts
@@ -34,8 +34,7 @@ const momentum: IndicatorTemplate = {
calc: (dataList, indicator) => {
const params = indicator.calcParams
let mtmSum = 0
- const mtmList: Mtm[] = []
- const result: Record = {}
+ const result: Mtm[] = []
dataList.forEach((kLineData, i) => {
const mtm: Mtm = {}
if (i >= params[0]) {
@@ -45,11 +44,10 @@ const momentum: IndicatorTemplate = {
mtmSum += mtm.mtm
if (i >= params[0] + params[1] - 1) {
mtm.maMtm = mtmSum / params[1]
- mtmSum -= (mtmList[i - (params[1] - 1)].mtm ?? 0)
+ mtmSum -= (result[i - (params[1] - 1)].mtm ?? 0)
}
}
- mtmList.push(mtm)
- result[kLineData.timestamp] = mtm
+ result.push(mtm)
})
return result
}
diff --git a/src/extension/indicator/movingAverage.ts b/src/extension/indicator/movingAverage.ts
index a28bab8a5..ef27066e1 100644
--- a/src/extension/indicator/movingAverage.ts
+++ b/src/extension/indicator/movingAverage.ts
@@ -41,7 +41,7 @@ const movingAverage: IndicatorTemplate = {
calc: (dataList, indicator) => {
const { calcParams: params, figures } = indicator
const closeSums: number[] = []
- return dataList.reduce((prev, kLineData, i) => {
+ return dataList.map((kLineData, i) => {
const ma = {}
const close = kLineData.close
params.forEach((p, index) => {
@@ -51,9 +51,8 @@ const movingAverage: IndicatorTemplate = {
closeSums[index] -= dataList[i - (p - 1)].close
}
})
- prev[kLineData.timestamp] = ma
- return prev
- }, {})
+ return ma
+ })
}
}
diff --git a/src/extension/indicator/movingAverageConvergenceDivergence.ts b/src/extension/indicator/movingAverageConvergenceDivergence.ts
index 6964474e7..19f6add5f 100644
--- a/src/extension/indicator/movingAverageConvergenceDivergence.ts
+++ b/src/extension/indicator/movingAverageConvergenceDivergence.ts
@@ -68,7 +68,7 @@ const movingAverageConvergenceDivergence: IndicatorTemplate = {
let difSum = 0
let dea = 0
const maxPeriod = Math.max(params[0], params[1])
- return dataList.reduce((prev, kLineData, i) => {
+ return dataList.map((kLineData, i) => {
const macd: Macd = {}
const close = kLineData.close
closeSum += close
@@ -100,9 +100,8 @@ const movingAverageConvergenceDivergence: IndicatorTemplate = {
macd.dea = dea
}
}
- prev[kLineData.timestamp] = macd
- return prev
- }, {})
+ return macd
+ })
}
}
diff --git a/src/extension/indicator/onBalanceVolume.ts b/src/extension/indicator/onBalanceVolume.ts
index 9d710d0aa..c0d2620bd 100644
--- a/src/extension/indicator/onBalanceVolume.ts
+++ b/src/extension/indicator/onBalanceVolume.ts
@@ -35,8 +35,7 @@ const onBalanceVolume: IndicatorTemplate = {
const params = indicator.calcParams
let obvSum = 0
let oldObv = 0
- const obvList: Obv[] = []
- const result: Record = {}
+ const result: Obv[] = []
dataList.forEach((kLineData, i) => {
const prevKLineData = dataList[i - 1] ?? kLineData
if (kLineData.close < prevKLineData.close) {
@@ -48,10 +47,9 @@ const onBalanceVolume: IndicatorTemplate = {
obvSum += oldObv
if (i >= params[0] - 1) {
obv.maObv = obvSum / params[0]
- obvSum -= (obvList[i - (params[0] - 1)].obv ?? 0)
+ obvSum -= (result[i - (params[0] - 1)].obv ?? 0)
}
- obvList.push(obv)
- result[kLineData.timestamp] = obv
+ result.push(obv)
})
return result
}
diff --git a/src/extension/indicator/priceAndVolumeTrend.ts b/src/extension/indicator/priceAndVolumeTrend.ts
index 165fca3bf..4b96016cc 100644
--- a/src/extension/indicator/priceAndVolumeTrend.ts
+++ b/src/extension/indicator/priceAndVolumeTrend.ts
@@ -33,7 +33,7 @@ const priceAndVolumeTrend: IndicatorTemplate = {
],
calc: (dataList) => {
let sum = 0
- return dataList.reduce((prev, kLineData, i) => {
+ return dataList.map((kLineData, i) => {
const pvt: Pvt = {}
const close = kLineData.close
const volume = kLineData.volume ?? 1
@@ -45,9 +45,8 @@ const priceAndVolumeTrend: IndicatorTemplate = {
}
sum += x
pvt.pvt = sum
- prev[kLineData.timestamp] = pvt
- return prev
- }, {})
+ return pvt
+ })
}
}
diff --git a/src/extension/indicator/psychologicalLine.ts b/src/extension/indicator/psychologicalLine.ts
index 938379265..04bbef4ff 100644
--- a/src/extension/indicator/psychologicalLine.ts
+++ b/src/extension/indicator/psychologicalLine.ts
@@ -36,8 +36,7 @@ const psychologicalLine: IndicatorTemplate = {
let upCount = 0
let psySum = 0
const upList: number[] = []
- const psyList: Psy[] = []
- const result: Record = {}
+ const result: Psy[] = []
dataList.forEach((kLineData, i) => {
const psy: Psy = {}
const prevClose = (dataList[i - 1] ?? kLineData).close
@@ -49,12 +48,11 @@ const psychologicalLine: IndicatorTemplate = {
psySum += psy.psy
if (i >= params[0] + params[1] - 2) {
psy.maPsy = psySum / params[1]
- psySum -= (psyList[i - (params[1] - 1)].psy ?? 0)
+ psySum -= (result[i - (params[1] - 1)].psy ?? 0)
}
upCount -= upList[i - (params[0] - 1)]
}
- psyList.push(psy)
- result[kLineData.timestamp] = psy
+ result.push(psy)
})
return result
}
diff --git a/src/extension/indicator/rateOfChange.ts b/src/extension/indicator/rateOfChange.ts
index 8a46ddb4a..8f889acd1 100644
--- a/src/extension/indicator/rateOfChange.ts
+++ b/src/extension/indicator/rateOfChange.ts
@@ -33,8 +33,7 @@ const rateOfChange: IndicatorTemplate = {
],
calc: (dataList, indicator) => {
const params = indicator.calcParams
- const rocList: Roc[] = []
- const result: Record = {}
+ const result: Roc[] = []
let rocSum = 0
dataList.forEach((kLineData, i) => {
const roc: Roc = {}
@@ -49,11 +48,10 @@ const rateOfChange: IndicatorTemplate = {
rocSum += roc.roc
if (i >= params[0] - 1 + params[1] - 1) {
roc.maRoc = rocSum / params[1]
- rocSum -= (rocList[i - (params[1] - 1)].roc ?? 0)
+ rocSum -= (result[i - (params[1] - 1)].roc ?? 0)
}
}
- rocList.push(roc)
- result[kLineData.timestamp] = roc
+ result.push(roc)
})
return result
}
diff --git a/src/extension/indicator/relativeStrengthIndex.ts b/src/extension/indicator/relativeStrengthIndex.ts
index 979139b32..d8bd555fb 100644
--- a/src/extension/indicator/relativeStrengthIndex.ts
+++ b/src/extension/indicator/relativeStrengthIndex.ts
@@ -41,7 +41,7 @@ const relativeStrengthIndex: IndicatorTemplate = {
const { calcParams: params, figures } = indicator
const sumCloseAs: number[] = []
const sumCloseBs: number[] = []
- return dataList.reduce((prev, kLineData, i) => {
+ return dataList.map((kLineData, i) => {
const rsi = {}
const prevClose = (dataList[i - 1] ?? kLineData).close
const tmp = kLineData.close - prevClose
@@ -67,9 +67,8 @@ const relativeStrengthIndex: IndicatorTemplate = {
}
}
})
- prev[kLineData.timestamp] = rsi
- return prev
- }, {})
+ return rsi
+ })
}
}
diff --git a/src/extension/indicator/simpleMovingAverage.ts b/src/extension/indicator/simpleMovingAverage.ts
index bde0dc9d1..00ac1d9cd 100644
--- a/src/extension/indicator/simpleMovingAverage.ts
+++ b/src/extension/indicator/simpleMovingAverage.ts
@@ -35,7 +35,7 @@ const simpleMovingAverage: IndicatorTemplate = {
const params = indicator.calcParams
let closeSum = 0
let smaValue = 0
- return dataList.reduce((prev, kLineData, i) => {
+ return dataList.map((kLineData, i) => {
const sma: Sma = {}
const close = kLineData.close
closeSum += close
@@ -47,9 +47,8 @@ const simpleMovingAverage: IndicatorTemplate = {
}
sma.sma = smaValue
}
- prev[kLineData.timestamp] = sma
- return prev
- }, {})
+ return sma
+ })
}
}
diff --git a/src/extension/indicator/stoch.ts b/src/extension/indicator/stoch.ts
index c126bbf6d..91b478585 100644
--- a/src/extension/indicator/stoch.ts
+++ b/src/extension/indicator/stoch.ts
@@ -42,8 +42,7 @@ const stoch: IndicatorTemplate = {
],
calc: (dataList, indicator) => {
const params = indicator.calcParams
- const kdjList: Kdj[] = []
- const result: Record = {}
+ const result: Kdj[] = []
dataList.forEach((kLineData, i) => {
const kdj: Kdj = {}
const close = kLineData.close
@@ -53,12 +52,11 @@ const stoch: IndicatorTemplate = {
const ln = lhn[1]
const hnSubLn = hn - ln
const rsv = (close - ln) / (hnSubLn === 0 ? 1 : hnSubLn) * 100
- kdj.k = ((params[1] - 1) * (kdjList[i - 1]?.k ?? 50) + rsv) / params[1]
- kdj.d = ((params[2] - 1) * (kdjList[i - 1]?.d ?? 50) + kdj.k) / params[2]
+ kdj.k = ((params[1] - 1) * (result[i - 1]?.k ?? 50) + rsv) / params[1]
+ kdj.d = ((params[2] - 1) * (result[i - 1]?.d ?? 50) + kdj.k) / params[2]
kdj.j = 3.0 * kdj.k - 2.0 * kdj.d
}
- kdjList.push(kdj)
- result[kLineData.timestamp] = kdj
+ result.push(kdj)
})
return result
}
diff --git a/src/extension/indicator/stopAndReverse.ts b/src/extension/indicator/stopAndReverse.ts
index b4dec0d9f..0fb10eb60 100644
--- a/src/extension/indicator/stopAndReverse.ts
+++ b/src/extension/indicator/stopAndReverse.ts
@@ -58,7 +58,7 @@ const stopAndReverse: IndicatorTemplate = {
// 判断是上涨还是下跌 false:下跌
let isIncreasing = false
let sar = 0
- return dataList.reduce((prev, kLineData, i) => {
+ return dataList.map((kLineData, i) => {
// 上一个周期的sar
const preSar = sar
const high = kLineData.high
@@ -99,9 +99,8 @@ const stopAndReverse: IndicatorTemplate = {
sar = highMax
}
}
- prev[kLineData.timestamp] = { high, low, sar }
- return prev
- }, {})
+ return { high, low, sar }
+ })
}
}
diff --git a/src/extension/indicator/tripleExponentiallySmoothedAverage.ts b/src/extension/indicator/tripleExponentiallySmoothedAverage.ts
index be0aca82d..7518ab517 100644
--- a/src/extension/indicator/tripleExponentiallySmoothedAverage.ts
+++ b/src/extension/indicator/tripleExponentiallySmoothedAverage.ts
@@ -49,8 +49,7 @@ const tripleExponentiallySmoothedAverage: IndicatorTemplate = {
let ema1Sum = 0
let ema2Sum = 0
let trixSum = 0
- const trixList: Trix[] = []
- const result: Record = {}
+ const result: Trix[] = []
dataList.forEach((kLineData, i) => {
const trix: Trix = {}
const close = kLineData.close
@@ -83,13 +82,12 @@ const tripleExponentiallySmoothedAverage: IndicatorTemplate = {
trixSum += trixValue
if (i >= params[0] * 3 + params[1] - 4) {
trix.maTrix = trixSum / params[1]
- trixSum -= (trixList[i - (params[1] - 1)].trix ?? 0)
+ trixSum -= (result[i - (params[1] - 1)].trix ?? 0)
}
}
}
}
- trixList.push(trix)
- result[kLineData.timestamp] = trix
+ result.push(trix)
})
return result
}
diff --git a/src/extension/indicator/volume.ts b/src/extension/indicator/volume.ts
index b640c0679..741d3f30b 100644
--- a/src/extension/indicator/volume.ts
+++ b/src/extension/indicator/volume.ts
@@ -69,7 +69,7 @@ const volume: IndicatorTemplate = {
calc: (dataList, indicator) => {
const { calcParams: params, figures } = indicator
const volSums: number[] = []
- return dataList.reduce((prev, kLineData, i) => {
+ return dataList.map((kLineData, i) => {
const volume = kLineData.volume ?? 0
const vol: Vol = { volume, open: kLineData.open, close: kLineData.close }
params.forEach((p, index) => {
@@ -79,9 +79,8 @@ const volume: IndicatorTemplate = {
volSums[index] -= (dataList[i - (p - 1)].volume ?? 0)
}
})
- prev[kLineData.timestamp] = vol
- return prev
- }, {})
+ return vol
+ })
}
}
diff --git a/src/extension/indicator/volumeRatio.ts b/src/extension/indicator/volumeRatio.ts
index d63e0b5da..017ddf565 100644
--- a/src/extension/indicator/volumeRatio.ts
+++ b/src/extension/indicator/volumeRatio.ts
@@ -41,8 +41,7 @@ const volumeRatio: IndicatorTemplate = {
let dvs = 0
let pvs = 0
let vrSum = 0
- const vrList: Vr[] = []
- const result: Record = {}
+ const result: Vr[] = []
dataList.forEach((kLineData, i) => {
const vr: Vr = {}
const close = kLineData.close
@@ -65,7 +64,7 @@ const volumeRatio: IndicatorTemplate = {
vrSum += vr.vr
if (i >= params[0] + params[1] - 2) {
vr.maVr = vrSum / params[1]
- vrSum -= (vrList[i - (params[1] - 1)].vr ?? 0)
+ vrSum -= (result[i - (params[1] - 1)].vr ?? 0)
}
const agoData = dataList[i - (params[0] - 1)]
@@ -80,8 +79,7 @@ const volumeRatio: IndicatorTemplate = {
pvs -= agoVolume
}
}
- vrList.push(vr)
- result[kLineData.timestamp] = vr
+ result.push(vr)
})
return result
}
diff --git a/src/extension/indicator/williamsR.ts b/src/extension/indicator/williamsR.ts
index 7564dc87e..c9d2c8e71 100644
--- a/src/extension/indicator/williamsR.ts
+++ b/src/extension/indicator/williamsR.ts
@@ -39,7 +39,7 @@ const williamsR: IndicatorTemplate = {
regenerateFigures: (params) => params.map((_, i) => ({ key: `wr${i + 1}`, title: `WR${i + 1}: `, type: 'line' })),
calc: (dataList, indicator) => {
const { calcParams: params, figures } = indicator
- return dataList.reduce((prev, kLineData, i) => {
+ return dataList.map((kLineData, i) => {
const wr: Wr = {}
const close = kLineData.close
params.forEach((param, index) => {
@@ -52,9 +52,8 @@ const williamsR: IndicatorTemplate = {
wr[figures[index].key] = hnSubLn === 0 ? 0 : (close - hn) / hnSubLn * 100
}
})
- prev[kLineData.timestamp] = wr
- return prev
- }, {})
+ return wr
+ })
}
}
diff --git a/src/extension/overlay/freePath.ts b/src/extension/overlay/freePath.ts
new file mode 100644
index 000000000..02c56a70f
--- /dev/null
+++ b/src/extension/overlay/freePath.ts
@@ -0,0 +1,46 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { OverlayTemplate } from '../../component/Overlay'
+
+/**
+ * Free path overlay - freehand drawing with click and drag
+ * Uses continuous drawing mode for smooth path creation
+ */
+const freePath: OverlayTemplate = {
+ name: 'freePath',
+ totalStep: 2,
+ drawingMode: 'continuous',
+ needDefaultPointFigure: false,
+ needDefaultXAxisFigure: false,
+ needDefaultYAxisFigure: false,
+ createPointFigures: ({ coordinates }) => {
+ if (coordinates.length < 2) {
+ return []
+ }
+ return [
+ {
+ type: 'line',
+ attrs: { coordinates },
+ styles: {
+ smooth: false,
+ lineCap: 'round',
+ lineJoin: 'round'
+ }
+ }
+ ]
+ }
+}
+
+export default freePath
diff --git a/src/extension/overlay/index.ts b/src/extension/overlay/index.ts
index 8f65651c1..883cc8892 100644
--- a/src/extension/overlay/index.ts
+++ b/src/extension/overlay/index.ts
@@ -32,6 +32,7 @@ import verticalStraightLine from './verticalStraightLine'
import simpleAnnotation from './simpleAnnotation'
import simpleTag from './simpleTag'
+import freePath from './freePath'
const overlays: Record = {}
@@ -39,7 +40,7 @@ const extensions = [
fibonacciLine, horizontalRayLine, horizontalSegment, horizontalStraightLine,
parallelStraightLine, priceChannelLine, priceLine, rayLine, segment,
straightLine, verticalRayLine, verticalSegment, verticalStraightLine,
- simpleAnnotation, simpleTag
+ simpleAnnotation, simpleTag, freePath
]
extensions.forEach((template: OverlayTemplate) => {
diff --git a/src/index.ts b/src/index.ts
index 672f722c2..ffe3cfe5e 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -46,9 +46,9 @@ import {
import { calcTextWidth } from './common/utils/canvas'
import type { ActionType } from './common/Action'
import type { IndicatorSeries } from './component/Indicator'
-import type { OverlayMode } from './component/Overlay'
+import type { OverlayMode, OverlayDrawingMode } from './component/Overlay'
-import type { FormatDateType, Options, ZoomAnchor } from './Options'
+import type { FormatDateType, Options, BarSpaceLimit, ZoomAnchor } from './Options'
import ChartImp, { type Chart, type DomPosition } from './Chart'
import { checkCoordinateOnArc } from './extension/figure/arc'
@@ -174,6 +174,6 @@ export {
registerXAxis, registerYAxis,
utils,
type LineType, type PolygonType, type TooltipShowRule, type TooltipShowType, type FeatureType, type TooltipFeaturePosition, type CandleTooltipRectPosition,
- type CandleType, type FormatDateType, type ZoomAnchor,
- type DomPosition, type ActionType, type IndicatorSeries, type OverlayMode
+ type CandleType, type FormatDateType, type BarSpaceLimit, type ZoomAnchor,
+ type DomPosition, type ActionType, type IndicatorSeries, type OverlayMode, type OverlayDrawingMode
}
diff --git a/src/view/CandleBarView.ts b/src/view/CandleBarView.ts
index c1f56a5e2..fe44b2638 100644
--- a/src/view/CandleBarView.ts
+++ b/src/view/CandleBarView.ts
@@ -55,6 +55,7 @@ export default class CandleBarView extends ChildrenView {
halfOhlcSize = Math.floor(ohlcSize / 2)
}
const yAxis = pane.getAxisComponent()
+
this.eachChildren((visibleData, barSpace) => {
const { x, data: { current, prev } } = visibleData
if (isValid(current)) {
@@ -86,7 +87,8 @@ export default class CandleBarView extends ChildrenView {
const correction = barSpace.gapBar % 2 === 0 ? 1 : 0
let rects: Array>> = []
switch (type) {
- case 'candle_solid': {
+ case 'candle_solid':
+ case 'heikin_ashi': {
rects = this._createSolidBar(x, priceY, barSpace, colors, correction)
break
}
@@ -150,7 +152,7 @@ export default class CandleBarView extends ChildrenView {
this.createFigure(rect, handler ?? undefined)?.draw(ctx)
})
}
- })
+ }, true)
}
}
diff --git a/src/view/CandleLastPriceLabelView.ts b/src/view/CandleLastPriceLabelView.ts
index bd3cc1608..3e7ec7df0 100644
--- a/src/view/CandleLastPriceLabelView.ts
+++ b/src/view/CandleLastPriceLabelView.ts
@@ -35,8 +35,9 @@ export default class CandleLastPriceLabelView extends View {
if (priceMarkStyles.show && lastPriceMarkStyles.show && lastPriceMarkTextStyles.show) {
const precision = chartStore.getSymbol()?.pricePrecision ?? SymbolDefaultPrecisionConstants.PRICE
const yAxis = pane.getAxisComponent() as YAxis
- const dataList = chartStore.getDataList()
+ const dataList = chartStore.getDataList(true)
const data = dataList[dataList.length - 1]
+
if (isValid(data)) {
const { close, open } = data
const comparePrice = lastPriceMarkStyles.compareRule === 'current_open' ? open : (dataList[dataList.length - 2]?.close ?? close)
diff --git a/src/view/CandleLastPriceLineView.ts b/src/view/CandleLastPriceLineView.ts
index 3d08764e3..14de1bd59 100644
--- a/src/view/CandleLastPriceLineView.ts
+++ b/src/view/CandleLastPriceLineView.ts
@@ -28,8 +28,9 @@ export default class CandleLastPriceView extends View {
const lastPriceMarkLineStyles = lastPriceMarkStyles.line
if (priceMarkStyles.show && lastPriceMarkStyles.show && lastPriceMarkLineStyles.show) {
const yAxis = pane.getAxisComponent() as YAxis
- const dataList = chartStore.getDataList()
+ const dataList = chartStore.getDataList(true)
const data = dataList[dataList.length - 1]
+
if (isValid(data)) {
const { close, open } = data
const comparePrice = lastPriceMarkStyles.compareRule === 'current_open' ? open : (dataList[dataList.length - 2]?.close ?? close)
diff --git a/src/view/ChildrenView.ts b/src/view/ChildrenView.ts
index ec517159d..f55813f87 100644
--- a/src/view/ChildrenView.ts
+++ b/src/view/ChildrenView.ts
@@ -24,10 +24,10 @@ export type EachChildCallback = (
) => void
export default abstract class ChildrenView extends View {
- protected eachChildren (childCallback: EachChildCallback): void {
+ protected eachChildren (childCallback: EachChildCallback, mutateToCandleType = false): void {
const pane = this.getWidget().getPane()
const chartStore = pane.getChart().getChartStore()
- const visibleRangeDataList = chartStore.getVisibleRangeDataList()
+ const visibleRangeDataList = chartStore.getVisibleRangeDataList(mutateToCandleType)
const barSpace = chartStore.getBarSpace()
const dataLength = visibleRangeDataList.length
let index = 0
diff --git a/src/view/CrosshairFeatureView.ts b/src/view/CrosshairFeatureView.ts
index 19b497bce..183b3ad9a 100644
--- a/src/view/CrosshairFeatureView.ts
+++ b/src/view/CrosshairFeatureView.ts
@@ -117,7 +117,7 @@ export default class CrosshairFeatureView extends View {
finalBackgroundColor = activeBackgroundColor ?? backgroundColor
}
const eventHandler = {
- mouseClickEvent: this._featureClickEvent({ crosshair, feature }),
+ mouseDownEvent: this._featureClickEvent({ crosshair, feature }),
mouseMoveEvent: this._featureMouseMoveEvent({ crosshair, feature })
}
if (type === 'icon_font') {
diff --git a/src/view/IndicatorLastValueView.ts b/src/view/IndicatorLastValueView.ts
index 26a6dbeec..e873fe139 100644
--- a/src/view/IndicatorLastValueView.ts
+++ b/src/view/IndicatorLastValueView.ts
@@ -33,67 +33,58 @@ export default class IndicatorLastValueView extends View {
const yAxis = pane.getAxisComponent()
const yAxisRange = yAxis.getRange()
const dataList = chartStore.getDataList()
- const kLineData = dataList[dataList.length - 1] ?? {}
- const prevKLineData = dataList[dataList.length - 2] ?? {}
+ const dataIndex = dataList.length - 1
const indicators = chartStore.getIndicatorsByPaneId(pane.getId())
const formatter = chartStore.getInnerFormatter()
const decimalFold = chartStore.getDecimalFold()
const thousandsSeparator = chartStore.getThousandsSeparator()
indicators.forEach(indicator => {
const result = indicator.result
- const data = result[kLineData.timestamp] ?? result[prevKLineData.timestamp] ?? {}
+ const data = result[dataIndex] ?? {}
if (isValid(data) && indicator.visible) {
const precision = indicator.precision
- eachFigures(
- indicator,
- {
- prev: prevKLineData.timestamp,
- current: kLineData.timestamp,
- next: null
- },
- defaultStyles,
- (figure: IndicatorFigure, figureStyles: Required) => {
+ eachFigures(indicator, dataIndex, defaultStyles, (figure: IndicatorFigure, figureStyles: Required) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ignore
- const value = data[figure.key]
- if (isNumber(value)) {
- const y = yAxis.convertToNicePixel(value)
- let text = yAxis.displayValueToText(
- yAxis.realValueToDisplayValue(
- yAxis.valueToRealValue(value, { range: yAxisRange }),
- { range: yAxisRange }
- ),
- precision
- )
- if (indicator.shouldFormatBigNumber) {
- text = formatter.formatBigNumber(text)
- }
- text = decimalFold.format(thousandsSeparator.format(text))
- let x = 0
- let textAlign: CanvasTextAlign = 'left'
- if (yAxis.isFromZero()) {
- x = 0
- textAlign = 'left'
- } else {
- x = bounding.width
- textAlign = 'right'
- }
-
- this.createFigure({
- name: 'text',
- attrs: {
- x,
- y,
- text,
- align: textAlign,
- baseline: 'middle'
- },
- styles: {
- ...lastValueMarkTextStyles,
- backgroundColor: figureStyles.color
- }
- })?.draw(ctx)
+ const value = data[figure.key]
+ if (isNumber(value)) {
+ const y = yAxis.convertToNicePixel(value)
+ let text = yAxis.displayValueToText(
+ yAxis.realValueToDisplayValue(
+ yAxis.valueToRealValue(value, { range: yAxisRange }),
+ { range: yAxisRange }
+ ),
+ precision
+ )
+ if (indicator.shouldFormatBigNumber) {
+ text = formatter.formatBigNumber(text)
}
- })
+ text = decimalFold.format(thousandsSeparator.format(text))
+ let x = 0
+ let textAlign: CanvasTextAlign = 'left'
+ if (yAxis.isFromZero()) {
+ x = 0
+ textAlign = 'left'
+ } else {
+ x = bounding.width
+ textAlign = 'right'
+ }
+
+ this.createFigure({
+ name: 'text',
+ attrs: {
+ x,
+ y,
+ text,
+ align: textAlign,
+ baseline: 'middle'
+ },
+ styles: {
+ ...lastValueMarkTextStyles,
+ backgroundColor: figureStyles.color
+ }
+ })?.draw(ctx)
+ }
+ })
}
})
}
diff --git a/src/view/IndicatorTooltipView.ts b/src/view/IndicatorTooltipView.ts
index af47d2935..f97a38661 100644
--- a/src/view/IndicatorTooltipView.ts
+++ b/src/view/IndicatorTooltipView.ts
@@ -213,7 +213,7 @@ export default class IndicatorTooltipView extends View {
featureInfo.indicator = indicator
}
const eventHandler = {
- mouseClickEvent: this._featureClickEvent(actionType, featureInfo),
+ mouseDownEvent: this._featureClickEvent(actionType, featureInfo),
mouseMoveEvent: this._featureMouseMoveEvent(featureInfo)
}
let contentWidth = 0
@@ -343,37 +343,23 @@ export default class IndicatorTooltipView extends View {
const thousandsSeparator = chartStore.getThousandsSeparator()
const legends: TooltipLegend[] = []
if (indicator.visible) {
- const kLineDataList = chartStore.getDataList()
- const kLineData = kLineDataList[dataIndex] ?? {}
- const prevKLineData = kLineDataList[dataIndex - 1] ?? {}
- let data = result[kLineData.timestamp]
- if (!isValid(data) && dataIndex === kLineDataList.length - 1) {
- data = result[prevKLineData.timestamp]
- }
+ const data = result[dataIndex] ?? {}
const defaultValue = tooltipStyles.legend.defaultValue
- eachFigures(
- indicator,
- {
- prev: prevKLineData.timestamp,
- current: kLineData.timestamp,
- next: kLineDataList[dataIndex]?.timestamp
- },
- styles,
- (figure: IndicatorFigure, figureStyles: Required) => {
- if (isString(figure.title)) {
- const color = figureStyles.color
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ignore
- let value = data?.[figure.key]
- if (isNumber(value)) {
- value = formatPrecision(value, indicator.precision)
- if (indicator.shouldFormatBigNumber) {
- value = formatter.formatBigNumber(value as string)
- }
- value = decimalFold.format(thousandsSeparator.format(value as string))
+ eachFigures(indicator, dataIndex, styles, (figure: IndicatorFigure, figureStyles: Required) => {
+ if (isString(figure.title)) {
+ const color = figureStyles.color
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ignore
+ let value = data[figure.key]
+ if (isNumber(value)) {
+ value = formatPrecision(value, indicator.precision)
+ if (indicator.shouldFormatBigNumber) {
+ value = formatter.formatBigNumber(value as string)
}
- legends.push({ title: { text: figure.title, color }, value: { text: (value ?? defaultValue) as string, color } })
+ value = decimalFold.format(thousandsSeparator.format(value as string))
}
- })
+ legends.push({ title: { text: figure.title, color }, value: { text: (value ?? defaultValue) as string, color } })
+ }
+ })
tooltipData.legends = legends
}
diff --git a/src/view/IndicatorView.ts b/src/view/IndicatorView.ts
index fb583476a..c44a835de 100644
--- a/src/view/IndicatorView.ts
+++ b/src/view/IndicatorView.ts
@@ -96,112 +96,104 @@ export default class IndicatorView extends CandleBarView {
this.eachChildren((data, barSpace) => {
const { halfGapBar } = barSpace
- const { dataIndex, x, data: kLineNeighborData } = data
+ const { dataIndex, x } = data
const prevX = xAxis.convertToPixel(dataIndex - 1)
const nextX = xAxis.convertToPixel(dataIndex + 1)
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ignore
- const prevData: Record = result[kLineNeighborData.prev?.timestamp ?? ''] ?? {}
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ignore
- const currentData: Record = result[kLineNeighborData.current?.timestamp ?? ''] ?? {}
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ignore
- const nextData: Record = result[kLineNeighborData.next?.timestamp ?? ''] ?? {}
+ const prevData = result[dataIndex - 1] ?? null
+ const currentData = result[dataIndex] ?? null
+ const nextData = result[dataIndex + 1] ?? null
const prevCoordinate = { x: prevX }
const currentCoordinate = { x }
const nextCoordinate = { x: nextX }
indicator.figures.forEach(({ key }) => {
- const prevValue = prevData[key]
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ignore
+ const prevValue = prevData?.[key]
if (isNumber(prevValue)) {
prevCoordinate[key] = yAxis.convertToPixel(prevValue)
}
- const currentValue = currentData[key]
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ignore
+ const currentValue = currentData?.[key]
if (isNumber(currentValue)) {
currentCoordinate[key] = yAxis.convertToPixel(currentValue)
}
- const nextValue = nextData[key]
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ignore
+ const nextValue = nextData?.[key]
if (isNumber(nextValue)) {
nextCoordinate[key] = yAxis.convertToPixel(nextValue)
}
})
- eachFigures(
- indicator,
- {
- prev: kLineNeighborData.prev?.timestamp ?? null,
- current: kLineNeighborData.current?.timestamp ?? null,
- next: kLineNeighborData.next?.timestamp ?? null
- },
- defaultStyles,
- (figure: IndicatorFigure, figureStyles: IndicatorFigureStyle, figureIndex: number) => {
- if (isValid(currentData[figure.key])) {
+ eachFigures(indicator, dataIndex, defaultStyles, (figure: IndicatorFigure, figureStyles: IndicatorFigureStyle, figureIndex: number) => {
+ if (isValid(currentData?.[figure.key])) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ignore
- const valueY = currentCoordinate[figure.key]
- let attrs = figure.attrs?.({
- data: { prev: prevData, current: currentData, next: nextData },
- coordinate: { prev: prevCoordinate, current: currentCoordinate, next: nextCoordinate },
- bounding,
- barSpace,
- xAxis,
- yAxis
- })
- if (!isValid(attrs)) {
- switch (figure.type) {
- case 'circle': {
+ const valueY = currentCoordinate[figure.key]
+ let attrs = figure.attrs?.({
+ data: { prev: prevData, current: currentData, next: nextData },
+ coordinate: { prev: prevCoordinate, current: currentCoordinate, next: nextCoordinate },
+ bounding,
+ barSpace,
+ xAxis,
+ yAxis
+ })
+ if (!isValid(attrs)) {
+ switch (figure.type) {
+ case 'circle': {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ignore
- attrs = { x, y: valueY, r: Math.max(1, halfGapBar) }
- break
+ attrs = { x, y: valueY, r: Math.max(1, halfGapBar) }
+ break
+ }
+ case 'rect':
+ case 'bar': {
+ const baseValue = figure.baseValue ?? yAxis.getRange().from
+ const baseValueY = yAxis.convertToPixel(baseValue)
+ let height = Math.abs(baseValueY - (valueY as number))
+ if (baseValue !== currentData?.[figure.key]) {
+ height = Math.max(1, height)
}
- case 'rect':
- case 'bar': {
- const baseValue = figure.baseValue ?? yAxis.getRange().from
- const baseValueY = yAxis.convertToPixel(baseValue)
- let height = Math.abs(baseValueY - (valueY as number))
- if (baseValue !== currentData[figure.key]) {
- height = Math.max(1, height)
- }
- let y = 0
- if (valueY > baseValueY) {
- y = baseValueY
- } else {
+ let y = 0
+ if (valueY > baseValueY) {
+ y = baseValueY
+ } else {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ignore
- y = valueY
- }
- attrs = {
- x: x - halfGapBar,
- y,
- width: Math.max(1, halfGapBar * 2),
- height
- }
- break
+ y = valueY
+ }
+ attrs = {
+ x: x - halfGapBar,
+ y,
+ width: Math.max(1, halfGapBar * 2),
+ height
}
- case 'line': {
- if (!isValid(lines[figureIndex])) {
- lines[figureIndex] = []
- }
- if (isNumber(currentCoordinate[figure.key]) && isNumber(nextCoordinate[figure.key])) {
- lines[figureIndex].push({
- coordinates: [
+ break
+ }
+ case 'line': {
+ if (!isValid(lines[figureIndex])) {
+ lines[figureIndex] = []
+ }
+ if (isNumber(currentCoordinate[figure.key]) && isNumber(nextCoordinate[figure.key])) {
+ lines[figureIndex].push({
+ coordinates: [
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ignore
+ { x: currentCoordinate.x, y: currentCoordinate[figure.key] },
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ignore
- { x: currentCoordinate.x, y: currentCoordinate[figure.key] },
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ignore
- { x: nextCoordinate.x, y: nextCoordinate[figure.key] }
- ],
- styles: figureStyles as unknown as SmoothLineStyle
- })
- }
- break
+ { x: nextCoordinate.x, y: nextCoordinate[figure.key] }
+ ],
+ styles: figureStyles as unknown as SmoothLineStyle
+ })
}
- default: { break }
+ break
}
- }
- const type = figure.type!
- if (isValid(attrs) && type !== 'line') {
- this.createFigure({
- name: type === 'bar' ? 'rect' : type,
- attrs,
- styles: figureStyles
- })?.draw(ctx)
+ default: { break }
}
}
- })
+ const type = figure.type!
+ if (isValid(attrs) && type !== 'line') {
+ this.createFigure({
+ name: type === 'bar' ? 'rect' : type,
+ attrs,
+ styles: figureStyles
+ })?.draw(ctx)
+ }
+ }
+ })
})
// merge line and render
diff --git a/src/view/OverlayView.ts b/src/view/OverlayView.ts
index 2a179fd6c..29e49efd8 100644
--- a/src/view/OverlayView.ts
+++ b/src/view/OverlayView.ts
@@ -165,7 +165,32 @@ export default class OverlayView extends View {
}
}
return false
+ }).registerEvent('mouseDownEvent', event => {
+ // Handle continuous drawing mode - start drawing on mouse down
+ const progressOverlayInfo = chartStore.getProgressOverlayInfo()
+ if (progressOverlayInfo !== null) {
+ const overlay = progressOverlayInfo.overlay
+ if (overlay.isContinuousDrawing() && overlay.isStart()) {
+ chartStore.updateProgressOverlayInfo(paneId, true)
+ const point = this._coordinateToPoint(overlay, event)
+ overlay.startContinuousDrawing(point)
+ overlay.onDrawStart?.({ chart, overlay, ...event })
+ return true
+ }
+ }
+ return false
}).registerEvent('mouseUpEvent', event => {
+ // Handle continuous drawing mode - complete on mouse up
+ const progressOverlayInfo = chartStore.getProgressOverlayInfo()
+ if (progressOverlayInfo !== null) {
+ const overlay = progressOverlayInfo.overlay
+ if (overlay.isContinuousDrawing() && overlay.isDrawing() && !overlay.isStart()) {
+ overlay.completeContinuousDrawing()
+ chartStore.progressOverlayComplete()
+ overlay.onDrawEnd?.({ chart, overlay, ...event })
+ return true
+ }
+ }
const { overlay, figure } = chartStore.getPressedOverlayInfo()
if (overlay !== null) {
if (checkOverlayFigureEvent('onPressedMoveEnd', figure)) {
@@ -181,6 +206,17 @@ export default class OverlayView extends View {
})
return false
}).registerEvent('pressedMouseMoveEvent', event => {
+ // Handle continuous drawing mode - accumulate points while mouse is pressed
+ const progressOverlayInfo = chartStore.getProgressOverlayInfo()
+ if (progressOverlayInfo !== null) {
+ const overlay = progressOverlayInfo.overlay
+ if (overlay.isContinuousDrawing() && overlay.isDrawing() && !overlay.isStart()) {
+ const point = this._coordinateToPoint(overlay, event)
+ overlay.addPointForContinuousDrawing(point)
+ overlay.onDrawing?.({ chart, overlay, ...event })
+ return true
+ }
+ }
const { overlay, figureType, figureIndex, figure } = chartStore.getPressedOverlayInfo()
if (overlay !== null) {
if (checkOverlayFigureEvent('onPressedMoving', figure)) {
@@ -345,11 +381,19 @@ export default class OverlayView extends View {
const paneId = pane.getId()
const chartStore = chart.getChartStore()
if (this.coordinateToPointTimestampDataIndexFlag()) {
- const xAxis = chart.getXAxisPane().getAxisComponent()
- const dataIndex = xAxis.convertFromPixel(coordinate.x)
- const timestamp = chartStore.dataIndexToTimestamp(dataIndex) ?? undefined
- point.timestamp = timestamp
- point.dataIndex = dataIndex
+ const overlayImp = o as OverlayImp
+ if (overlayImp.isContinuousDrawing() && !overlayImp.isStart()) {
+ // For continuous drawing, store precise timestamp for sub-bar positioning
+ const floatIndex = chartStore.coordinateToFloatIndex(coordinate.x)
+ point.dataIndex = floatIndex
+ point.timestamp = chartStore.floatIndexToTimestamp(floatIndex) ?? undefined
+ } else {
+ // For step-based drawing, snap to candle boundaries
+ const xAxis = chart.getXAxisPane().getAxisComponent()
+ const dataIndex = xAxis.convertFromPixel(coordinate.x)
+ point.dataIndex = dataIndex
+ point.timestamp = chartStore.dataIndexToTimestamp(dataIndex) ?? undefined
+ }
}
if (this.coordinateToPointValueFlag()) {
const yAxis = pane.getAxisComponent()
@@ -415,7 +459,8 @@ export default class OverlayView extends View {
}
override dispatchEvent (name: EventName, event: MouseTouchEvent): boolean {
- if (this.getWidget().getPane().getChart().getChartStore().isOverlayDrawing()) {
+ const isDrawing = this.getWidget().getPane().getChart().getChartStore().isOverlayDrawing()
+ if (isDrawing) {
return this.onEvent(name, event)
}
return super.dispatchEvent(name, event)
@@ -443,15 +488,23 @@ export default class OverlayView extends View {
const chart = pane.getChart()
const chartStore = chart.getChartStore()
const yAxis = pane.getAxisComponent() as unknown as Nullable
- const xAxis = chart.getXAxisPane().getAxisComponent()
+ // For continuous drawing overlays, use float indices for smooth rendering
+ const isContinuous = overlay.isContinuousDrawing()
const coordinates = points.map(point => {
let dataIndex: Nullable = null
- if (isNumber(point.timestamp)) {
+ if (isContinuous && isNumber(point.timestamp)) {
+ // Use timestampToFloatIndex for sub-bar precision
+ dataIndex = chartStore.timestampToFloatIndex(point.timestamp)
+ } else if (isNumber(point.timestamp)) {
+ // For regular overlays, use integer timestamp lookup
dataIndex = chartStore.timestampToDataIndex(point.timestamp)
+ } else if (isNumber(point.dataIndex)) {
+ // Fallback to dataIndex if no timestamp
+ dataIndex = point.dataIndex
}
const coordinate = { x: 0, y: 0 }
if (isNumber(dataIndex)) {
- coordinate.x = xAxis.convertToPixel(dataIndex)
+ coordinate.x = chartStore.dataIndexToCoordinate(dataIndex)
}
if (isNumber(point.value)) {
coordinate.y = yAxis?.convertToPixel(point.value) ?? 0