hyperprop-charting-library 0.1.135 → 0.1.137

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/docs/API.md CHANGED
@@ -483,9 +483,34 @@ chart.setActiveDrawingTool("fib-extension"); // three clicks: trend start, tre
483
483
  chart.setActiveDrawingTool("long-position"); // single click drops a long risk/reward box
484
484
  chart.setActiveDrawingTool("short-position"); // single click drops a short risk/reward box
485
485
  chart.setActiveDrawingTool("measure"); // next drag (or click-move-click) measures; see below
486
+ chart.setActiveDrawingTool("parallel-channel"); // three clicks: base start, base end, parallel offset
487
+ chart.setActiveDrawingTool("ellipse"); // two clicks: opposite corners of the bounding box
488
+ chart.setActiveDrawingTool("arrow"); // like trendline, with an arrowhead at the second point
489
+ chart.setActiveDrawingTool("brush"); // press and drag to draw freehand; release commits
490
+ chart.setActiveDrawingTool("callout"); // two clicks: anchor tip, then text box (opens text editor)
486
491
  chart.setActiveDrawingTool(null); // back to normal cursor/pan
487
492
  ```
488
493
 
494
+ New shape/annotation tools:
495
+
496
+ - `parallel-channel`: three-point channel, TradingView style. The first two
497
+ clicks set the base trendline; the third click sets the parallel offset.
498
+ Renders both lines, a dashed centre line, and a translucent fill. Handles:
499
+ the two base anchors plus a third on the parallel line. The offset is
500
+ constant in price, so the channel stays parallel at any zoom.
501
+ - `ellipse`: two-anchor bounding-box ellipse, created and resized exactly like
502
+ `rectangle` (corner handles, including the derived corners).
503
+ - `arrow`: a `trendline` with a filled arrowhead at the second point (head
504
+ size scales with `width`). Supports the same Shift 0/45/90° constraint.
505
+ - `brush`: freehand polyline. Press and drag to draw; release commits. Points
506
+ are appended only when the cursor moves ≥3px, and strokes render smoothed
507
+ with quadratic curves. No reshape handles — drag the stroke body to move it.
508
+ - `callout`: anchored text box. First click drops the anchor (arrow tip),
509
+ second click places the box, and `onDrawingEditText` fires so the host can
510
+ show its text editor. Stores content in `label` and size in `fontSize`, like
511
+ `text`/`note`. The connector line + arrowhead re-route to the box edge
512
+ nearest the anchor.
513
+
489
514
  Use `getDrawings()` / `setDrawings()` for persistence.
490
515
 
491
516
  ### Measure tool (TradingView-style ruler)
@@ -533,12 +558,13 @@ an up measurement, red for down.
533
558
  - `resetViewport(): void` (fit x + reset y auto-scale)
534
559
  - `setSelectedDrawing(id: string | null): void` (marks a drawing selected; only the selected/drafted drawing renders its handles, and position tools render their lines/labels only while selected)
535
560
  - `onDrawingDoubleClick(handler): void` (fires when a drawing is double-clicked; use it to open a settings dialog, e.g. for position tools)
536
- - `onDrawingEditText(handler): void` (fires when a `text`/`note` tool is placed or double-clicked; use it to show an inline text editor at `{x, y}` and write the result back via `updateDrawing(id, { label })`). `text`/`note` drawings store their content in `label` and size in `fontSize`.
561
+ - `onDrawingEditText(handler): void` (fires when a `text`/`note`/`callout` tool is placed or double-clicked; use it to show an inline text editor at `{x, y}` and write the result back via `updateDrawing(id, { label })`). These drawings store their content in `label` and size in `fontSize`.
537
562
  - `setTradeMarkers(markers: TradeMarkerOptions[]): void` — draw trade execution markers (arrow + `qty @ price` label) on the candle of each fill's `time`. Buy = arrow below the low pointing up; sell = arrow above the high pointing down. Each marker: `{ time (ms|ISO), price, side: "buy"|"sell", qty?, text?, color?, textColor? }` (defaults: buy `#2962ff`, sell `#f23645`, text `#d1d4dc`). Markers resolve to bars by time, so they persist across timeframe switches and scrolling. Pass `[]` to clear.
538
563
  - `cancelDrawing(): boolean` — abort an in-progress multi-click drawing (between the first click and the final point, e.g. a half-drawn trendline/ray/fib). Returns `true` if a draft was cancelled. Wire it to Escape.
539
- - Hold **Shift** while drawing or dragging an endpoint of a `trendline`/`ray` to constrain it to the nearest 0/45/90° angle (e.g. a perfectly horizontal line).
564
+ - Hold **Shift** while drawing or dragging an endpoint of a `trendline`/`ray`/`arrow` to constrain it to the nearest 0/45/90° angle (e.g. a perfectly horizontal line).
540
565
  - `setMagnetMode(mode): void` / `getMagnetMode()` — magnet/snap for all drawing tools. `"none"` off, `"weak"` snaps to a candle's OHLC only when the cursor is near a value, `"strong"` always snaps to the nearest OHLC of the bar under the cursor. Holding Cmd/Ctrl while drawing/dragging forces strong snapping regardless of the mode.
541
- - `setActiveDrawingTool(tool: DrawingToolType | null): void` (`DrawingToolType` = `"horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "rectangle" | "text" | "note" | "measure"`)
566
+ - `setPriceScale(options: { mode?: "linear" | "log" | "percent"; inverted?: boolean }): void` / `getPriceScale()` price-scale modes for the main pane (TradingView-style). `"log"` maps prices through log10 (wide ranges get 1/2/5-per-decade axis marks). `"percent"` keeps the linear mapping but relabels the axis (ticks, last-price/PDC/H/L/bid/ask tags and the crosshair) as % change from the first visible bar's close, updating as you pan. `inverted: true` flips the axis top-to-bottom. All viewport state stays in plain price space, so switching modes never moves your zoom, and drawings/orders/indicators follow automatically. Indicator panes keep their own scales. Included in `saveState()` as `priceScale`.
567
+ - `setActiveDrawingTool(tool: DrawingToolType | null): void` (`DrawingToolType` = `"horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "rectangle" | "text" | "note" | "parallel-channel" | "ellipse" | "arrow" | "brush" | "callout" | "measure"`)
542
568
  - `getActiveDrawingTool(): DrawingToolType | null`
543
569
  - `onActiveDrawingToolChange(handler: ((tool: DrawingToolType | null) => void) | null): void` — fires whenever the active tool changes, including the chart auto-reverting to the cursor after a tool completes (all tools do this when their shape is placed; `measure` does it after every measurement). Keeps host toolbars in sync without polling.
544
570
  - `setDrawings(drawings: DrawingObjectOptions[]): void`
@@ -558,6 +584,22 @@ an up measurement, red for down.
558
584
  - `updateIndicator(id: string, patch: Partial<IndicatorInstanceOptions>): void`
559
585
  - `removeIndicator(id: string): void`
560
586
  - `setIndicators(indicators: IndicatorInstanceOptions[]): void`
587
+ - `saveState(): ChartSavedState` — one JSON-serializable snapshot of the full user-visible setup: `{ version: 1, chartType, viewport, drawings, indicators, magnetMode, priceScale, drawingDefaults }`. Chart *data* is not included; keep feeding bars through `setData`/`upsertBar`.
588
+ - `loadState(state: Partial<ChartSavedState>): void` — restore a `saveState()` blob. Tolerant of partial blobs (missing sections are left untouched), so it doubles as a bulk setter. Register custom script indicator plugins with `registerIndicator` *before* calling it so their instances resolve. Viewport is applied last so pane changes don't clobber the restore.
589
+ - `takeScreenshot(options?: { type?: "image/png" | "image/jpeg"; quality?: number }): string` — returns a data URL of the current frame (default PNG) at the chart's device pixel ratio. Everything on the canvas is included: series, indicators, panes, drawings, legends.
590
+
591
+ ```ts
592
+ // Persist / restore the user's whole chart setup
593
+ localStorage.setItem("layout", JSON.stringify(chart.saveState()));
594
+ chart.loadState(JSON.parse(localStorage.getItem("layout")!));
595
+
596
+ // Download a chart snapshot
597
+ const link = document.createElement("a");
598
+ link.href = chart.takeScreenshot();
599
+ link.download = "chart.png";
600
+ link.click();
601
+ ```
602
+
561
603
  - `resize(width?: number, height?: number): void`
562
604
  - `destroy(): void`
563
605
 
@@ -612,6 +654,43 @@ Script contract:
612
654
  - `bars`: `Array<{ time: Date, o, h, l, c, v? }>` — the full series.
613
655
  - `inputs`: resolved input values (defaults merged with per-instance overrides). `showValueLine`
614
656
  is injected automatically and honored by separate panes.
657
+
658
+ ### Pine-style input declarations
659
+
660
+ Instead of maintaining a separate JSON `inputs` schema, scripts can declare their settings inline
661
+ at the top level — the same idea as TradingView's `input.int(14, "Length")`. The declared
662
+ constants always hold the live settings values, and hosts can auto-generate the settings dialog
663
+ from the declarations:
664
+
665
+ ```ts
666
+ const length = input.int("Length", 14, { min: 1, max: 500 });
667
+ const source = input.source("Source", "close"); // open/high/low/close/hl2/hlc3/ohlc4
668
+ const smooth = input.bool("Smooth", true);
669
+ const color = input.color("Line color", "#f59e0b");
670
+
671
+ function compute(bars, inputs, hp) {
672
+ const values = bars.map(b => source === "open" ? b.o : b.c);
673
+ const line = smooth ? hp.ema(values, length) : hp.sma(values, length);
674
+ return { plots: [{ title: "MA", values: line, color }] };
675
+ }
676
+ ```
677
+
678
+ Available declarations (all return the resolved value; `opts` is `{ key?, min?, max?, step? }`):
679
+
680
+ - `input.int(label, default, opts?)` / `input.float(label, default, opts?)` → number
681
+ - `input.bool(label, default?)` → boolean
682
+ - `input.color(label, default)` → color string
683
+ - `input.string(label, default?)` → string
684
+ - `input.select(label, default, choices)` → string (choices: `string[]` or `{value, label}[]`)
685
+ - `input.source(label?, default?)` → one of `open/high/low/close/hl2/hlc3/ohlc4`
686
+
687
+ Settings keys default to a slug of the label (`"Line color"` → `line_color`); pass
688
+ `{ key: "..." }` to pin one explicitly (keeps saved settings stable if you rename the label).
689
+
690
+ `extractScriptInputs(source: string): { inputs: ScriptIndicatorInputDef[], error: string | null }`
691
+ — runs the script's top level with a recording helper and returns the declared schema; hosts use
692
+ this to auto-generate settings dialogs. The legacy JSON `inputs` array on
693
+ `ScriptIndicatorDefinition` still works; on key collisions the inline declarations win.
615
694
  - `hp`: TA helpers, all `(values: Array<number|null>, length) => Array<number|null>` unless noted:
616
695
  `sma`, `ema`, `rma`, `highest`, `lowest`, `stdev`, `change(values, length = 1)`,
617
696
  `atr(bars, length)`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.135",
3
+ "version": "0.1.137",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",