@trdrs/quickcharts 4.0.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/README.md +325 -79
  3. package/dist/adapters/rest.d.ts +4 -4
  4. package/dist/adapters/rest.js +1 -1
  5. package/dist/adapters/rest.js.map +1 -1
  6. package/dist/ar-TG26ZCCE.js +1322 -0
  7. package/dist/ar-TG26ZCCE.js.map +1 -0
  8. package/dist/{assets-Gg-UquDA.d.ts → assets-DF3nozNV.d.ts} +167 -18
  9. package/dist/ca_ES-WJWH5BXU.js +1322 -0
  10. package/dist/ca_ES-WJWH5BXU.js.map +1 -0
  11. package/dist/{chunk-D3S26ARP.js → chunk-EXJAJZTN.js} +1 -1
  12. package/dist/chunk-EXJAJZTN.js.map +1 -0
  13. package/dist/{chunk-C4MEVUNK.js → chunk-Q3DCXQDW.js} +992 -284
  14. package/dist/chunk-Q3DCXQDW.js.map +1 -0
  15. package/dist/de-SUDTTPQ3.js +1322 -0
  16. package/dist/de-SUDTTPQ3.js.map +1 -0
  17. package/dist/drawings.d.ts +18 -7
  18. package/dist/drawings.js +7 -1
  19. package/dist/es-FOBXUK2X.js +1322 -0
  20. package/dist/es-FOBXUK2X.js.map +1 -0
  21. package/dist/feature-manifest.json +105 -9
  22. package/dist/fr-7KQC4IJH.js +1322 -0
  23. package/dist/fr-7KQC4IJH.js.map +1 -0
  24. package/dist/he_IL-2T6XBA6D.js +1322 -0
  25. package/dist/he_IL-2T6XBA6D.js.map +1 -0
  26. package/dist/id_ID-SI25N57C.js +1322 -0
  27. package/dist/id_ID-SI25N57C.js.map +1 -0
  28. package/dist/index.d.ts +952 -176
  29. package/dist/index.js +11905 -7472
  30. package/dist/index.js.map +1 -1
  31. package/dist/it-PMSTWZDL.js +1322 -0
  32. package/dist/it-PMSTWZDL.js.map +1 -0
  33. package/dist/ja-PABDRAXV.js +1322 -0
  34. package/dist/ja-PABDRAXV.js.map +1 -0
  35. package/dist/ko-JHRYYFCV.js +1322 -0
  36. package/dist/ko-JHRYYFCV.js.map +1 -0
  37. package/dist/ms_MY-U4EE5Q4V.js +1322 -0
  38. package/dist/ms_MY-U4EE5Q4V.js.map +1 -0
  39. package/dist/pl-4HCG3T3J.js +1322 -0
  40. package/dist/pl-4HCG3T3J.js.map +1 -0
  41. package/dist/pt-6LTNN653.js +1322 -0
  42. package/dist/pt-6LTNN653.js.map +1 -0
  43. package/dist/quickcharts.css +627 -208
  44. package/dist/{resources-Le6flcEn.d.ts → resources-XglhzzW0.d.ts} +5 -3
  45. package/dist/rest-openapi.json +16 -11
  46. package/dist/ru-LFHCSDBV.js +1322 -0
  47. package/dist/ru-LFHCSDBV.js.map +1 -0
  48. package/dist/sv-4XVUXYK6.js +1322 -0
  49. package/dist/sv-4XVUXYK6.js.map +1 -0
  50. package/dist/{symbols-6V22WaRX.d.ts → symbols-DdHLHQFC.d.ts} +77 -8
  51. package/dist/symbols.d.ts +1 -1
  52. package/dist/th-WM2PVA24.js +1322 -0
  53. package/dist/th-WM2PVA24.js.map +1 -0
  54. package/dist/theme-manifest.json +105 -21
  55. package/dist/tr-HMAZXSJM.js +1322 -0
  56. package/dist/tr-HMAZXSJM.js.map +1 -0
  57. package/dist/vi-ITFBV5B7.js +1322 -0
  58. package/dist/vi-ITFBV5B7.js.map +1 -0
  59. package/dist/zh-ZGU2RI7K.js +1322 -0
  60. package/dist/zh-ZGU2RI7K.js.map +1 -0
  61. package/dist/zh_TW-PNINSQ6F.js +1322 -0
  62. package/dist/zh_TW-PNINSQ6F.js.map +1 -0
  63. package/package.json +1 -1
  64. package/dist/ar-FXZ5QJUR.js +0 -1178
  65. package/dist/ar-FXZ5QJUR.js.map +0 -1
  66. package/dist/ca_ES-3WVKFBIF.js +0 -1178
  67. package/dist/ca_ES-3WVKFBIF.js.map +0 -1
  68. package/dist/chunk-C4MEVUNK.js.map +0 -1
  69. package/dist/chunk-D3S26ARP.js.map +0 -1
  70. package/dist/de-V72Q3OI7.js +0 -1178
  71. package/dist/de-V72Q3OI7.js.map +0 -1
  72. package/dist/es-CNQ3OJ6E.js +0 -1178
  73. package/dist/es-CNQ3OJ6E.js.map +0 -1
  74. package/dist/fr-5WQBCGR4.js +0 -1178
  75. package/dist/fr-5WQBCGR4.js.map +0 -1
  76. package/dist/he_IL-WXKA77UE.js +0 -1178
  77. package/dist/he_IL-WXKA77UE.js.map +0 -1
  78. package/dist/id_ID-XPBAYSWK.js +0 -1178
  79. package/dist/id_ID-XPBAYSWK.js.map +0 -1
  80. package/dist/it-DR2EBNWX.js +0 -1178
  81. package/dist/it-DR2EBNWX.js.map +0 -1
  82. package/dist/ja-URB2CPXO.js +0 -1178
  83. package/dist/ja-URB2CPXO.js.map +0 -1
  84. package/dist/ko-OE47NYVP.js +0 -1178
  85. package/dist/ko-OE47NYVP.js.map +0 -1
  86. package/dist/ms_MY-BKCFRK2K.js +0 -1178
  87. package/dist/ms_MY-BKCFRK2K.js.map +0 -1
  88. package/dist/pl-3IKQRG3Y.js +0 -1178
  89. package/dist/pl-3IKQRG3Y.js.map +0 -1
  90. package/dist/pt-PZHV5EZE.js +0 -1178
  91. package/dist/pt-PZHV5EZE.js.map +0 -1
  92. package/dist/ru-BAL6RQUV.js +0 -1178
  93. package/dist/ru-BAL6RQUV.js.map +0 -1
  94. package/dist/sv-Y6GBGDHM.js +0 -1178
  95. package/dist/sv-Y6GBGDHM.js.map +0 -1
  96. package/dist/th-MAMLMZKU.js +0 -1178
  97. package/dist/th-MAMLMZKU.js.map +0 -1
  98. package/dist/tr-3LQHINUZ.js +0 -1178
  99. package/dist/tr-3LQHINUZ.js.map +0 -1
  100. package/dist/vi-GR4PSDKP.js +0 -1178
  101. package/dist/vi-GR4PSDKP.js.map +0 -1
  102. package/dist/zh-2RJ5VNAI.js +0 -1178
  103. package/dist/zh-2RJ5VNAI.js.map +0 -1
  104. package/dist/zh_TW-XEBSACHU.js +0 -1178
  105. package/dist/zh_TW-XEBSACHU.js.map +0 -1
package/README.md CHANGED
@@ -43,15 +43,35 @@ await widget.ready()
43
43
  Implement `ChartDatafeed` (see `datafeed.ts`). Required methods: `search`, `resolve`, `history`,
44
44
  `subscribeBars`. Optional: `serverTime` (countdown skew correction) and `config` (a feed-level
45
45
  capability declaration, described [below](#capability-declaration-config-optional)). The datafeed serves
46
- symbol metadata, bars and bar updates; your host serves quotes (last, change, volume, the top of
47
- book) to its own consumers from its own source.
46
+ symbol metadata, bars and bar updates.
48
47
 
49
- When `appearance.countdown` is enabled, a live streaming time bar replaces the native last-value
50
- label with one price-and-time label. The price uses the resolved symbol formatter. Replay, stale,
48
+ `subscribePrices(symbol, { onPrices })`, also optional, serves the prices one symbol stands at
49
+ (`SymbolPrices`: `bid`, `ask`, `last`, and `previousClose` or the `change` since it) and returns the
50
+ unsubscribe. A delivery may carry only what moved; a field it leaves out keeps its last value. The
51
+ chart subscribes only while a setting draws the prices: the bid and ask labels and lines, the
52
+ legend's change since the previous close, the previous close's label and line, and the percentage
53
+ under the last value. Without the port the bid and ask draw nothing, and the previous close is the
54
+ last regular-hours close of the trading day before the newest bar's, read off the bars.
55
+
56
+ ```ts
57
+ import type { ChartDatafeed, SymbolPrices } from '@trdrs/quickcharts'
58
+ declare const feed: ChartDatafeed
59
+ declare function onBook(symbol: string, push: (prices: SymbolPrices) => void): () => void
60
+
61
+ const withPrices: ChartDatafeed = {
62
+ ...feed,
63
+ subscribePrices: (symbol, { onPrices }) => onBook(symbol, onPrices),
64
+ }
65
+ ```
66
+
67
+ While the `priceLabels.countdown` setting is on, as it is by default, a live streaming time bar
68
+ replaces the native last-value label with one price-and-time label. The price uses the resolved symbol formatter. Replay, stale,
51
69
  delayed or end-of-day data, tick bars, closed declared sessions and missing bars retain the native
52
70
  price label. `serverTime` corrects clock skew when available; absence or failure uses the client
53
71
  clock. Bar opens define fixed and calendar alignment, and declared session facts shorten only a
54
- bar they actually close.
72
+ bar they actually close. Under `priceLabels.symbolValueMode: 'priceAndPercent'` the same label
73
+ writes the change since the previous session's close under the price, two decimals and its sign,
74
+ with the countdown beneath that; each line is 14px below the one before at the 12px text size.
55
75
 
56
76
  `resolve` answers with `SymbolInfo`, the symbology contract ([Symbology](#symbology)): the
57
77
  symbol's identity (`ticker`, `name`, `description`), venue and type (`exchange`,
@@ -631,8 +651,8 @@ A layout also refuses writes while any child chart is not saving. A complete lay
631
651
  its children only after the whole content and layout binding commit, and detaches their previous
632
652
  standalone chart bindings. Partial content and copy saves do not establish recovery.
633
653
 
634
- A saved chart carries its symbol, timeframe, style, scale, appearance, comparisons, extension
635
- state, combined-mode drawings, and every indicator instance. Indicator definitions remain code in
654
+ A saved chart carries its symbol, timeframe, style, scale, the chart settings a viewer chose,
655
+ comparisons, extension state, combined-mode drawings, and every indicator instance. Indicator definitions remain code in
636
656
  your widget. The blob names each definition by `manifest.id` and carries the instance id, explicit
637
657
  inputs, color, title, overrides and hidden state. Loading replaces the full instance list, so the
638
658
  constructor's `indicators` are seeds only for a chart with no saved content. Definitions resolve
@@ -640,12 +660,14 @@ against the widget's built-ins and the host definitions that widget has carried.
640
660
  access-denied definition is left out with one counted notice while the remaining content lands. A
641
661
  definition of your own survives a save only when its manifest declares an `id`.
642
662
 
643
- The opaque version-4 reader validates the whole indicator list before changing a chart or layout.
663
+ The opaque version-5 reader validates the whole indicator list before changing a chart or layout.
644
664
  Missing lists, duplicate instance ids and malformed inputs, titles, colors or overrides refuse the
645
665
  body as `invalid`; values are not silently filtered or normalized. A load that omits a definition
646
666
  because it is unavailable is partial and cannot clear an earlier `notSaving()` state.
647
- Complete recovery requires every typed appearance leaf with a valid concrete color value and,
648
- in combined mode, a drawings array. Separate-mode chart content does not need that array.
667
+ Complete recovery requires a `settings` record whose every leaf holds a value its leaf can hold,
668
+ every color a concrete one the renderer paints, and, in combined mode, a drawings array.
669
+ Separate-mode chart content does not need that array. A version-4 blob reads its `appearance`
670
+ record into the settings sections its leaves now live in.
649
671
 
650
672
  Hydration and rollback do not emit `saveNeeded` for their own storage writes. An event already
651
673
  queued by a viewer's edit is preserved. A late save result does not replace a newer load's binding;
@@ -702,7 +724,8 @@ tile.
702
724
  ### Templates
703
725
 
704
726
  `adapter.templates(kind)` is a store per kind: `'indicator'` for indicator settings, `'drawing'` for
705
- a drawing tool's look, and `'palette'` for chart appearance. A drawing template carries the `tool`
727
+ a drawing tool's look, `'chart'` for the chart settings a viewer saves from the settings dialog's
728
+ Template menu, and `'palette'`, which the chart itself does not write. A drawing template carries the `tool`
706
729
  it belongs to, because a trend-line template means nothing on a rectangle. Their content is opaque,
707
730
  like a chart's.
708
731
 
@@ -1343,7 +1366,14 @@ function toolbar(widget: ChartWidget): void {
1343
1366
 
1344
1367
  A command whose one id spans many subjects declares `refuses(arg)` beside `available()`: an
1345
1368
  argument your access policy turns away answers `denied` before availability is asked, which is how
1346
- `chart.drawings.arm` refuses a tool. `onChange` fires when the registered set or a shortcut changes,
1369
+ `chart.drawings.arm` refuses a tool. `available(arg)` answers for the subject an argument names, and
1370
+ `commands.available(id, arg)` asks it: `chart.panes.moveUp` and `chart.panes.moveDown` take a pane's
1371
+ index, the top pane 0, so `commands.available('chart.panes.moveUp', 0)` is false, as is a move down
1372
+ for the bottom pane, and asked with no argument either says whether any pane could move.
1373
+ `chart.drawings.placeHorizontalLine` run with no argument places its line at the crosshair's price,
1374
+ so asked with none it answers for that price, and is false while the crosshair is off the main
1375
+ pane's plot. A refused
1376
+ argument is unavailable to ask about. `onChange` fires when the registered set or a shortcut changes,
1347
1377
  and when `widget.refreshAccess()` says your policy may answer differently. A refusal is a value,
1348
1378
  never a throw: `ok`, `unavailable`,
1349
1379
  `denied`, `unknown`, or `failed` with the
@@ -1525,8 +1555,8 @@ container.before(undo, search)
1525
1555
  ```
1526
1556
 
1527
1557
  The chart's own dialogs still open from their commands while its bars are hidden.
1528
- `chart.symbol.search`, `chart.indicators.open`, `chart.compare.open` and `widget.layout.open` raise
1529
- them in the widget's layer on the document, and `widget.layout.save` asks a layout that was never
1558
+ `chart.symbol.search`, `chart.indicators.open`, `chart.compare.open`, `chart.settings.open` and
1559
+ `widget.layout.open` raise them in the widget's layer on the document, and `widget.layout.save` asks a layout that was never
1530
1560
  saved for its name. Hide a dialog in `ui` when your interface provides its own.
1531
1561
 
1532
1562
  The library draws its own controls over its own renderer and keeps its internal components to
@@ -1554,7 +1584,8 @@ async function shareable(widget: ChartWidget): Promise<void> {
1554
1584
 
1555
1585
  `capture()` composes the chart, or every chart of a layout, into one PNG under a header carrying the
1556
1586
  identity and the attribution you configured through `image`. Nothing is uploaded, shared or stored:
1557
- you receive a blob and decide.
1587
+ you receive a blob and decide. `copy()` and `download()` take the same image, and an extension takes
1588
+ part in each through `contributeCapture` (see Extensions).
1558
1589
 
1559
1590
  ### Indicator picker content
1560
1591
 
@@ -1639,12 +1670,15 @@ chrome is painted from it and renders nothing without it.
1639
1670
  the 55 arrangements and the five sync switches); the saved-layouts menu, shown with a layouts
1640
1671
  store, saves, copies, renames, opens and deletes layouts through `saveLoad.layouts` and marks
1641
1672
  unsaved changes, with an autosave switch and Download chart data.
1642
- Chart settings edits appearance, grid and session shading, the price-scale mode and the theme
1643
- mode, and its Reset defaults row runs `chart.appearance.reset`, which drops the viewer's own
1644
- appearance edits and returns the price scale to normal so the chart reads as the theme and your
1645
- constructor options paint it. `ui: { topBar: { settings: { theme: false } } }` removes its Theme
1646
- section and leaves the rest of the menu, for a host that offers the theme choice in its own
1647
- settings; the widget's theme API and theme commands are untouched. Fullscreen and the image menu (Download image, Copy image where the browser can, and
1673
+ Chart settings opens the chart settings dialog: the Symbol, Status line, Scales and lines,
1674
+ Canvas and Events pages of the active chart's settings, and any page an extension adds. Every
1675
+ change previews on the chart; Cancel, the close and Escape put back the viewer's settings, the
1676
+ price-scale mode and the timezone as they were, and Ok keeps them. Its Template menu applies the
1677
+ defaults (`chart.settings.reset`), saves the viewer's settings under a name in
1678
+ `saveLoad.templates('chart')`, and lists the saved ones. `chart.settings.open` opens the same
1679
+ dialog, on the page `{ page }` names, whether or not the bar shows the gear. The theme mode is not
1680
+ among its pages: `ui: { topBar: { settings: { theme: false } } }` stays accepted and changes
1681
+ nothing, and the widget's theme API and theme commands are untouched. Fullscreen and the image menu (Download image, Copy image where the browser can, and
1648
1682
  Download chart data when there is no layouts store) close the bar.
1649
1683
  - **The bottom bar.** The range presets the chart offers, the clock in the display zone with the
1650
1684
  list of the timezones it offers (UTC and the exchange choice first), and the session view for a
@@ -1662,8 +1696,8 @@ chrome is painted from it and renders nothing without it.
1662
1696
  image that could not be copied, or a save the store refused.
1663
1697
 
1664
1698
  Undo and redo step through the chart's own content: the symbol, the timeframe, the style, the price
1665
- scale and whether it frames itself, the appearance a viewer authored, the comparisons, the
1666
- indicators and the drawings. A step is one reading of that content, so a step back puts the whole
1699
+ scale and whether it frames itself, the chart settings a viewer authored, the comparisons, the
1700
+ indicators, the order of the panes and the drawings. A step is one reading of that content, so a step back puts the whole
1667
1701
  reading back rather than reversing a single verb, and the two controls name the change they would
1668
1702
  move. Each chart keeps its own last 100 steps for as long as it is mounted. `chart.history.undo`
1669
1703
  and `chart.history.redo` are the verbs, `chart.history` on the handle reads the two stacks, the
@@ -1802,7 +1836,9 @@ as a `TypeError` when the widget is created.
1802
1836
  An id names what a glyph means, not one control. `settings` is the top bar's settings button, a
1803
1837
  indicator's gear in the legend and the drawing settings bar's gear, so one drawing stands in all
1804
1838
  three. Drawing tools take `tool.<type>`, chart styles `style.<style>` and layout arrangements
1805
- `layout.<code>`, from the registries the rest of the API uses.
1839
+ `layout.<code>`, from the registries the rest of the API uses. The glyphs a time-scale mark wears in
1840
+ its ring take `mark.<shape>`, named for their shapes, since what a mark means is yours (see Neutral
1841
+ marks).
1806
1842
 
1807
1843
  ```ts
1808
1844
  import { CHART_ICON_IDS, createChart, createUdfDatafeed, type ChartIconFactory, type ChartIcons } from '@trdrs/quickcharts'
@@ -1864,7 +1900,8 @@ something other than an `<svg>` element, or answers an element already in the do
1864
1900
  before costs that one glyph its artwork. The control draws the chart's own glyph, and
1865
1901
  `widget.chrome.iconDiagnostics()` records the icon's first failure.
1866
1902
 
1867
- `icons` covers the chart's own controls. The emoji, sticker and icon glyphs a drawing places come
1903
+ `icons` covers the chart's own controls and the glyphs of the time-scale marks, whose ring the chart
1904
+ draws around your drawing in the mark's color. The emoji, sticker and icon glyphs a drawing places come
1868
1905
  through the asset port, and a chart extension draws its rows and layers from the
1869
1906
  `ChartExtensionIcon` descriptors it contributes. The product's mark in the plot's corner is the
1870
1907
  chart's own artwork, outside `icons`. `mountContextMenu`, `openSymbolSearch`, `mountSymbolSearch`,
@@ -1874,13 +1911,43 @@ you mount without a widget.
1874
1911
  ### Neutral marks
1875
1912
 
1876
1913
  Serve `marks` and `timescaleMarks` from your datafeed and the chart draws them. A mark is a note
1877
- about a moment: its color is a semantic theme role rather than a literal, its words are yours, and
1878
- the chart draws it as given.
1879
-
1880
- ```ts
1881
- import { type BarMark, type ChartDatafeed } from '@trdrs/quickcharts'
1882
-
1883
- const withMarks: Pick<ChartDatafeed, 'marks'> = {
1914
+ about a moment: its words are yours, and the chart draws it as given. Its color stays readable in
1915
+ both modes: a semantic theme role (`neutral`, `up`, `down`, `info`, `warning`, `positive` or
1916
+ `negative`) that the mode resolves, or a `MarkColorPair`, `{ light, dark }`, one CSS color for each
1917
+ mode, of which the chart wears the one for the mode in effect. A single color for both modes is not
1918
+ a mark color, since a color chosen for one background can vanish on the other, and a pair with a
1919
+ side the chart cannot paint draws in the `neutral` role.
1920
+
1921
+ A bar mark (`BarMark`) sits on its bar, above or below it, with a letter or two. A time-scale mark
1922
+ (`TimescaleMark`) stands at the foot of the pane:
1923
+
1924
+ - **Its look.** Without `icon` it is a small dot. With one it is a 21px ring in its color, its foot
1925
+ just above the time scale and the chart's background inside, around a glyph of the icon catalog:
1926
+ `mark.bolt`, `mark.flag`, `mark.star`, `mark.clock` or `mark.exclamation`. Your `icons` drawing for
1927
+ one of those ids stands in the ring in place of the chart's own.
1928
+ - **Its line.** `line: { style, color, width }` draws a line through the whole pane at its time,
1929
+ always and under the bars: `solid`, `dashed` (5px drawn, 6px clear) or `dotted` (1px drawn, 4px
1930
+ clear). `color` takes a theme role or a light and dark pair, as a mark's does, and a line without
1931
+ one wears its mark's color; `width` is 1 to 4 CSS pixels, and 1 without it.
1932
+ - **Hovering it.** The pointer over a mark, or a finger or a pen pressing it, runs a dashed line in
1933
+ its color from the top of the pane down to it and tints its ring. A mark with a `label` shows it
1934
+ in a tooltip above the mark, and a mark without one shows the line alone. A press anywhere else
1935
+ lets go of a pressed mark.
1936
+ - **Its time.** A mark stands on the bar whose bucket holds its time, on the next bar when the time
1937
+ falls between bars (a session gap, a weekend), and after the last bar at the slot the time falls
1938
+ in, counting the timeframe's bar interval on from the last bar. Bar replay draws none after the
1939
+ last bar it shows, and a mark outside the view draws nothing.
1940
+
1941
+ The chart asks `marks` for the loaded bars' window (epoch seconds, inclusive of both ends) and
1942
+ `timescaleMarks` for the same window run on past the last bar while the view shows the empty space
1943
+ after it: to the end of the view, and as many bar intervals again as the view spans. It asks again
1944
+ on each page of history, and once the view comes to rest past what it asked for. A mark at a time
1945
+ to come therefore stands in that space as soon as the view reaches it.
1946
+
1947
+ ```ts
1948
+ import { type BarMark, type ChartDatafeed, type TimescaleMark } from '@trdrs/quickcharts'
1949
+
1950
+ const withMarks: Pick<ChartDatafeed, 'marks' | 'timescaleMarks'> = {
1884
1951
  async marks(symbol, from, to): Promise<readonly BarMark[]> {
1885
1952
  const rows = await myBackend.events(symbol, from, to)
1886
1953
  return rows.map((row: { id: string; at: number; headline: string }) => ({
@@ -1891,11 +1958,25 @@ const withMarks: Pick<ChartDatafeed, 'marks'> = {
1891
1958
  label: row.headline,
1892
1959
  }))
1893
1960
  },
1961
+ async timescaleMarks(symbol, from, to): Promise<readonly TimescaleMark[]> {
1962
+ const rows = await myBackend.moments(symbol, from, to)
1963
+ return rows.map((row: { id: string; at: number; headline: string }) => ({
1964
+ id: row.id,
1965
+ time: row.at,
1966
+ color: { light: '#7b1fa2', dark: '#ab47bc' },
1967
+ icon: 'mark.bolt',
1968
+ label: row.headline,
1969
+ }))
1970
+ },
1894
1971
  }
1895
1972
  void withMarks
1896
1973
  ```
1897
1974
 
1898
- `marks: false` draws none, even from a feed that serves them.
1975
+ When a setting of your own changes what your feed serves, `chart.refreshMarks()` asks for both
1976
+ families again at once and draws the answer, so the change shows while your control is still open.
1977
+ The `chart.marks.refresh` command does the same from a menu, a shortcut or an automation adapter,
1978
+ and an extension calls `ctx.refreshMarks()`. `marks: false` draws none, even from a feed that serves
1979
+ them, and asks for none.
1899
1980
 
1900
1981
  ### The rest of the widget
1901
1982
 
@@ -1904,8 +1985,10 @@ void withMarks
1904
1985
  - **The price scale** (shown by default; `ui: { priceScale: false }` hides it). Without it the bars
1905
1986
  span the chart's whole width, and the last price draws no line across the plot and no label, since
1906
1987
  nothing on screen points at one.
1907
- - **Session bands** (on by default; `features.sessions: false` opts out). Every stretch outside
1908
- regular hours shades under the bars, driven by the session model built from the symbol's own
1988
+ - **Session bands** (`features.sessions: false` opts out). While the chart's trading hours show
1989
+ the stretches outside regular hours (`symbol.session` of `extended` or `allHours`), each shades
1990
+ under the bars in the color the settings give it, and the `events.sessionBreaks` setting draws a
1991
+ line at each trading day's start, driven by the session model built from the symbol's own
1909
1992
  `session`, `sessionHolidays`, `corrections` and `subsessions` in `resolve()`'s answer (see
1910
1993
  Timezones and sessions). A continuous market never bands; intraday only; an UNRESOLVED symbol
1911
1994
  never bands, which is the honest default rather than a coerced one, and it is enforced: the
@@ -1913,7 +1996,10 @@ void withMarks
1913
1996
  outside a `resolve()`, or supplies its own, calls the primitive's `refresh()` when it does; the
1914
1997
  chart does not invalidate the pane on a getter's value changing.
1915
1998
  - **A legend** (on by default; `ui: { legend: false }` removes it). The header shows the resolved
1916
- display name, venue, timeframe, OHLC and change against the previous painted close. Prices use
1999
+ name (the feed's description, its symbol, or both, as `statusLine.titleSource` says), venue,
2000
+ timeframe, OHLC, change against the previous painted close and, when `statusLine.volume` asks,
2001
+ the bar's volume; the `statusLine` settings show or hide each part over a backdrop of the
2002
+ chart's background. Prices use
1917
2003
  the symbol's formatter. Hover selects a bar; leaving restores the latest painted reading. Replay
1918
2004
  does not expose bars beyond its cursor. Missing metadata leaves the supplied symbol unchanged.
1919
2005
  Legend and search rows wear your `symbolMark`, or a decorative monogram without one.
@@ -1935,11 +2021,13 @@ void withMarks
1935
2021
  render can import it.
1936
2022
  A host with a language the package does not ship registers it through `createChartI18n(code,
1937
2023
  { locales })`: a `ChartCustomLocale` names the code, tag, direction and endonym and supplies the
1938
- dictionary chunk, a `ChartDictionary` typed against the English catalog so it cannot miss a key or
1939
- flatten a plural. A code or tag the built-in inventory already holds is refused. A dictionary that
1940
- arrives from data and misses a key reads English for that key and reports it to the `onMissing`
1941
- option. A host can also supply `i18n: ChartI18n` of its own to own its codes, tags, dictionaries,
1942
- loading, and fallback outright.
2024
+ dictionary chunk, a `ChartDictionary` typed against the English catalog so it cannot flatten a
2025
+ plural or miss a key of the major's first release. A key the catalog gains within a major is
2026
+ optional in your dictionary: the chart reads it in English until your dictionary carries it and
2027
+ reports it to the `onMissing` option, and the next major makes it required. A code or tag the
2028
+ built-in inventory already holds is refused. A dictionary that arrives from data and misses a key
2029
+ reads English for that key and reports it to `onMissing` too. A host can also supply
2030
+ `i18n: ChartI18n` of its own to own its codes, tags, dictionaries, loading, and fallback outright.
1943
2031
  Every piece of the chart's own chrome speaks the language; symbols, prices and anything the
1944
2032
  datafeed says are data and pass through untranslated. A host composing the chrome modules itself
1945
2033
  hands them a `ChartI18n` from `createChartI18n(code)` and reads the chart's words for drawing
@@ -2154,6 +2242,12 @@ between `quickcharts.chart` and `host`. Inline styles the chart writes for measu
2154
2242
  outrank any stylesheet rule, and a rule marked `!important` reverses layer order, so neither is a
2155
2243
  way to restyle the chart.
2156
2244
 
2245
+ The chart states its box model and every inherited text value its measurements are built on at
2246
+ each of its roots, among them the widget's root and its layer on the document body, where its
2247
+ dialogs, menus and panels stand. An unlayered rule of yours that sizes every element by its border
2248
+ box, or the font, line height, weight, letter spacing or alignment you set on your page's body,
2249
+ leaves each of those surfaces as it is measured.
2250
+
2157
2251
  ### Supported styling hooks
2158
2252
 
2159
2253
  A hook is a block-level surface you may write a rule against. Each announces its state through an
@@ -2195,30 +2289,111 @@ If a value you supply is not valid for the role it is written for, the chart kee
2195
2289
  value and reports it. Read `theme.diagnostics()` for the role, the code and a sentence naming the
2196
2290
  problem. Configuration errors never reach a render.
2197
2291
 
2198
- ### Theme and appearance are two ladders
2292
+ ### Theme and chart settings are two ladders
2199
2293
 
2200
- The theme palette is the broad brand surface. Chart appearance is the specific one: series colors,
2201
- candle anatomy, grid visibility and indicator visuals in `ChartOverrides.appearance`. Where both
2202
- could affect the same pixel, appearance wins.
2294
+ The theme palette is the broad brand surface. The chart settings are the specific one: one typed
2295
+ tree, `ChartSettings`, of every property a viewer tunes on a chart, from the candle anatomy and the
2296
+ line styles to the status line, the price labels, the time scale and the canvas. Where both could
2297
+ affect the same pixel, the settings win.
2203
2298
 
2204
2299
  Theme palette precedence, lowest first:
2205
2300
 
2206
2301
  1. the built-in palette for the selected mode;
2207
2302
  2. your custom palette for that mode.
2208
2303
 
2209
- Chart appearance precedence, lowest first:
2304
+ Chart settings precedence, lowest first:
2210
2305
 
2211
- 1. the built-in appearance for the selected mode;
2212
- 2. the constructor's `appearance` partial;
2213
- 3. restored viewer appearance;
2214
- 4. runtime `chart.applyAppearance` patches.
2306
+ 1. the theme's factory values for the mode in effect, `chartSettingsDefaults(theme)`;
2307
+ 2. the constructor's `settings` partial;
2308
+ 3. the viewer's own leaves: a restored chart, the settings dialog, and `chart.applySettings`.
2215
2309
 
2216
- Resetting custom palettes returns the chart to the built-in mode and leaves saved chart appearance
2310
+ Resetting custom palettes returns the chart to the built-in mode and leaves saved chart settings
2217
2311
  alone.
2218
2312
 
2219
- A saved chart carries the appearance leaves a viewer chose and no others, so a chart nobody restyled
2220
- follows whatever theme the host gives it on the next load, and one whose owner picked candle colors
2221
- opens in those colors on any theme.
2313
+ A saved chart carries the settings a viewer chose and no others, so a chart nobody restyled follows
2314
+ whatever theme the host gives it on the next load, and one whose owner picked candle colors opens in
2315
+ those colors on any theme.
2316
+
2317
+ ## Chart settings
2318
+
2319
+ `chart.settings()` answers the effective tree; `chart.applySettings(partial)` layers a partial over
2320
+ the viewer's own leaves, ignoring a leaf the tree does not have or a value its leaf cannot hold; and
2321
+ `chart.resetSettings()` drops the viewer's leaves and returns the price scale to normal. The
2322
+ commands are `chart.settings.apply` with `{ settings }` and `chart.settings.reset`. A change is a
2323
+ content change: it marks the chart for saving and is a step of its history.
2324
+
2325
+ ```ts
2326
+ import type { ChartWidget } from '@trdrs/quickcharts'
2327
+ declare const widget: ChartWidget
2328
+
2329
+ const chart = widget.activeChart()
2330
+ chart.applySettings({ candles: { upColor: '#26a69a' }, canvas: { marginRight: 20 } })
2331
+ chart.settings().candles.upColor // '#26a69a'
2332
+ widget.commands.execute('chart.settings.apply', { settings: { statusLine: { volume: true } } })
2333
+ chart.resetSettings()
2334
+ ```
2335
+
2336
+ The factory values change with the mode in six canvas colors only: the background, both grids,
2337
+ the watermark, and the scale text and lines, each read from its theme role. Every other color is
2338
+ the same in both modes, in `CHART_FACTORY_COLORS`. The sections and what stands behind them:
2339
+
2340
+ - `candles`, `hollowCandles` and `bars`: the body, borders and wick, each on its own switch and in
2341
+ its own pair; coloring by the previous bar's close, written as each bar's own colors; thin bars
2342
+ and the open tick (`hlcBars`).
2343
+ - `line`, `stepLine`, `area` and `baseline`: the price source each bar is drawn from (its open,
2344
+ high or low, its close, or one of their averages), the line's color, style and width, the fills,
2345
+ and the baseline's level as a percentage of the pane. A line or step line in a gradient (the factory
2346
+ look) is stroked from its top color at the highest price on screen to its bottom color at the
2347
+ lowest, and a baseline whose halves differ in width strokes each at its own.
2348
+ - `symbol`: the trading hours (`session`: `regular`, `extended` or `allHours`), which are the
2349
+ chart's subsession, with the shading of the pre-market, post-market and overnight stretches; and
2350
+ the precision every price is written at, through the chart's one formatter.
2351
+ - `statusLine`: the legend's mark, its title and whether the title is the name, the symbol or both,
2352
+ the bar's values, its change and its volume, the latest price's change since the previous
2353
+ session's close (`lastDayChange`) in its direction's color, the indicator rows' titles, inputs
2354
+ and values, and a backdrop of the chart's background at an opacity.
2355
+ - `priceLabels`: the countdown, labels kept apart, the symbol's last value, its dotted line (its
2356
+ color, null to follow the last bar, and width) and its name, and each indicator's value and name.
2357
+ The previous session's close (on an intraday chart), the high and low of the bars in view, and
2358
+ the bid and ask from the feed's prices each take a value box on the scale in their color, a tag
2359
+ beside it ("High", "Low", "Bid", "Ask"), and a dotted line across the plot, each part on its own
2360
+ switch. The high and low wear grey lines and navy labels while their color is null. On an
2361
+ intraday chart of regular hours, a newest bar in a pre-market, post-market or overnight stretch
2362
+ is left off the chart and its close is marked instead, in that stretch's label color. The plus
2363
+ button (`plusButton`) is joined to the crosshair's price label while the crosshair is on the
2364
+ main pane's plot: the renderer paints it with the label, a box as tall as the label one pixel of
2365
+ the chart short of the scale's edge, in the label's fill with a ringed plus in the label's ink. Its
2366
+ press opens the price menu under it, which holds the actions on that price alone: every `level`
2367
+ row an extension contributes, each extension's rows together in the order the extensions
2368
+ attach, and then "Draw horizontal line at" the price, which runs
2369
+ `chart.drawings.placeHorizontalLine` and prints that command's shortcut ("Alt + H" unless you
2370
+ remap it). A chart without its context menu shows no plus.
2371
+ - `priceScale`: where the scales stand (`placement`): `left` stands the main series' scale on
2372
+ the left, with its mode, its framing and every series of the main pane that shares it, and a
2373
+ comparison on a scale of its own on the right; `right` and `auto` keep it on the right, with
2374
+ such a comparison on the left. The auto-scale and logarithmic buttons (`scaleModeButtons`) stand
2375
+ at the foot of the main scale while the pointer is over it, always, or never: "A" switches the
2376
+ scale between framing itself and holding where it was dragged, and "L" between a regular and a
2377
+ logarithmic scale, each the chart's own verb. The currency and unit box (`currencyAndUnit`)
2378
+ names what the symbol's prices are in (its `currencyCode` and `unitId`) at the top of the main
2379
+ scale on the same terms, and shows nothing for a symbol that states neither. The price-to-bar
2380
+ ratio lock (`lockPriceToBarRatio`) holds the price one bar's width spans: engaging it with no
2381
+ `priceToBarRatio` of its own takes the ratio on screen into that leaf, the scale stops framing
2382
+ itself, and every zoom of the time scale rescales the price axis by the same factor about its
2383
+ middle. A scale that is not regular holds no ratio.
2384
+ - `timeScale`: the crosshair label's date format (`CHART_DATE_FORMATS`), the weekday before it, and
2385
+ a clock of 24 hours or of 12, which the axis times follow too. With `keepLeftEdge`, a timeframe
2386
+ change stands the new bars from the moment the view's left edge stood at, showing as many bar
2387
+ slots, asking the feed once for older bars when that moment lies before the first page, in place
2388
+ of fitting the loaded history.
2389
+ - `canvas`: the background, solid or a vertical gradient; each grid's switch, color and stroke; the
2390
+ crosshair's color, stroke and width; the watermark's parts and ink; the scales' text and lines;
2391
+ when the navigation buttons show; when each pane's own buttons show (`paneButtons`): at a pane's
2392
+ top right, move it up or down past its neighbour, delete it, collapse or open it, and maximize or
2393
+ restore it, through the same pane operations and move verbs the legend's rows run, the main pane
2394
+ moving too and otherwise carrying maximize alone while another pane stands; and the margins, the
2395
+ right one in bars. The order the panes stand in is saved with the chart.
2396
+ - `events`: a line at each trading day's start, in its own stroke.
2222
2397
 
2223
2398
  ## Compare
2224
2399
 
@@ -2343,8 +2518,9 @@ if (model) {
2343
2518
  parseSessionModel({ timezone: 'Etc/UTC', session: 'later' }) // null: the chart claims no session it cannot read
2344
2519
  ```
2345
2520
 
2346
- Which named session a chart displays is its subsession, a per-chart preference
2347
- with `regular` as the default. On a symbol with extended hours, `regular` filters intraday bars to
2521
+ Which named session a chart displays is its subsession, the chart's `symbol.session` setting, with
2522
+ `regular` as the factory value: `chart.subsession()` reads it and `chart.setSubsession()` writes it
2523
+ as the viewer's own. On a symbol with extended hours, `regular` filters intraday bars to
2348
2524
  regular hours and `extended` shows every bar; a symbol with one continuous session has nothing to
2349
2525
  filter.
2350
2526
 
@@ -2873,8 +3049,13 @@ out instead (see Refused controls), and a command the policy refuses answers `de
2873
3049
  toolbar as from anywhere else, including `chart.drawings.arm` for a refused tool. A tool your
2874
3050
  `drawingTools` list leaves out is not on the toolbar at all (see Offered drawing tools). An image is
2875
3051
  placed through `chart.drawings.placeImage`, from the picker or a system-clipboard paste over the
2876
- chart. Every flyout, palette and dialog a surface opens sits inside the chart root and closes with
2877
- it, and the eye and lock all announce their state through a live region. Lock all also makes Paste
3052
+ chart, and a horizontal line at a price through `chart.drawings.placeHorizontalLine` with that
3053
+ price, which asks what arming the horizontal line asks and places nothing while lock all holds.
3054
+ Run with no price, as its shortcut `Alt+KeyH` runs it, the command places the line at the
3055
+ crosshair's price while the crosshair stands on the main pane's plot, on the symbol's own price
3056
+ grid, and is unavailable anywhere else, so the key is left to your page there.
3057
+ Every flyout, palette and dialog a surface opens sits inside the chart root and closes with it, and
3058
+ the eye and lock all announce their state through a live region. Lock all also makes Paste
2878
3059
  unavailable, so a control never reports success for a drawing the layer refused.
2879
3060
 
2880
3061
  Arm the transient tools by name: `measure` draws a readout the next gesture clears, `zoom` sets
@@ -2888,8 +3069,10 @@ chosen.
2888
3069
  Selecting a drawing shows the settings bar: templates, the stroke color with its opacity, the
2889
3070
  background for tools that have one, the text color and font size for text tools, thickness and
2890
3071
  line style, the settings gear, lock, delete, and a More menu with the stacking moves, the
2891
- per-timeframe visibility presets, clone, copy and hide. Every edit persists at once and becomes the
2892
- tool's default for the next drawing of that type. The settings dialog opens from the gear with
3072
+ per-timeframe visibility presets, clone, copy and hide. A right-click on a drawing raises its own
3073
+ menu instead of the chart's: a table's edits first, then the templates, the stacking moves and the
3074
+ visibility presets as submenus, clone, copy, lock, hide, remove and the settings. Every edit persists
3075
+ at once and becomes the tool's default for the next drawing of that type. The settings dialog opens from the gear with
2893
3076
  Inputs, Style, Text, Table, Coordinates and Visibility pages as the tool has them; its edits apply
2894
3077
  live, Cancel restores the drawing, and Ok commits the session as one edit. Its header carries the
2895
3078
  drawing's name, the tool's own until the viewer renames it with the pencil beside it; the name is
@@ -2910,10 +3093,10 @@ bar's palette offers them too.
2910
3093
 
2911
3094
  The line tools' Style page carries the stroke with its two ends, the extensions, the middle point,
2912
3095
  the price labels and, under Info, the stats a line reads (price range, percent change, the change
2913
- counted in the symbol's smallest price move, bars, date and time range, angle), where they stand
2914
- (left, center, right, or auto, which keeps them inside the pane), and whether they show always or
2915
- only while the line is selected. A horizontal line offers its price label, a vertical line its time
2916
- label, a cross line both. A rectangle offers its extensions, its border, a middle line with a stroke
3096
+ counted in the symbol's smallest price move, bars, date and time range, its length on the pane,
3097
+ angle), where they stand (left, center, right, or auto, which keeps them inside the pane), and
3098
+ whether they show always or only while the line is selected. A horizontal line offers its price
3099
+ label, a vertical line its time label, a cross line both. A rectangle offers its extensions, its border, a middle line with a stroke
2917
3100
  of its own and its background; every other shape its border and background; a curve its stroke and
2918
3101
  ends, its extensions along the tangents it ends on, and its background. A background switched off
2919
3102
  keeps its color and opacity for when it is switched back on. The Text page sets the words' color,
@@ -2922,6 +3105,15 @@ above, on or below a line, above, inside or below a box, along it from left to r
2922
3105
  vertical line whether they read across it or run up it. `NO_COORDINATES_TAB` names the tools whose
2923
3106
  points are drawn rather than typed, and `PRICE_ONLY_COORDS` the levels that span every bar.
2924
3107
 
3108
+ On the chart a line rests as its stroke. While the pointer rests on it its handles show thin, and a
3109
+ press on one moves that point and selects the line; two clicks place a line, its second point
3110
+ following the pointer between them. Selected, a line marks its points' prices and times on the
3111
+ axes, the times in the chart's own format, with a band across their span. A horizontal line's
3112
+ handle stands nine tenths of the way across the pane and a vertical line's nine tenths of the way
3113
+ down, and a drag of either sets both coordinates of its point. An info line stands its stats in a
3114
+ box beside its middle, a row for the price move, one for the span and one for the angle, and a
3115
+ trend angle marks its angle with a dotted level and an arc from its first point.
3116
+
2925
3117
  A retracement, a trend-based extension and a fib channel lay their levels out two to a line, each
2926
3118
  level its switch, its value and its color, under the thickness and style every level line is drawn
2927
3119
  in. The one color recolors every level at once, and shows the color the levels share or a split
@@ -2985,8 +3177,8 @@ stand above it, inside it or below it with `textVAlign`, at its start, middle or
2985
3177
  parallel stands from the baseline.
2986
3178
 
2987
3179
  A sector shades the two halves of its slice, parted by the ray through its arc's middle, in `color1`
2988
- and `color2` while `fillBackground` is on. A bars pattern paints its captured bars as `mode` says:
2989
- `hl` their high-low ranges, `oc` their open-close ranges, or a line through each bar's close, open,
3180
+ and `color2` while `fillBackground` is on. A bars pattern paints its captured bars as `mode` says
3181
+ (`BARS_PATTERN_MODES`): `hl` their high-low ranges, `oc` their open-close ranges, or a line through each bar's close, open,
2990
3182
  high, low or `hl2`. A ghost feed's average candle span is held as a price and written on its page in
2991
3183
  the symbol's minimum ticks.
2992
3184
 
@@ -3041,12 +3233,14 @@ ends; a pin keeps its place without words. `AttachDrawingsOptions.placeholder` n
3041
3233
  empty drawing shows and `AttachDrawingsOptions.inks` the chart's own inks (`DrawingInks`).
3042
3234
 
3043
3235
  A table types into the cell a click on the selected table lands in, Tab moving on to the next cell
3044
- and from the last to the first. Its floating bar and the chart's menu over it add a column right of
3045
- the cell last typed in and a row below it, or at the table's ends where no cell is, and the menu
3046
- removes that cell's row and column (`chart.drawings.tableRemoveRow`,
3047
- `chart.drawings.tableRemoveColumn`). A hovered grid line resizes its column or row, and the corners
3048
- scale the table. The price label, the price note, the arrow marks, the arrow marker and the flag
3049
- open no editor: their words, where they have any, are set in their settings, which a double-click
3236
+ and from the last to the first. Its floating bar and its menu add a column right of the cell last
3237
+ typed in and a row below it, or at the table's ends where no cell is, and its menu removes that
3238
+ cell's row and column (`chart.drawings.tableRemoveRow`, `chart.drawings.tableRemoveColumn`). A
3239
+ hovered grid line resizes its column or row, and the corners scale the table. A line types its
3240
+ words along it: a selected line without words invites them where they would stand, turned with the
3241
+ line, and a click there or on its words types them in place; a horizontal ray's words are set in
3242
+ its settings. The price label, the price note, the arrow marks, the arrow marker and the flag open
3243
+ no editor: their words, where they have any, are set in their settings, which a double-click
3050
3244
  opens. Any other text-bearing tool types in a box of its own: Ctrl or Cmd with Enter commits,
3051
3245
  Escape cancels, and a press on the chart commits.
3052
3246
 
@@ -3175,17 +3369,18 @@ What to know:
3175
3369
  off) lives in the adapter's chart-bound scope for the symbol; a shared drawing lives in the
3176
3370
  symbol's scope, and a refused write merges the stored document over the layer's own before it
3177
3371
  writes again.
3178
- - **A save keeps its look.** A drawing saves at `v: 3`. A save at `v: 2` restores with the look it
3372
+ - **A save keeps its look.** A drawing saves at `v: 3` (`SERIAL_VERSION`). A save at `v: 2` restores with the look it
3179
3373
  was saved with: its tool writes the values that paint it so and saves at `v: 3` with them, the
3180
3374
  looks the tool's pages do not set riding in the props `INERT_PROPS` lists. A gann square, a gann
3181
3375
  square fixed, a speed resistance fan, a pitchfan, the text and annotation tools, the marks and a
3182
3376
  table saved at `v: 2` paint as they were saved (`savedLook`) until their settings change. A host
3183
3377
  that writes drawings itself writes `v: 3`.
3184
3378
  - **Keys run through the registry.** Delete and Backspace remove the selection, Escape cancels a
3185
- placement, disarms, or closes the inline editor, and Ctrl (or Cmd) with C and V copy and paste a
3186
- drawing. Each resolves to a `chart.drawings.*` command, so your access policy gates the keyboard
3187
- as it gates the toolbar; the keys bind to the chart element and never the page, so an embedded
3188
- chart cannot swallow the host page's keys.
3379
+ placement, disarms, or closes the inline editor, Ctrl (or Cmd) with C and V copy and paste a
3380
+ drawing, and Alt with H places a horizontal line at the crosshair's price while the crosshair
3381
+ stands on the main pane's plot. Each resolves to a `chart.drawings.*` command, so your access
3382
+ policy gates the keyboard as it gates the toolbar; the keys bind to the chart element and never
3383
+ the page, so an embedded chart cannot swallow the host page's keys.
3189
3384
  - **Gestures follow the standard grammar.** Drag a drawing to move it (rigid whole-bar translation,
3190
3385
  so anchors never drift apart); grab an anchor handle to reshape; hold Shift to constrain a
3191
3386
  two-point placement or an anchor drag to 45 degree rays; Ctrl-drag duplicates; the magnet pulls
@@ -3268,6 +3463,10 @@ What to know:
3268
3463
  default), an action on the price the pointer landed on, placed under Copy price and Paste; or
3269
3464
  `view`, a switch over what the chart shows, placed under the remove rows. Rows keep the order
3270
3465
  they were contributed in within their group.
3466
+ - **A right-click on a drawing carries your rows too.** It raises the drawing's own menu, and your
3467
+ `level` rows stand under its Clone and Copy and your `view` rows under its Remove. On a table,
3468
+ `ChartExtensionMenuContext.table` names it, and `table.cell` says whether a cell of it is being
3469
+ typed in; anywhere else the context carries no `table`.
3271
3470
  - **A drawn layer can join the drawing toolbar's eye.**
3272
3471
  `ctx.contributeHideLayer({ id, label, icon, apply })` lists the layer in the eye's menu after the
3273
3472
  chart's own drawings and indicators and before "Hide all", which blanks it with them. `label`
@@ -3275,6 +3474,49 @@ What to know:
3275
3474
  its subject, and `apply(hidden)` is called with the current state at contribution and on every
3276
3475
  change. The handle reads `hidden()`, flips `setHidden()` through the same eye the drawing toolbar
3277
3476
  drives, and `remove()` withdraws the layer.
3477
+ - **Your settings join the chart settings dialog.** `ctx.contributeSettings(contribution)` adds a
3478
+ page of your own, placed after the page `place.page.after` names (or last), or rows inside one of
3479
+ the chart's pages, placed before the first of the rows `place.before` names (or at its end).
3480
+ `after` names one of the chart's pages or another contributed page, and a list names several, of
3481
+ which the first the dialog holds wins: `after: ['trading', 'canvas']` follows a contributed
3482
+ `trading` page where there is one and the Canvas page otherwise. The order is read once every
3483
+ contribution is known, so a page follows the contributed page it names whichever attached first.
3484
+ Pages that name the same page stand after it in the order their extensions attach, each followed
3485
+ by the pages that name it: the order of `extensions` (an extension attached again on a symbol
3486
+ switch keeps its place), and one extension's pages in the order it contributes them. A page that
3487
+ names no page the dialog holds stands last in the same order. The chart's rows are named by the
3488
+ leaf they edit, such as `background` on the Status line page, and the sections it shows only in
3489
+ some contexts by their own names, such as `indicators`.
3490
+ `build(form)` builds your rows with the form the chart builds its own with: headings, checkbox
3491
+ rows with a hint or a tip, sub-rows, and colors, lists, numbers and sliders, so your rows are the
3492
+ chart's in every measure. The dialog calls `open()` as it opens, `cancel(state)` with what `open`
3493
+ answered when the viewer cancels, `commit()` on Ok and `applyDefaults()` from the Template menu.
3494
+ The returned function withdraws the contribution, and detach withdraws it either way.
3495
+ - **A row of your own can stand in the legend.** `ctx.contributeLegendRow({ element, rank })`
3496
+ places your element in the pane's legend, under the symbol's reading and above the indicator
3497
+ rows, rows of a lower `rank` first and then as they were contributed. The row wears
3498
+ the status line's backdrop like the legend's own parts and takes the pointer, so a press on its
3499
+ controls reaches them rather than the chart, and collapsing the indicator rows leaves it standing.
3500
+ What it holds and whether it shows are yours: fill or hide the element as you like. The returned
3501
+ function takes it out, detach takes it out either way, and the element is given back unchanged.
3502
+ - **A setting of yours can redraw the marks at once.** `ctx.refreshMarks()` asks the datafeed for
3503
+ the chart's bar marks and time-scale marks again and draws the answer, so a row you contribute to
3504
+ the settings dialog previews its change while the dialog is open. A detached context asks nothing.
3505
+ - **What you draw can take part in the chart's image.** `ctx.contributeCapture({ before, paint })`
3506
+ is called around every image the chart produces of itself, by `widget.image.capture()`, `copy()`
3507
+ and `download()` alike. `before()` runs synchronously before the renderer draws the image and may
3508
+ answer a restore, which runs once the image is taken: hold out what you draw, and put it back.
3509
+ The renderer draws every pending change before it takes the image, so a price line you updated
3510
+ or a primitive you asked to redraw shows as it now stands. `paint(target, frame)` runs on the bitmap's
3511
+ 2D context once it is drawn, to paint what the chart's canvases do not carry, such as labels on
3512
+ an overlay of your own. The origin is the chart element's top left, which is the top left of
3513
+ `ctx.container`; `frame.width` and `frame.height` are the element's size in CSS pixels, and
3514
+ `frame.pixelRatio` is the image's pixels per CSS pixel. The context arrives untransformed, and
3515
+ the chart puts back its state after each paint. Contributions run in the order their extensions
3516
+ attach and then as they were contributed, restores run the last answered first, and a throw in a
3517
+ `before`, a `paint` or a restore
3518
+ is contained: the image is still produced and every restore still runs. The returned function
3519
+ withdraws the contribution, and detach withdraws it either way.
3278
3520
  - **A printed chord is a real binding.** `ChartExtensionMenuItem.shortcut` prints on the row and
3279
3521
  answers to the key. The chart's dispatcher offers a press no built-in verb claims to the rows your
3280
3522
  `contributeContextMenu` callback returns for the level under the pointer and runs that row's own
@@ -3304,8 +3546,8 @@ What to know:
3304
3546
  re-attaches it on every switch, so a market-scoped overlay cannot carry one market's drawing onto
3305
3547
  another's bars.
3306
3548
  - **A failing extension is its own problem.** A throw in `attach` drops that extension and the
3307
- chart still mounts; a throw in a subscriber, a menu builder, a command or a teardown is
3308
- contained.
3549
+ chart still mounts; a throw in a subscriber, a menu builder, a command, a capture or a teardown
3550
+ is contained.
3309
3551
  - **Layouts attach per chart.** A widget hands its shared options to every chart it tiles, so each
3310
3552
  chart gets its own attachment, its own context and its own state slot.
3311
3553
 
@@ -3344,6 +3586,10 @@ Every claim in this document maps to a test or a generated artifact in the packa
3344
3586
  and fields (`config` and `serverTime` are the pattern): an existing
3345
3587
  implementation keeps compiling, and the widget treats absence as "unconstrained / not supported".
3346
3588
  Your integration never breaks by standing still within a major.
3589
+ - **A key the catalog gains within a major is optional in a dictionary of your own.**
3590
+ `ChartDictionary` requires every key of the major's first release. A key added since reads English
3591
+ until your dictionary carries it, `onMissing` reports it, and the next major makes it required,
3592
+ its upgrading guide listing each such key.
3347
3593
  - **A major version may rename or remove public names.** Its upgrading guide in
3348
3594
  [CHANGELOG.md](CHANGELOG.md) lists each one, old to new, with how saved state carries over: what
3349
3595
  the previous major saved still opens, and the next save writes the current names.