openalgo-charts 2.5.2 → 2.5.4

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
@@ -12,7 +12,7 @@ with no runtime dependencies.
12
12
  [![npm version](https://img.shields.io/npm/v/openalgo-charts.svg?color=cb3837&label=npm)](https://www.npmjs.com/package/openalgo-charts)
13
13
  [![license](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](./LICENSE)
14
14
  [![npm downloads](https://img.shields.io/npm/dm/openalgo-charts.svg?color=0ea5e9&label=npm%20downloads)](https://www.npmjs.com/package/openalgo-charts)
15
- [![tests](https://img.shields.io/badge/engine%20tests-6023%20passing-brightgreen.svg)](#develop)
15
+ [![tests](https://img.shields.io/badge/engine%20tests-6095%20passing-brightgreen.svg)](#develop)
16
16
  [![dependencies](https://img.shields.io/badge/runtime%20deps-0-brightgreen.svg)](#principles)
17
17
 
18
18
  [**Documentation**](https://marketcalls.github.io/openalgo-charts/)  ·  [**Live examples**](https://marketcalls.github.io/openalgo-charts/examples)  ·  [**Getting started**](./docs/getting-started.md)  ·  [**Migrating to 2.0**](./docs/migrating-to-2.md)  ·  [**Architecture**](./ARCHITECTURE.md)
@@ -25,7 +25,7 @@ with no runtime dependencies.
25
25
  npm install openalgo-charts
26
26
  ```
27
27
 
28
- Current version: **2.5.2**. Add anchored volume studies, share drawings and appearance, and open grouped timeline events.
28
+ Current version: **2.5.4**. Go to any date, lay out a grid of charts, collapse panes, route study output and lock host drawings.
29
29
  See the [changelog](./CHANGELOG.md) for release notes.
30
30
 
31
31
  ## Quick start
@@ -56,7 +56,7 @@ for live data, or [the widget example](#the-whole-terminal-in-one-call) for char
56
56
 
57
57
  ## Architecture
58
58
 
59
- <a href="docs/architecture-diagram.svg"><img src="docs/architecture-diagram.svg" alt="OpenAlgo Charts 2.5.2 architecture: host responsibilities, the base data-to-rendering pipeline with alerts and shared replay, and eight optional tiers including workspace storage, trading tools and the widget" width="920" /></a>
59
+ <a href="docs/architecture-diagram.svg"><img src="docs/architecture-diagram.svg" alt="OpenAlgo Charts 2.5.4 architecture: host responsibilities, the base data-to-rendering pipeline with alerts and shared replay, and eight optional tiers including workspace storage, trading tools and the widget" width="920" /></a>
60
60
 
61
61
  How data reaches the chart, what your app owns, and which features you can import.
62
62
  [Open the full-size diagram](docs/architecture-diagram.svg).
@@ -107,7 +107,7 @@ You can also load the library from a CDN in a plain HTML page:
107
107
  ```html
108
108
  <div id="chart" style="width:100vw;height:100vh"></div>
109
109
  <script type="module">
110
- import { createChart, generateBars } from 'https://unpkg.com/openalgo-charts@2.5.2/dist/openalgo-charts.mjs';
110
+ import { createChart, generateBars } from 'https://unpkg.com/openalgo-charts@2.5.4/dist/openalgo-charts.mjs';
111
111
  const chart = createChart(document.getElementById('chart'), { timezone: 'Asia/Kolkata' });
112
112
  chart.addSeries('candlestick').setData(generateBars(1700000000, 200, 3600));
113
113
  chart.fitContent();
@@ -142,7 +142,10 @@ widget.draw; // the DrawingController the rail drives
142
142
 
143
143
  The optional widget adds symbol search, interval and chart-type controls, a drawing
144
144
  toolbar, settings, indicators, a status line, mobile controls and layout persistence.
145
- Its dialogs use the chart's settings schemas and follow the active theme.
145
+ Its dialogs use the chart's settings schemas and follow the active theme. **Go to**
146
+ jumps to a date or a range, loading older history first (`widget.goTo`), and
147
+ `createChartGrid` lays out one widget per cell, from `1x1` to `2x2`, with splitters,
148
+ linked charts and portable workspace documents.
146
149
 
147
150
  The widget uses the same public API as a custom interface. Your app supplies the
148
151
  data feed and handles order requests; the widget does not choose a broker or send
@@ -157,17 +160,17 @@ Unused optional tiers stay out of the base chart download.
157
160
 
158
161
  | Import | Contents | Brotli |
159
162
  |---|---|---|
160
- | `openalgo-charts` | Engine, 13 chart types, panes and scales, custom indicator registry, primitives, alerts, replay, comparisons, chart linking, state, feeds, bar cache, trading overlays, CSV and SVG export | 95.04 kB |
161
- | `openalgo-charts/indicators` | 105 built-in indicators, calculation helpers and helpers for studies that use external data | 29.84 kB |
162
- | `openalgo-charts/draw` | 87 drawing tools + a headless drawing controller, clipboard, settings schema, level palette, freehand geometry and SVG icons | 40.73 kB |
163
+ | `openalgo-charts` | Engine, 13 chart types, panes and scales, custom indicator registry, primitives, alerts, replay, comparisons, chart linking, state, feeds, bar cache, trading overlays, CSV and SVG export | 117.42 kB |
164
+ | `openalgo-charts/indicators` | 105 built-in indicators, calculation helpers and helpers for studies that use external data | 36.27 kB |
165
+ | `openalgo-charts/draw` | 87 drawing tools + a headless drawing controller, clipboard, settings schema, level palette, freehand geometry and SVG icons | 42.53 kB |
163
166
  | `openalgo-charts/transform` | Heikin Ashi, Renko, Range bars, Line Break, Point &amp; Figure, Kagi, and symbol arithmetic (`AAPL/MSFT`) | 4.50 kB |
164
167
  | `openalgo-charts/profile` | Volume Profile, Market Profile (TPO) with compact pixel letters, Footprint, order flow | 14.96 kB |
165
168
  | `openalgo-charts/trade` | Order, position and bracket tools, plus a depth-of-market ladder | 8.01 kB |
166
169
  | `openalgo-charts/webgl` | GPU drawing for supported series, with Canvas 2D fallback | 6.39 kB |
167
- | `openalgo-charts/widget` | `createWidget`: toolbar, drawing controls, dialogs, mobile controls, status line, shortcuts and optional layout persistence | 51.06 kB |
168
- | `openalgo-charts/workspace` | Validated workspace and indicator-template documents, named catalogs with revision checks, asynchronous storage and an IndexedDB adapter; no DOM | 5.55 kB |
170
+ | `openalgo-charts/widget` | `createWidget`: toolbar, Data and Objects dock, symbol search, dialogs, mobile controls, shortcuts and optional layout persistence | 71.33 kB |
171
+ | `openalgo-charts/workspace` | Validated workspace and indicator-template documents, named catalogs with revision checks, asynchronous storage and an IndexedDB adapter; no DOM | 8.67 kB |
169
172
 
170
- Everything together is **256.08 kB Brotli**; a widget terminal with built-in indicators (base + draw + indicators + widget) is 216.67 kB. Figures are measured from the 2.5.2 release build. The trade tier is 8.01 kB on its own; base + trade costs 103.05 kB. Sizes use decimal kB.
173
+ Everything together is **310.09 kB Brotli**; a widget terminal with built-in indicators (base + draw + indicators + widget) is 267.55 kB. Figures are measured from the 2.5.4 release build. The trade tier is 8.01 kB on its own; base + trade costs 125.42 kB. Sizes use decimal kB.
171
174
 
172
175
  ## What's built
173
176
 
@@ -225,7 +228,7 @@ draw.setTool('trend-line'); // the next two clicks place it
225
228
 
226
229
  87 tools. Lines, channels and four pitchfork variants. Fibonacci levels, time projections, fans, circles, arcs, wedges and spirals. Gann fans, boxes and squares. Harmonic patterns, Elliott waves and Head and Shoulders. Geometric wavefronts and tessellation. Shapes, paths, forecasts, positions, measurements, text, tables and freehand brushes. Every tool appears in the [editable drawing gallery](https://marketcalls.github.io/openalgo-charts/demos/drawings/index.html), with simulated NIFTY prices near 23800 and desktop or touch controls.
227
230
 
228
- Headless by design: no toolbar, no dialogs. Placement with live preview, selection, whole-shape and per-anchor dragging, magnet snap to O/H/L/C, undo/redo (a drag is one step), and persistence. Anchors are `{ time, price }`, never pixels, so they survive zoom and resolve inside collapsed session gaps and past the last bar.
231
+ Headless by design: no toolbar, no dialogs. Placement with live preview, selection, whole-shape and per-anchor dragging, magnet snap to O/H/L/C, undo/redo (a drag is one step), and persistence. A drawing's `policy` makes it read-only, unselectable, transient or unlisted, for levels the host places and the user should not move. Anchors are `{ time, price }`, never pixels, so they survive zoom and resolve inside collapsed session gaps and past the last bar.
229
232
 
230
233
  `draw.copy()`, `draw.cut()` and `draw.paste()` move drawings through the OS clipboard, including between two charts on the page. The payload is JSON under one namespaced key, so foreign text pastes nothing instead of throwing at your Ctrl+V handler, and every field is validated before it reaches the model. A refused clipboard permission does not lose the copy: every write also lands in a shared in-memory clipboard, and a cut deletes only after the write succeeds. A paste is one undo step of fresh objects, nudged two bars and 16 px so it is visibly a second shape. The key bindings stay yours; the engine installs no listeners.
231
234
 
@@ -252,7 +255,7 @@ A drawing carries a paint order (`zIndex`: below zero paints under the series, a
252
255
  Under the hand: the drawing under the pointer shows its handles faintly before it is grabbed (`hovered()`, `drawing:hover`), at the cost of the overlay tier only; Shift locks a line to 45 degree steps while placing or dragging a handle; the magnet paints a ring on the bar centre where the next click will land, and `'weak'` pulls only when an O/H/L/C is within a few pixels; grab targets grow for a touch pointer; Escape, Enter and Backspace cancel, finish or pop an anchor while placing, through `keyToDrawingAction`, which also maps undo, redo, copy, cut, paste, duplicate, delete and arrow nudge as a pure function you wire yourself. Line tools take a `showStats` readout of change, percent, bars and angle. The brush and highlighter ink every coalesced pointer sample, thin on release and paint as a spline, with pen pressure driving the width when `style.pressure` is on. Every tool icon and a chrome set ship as path data with builders for an inline `<svg>`, a sprite with `<use>` and a CSS cursor (`iconSvg`, `iconSprite`, `iconUse`, `toolCursor`), so a rail, a flyout and the armed cursor derive from one registry.
253
256
 
254
257
  ### Panes, scales &amp; legends
255
- Draggable pane dividers, move / maximize / remove, and pane legends showing one reading per plot in that plot's own colour, with inline show-hide / settings / move / delete controls revealed on hover. The status line is switchable field by field (logo, title, market status, OHLC, bar change, volume, last day change, last value) over a host-supplied data source.
258
+ Draggable pane dividers, move / maximize / collapse to a strip / remove, and pane legends showing one reading per plot in that plot's own colour, with inline show-hide / settings / move / delete controls revealed on hover. The status line is switchable field by field (logo, title, market status, OHLC, bar change, volume, last day change, last value) over a host-supplied data source.
256
259
 
257
260
  Each pane carries a right, a left and a hidden overlay price scale, in four modes: linear, logarithmic, and the two rebasing modes **percentage** (`+3.42%`) and **indexed-to-100** (`103.42`), which quote every price against a baseline taken from the first visible bar, so panning re-bases the axis.
258
261
 
@@ -322,6 +325,8 @@ Hover one chart and the same **instant** is marked on the others; pan or zoom on
322
325
 
323
326
  Nothing crosses a chart boundary as a logical index. The x axis is a gapless index over each chart's own bars, so index 300 is a different instant on every chart: every value is converted index to time on the sender and time back to index on the receiver, against that chart's own data. A daily chart and an hourly chart with different history depth therefore stay on the same instant, which the naive index copy gets right only when both charts hold the same bars. An instant outside a follower's first or last bar is an absence, not a gap, so it draws nothing; inside its range with no bar there it snaps to the nearest bar in time, or draws nothing under `whenMissing: 'hide'`. The linked crosshair is a vertical line only, at reduced opacity: a mirrored horizontal line would assert a price that belongs to another instrument.
324
327
 
328
+ For a ready-made grid of widgets with these links, splitters and saved layouts, use `createChartGrid` from `openalgo-charts/widget`.
329
+
325
330
  Symbol sync remains a host partnership even when using optional instrument metadata: the host emits `'symbol'` on the chart's bus (or calls `group.setSymbol`) and supplies the per-member `onSymbol` that loads the bars. A member with no `onSymbol` broadcasts but never follows, which is how you pin one chart of a grid.
326
331
 
327
332
  ### Settings &amp; context menu
@@ -489,17 +494,17 @@ Enforced in CI by [`size-limit`](./.size-limit.json). Nothing is excluded, becau
489
494
 
490
495
  | Bundle | Limit | Actual |
491
496
  |---|---|---|
492
- | Base engine | 95.5 kB | 95.04 kB |
493
- | Base + trade | 103.5 kB | 103.05 kB |
494
- | Indicators tier | 30 kB | 29.84 kB |
495
- | Draw tier | 41 kB | 40.73 kB |
497
+ | Base engine | 117.42 kB | 117.42 kB |
498
+ | Base + trade | 125.43 kB | 125.42 kB |
499
+ | Indicators tier | 36.3 kB | 36.27 kB |
500
+ | Draw tier | 42.53 kB | 42.53 kB |
496
501
  | Transform tier | 6 kB | 4.50 kB |
497
502
  | Profile tier | 15 kB | 14.96 kB |
498
503
  | WebGL2 tier | 7 kB | 6.39 kB |
499
- | Widget tier | 51.5 kB | 51.06 kB |
500
- | Widget terminal (base + draw + indicators + widget) | 217 kB | 216.67 kB |
501
- | Workspace tier | 6 kB | 5.55 kB |
502
- | **Everything** | 256.75 kB | 256.08 kB |
504
+ | Widget tier | 71.34 kB | 71.33 kB |
505
+ | Widget terminal (base + draw + indicators + widget) | 267.56 kB | 267.55 kB |
506
+ | Workspace tier | 8.75 kB | 8.67 kB |
507
+ | **Everything** | 310.09 kB | 310.09 kB |
503
508
 
504
509
  ## Documentation
505
510
 
@@ -544,8 +549,8 @@ See [Contributing](./CONTRIBUTING.md) for setup, targeted checks, documentation
544
549
  ```bash
545
550
  npm install # install dev toolchain
546
551
  npm run typecheck # strict TypeScript check
547
- npm test # engine unit tests (Vitest): 6023 across 256 files
548
- npm run test:demo # reference-host tests: 415 across 32 files
552
+ npm test # engine unit tests (Vitest): 8228 across 359 files
553
+ npm run test:demo # reference-host tests: 542 across 50 files
549
554
  npm run test:endurance # node endurance-harness tests: 7 cases
550
555
  npm run build # Rollup -> dist/ (minified ESM per tier + types)
551
556
  npm run size # size-limit (Brotli) against the budget
@@ -563,7 +568,7 @@ npm run verify # lint + types + unit + endurance harness + build + demo + dt
563
568
 
564
569
  ## Status &amp; limitations
565
570
 
566
- Version **2.5.2**. All engine build phases are implemented. Upgrading a 1.9.x host: [Migrating to 2.0](./docs/migrating-to-2.md).
571
+ Version **2.5.4**. All engine build phases are implemented. Upgrading a 1.9.x host: [Migrating to 2.0](./docs/migrating-to-2.md).
567
572
 
568
573
  Known gaps, stated plainly:
569
574
 
@@ -250,6 +250,41 @@ interface DrawingStyle {
250
250
  /** Brush: let pen pressure drive the stroke width. */
251
251
  pressure?: boolean;
252
252
  }
253
+ /**
254
+ * What a host allows the user to do with one drawing. The flags are
255
+ * independent of one another and of `locked`, and each defaults to true, so a
256
+ * drawing without a policy behaves exactly as one did before policies
257
+ * existed. A copy (paste, duplicate) never inherits a policy: it is the
258
+ * user's own drawing.
259
+ *
260
+ * `selectable`, `persistent` and `listed` bind every caller. `editable`
261
+ * restricts the user: the controller cannot tell a click from a call, so its
262
+ * methods treat every call as the user's unless it passes `{ force: true }`
263
+ * (see `DrawingEditOptions`), which is how the host that placed the drawing
264
+ * moves or retires it. Stacking order (front, back, either side of the
265
+ * series) is outside the policy.
266
+ */
267
+ interface DrawingPolicy {
268
+ /**
269
+ * `false`: never joins the selection, whoever asks. A click passes through
270
+ * to what lies under it, and nothing hovers, drags or selects it.
271
+ */
272
+ selectable?: boolean;
273
+ /**
274
+ * `false`: read-only to the user. It still selects, copies and duplicates,
275
+ * but nothing moves, reshapes, restyles, hides, locks, cuts, deletes or
276
+ * regroups it, and undo and redo never touch it or the group it is in.
277
+ */
278
+ editable?: boolean;
279
+ /**
280
+ * `false`: transient. Left out of `toJSON`, and so out of the chart state
281
+ * and every saved layout, while it renders and undoes as usual in the
282
+ * session. A restore replaces it with what was saved.
283
+ */
284
+ persistent?: boolean;
285
+ /** `false`: left out of the object inventory (`ChartObjects`) and every panel built on it. */
286
+ listed?: boolean;
287
+ }
253
288
  interface Drawing {
254
289
  id: string;
255
290
  /** Registered tool id. */
@@ -265,8 +300,14 @@ interface Drawing {
265
300
  */
266
301
  props?: Record<string, unknown>;
267
302
  paneIndex: number;
268
- /** Locked drawings render but cannot be selected or dragged. */
303
+ /**
304
+ * Locked drawings render but cannot be selected or dragged on the chart.
305
+ * The user toggles it; a host restriction the user cannot lift is a
306
+ * {@link DrawingPolicy}.
307
+ */
269
308
  locked?: boolean;
309
+ /** Host restrictions on this drawing. Absent means none. */
310
+ policy?: DrawingPolicy;
270
311
  /** Default true. */
271
312
  visible?: boolean;
272
313
  /**
@@ -288,17 +329,27 @@ type DrawingInput = Omit<Drawing, 'id' | 'zIndex' | 'createdAt'> & {
288
329
  zIndex?: number;
289
330
  createdAt?: number;
290
331
  };
291
- /** The fields `DrawingController.update` and `updateMany` can change. */
292
- type DrawingPatch = Partial<Pick<Drawing, 'points' | 'style' | 'text' | 'props' | 'locked' | 'visible' | 'zIndex'>>;
332
+ /**
333
+ * The fields `DrawingController.update` and `updateMany` can change. `policy`
334
+ * merges flag by flag, the way `style` does.
335
+ */
336
+ type DrawingPatch = Partial<Pick<Drawing, 'points' | 'style' | 'text' | 'props' | 'locked' | 'visible' | 'zIndex' | 'policy'>>;
293
337
  /** The persisted shape's version; bumped when {@link Drawing} changes. */
294
338
  declare const DRAWING_STATE_VERSION = 2;
295
339
  /**
296
340
  * What `toJSON` returns and `ChartState.drawings` carries. Versioned so a 1.9.x
297
341
  * save (a bare `Drawing[]`) is recognisable and upgraded rather than misread.
298
342
  */
343
+ /** A named set of drawing ids. A drawing belongs to at most one group. */
344
+ interface DrawingGroup {
345
+ id: string;
346
+ name: string;
347
+ members: string[];
348
+ }
299
349
  interface DrawingsDocument {
300
350
  version: 2;
301
351
  drawings: Drawing[];
352
+ groups?: DrawingGroup[];
302
353
  }
303
354
  /** A point mapped to the pane, in media px. */
304
355
  interface ScreenPoint {
@@ -678,10 +729,12 @@ interface DrawingChartHost {
678
729
  timeToCoordinate?(time: number): number;
679
730
  coordinateToTime?(x: number): number;
680
731
  /**
681
- * Optional, and used only to keep a paste from a chart with more panes than
682
- * this one landing on a pane the user cannot see. Adding a primitive creates
683
- * the pane it names, so without this a drawing copied out of an indicator
684
- * pane would conjure an empty pane in a single-pane chart.
732
+ * Optional. It keeps a paste from a chart with more panes than this one
733
+ * landing on a pane the user cannot see: adding a primitive creates the pane
734
+ * it names, so without this a drawing copied out of an indicator pane would
735
+ * conjure an empty pane in a single-pane chart. An entry with `priceToY` and
736
+ * `yToPrice`, as a chart pane has, also lets an alert read a drawing on a
737
+ * pane the chart maps no price for, one collapsed to its header strip.
685
738
  */
686
739
  panes?(): readonly unknown[];
687
740
  }
@@ -728,12 +781,40 @@ interface DrawingControllerOptions {
728
781
  }
729
782
  /** What `drawing:change` reports happened to the listed ids. */
730
783
  type DrawingChangeKind = 'add' | 'update' | 'remove' | 'reorder' | 'undo' | 'redo';
784
+ /** Options for a call that changes, groups or deletes drawings. */
785
+ interface DrawingEditOptions {
786
+ /**
787
+ * Reach drawings whose policy sets `editable: false` as well, and the
788
+ * groups that hold them. Without it they are left exactly as they are,
789
+ * which is what keeps every control a host wires to the user off them. The
790
+ * host that placed such a drawing passes it to move, restyle, regroup or
791
+ * retire it. A forced call is the host's own act, not the user's, so it
792
+ * records no undo step, and every step already recorded takes it too: no
793
+ * later undo or redo reverses it, and a step it leaves with nothing to do
794
+ * is dropped.
795
+ *
796
+ * Cost: taking a call into the recorded steps is one pass over the undo
797
+ * and redo history, parsing and rewriting both snapshots of every step, so
798
+ * it grows with the number of recorded steps (see `historyLimit`) times
799
+ * the drawing count. A forced delete, a forced grouping call, a forced
800
+ * patch to a drawing the user may edit or one that carries `zIndex`, any
801
+ * patch that carries `policy` and a linked chart's change of policy make
802
+ * that pass. A forced patch to a read-only drawing that carries neither
803
+ * `policy` nor `zIndex` (a level the host trails on every tick) makes
804
+ * none: history cannot reach that drawing's content while it stays
805
+ * read-only, so the patch is held and goes in with the next pass, which a
806
+ * change of its policy always makes.
807
+ */
808
+ force?: boolean;
809
+ }
731
810
  declare class DrawingController {
732
811
  private readonly _chart;
733
812
  private _opts;
734
813
  private readonly _clipboard;
735
814
  private readonly _layers;
736
815
  private _drawings;
816
+ private _groups;
817
+ private _nextGroup;
737
818
  private readonly _linkedPreviews;
738
819
  private _destroyed;
739
820
  private _tool;
@@ -758,6 +839,8 @@ declare class DrawingController {
758
839
  private _undo;
759
840
  private _redo;
760
841
  private _pendingHistory;
842
+ /** The host's patches the recorded steps have yet to take, merged per drawing. */
843
+ private readonly _hostPatches;
761
844
  /**
762
845
  * One gesture's starting state. `items` are ids rather than objects because
763
846
  * an undo mid-drag replaces every drawing object, and a stale reference
@@ -797,6 +880,31 @@ declare class DrawingController {
797
880
  * moves one; the list is the tie-break for equal `zIndex`, so it is also the
798
881
  * paint order within a band.
799
882
  */
883
+ /** Named drawing sets, returned as detached records. */
884
+ groups(): readonly DrawingGroup[];
885
+ /**
886
+ * Group live drawings, replacing any previous membership for those ids. A
887
+ * read-only drawing's group is the host's, so it is left where it is
888
+ * unless `options.force` is set.
889
+ */
890
+ createGroup(name: string, ids: readonly string[], options?: DrawingEditOptions): DrawingGroup | null;
891
+ /** Rename a group. One that holds a read-only drawing is the host's, and needs `options.force`. */
892
+ renameGroup(id: string, name: string, options?: DrawingEditOptions): boolean;
893
+ /**
894
+ * Remove a group, optionally deleting all its drawings as one edit. One
895
+ * that holds a read-only drawing is the host's, and needs `options.force`.
896
+ */
897
+ removeGroup(id: string, removeDrawings?: boolean, options?: DrawingEditOptions): boolean;
898
+ /**
899
+ * Make a grouping edit. The host's forced one is its own act, so every
900
+ * recorded step takes it as well, and no undo or redo reverses it.
901
+ */
902
+ private _regroup;
903
+ /** The live group `id`, unless it holds a read-only drawing and the call is not forced. */
904
+ private _group;
905
+ /** Move one step through the rendered stack, preserving the side of the series. */
906
+ reorder(id: string, direction: -1 | 1): boolean;
907
+ private _remapPanes;
800
908
  drawings(): readonly Drawing[];
801
909
  get(id: string): Drawing | undefined;
802
910
  get isDestroyed(): boolean;
@@ -810,7 +918,11 @@ declare class DrawingController {
810
918
  alertInfo(id: string): AlertDrawingInfo;
811
919
  /** Numeric drawing value using the actual pane projection and collapsed time axis. */
812
920
  valueAt(id: string, time: number, level?: string): AlertDrawingValue | undefined;
813
- /** Add a fully-specified drawing (import, or a host-authored one). */
921
+ /**
922
+ * Add a fully-specified drawing (import, or a host-authored one). One that
923
+ * carries a restriction is the host's, and placing it is not a step the
924
+ * user can take back, so it is not recorded.
925
+ */
814
926
  add(drawing: DrawingInput): Drawing;
815
927
  /**
816
928
  * Append one drawing without touching history or the layers. Split out so a
@@ -818,32 +930,46 @@ declare class DrawingController {
818
930
  */
819
931
  private _insert;
820
932
  private _mintId;
821
- update(id: string, patch: DrawingPatch): boolean;
933
+ /**
934
+ * Patch one drawing. False when there is no such drawing, or when it is
935
+ * read-only to the user and `options.force` is not set. A patch that
936
+ * carries `policy` is the host's, and like a forced one records no step.
937
+ */
938
+ update(id: string, patch: DrawingPatch, options?: DrawingEditOptions): boolean;
822
939
  /**
823
940
  * Patch several drawings as one undo entry: a colour change across a
824
941
  * multi-selection is one edit to the user, so it is one Ctrl+Z too. Ids that
825
- * no longer exist are skipped; nothing is recorded when none exist.
942
+ * no longer exist are skipped, and so are read-only drawings unless
943
+ * `options.force` is set; nothing is recorded when nothing is left, or
944
+ * when every patch is the host's (forced, or carrying `policy`).
826
945
  */
827
946
  updateMany(patches: ReadonlyArray<{
828
947
  id: string;
829
948
  patch: DrawingPatch;
830
- }>): void;
949
+ }>, options?: DrawingEditOptions): void;
831
950
  private _applyPatch;
832
- remove(id: string): boolean;
833
- /** Delete several drawings as one undo entry. Unknown ids are ignored. */
834
- removeMany(ids: readonly string[]): void;
951
+ /** Delete one drawing. A read-only one goes only with `options.force`. */
952
+ remove(id: string, options?: DrawingEditOptions): boolean;
953
+ /**
954
+ * Delete several drawings as one undo entry. Unknown ids are ignored, and
955
+ * read-only drawings stay unless `options.force` is set.
956
+ */
957
+ removeMany(ids: readonly string[], options?: DrawingEditOptions): void;
835
958
  /**
836
959
  * The delete shared by `remove`, `removeMany`, `cut` and `clear`. Returns
837
960
  * what went, and records nothing when nothing did.
838
961
  */
839
962
  private _removeIds;
840
- clear(): void;
963
+ /** Delete every drawing the user may, as one undo entry; `options.force` takes the read-only ones too. */
964
+ clear(options?: DrawingEditOptions): void;
841
965
  /**
842
966
  * Replace the selection, or with `additive` toggle each id into it (the
843
- * shift-click gesture). Ids that name nothing are ignored, so the selection
844
- * never refers to a drawing that is not there. Pass null to clear.
967
+ * shift-click gesture). Ids that name nothing, or a drawing whose policy
968
+ * says it cannot be selected, are ignored, so the selection only ever holds
969
+ * drawings that are there to act on. Pass null to clear.
845
970
  */
846
971
  select(id: string | readonly string[] | null, additive?: boolean): void;
972
+ private _selectable;
847
973
  /** The primary selection: the first id picked, or null. */
848
974
  selected(): string | null;
849
975
  /** Every selected id, in the order they were picked. */
@@ -865,13 +991,14 @@ declare class DrawingController {
865
991
  /**
866
992
  * Move drawings by a screen distance, `dx` right and `dy` down in media px,
867
993
  * as one undo entry. Pixels rather than data units so an arrow key moves a
868
- * shape the same visible amount on every pane and scale. Locked drawings
869
- * stay put.
994
+ * shape the same visible amount on every pane and scale. Locked and
995
+ * read-only drawings stay put.
870
996
  */
871
997
  nudge(ids: readonly string[], dxPx: number, dyPx: number): void;
872
998
  /**
873
999
  * Clone drawings, offset like a paste so the copies are visibly new, and
874
- * select the clones. One undo entry. Ids that name nothing are ignored.
1000
+ * select the clones. One undo entry. Ids that name nothing are ignored. A
1001
+ * clone is the user's own drawing, so it carries no policy.
875
1002
  */
876
1003
  duplicate(ids: readonly string[]): Drawing[];
877
1004
  /**
@@ -883,7 +1010,8 @@ declare class DrawingController {
883
1010
  /**
884
1011
  * Copy, then delete. The delete happens **only** after the clipboard write
885
1012
  * resolves successfully, so a refused write leaves the model exactly as it
886
- * was rather than destroying a drawing that went nowhere.
1013
+ * was rather than destroying a drawing that went nowhere. A read-only
1014
+ * drawing cannot be deleted, so it is not cut either: it stays, uncopied.
887
1015
  */
888
1016
  cut(target?: string | readonly string[] | null): Promise<boolean>;
889
1017
  /**
@@ -915,13 +1043,25 @@ declare class DrawingController {
915
1043
  private _offsetTime;
916
1044
  undo(): boolean;
917
1045
  redo(): boolean;
1046
+ /**
1047
+ * Move the model from one snapshot to the other; false when the policy left
1048
+ * nothing to move. Without `kind` it only answers, and moves nothing.
1049
+ */
918
1050
  private _applyHistory;
919
- /** Drop selected ids the model no longer holds, after a history jump. */
1051
+ /** Drop selected ids the model no longer holds, or that can no longer be selected. */
920
1052
  private _pruneSelection;
921
1053
  canUndo(): boolean;
922
1054
  canRedo(): boolean;
923
- /** Serialisable document, the same shape `ChartState.drawings` carries. */
1055
+ /**
1056
+ * Serialisable document, the same shape `ChartState.drawings` carries.
1057
+ * Transient drawings (`policy.persistent` false) are left out, and so is
1058
+ * their group membership.
1059
+ */
924
1060
  toJSON(): DrawingsDocument;
1061
+ /** `drawings` as a document, with the groups narrowed to them. */
1062
+ private _document;
1063
+ /** Every drawing, transient ones too: an undo in the session reaches them. */
1064
+ private _historyText;
925
1065
  /**
926
1066
  * Replace every drawing. Accepts a {@link DrawingsDocument} or a 1.9.x bare
927
1067
  * `Drawing[]`; both go through the migration, so an old save upgrades on
@@ -1080,6 +1220,24 @@ declare class DrawingController {
1080
1220
  */
1081
1221
  private _syncSnapRing;
1082
1222
  private _pushUndo;
1223
+ /**
1224
+ * Open an edit: recorded, or, for a change the history does not hold (a
1225
+ * host placing or moving a read-only drawing), not recorded and leaving
1226
+ * both branches alone. Either way a drag in progress ends first.
1227
+ */
1228
+ private _begin;
1229
+ /**
1230
+ * What the host does (a policy, a forced call) is its own act and never
1231
+ * history's to reverse: `edit` makes the same change to every recorded
1232
+ * snapshot, as if it had always been so, and a step left with nothing to do
1233
+ * is dropped, so `canUndo` and `canRedo` match what a press would do. The
1234
+ * step still being recorded stays whatever it holds so far. `edit` may
1235
+ * share live objects, since each snapshot is serialised at once. The
1236
+ * host's patches still held back go in first, being older. Both snapshots
1237
+ * of every step are parsed and written, which is why a forced move of a
1238
+ * read-only drawing is held back rather than paying for this.
1239
+ */
1240
+ private _rebase;
1083
1241
  }
1084
1242
 
1085
1243
  /** Both fields must be known. A host may use its feed namespace as the exchange. */
@@ -1959,4 +2117,4 @@ declare const PATTERN_DRAWING_TOOLS: readonly DrawingTool[];
1959
2117
 
1960
2118
  declare const DRAW_TIER: "draw";
1961
2119
 
1962
- export { ADVANCED_GEOMETRY_TOOLS, ADVANCED_LINE_TOOLS, ALIGN_OPTIONS, ANCHORED_VWAP, ARC, ARROW, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT, ARROW_UP, type AnalysisProfileRow, type AnalysisStatus, type AnchoredVwapOptions, type AnchoredVwapPoint, type AnchoredVwapResult, type AnchoredVwapSource, BALLOON, BRUSH, BUILTIN_DRAWING_TOOLS, CALLOUT, CHROME_ICONS, CHROME_ICON_ATTRS, CHROME_ICON_FILLED, CHROME_ICON_STROKE, CHROME_ICON_VIEWBOX, CIRCLE, COLOR_FIELD, COMMENT, CROSS_LINE, CURVE, CYCLE_PALETTE, CYCLIC_LINES, type ClipboardPort, DATE_RANGE, DEFAULT_FIB, DEFAULT_FIB_FAN, DEFAULT_FIB_TIME_ZONE, DEFAULT_GANN_BOX, DEFAULT_GANN_FAN, DOUBLE_CURVE, DRAWING_CLIPBOARD_KEY, DRAWING_CLIPBOARD_VERSION, DRAWING_STATE_VERSION, DRAWING_TOOL_ICONS, DRAW_TIER, type DrawContext, type Drawing, type DrawingChangeKind, type DrawingChartHost, DrawingClipboard, type DrawingClipboardOptions, DrawingController, type DrawingControllerOptions, type DrawingInput, type DrawingKeyAction, type DrawingKeyContext, type DrawingKeyEvent, DrawingLayer, type DrawingLayerOrder, type DrawingLinkChart, type DrawingLinkContext, type DrawingLinkContextSource, DrawingLinkGroup, type DrawingLinkOptions, type DrawingPatch, type DrawingPoint, type DrawingPointerKind, type DrawingStyle, type DrawingText, type DrawingTool, type DrawingValueContext, type DrawingsDocument, ELLIPSE, EXTENDED_LINE, EXTEND_FIELDS, type ExpandContext, FIB_CHANNEL, FIB_EXTENSION, FIB_FAN, FIB_RETRACEMENT, FIB_TIME_ZONE, FILL_FIELDS, FIXED_RANGE_VOLUME_PROFILE, FLAG_MARK, FONT_FIELDS, FONT_OPTIONS, FORECAST, type FibLevel, type FieldGroup, type FieldKind, type FixedRangeVolumeProfileOptions, type FixedRangeVolumeProfileResult, GANN_BOX, GANN_FAN, HIGHLIGHTER, HORIZONTAL_LINE, HORIZONTAL_RAY, type HitContext, ICON_ATTRS, ICON_STROKE, ICON_SYMBOL_PREFIX, ICON_VIEWBOX, type IconAttrs, type IconSvgOptions, LEVEL_FIELDS, LEVEL_NEUTRAL, LINE_FIELDS, LINE_STYLE_FIELD, LINE_STYLE_OPTIONS, LINE_WIDTH_FIELD, LONG_POSITION, MEASURE, type MagnetMode, NOTE, NUDGE_STEP_PX, NUDGE_STEP_SHIFT_PX, PARALLEL_CHANNEL, PATH, PATTERN_DRAWING_TOOLS, PLATE_TEXT_FIELDS, POLYLINE, PRICE_LABEL, PRICE_NOTE, PRICE_RANGE, RAY, RECTANGLE, ROTATED_RECTANGLE, SHAPE_TEXT_FIELDS, SHORT_POSITION, SHOW_LABELS_FIELD, SIGNPOST, SINE_LINE, type ScreenPoint, type SettingsField, type SettingsSchema, type ShortcutEvent, TABLE, TEXT, TEXT_FIELDS, TEXT_POSITION_OPTIONS, TEXT_VALUE_FIELD, TIME_CYCLES, TREND_LINE, TRIANGLE, type ToolCursorOptions, VALIGN_OPTIONS, VERTICAL_LINE, anchoredVwapAnalysis, applyDrawingSettings, boundsOf, catmullRom, chromeIcon, chromeIconIds, chromeIconSvg, clearMemoryClipboard, cloneDrawing, cloneLevels, coerceSettingValue, composeSettings, createDrawingLinkGroup, cycleColor, decodeClipboardPayload, distToEllipse, distToLine, distToPolyline, distToRect, distToSegment, drawingSettingsSchema, drawingShortcuts, drawingToolIcon, drawingToolIconIds, encodeClipboardPayload, extendSegment, fixedRangeVolumeProfileAnalysis, formatRatio, gannLabel, getDrawingTool, hasDrawingTool, iconSprite, iconSvg, iconUse, keyToDrawingAction, levelColor, matchDrawingShortcut, migrateDrawings, pressureWidth, rdpSimplify, readDrawingSetting, readDrawingSettings, rectOf, registerBuiltinDrawingTools, registerDrawingTool, registeredDrawingTools, sanitizeDrawing, sortByZIndex, systemClipboard, toolCursor };
2120
+ export { ADVANCED_GEOMETRY_TOOLS, ADVANCED_LINE_TOOLS, ALIGN_OPTIONS, ANCHORED_VWAP, ARC, ARROW, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT, ARROW_UP, type AnalysisProfileRow, type AnalysisStatus, type AnchoredVwapOptions, type AnchoredVwapPoint, type AnchoredVwapResult, type AnchoredVwapSource, BALLOON, BRUSH, BUILTIN_DRAWING_TOOLS, CALLOUT, CHROME_ICONS, CHROME_ICON_ATTRS, CHROME_ICON_FILLED, CHROME_ICON_STROKE, CHROME_ICON_VIEWBOX, CIRCLE, COLOR_FIELD, COMMENT, CROSS_LINE, CURVE, CYCLE_PALETTE, CYCLIC_LINES, type ClipboardPort, DATE_RANGE, DEFAULT_FIB, DEFAULT_FIB_FAN, DEFAULT_FIB_TIME_ZONE, DEFAULT_GANN_BOX, DEFAULT_GANN_FAN, DOUBLE_CURVE, DRAWING_CLIPBOARD_KEY, DRAWING_CLIPBOARD_VERSION, DRAWING_STATE_VERSION, DRAWING_TOOL_ICONS, DRAW_TIER, type DrawContext, type Drawing, type DrawingChangeKind, type DrawingChartHost, DrawingClipboard, type DrawingClipboardOptions, DrawingController, type DrawingControllerOptions, type DrawingEditOptions, type DrawingGroup, type DrawingInput, type DrawingKeyAction, type DrawingKeyContext, type DrawingKeyEvent, DrawingLayer, type DrawingLayerOrder, type DrawingLinkChart, type DrawingLinkContext, type DrawingLinkContextSource, DrawingLinkGroup, type DrawingLinkOptions, type DrawingPatch, type DrawingPoint, type DrawingPointerKind, type DrawingPolicy, type DrawingStyle, type DrawingText, type DrawingTool, type DrawingValueContext, type DrawingsDocument, ELLIPSE, EXTENDED_LINE, EXTEND_FIELDS, type ExpandContext, FIB_CHANNEL, FIB_EXTENSION, FIB_FAN, FIB_RETRACEMENT, FIB_TIME_ZONE, FILL_FIELDS, FIXED_RANGE_VOLUME_PROFILE, FLAG_MARK, FONT_FIELDS, FONT_OPTIONS, FORECAST, type FibLevel, type FieldGroup, type FieldKind, type FixedRangeVolumeProfileOptions, type FixedRangeVolumeProfileResult, GANN_BOX, GANN_FAN, HIGHLIGHTER, HORIZONTAL_LINE, HORIZONTAL_RAY, type HitContext, ICON_ATTRS, ICON_STROKE, ICON_SYMBOL_PREFIX, ICON_VIEWBOX, type IconAttrs, type IconSvgOptions, LEVEL_FIELDS, LEVEL_NEUTRAL, LINE_FIELDS, LINE_STYLE_FIELD, LINE_STYLE_OPTIONS, LINE_WIDTH_FIELD, LONG_POSITION, MEASURE, type MagnetMode, NOTE, NUDGE_STEP_PX, NUDGE_STEP_SHIFT_PX, PARALLEL_CHANNEL, PATH, PATTERN_DRAWING_TOOLS, PLATE_TEXT_FIELDS, POLYLINE, PRICE_LABEL, PRICE_NOTE, PRICE_RANGE, RAY, RECTANGLE, ROTATED_RECTANGLE, SHAPE_TEXT_FIELDS, SHORT_POSITION, SHOW_LABELS_FIELD, SIGNPOST, SINE_LINE, type ScreenPoint, type SettingsField, type SettingsSchema, type ShortcutEvent, TABLE, TEXT, TEXT_FIELDS, TEXT_POSITION_OPTIONS, TEXT_VALUE_FIELD, TIME_CYCLES, TREND_LINE, TRIANGLE, type ToolCursorOptions, VALIGN_OPTIONS, VERTICAL_LINE, anchoredVwapAnalysis, applyDrawingSettings, boundsOf, catmullRom, chromeIcon, chromeIconIds, chromeIconSvg, clearMemoryClipboard, cloneDrawing, cloneLevels, coerceSettingValue, composeSettings, createDrawingLinkGroup, cycleColor, decodeClipboardPayload, distToEllipse, distToLine, distToPolyline, distToRect, distToSegment, drawingSettingsSchema, drawingShortcuts, drawingToolIcon, drawingToolIconIds, encodeClipboardPayload, extendSegment, fixedRangeVolumeProfileAnalysis, formatRatio, gannLabel, getDrawingTool, hasDrawingTool, iconSprite, iconSvg, iconUse, keyToDrawingAction, levelColor, matchDrawingShortcut, migrateDrawings, pressureWidth, rdpSimplify, readDrawingSetting, readDrawingSettings, rectOf, registerBuiltinDrawingTools, registerDrawingTool, registeredDrawingTools, sanitizeDrawing, sortByZIndex, systemClipboard, toolCursor };