hyperprop-charting-library 0.1.27 → 0.1.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -153,7 +153,7 @@ Volume scaling tip (for large spikes):
153
153
  chart.updateIndicator(volumeId, {
154
154
  inputs: {
155
155
  scaleMode: "visible", // default; scales using current viewport only
156
- scaleType: "log", // default; better for large volume ranges
156
+ scaleType: "sqrt", // default; balanced compression
157
157
  clampPercentile: 0.95 // optional outlier clamp
158
158
  }
159
159
  });
@@ -533,7 +533,7 @@ var BUILTIN_VOLUME_INDICATOR = {
533
533
  minBarWidth: 1,
534
534
  overlayHeightRatio: 0.22,
535
535
  scaleMode: "visible",
536
- scaleType: "log",
536
+ scaleType: "sqrt",
537
537
  clampPercentile: 1
538
538
  },
539
539
  draw: (ctx, renderContext, inputs) => {
@@ -544,7 +544,7 @@ var BUILTIN_VOLUME_INDICATOR = {
544
544
  const paneHeight = overlayMode ? Math.max(20, Math.round(fullPaneHeight * overlayHeightRatio)) : fullPaneHeight;
545
545
  const paneBottom = chartBottom;
546
546
  const scaleMode = inputs.scaleMode === "full" ? "full" : "visible";
547
- const scaleType = inputs.scaleType === "linear" ? "linear" : "log";
547
+ const scaleType = inputs.scaleType === "linear" ? "linear" : inputs.scaleType === "log" ? "log" : "sqrt";
548
548
  const scalingPoints = scaleMode === "full" ? data : data.slice(startIndex, endIndex + 1);
549
549
  const scalingVolumes = scalingPoints.map((point) => Math.max(0, point.v ?? 0)).filter((value) => value > 0);
550
550
  const clampPercentile = Number.isFinite(inputs.clampPercentile) ? Number(inputs.clampPercentile) : 1;
@@ -561,7 +561,7 @@ var BUILTIN_VOLUME_INDICATOR = {
561
561
  continue;
562
562
  }
563
563
  const clampedVolume = Math.min(maxVolume, Math.max(0, point.v));
564
- const ratio = scaleType === "linear" ? Math.min(1, Math.max(0, clampedVolume / maxVolume)) : Math.min(1, Math.max(0, Math.log1p(clampedVolume) / Math.log1p(maxVolume)));
564
+ const ratio = scaleType === "linear" ? Math.min(1, Math.max(0, clampedVolume / maxVolume)) : scaleType === "log" ? Math.min(1, Math.max(0, Math.log1p(clampedVolume) / Math.log1p(maxVolume))) : Math.min(1, Math.max(0, Math.sqrt(clampedVolume) / Math.sqrt(maxVolume)));
565
565
  const volumeHeight = Math.max(1, Math.round(paneHeight * ratio));
566
566
  const xCenter = xFromIndex(index);
567
567
  const barX = Math.round(xCenter - barWidth / 2);
@@ -2665,9 +2665,11 @@ function createChart(element, options = {}) {
2665
2665
  };
2666
2666
  const setData = (nextData) => {
2667
2667
  const hadData = data.length > 0;
2668
+ const previousCount = data.length;
2668
2669
  const previousCenterRounded = hadData ? Math.round(xCenter) : 0;
2669
2670
  const previousCenterFraction = hadData ? xCenter - previousCenterRounded : 0;
2670
2671
  const previousCenterTimeMs = hadData && previousCenterRounded >= 0 && previousCenterRounded < data.length ? data[previousCenterRounded]?.time.getTime() ?? null : null;
2672
+ const wasAtRightEdge = hadData && xCenter + xSpan / 2 >= previousCount - 1;
2671
2673
  data = parseData(nextData);
2672
2674
  if (data.length === 0) {
2673
2675
  xCenter = 0;
@@ -2681,7 +2683,9 @@ function createChart(element, options = {}) {
2681
2683
  resetYViewport();
2682
2684
  } else {
2683
2685
  if (mergedOptions.preserveViewportOnDataUpdate) {
2684
- if (previousCenterTimeMs !== null) {
2686
+ if (wasAtRightEdge) {
2687
+ xCenter = data.length - xSpan / 2 + rightEdgePaddingBars;
2688
+ } else if (previousCenterTimeMs !== null) {
2685
2689
  const nextCenter = findNearestIndexForTimeMs(previousCenterTimeMs);
2686
2690
  if (nextCenter !== null) {
2687
2691
  xCenter = nextCenter + previousCenterFraction;
@@ -509,7 +509,7 @@ var BUILTIN_VOLUME_INDICATOR = {
509
509
  minBarWidth: 1,
510
510
  overlayHeightRatio: 0.22,
511
511
  scaleMode: "visible",
512
- scaleType: "log",
512
+ scaleType: "sqrt",
513
513
  clampPercentile: 1
514
514
  },
515
515
  draw: (ctx, renderContext, inputs) => {
@@ -520,7 +520,7 @@ var BUILTIN_VOLUME_INDICATOR = {
520
520
  const paneHeight = overlayMode ? Math.max(20, Math.round(fullPaneHeight * overlayHeightRatio)) : fullPaneHeight;
521
521
  const paneBottom = chartBottom;
522
522
  const scaleMode = inputs.scaleMode === "full" ? "full" : "visible";
523
- const scaleType = inputs.scaleType === "linear" ? "linear" : "log";
523
+ const scaleType = inputs.scaleType === "linear" ? "linear" : inputs.scaleType === "log" ? "log" : "sqrt";
524
524
  const scalingPoints = scaleMode === "full" ? data : data.slice(startIndex, endIndex + 1);
525
525
  const scalingVolumes = scalingPoints.map((point) => Math.max(0, point.v ?? 0)).filter((value) => value > 0);
526
526
  const clampPercentile = Number.isFinite(inputs.clampPercentile) ? Number(inputs.clampPercentile) : 1;
@@ -537,7 +537,7 @@ var BUILTIN_VOLUME_INDICATOR = {
537
537
  continue;
538
538
  }
539
539
  const clampedVolume = Math.min(maxVolume, Math.max(0, point.v));
540
- const ratio = scaleType === "linear" ? Math.min(1, Math.max(0, clampedVolume / maxVolume)) : Math.min(1, Math.max(0, Math.log1p(clampedVolume) / Math.log1p(maxVolume)));
540
+ const ratio = scaleType === "linear" ? Math.min(1, Math.max(0, clampedVolume / maxVolume)) : scaleType === "log" ? Math.min(1, Math.max(0, Math.log1p(clampedVolume) / Math.log1p(maxVolume))) : Math.min(1, Math.max(0, Math.sqrt(clampedVolume) / Math.sqrt(maxVolume)));
541
541
  const volumeHeight = Math.max(1, Math.round(paneHeight * ratio));
542
542
  const xCenter = xFromIndex(index);
543
543
  const barX = Math.round(xCenter - barWidth / 2);
@@ -2641,9 +2641,11 @@ function createChart(element, options = {}) {
2641
2641
  };
2642
2642
  const setData = (nextData) => {
2643
2643
  const hadData = data.length > 0;
2644
+ const previousCount = data.length;
2644
2645
  const previousCenterRounded = hadData ? Math.round(xCenter) : 0;
2645
2646
  const previousCenterFraction = hadData ? xCenter - previousCenterRounded : 0;
2646
2647
  const previousCenterTimeMs = hadData && previousCenterRounded >= 0 && previousCenterRounded < data.length ? data[previousCenterRounded]?.time.getTime() ?? null : null;
2648
+ const wasAtRightEdge = hadData && xCenter + xSpan / 2 >= previousCount - 1;
2647
2649
  data = parseData(nextData);
2648
2650
  if (data.length === 0) {
2649
2651
  xCenter = 0;
@@ -2657,7 +2659,9 @@ function createChart(element, options = {}) {
2657
2659
  resetYViewport();
2658
2660
  } else {
2659
2661
  if (mergedOptions.preserveViewportOnDataUpdate) {
2660
- if (previousCenterTimeMs !== null) {
2662
+ if (wasAtRightEdge) {
2663
+ xCenter = data.length - xSpan / 2 + rightEdgePaddingBars;
2664
+ } else if (previousCenterTimeMs !== null) {
2661
2665
  const nextCenter = findNearestIndexForTimeMs(previousCenterTimeMs);
2662
2666
  if (nextCenter !== null) {
2663
2667
  xCenter = nextCenter + previousCenterFraction;
package/dist/index.cjs CHANGED
@@ -533,7 +533,7 @@ var BUILTIN_VOLUME_INDICATOR = {
533
533
  minBarWidth: 1,
534
534
  overlayHeightRatio: 0.22,
535
535
  scaleMode: "visible",
536
- scaleType: "log",
536
+ scaleType: "sqrt",
537
537
  clampPercentile: 1
538
538
  },
539
539
  draw: (ctx, renderContext, inputs) => {
@@ -544,7 +544,7 @@ var BUILTIN_VOLUME_INDICATOR = {
544
544
  const paneHeight = overlayMode ? Math.max(20, Math.round(fullPaneHeight * overlayHeightRatio)) : fullPaneHeight;
545
545
  const paneBottom = chartBottom;
546
546
  const scaleMode = inputs.scaleMode === "full" ? "full" : "visible";
547
- const scaleType = inputs.scaleType === "linear" ? "linear" : "log";
547
+ const scaleType = inputs.scaleType === "linear" ? "linear" : inputs.scaleType === "log" ? "log" : "sqrt";
548
548
  const scalingPoints = scaleMode === "full" ? data : data.slice(startIndex, endIndex + 1);
549
549
  const scalingVolumes = scalingPoints.map((point) => Math.max(0, point.v ?? 0)).filter((value) => value > 0);
550
550
  const clampPercentile = Number.isFinite(inputs.clampPercentile) ? Number(inputs.clampPercentile) : 1;
@@ -561,7 +561,7 @@ var BUILTIN_VOLUME_INDICATOR = {
561
561
  continue;
562
562
  }
563
563
  const clampedVolume = Math.min(maxVolume, Math.max(0, point.v));
564
- const ratio = scaleType === "linear" ? Math.min(1, Math.max(0, clampedVolume / maxVolume)) : Math.min(1, Math.max(0, Math.log1p(clampedVolume) / Math.log1p(maxVolume)));
564
+ const ratio = scaleType === "linear" ? Math.min(1, Math.max(0, clampedVolume / maxVolume)) : scaleType === "log" ? Math.min(1, Math.max(0, Math.log1p(clampedVolume) / Math.log1p(maxVolume))) : Math.min(1, Math.max(0, Math.sqrt(clampedVolume) / Math.sqrt(maxVolume)));
565
565
  const volumeHeight = Math.max(1, Math.round(paneHeight * ratio));
566
566
  const xCenter = xFromIndex(index);
567
567
  const barX = Math.round(xCenter - barWidth / 2);
@@ -2665,9 +2665,11 @@ function createChart(element, options = {}) {
2665
2665
  };
2666
2666
  const setData = (nextData) => {
2667
2667
  const hadData = data.length > 0;
2668
+ const previousCount = data.length;
2668
2669
  const previousCenterRounded = hadData ? Math.round(xCenter) : 0;
2669
2670
  const previousCenterFraction = hadData ? xCenter - previousCenterRounded : 0;
2670
2671
  const previousCenterTimeMs = hadData && previousCenterRounded >= 0 && previousCenterRounded < data.length ? data[previousCenterRounded]?.time.getTime() ?? null : null;
2672
+ const wasAtRightEdge = hadData && xCenter + xSpan / 2 >= previousCount - 1;
2671
2673
  data = parseData(nextData);
2672
2674
  if (data.length === 0) {
2673
2675
  xCenter = 0;
@@ -2681,7 +2683,9 @@ function createChart(element, options = {}) {
2681
2683
  resetYViewport();
2682
2684
  } else {
2683
2685
  if (mergedOptions.preserveViewportOnDataUpdate) {
2684
- if (previousCenterTimeMs !== null) {
2686
+ if (wasAtRightEdge) {
2687
+ xCenter = data.length - xSpan / 2 + rightEdgePaddingBars;
2688
+ } else if (previousCenterTimeMs !== null) {
2685
2689
  const nextCenter = findNearestIndexForTimeMs(previousCenterTimeMs);
2686
2690
  if (nextCenter !== null) {
2687
2691
  xCenter = nextCenter + previousCenterFraction;
package/dist/index.js CHANGED
@@ -509,7 +509,7 @@ var BUILTIN_VOLUME_INDICATOR = {
509
509
  minBarWidth: 1,
510
510
  overlayHeightRatio: 0.22,
511
511
  scaleMode: "visible",
512
- scaleType: "log",
512
+ scaleType: "sqrt",
513
513
  clampPercentile: 1
514
514
  },
515
515
  draw: (ctx, renderContext, inputs) => {
@@ -520,7 +520,7 @@ var BUILTIN_VOLUME_INDICATOR = {
520
520
  const paneHeight = overlayMode ? Math.max(20, Math.round(fullPaneHeight * overlayHeightRatio)) : fullPaneHeight;
521
521
  const paneBottom = chartBottom;
522
522
  const scaleMode = inputs.scaleMode === "full" ? "full" : "visible";
523
- const scaleType = inputs.scaleType === "linear" ? "linear" : "log";
523
+ const scaleType = inputs.scaleType === "linear" ? "linear" : inputs.scaleType === "log" ? "log" : "sqrt";
524
524
  const scalingPoints = scaleMode === "full" ? data : data.slice(startIndex, endIndex + 1);
525
525
  const scalingVolumes = scalingPoints.map((point) => Math.max(0, point.v ?? 0)).filter((value) => value > 0);
526
526
  const clampPercentile = Number.isFinite(inputs.clampPercentile) ? Number(inputs.clampPercentile) : 1;
@@ -537,7 +537,7 @@ var BUILTIN_VOLUME_INDICATOR = {
537
537
  continue;
538
538
  }
539
539
  const clampedVolume = Math.min(maxVolume, Math.max(0, point.v));
540
- const ratio = scaleType === "linear" ? Math.min(1, Math.max(0, clampedVolume / maxVolume)) : Math.min(1, Math.max(0, Math.log1p(clampedVolume) / Math.log1p(maxVolume)));
540
+ const ratio = scaleType === "linear" ? Math.min(1, Math.max(0, clampedVolume / maxVolume)) : scaleType === "log" ? Math.min(1, Math.max(0, Math.log1p(clampedVolume) / Math.log1p(maxVolume))) : Math.min(1, Math.max(0, Math.sqrt(clampedVolume) / Math.sqrt(maxVolume)));
541
541
  const volumeHeight = Math.max(1, Math.round(paneHeight * ratio));
542
542
  const xCenter = xFromIndex(index);
543
543
  const barX = Math.round(xCenter - barWidth / 2);
@@ -2641,9 +2641,11 @@ function createChart(element, options = {}) {
2641
2641
  };
2642
2642
  const setData = (nextData) => {
2643
2643
  const hadData = data.length > 0;
2644
+ const previousCount = data.length;
2644
2645
  const previousCenterRounded = hadData ? Math.round(xCenter) : 0;
2645
2646
  const previousCenterFraction = hadData ? xCenter - previousCenterRounded : 0;
2646
2647
  const previousCenterTimeMs = hadData && previousCenterRounded >= 0 && previousCenterRounded < data.length ? data[previousCenterRounded]?.time.getTime() ?? null : null;
2648
+ const wasAtRightEdge = hadData && xCenter + xSpan / 2 >= previousCount - 1;
2647
2649
  data = parseData(nextData);
2648
2650
  if (data.length === 0) {
2649
2651
  xCenter = 0;
@@ -2657,7 +2659,9 @@ function createChart(element, options = {}) {
2657
2659
  resetYViewport();
2658
2660
  } else {
2659
2661
  if (mergedOptions.preserveViewportOnDataUpdate) {
2660
- if (previousCenterTimeMs !== null) {
2662
+ if (wasAtRightEdge) {
2663
+ xCenter = data.length - xSpan / 2 + rightEdgePaddingBars;
2664
+ } else if (previousCenterTimeMs !== null) {
2661
2665
  const nextCenter = findNearestIndexForTimeMs(previousCenterTimeMs);
2662
2666
  if (nextCenter !== null) {
2663
2667
  xCenter = nextCenter + previousCenterFraction;
package/docs/API.md CHANGED
@@ -312,7 +312,7 @@ Volume style inputs:
312
312
  - `minBarWidth`
313
313
  - `overlayHeightRatio` (when used as overlay)
314
314
  - `scaleMode` (`"visible"` default, or `"full"`)
315
- - `scaleType` (`"log"` default, or `"linear"`)
315
+ - `scaleType` (`"sqrt"` default, or `"log"` / `"linear"`)
316
316
  - `clampPercentile` (`0..1`, default `1`; e.g. `0.95` to reduce outlier crush)
317
317
 
318
318
  ---
package/docs/RECIPES.md CHANGED
@@ -151,7 +151,7 @@ const volumeId = chart.addIndicator("volume", { upOpacity: 0.72, downOpacity: 0.
151
151
  ```ts
152
152
  const volumeId = chart.addIndicator("volume", {
153
153
  scaleMode: "visible", // default behavior
154
- scaleType: "log", // default behavior
154
+ scaleType: "sqrt", // default behavior
155
155
  clampPercentile: 0.95 // clamp scaling reference to p95 of visible bars
156
156
  });
157
157
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",