acttrader-charts 1.4.0-beta.11 → 1.4.0-beta.13
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 +31 -0
- package/dist/{MACD-B8LTAGUU.d.cts → MACD-ico-au5d.d.cts} +0 -4
- package/dist/{MACD-B8LTAGUU.d.ts → MACD-ico-au5d.d.ts} +0 -4
- package/dist/index.cjs +222 -128
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -2
- package/dist/index.d.ts +43 -2
- package/dist/index.js +222 -128
- package/dist/index.js.map +1 -1
- package/dist/indicators/index.d.cts +2 -2
- package/dist/indicators/index.d.ts +2 -2
- package/dist/webview/chart.html +6 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -223,6 +223,21 @@ chart.on('compareRemoved', ({ symbol }) => console.log('-', symbol));
|
|
|
223
223
|
chart.on('compareError', ({ symbol, message }) => console.warn(symbol, message));
|
|
224
224
|
```
|
|
225
225
|
|
|
226
|
+
#### Saving and syncing compares
|
|
227
|
+
|
|
228
|
+
`getState().compares` is the ordered list of compare symbols, and `[]` once the
|
|
229
|
+
last one is removed (it is `undefined` only when no `compareDataLoader` is set).
|
|
230
|
+
Save the list as it is, including `[]`, so a removal reaches the trader's
|
|
231
|
+
other devices. `setState({ compares })` applies a saved list: `[]` clears every
|
|
232
|
+
compare, the current list is left as it is (no refetch), and a list applied
|
|
233
|
+
before the primary's first load waits as `loading` and is fetched with that
|
|
234
|
+
load instead of failing.
|
|
235
|
+
|
|
236
|
+
The % on each legend chip uses the same baseline as the lines: the compare's
|
|
237
|
+
close at the leftmost visible bar, measured at the last bar (or the hovered
|
|
238
|
+
bar). It updates as you scroll and zoom, so two screens showing different
|
|
239
|
+
ranges show different %.
|
|
240
|
+
|
|
226
241
|
When any compare is active the Y-axis switches to percent (`+12.34%` /
|
|
227
242
|
`-5.67%`); removing every compare returns it to absolute-price labels.
|
|
228
243
|
The Compare button in the advanced toolbar opens an in-chart picker filtered
|
|
@@ -1595,6 +1610,22 @@ const chart = new ChartEngine({
|
|
|
1595
1610
|
chart.setDrawingTool('icon'); // uses the last glyph picked
|
|
1596
1611
|
```
|
|
1597
1612
|
|
|
1613
|
+
#### Hosts with their own picker (native apps)
|
|
1614
|
+
|
|
1615
|
+
The panel above is part of the web drawing toolbar. A host that draws its own
|
|
1616
|
+
toolbar (the Android and iOS apps in native-UI mode) builds the picker from the
|
|
1617
|
+
catalog instead: the `icon` tool carries `glyphTabs`, each with a `kind`, a
|
|
1618
|
+
`label` and its `groups` (`id`, `name`, `tabIcon`, `glyphs`), the same list the
|
|
1619
|
+
web panel shows. On a tap, choose the glyph and then arm the tool:
|
|
1620
|
+
|
|
1621
|
+
```ts
|
|
1622
|
+
chart.setIconGlyph('😍', 'sticker'); // kind: 'emoji' (default) | 'sticker' | 'icon'
|
|
1623
|
+
chart.setDrawingTool('icon');
|
|
1624
|
+
```
|
|
1625
|
+
|
|
1626
|
+
Before `glyphTabs` and `setIconGlyph`, a host without the web toolbar had no
|
|
1627
|
+
list to show and every icon it placed was the default ⭐.
|
|
1628
|
+
|
|
1598
1629
|
#### The picker
|
|
1599
1630
|
|
|
1600
1631
|
Opening the **Icons & Emojis** group gives a panel, not a list of rows: a row per
|
|
@@ -323,10 +323,6 @@ interface ChartSettingsTemplate {
|
|
|
323
323
|
}
|
|
324
324
|
/** Mints a stable, collision-free id for a settings-template name. */
|
|
325
325
|
declare function makeSettingsTemplateId(name: string, existingIds?: readonly string[]): string;
|
|
326
|
-
/**
|
|
327
|
-
* Deep-copies a settings snapshot one level past `canvasColors`, so a saved
|
|
328
|
-
* template can never be mutated by later edits to the live chart (or vice versa).
|
|
329
|
-
*/
|
|
330
326
|
declare function cloneSettings(s: ChartSettingsSnapshot): ChartSettingsSnapshot;
|
|
331
327
|
/** Builds a template from a settings snapshot. */
|
|
332
328
|
declare function captureSettingsTemplate(name: string, settings: ChartSettingsSnapshot, opts?: {
|
|
@@ -323,10 +323,6 @@ interface ChartSettingsTemplate {
|
|
|
323
323
|
}
|
|
324
324
|
/** Mints a stable, collision-free id for a settings-template name. */
|
|
325
325
|
declare function makeSettingsTemplateId(name: string, existingIds?: readonly string[]): string;
|
|
326
|
-
/**
|
|
327
|
-
* Deep-copies a settings snapshot one level past `canvasColors`, so a saved
|
|
328
|
-
* template can never be mutated by later edits to the live chart (or vice versa).
|
|
329
|
-
*/
|
|
330
326
|
declare function cloneSettings(s: ChartSettingsSnapshot): ChartSettingsSnapshot;
|
|
331
327
|
/** Builds a template from a settings snapshot. */
|
|
332
328
|
declare function captureSettingsTemplate(name: string, settings: ChartSettingsSnapshot, opts?: {
|
package/dist/index.cjs
CHANGED
|
@@ -27089,6 +27089,123 @@ var IndicatorSettingsDialog = class {
|
|
|
27089
27089
|
}
|
|
27090
27090
|
};
|
|
27091
27091
|
|
|
27092
|
+
// src/core/templateStore.ts
|
|
27093
|
+
function slugify(name) {
|
|
27094
|
+
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
27095
|
+
}
|
|
27096
|
+
function mintId(name, existingIds = [], fallback = "template") {
|
|
27097
|
+
const base = slugify(name) || fallback;
|
|
27098
|
+
if (!existingIds.includes(base)) return base;
|
|
27099
|
+
let n = 2;
|
|
27100
|
+
while (existingIds.includes(`${base}-${n}`)) n++;
|
|
27101
|
+
return `${base}-${n}`;
|
|
27102
|
+
}
|
|
27103
|
+
function upsertById(list, entry) {
|
|
27104
|
+
const idx = list.findIndex((e) => e.id === entry.id);
|
|
27105
|
+
if (idx === -1) return [...list, entry];
|
|
27106
|
+
const next = [...list];
|
|
27107
|
+
next[idx] = entry;
|
|
27108
|
+
return next;
|
|
27109
|
+
}
|
|
27110
|
+
function removeById(list, id) {
|
|
27111
|
+
return list.filter((e) => e.id !== id);
|
|
27112
|
+
}
|
|
27113
|
+
function findByIdOrName(list, idOrName) {
|
|
27114
|
+
const byId = list.find((e) => e.id === idOrName);
|
|
27115
|
+
if (byId) return byId;
|
|
27116
|
+
const slug = slugify(idOrName);
|
|
27117
|
+
return list.find((e) => slugify(e.name) === slug);
|
|
27118
|
+
}
|
|
27119
|
+
|
|
27120
|
+
// src/core/settingsTemplates.ts
|
|
27121
|
+
function makeSettingsTemplateId(name, existingIds = []) {
|
|
27122
|
+
return mintId(name, existingIds, "settings");
|
|
27123
|
+
}
|
|
27124
|
+
function cloneCanvasColors(c) {
|
|
27125
|
+
if (!c) return c;
|
|
27126
|
+
return {
|
|
27127
|
+
...c,
|
|
27128
|
+
...c.dark ? { dark: { ...c.dark } } : {},
|
|
27129
|
+
...c.light ? { light: { ...c.light } } : {}
|
|
27130
|
+
};
|
|
27131
|
+
}
|
|
27132
|
+
function cloneSettings(s) {
|
|
27133
|
+
return {
|
|
27134
|
+
tradeDisplayFilter: s.tradeDisplayFilter,
|
|
27135
|
+
positionRenderStyle: s.positionRenderStyle,
|
|
27136
|
+
theme: s.theme,
|
|
27137
|
+
showTradeLevelsAlways: s.showTradeLevelsAlways,
|
|
27138
|
+
showPriceAxisCountdown: s.showPriceAxisCountdown,
|
|
27139
|
+
...s.canvasColors ? {
|
|
27140
|
+
canvasColors: {
|
|
27141
|
+
...s.canvasColors.dark ? { dark: { ...s.canvasColors.dark } } : {},
|
|
27142
|
+
...s.canvasColors.light ? { light: { ...s.canvasColors.light } } : {}
|
|
27143
|
+
}
|
|
27144
|
+
} : {},
|
|
27145
|
+
// Each group is one level deep, so a spread copy is a full copy.
|
|
27146
|
+
...s.statusLine ? { statusLine: { ...s.statusLine } } : {},
|
|
27147
|
+
...s.scales ? { scales: { ...s.scales } } : {},
|
|
27148
|
+
...s.canvas ? { canvas: { ...s.canvas } } : {},
|
|
27149
|
+
...s.barColorSource ? { barColorSource: s.barColorSource } : {}
|
|
27150
|
+
};
|
|
27151
|
+
}
|
|
27152
|
+
function captureSettingsTemplate(name, settings, opts = {}) {
|
|
27153
|
+
return {
|
|
27154
|
+
id: opts.id ?? makeSettingsTemplateId(name, opts.existingIds ?? []),
|
|
27155
|
+
name: name.trim(),
|
|
27156
|
+
settings: cloneSettings(settings),
|
|
27157
|
+
...opts.savedAt !== void 0 ? { savedAt: opts.savedAt } : {}
|
|
27158
|
+
};
|
|
27159
|
+
}
|
|
27160
|
+
function upsertSettingsTemplate(list, template) {
|
|
27161
|
+
return upsertById(list, template);
|
|
27162
|
+
}
|
|
27163
|
+
function removeSettingsTemplate(list, id) {
|
|
27164
|
+
return removeById(list, id);
|
|
27165
|
+
}
|
|
27166
|
+
function findSettingsTemplate(list, idOrName) {
|
|
27167
|
+
return findByIdOrName(list, idOrName);
|
|
27168
|
+
}
|
|
27169
|
+
function isGroup(v) {
|
|
27170
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
27171
|
+
}
|
|
27172
|
+
function sanitizeSettingsTemplates(input) {
|
|
27173
|
+
if (!Array.isArray(input)) return [];
|
|
27174
|
+
const out = [];
|
|
27175
|
+
for (const raw of input) {
|
|
27176
|
+
if (typeof raw !== "object" || raw === null) continue;
|
|
27177
|
+
const t = raw;
|
|
27178
|
+
if (typeof t.name !== "string" || t.name.trim() === "") continue;
|
|
27179
|
+
if (typeof t.settings !== "object" || t.settings === null) continue;
|
|
27180
|
+
const s = t.settings;
|
|
27181
|
+
out.push({
|
|
27182
|
+
id: typeof t.id === "string" && t.id !== "" ? t.id : makeSettingsTemplateId(
|
|
27183
|
+
t.name,
|
|
27184
|
+
out.map((x) => x.id)
|
|
27185
|
+
),
|
|
27186
|
+
name: t.name.trim(),
|
|
27187
|
+
settings: {
|
|
27188
|
+
tradeDisplayFilter: s.tradeDisplayFilter ?? "all",
|
|
27189
|
+
positionRenderStyle: s.positionRenderStyle ?? "line",
|
|
27190
|
+
theme: s.theme === "light" ? "light" : "dark",
|
|
27191
|
+
showTradeLevelsAlways: s.showTradeLevelsAlways === true,
|
|
27192
|
+
showPriceAxisCountdown: s.showPriceAxisCountdown === true,
|
|
27193
|
+
...typeof s.canvasColors === "object" && s.canvasColors !== null ? { canvasColors: s.canvasColors } : {},
|
|
27194
|
+
// Groups are carried through when present and dropped when not.
|
|
27195
|
+
// Absent is meaningful: it means "leave the chart as it is",
|
|
27196
|
+
// which is what an old template saved before these existed has
|
|
27197
|
+
// to mean if applying it is not to rearrange the chart.
|
|
27198
|
+
...isGroup(s.statusLine) ? { statusLine: s.statusLine } : {},
|
|
27199
|
+
...isGroup(s.scales) ? { scales: s.scales } : {},
|
|
27200
|
+
...isGroup(s.canvas) ? { canvas: s.canvas } : {},
|
|
27201
|
+
...s.barColorSource === "previousClose" || s.barColorSource === "open" ? { barColorSource: s.barColorSource } : {}
|
|
27202
|
+
},
|
|
27203
|
+
...typeof t.savedAt === "number" ? { savedAt: t.savedAt } : {}
|
|
27204
|
+
});
|
|
27205
|
+
}
|
|
27206
|
+
return out;
|
|
27207
|
+
}
|
|
27208
|
+
|
|
27092
27209
|
// src/ui/ChartSettingsDialog.ts
|
|
27093
27210
|
var TAB_LABELS = {
|
|
27094
27211
|
appearance: "Appearance",
|
|
@@ -27156,7 +27273,12 @@ var ChartSettingsDialog = class {
|
|
|
27156
27273
|
display: "flex",
|
|
27157
27274
|
flexDirection: "column",
|
|
27158
27275
|
width: "540px",
|
|
27159
|
-
|
|
27276
|
+
// One height for every tab. Sized to its content, the panel grew
|
|
27277
|
+
// and shrank as you switched tabs (Appearance tall, Status Line
|
|
27278
|
+
// short) and the footer jumped about; a long tab now scrolls
|
|
27279
|
+
// inside the content area instead.
|
|
27280
|
+
height: "min(620px, calc(100vh - 48px))",
|
|
27281
|
+
minHeight: "min(340px, calc(100vh - 48px))",
|
|
27160
27282
|
maxWidth: "calc(100vw - 32px)",
|
|
27161
27283
|
maxHeight: "calc(100vh - 48px)",
|
|
27162
27284
|
background: theme.tooltip.background,
|
|
@@ -27193,7 +27315,7 @@ var ChartSettingsDialog = class {
|
|
|
27193
27315
|
}
|
|
27194
27316
|
show(current, onChange, onReset) {
|
|
27195
27317
|
const wasVisible = this.isVisible;
|
|
27196
|
-
this.current = { ...current };
|
|
27318
|
+
this.current = { ...current, canvasColors: cloneCanvasColors(current.canvasColors) };
|
|
27197
27319
|
this.onChange = onChange;
|
|
27198
27320
|
this.onReset = onReset ?? null;
|
|
27199
27321
|
this.renderContent();
|
|
@@ -27253,7 +27375,7 @@ var ChartSettingsDialog = class {
|
|
|
27253
27375
|
}
|
|
27254
27376
|
// ── Private – emit ────────────────────────────────────────────────────────
|
|
27255
27377
|
emit() {
|
|
27256
|
-
this.onChange?.({ ...this.current });
|
|
27378
|
+
this.onChange?.({ ...this.current, canvasColors: cloneCanvasColors(this.current.canvasColors) });
|
|
27257
27379
|
}
|
|
27258
27380
|
// ── Private – panel theme ─────────────────────────────────────────────────
|
|
27259
27381
|
applyPanelTheme() {
|
|
@@ -28033,9 +28155,11 @@ var ChartSettingsDialog = class {
|
|
|
28033
28155
|
}
|
|
28034
28156
|
];
|
|
28035
28157
|
const makeSwatch = (def) => this.makeColorSwatch(current[def.field] ?? def.fallback, (hex) => {
|
|
28036
|
-
|
|
28037
|
-
|
|
28038
|
-
|
|
28158
|
+
const cc = this.current.canvasColors ?? {};
|
|
28159
|
+
this.current.canvasColors = {
|
|
28160
|
+
...cc,
|
|
28161
|
+
[themeKey]: { ...cc[themeKey] ?? {}, [def.field]: hex }
|
|
28162
|
+
};
|
|
28039
28163
|
this.emit();
|
|
28040
28164
|
});
|
|
28041
28165
|
for (const { label, s1, s2 } of rows) {
|
|
@@ -30734,6 +30858,25 @@ var CompareManager = class {
|
|
|
30734
30858
|
});
|
|
30735
30859
|
return;
|
|
30736
30860
|
}
|
|
30861
|
+
const entry = this.createEntry(symbol);
|
|
30862
|
+
await this.fetchOne(entry, this.lastParams, this.generation);
|
|
30863
|
+
}
|
|
30864
|
+
/** True once the primary has a loaded range that `add()` can fetch against. */
|
|
30865
|
+
hasRange() {
|
|
30866
|
+
return this.lastParams !== null;
|
|
30867
|
+
}
|
|
30868
|
+
/**
|
|
30869
|
+
* Add a compare symbol as `loading` without fetching. Used when restoring a
|
|
30870
|
+
* saved list before the primary's first load: the next `refetchAll()` (run
|
|
30871
|
+
* when the primary range arrives) loads it. No-op for a duplicate, an empty
|
|
30872
|
+
* symbol, or past `maxCompares`.
|
|
30873
|
+
*/
|
|
30874
|
+
addPending(symbol) {
|
|
30875
|
+
if (!symbol || this.entries.has(symbol)) return;
|
|
30876
|
+
if (this.entries.size >= this.maxCompares) return;
|
|
30877
|
+
this.createEntry(symbol);
|
|
30878
|
+
}
|
|
30879
|
+
createEntry(symbol) {
|
|
30737
30880
|
const color = this.assignColor();
|
|
30738
30881
|
const entry = {
|
|
30739
30882
|
symbol,
|
|
@@ -30744,7 +30887,7 @@ var CompareManager = class {
|
|
|
30744
30887
|
this.entries.set(symbol, entry);
|
|
30745
30888
|
this.order.push(symbol);
|
|
30746
30889
|
this.emitter.emit("compareAdded", { symbol, color });
|
|
30747
|
-
|
|
30890
|
+
return entry;
|
|
30748
30891
|
}
|
|
30749
30892
|
remove(symbol) {
|
|
30750
30893
|
if (!this.entries.has(symbol)) return;
|
|
@@ -32015,34 +32158,6 @@ function formatMeasureDate(time, timeframe, timezone) {
|
|
|
32015
32158
|
return `${date} ${pick(cp, "hour")}:${pick(cp, "minute")}`;
|
|
32016
32159
|
}
|
|
32017
32160
|
|
|
32018
|
-
// src/core/templateStore.ts
|
|
32019
|
-
function slugify(name) {
|
|
32020
|
-
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
32021
|
-
}
|
|
32022
|
-
function mintId(name, existingIds = [], fallback = "template") {
|
|
32023
|
-
const base = slugify(name) || fallback;
|
|
32024
|
-
if (!existingIds.includes(base)) return base;
|
|
32025
|
-
let n = 2;
|
|
32026
|
-
while (existingIds.includes(`${base}-${n}`)) n++;
|
|
32027
|
-
return `${base}-${n}`;
|
|
32028
|
-
}
|
|
32029
|
-
function upsertById(list, entry) {
|
|
32030
|
-
const idx = list.findIndex((e) => e.id === entry.id);
|
|
32031
|
-
if (idx === -1) return [...list, entry];
|
|
32032
|
-
const next = [...list];
|
|
32033
|
-
next[idx] = entry;
|
|
32034
|
-
return next;
|
|
32035
|
-
}
|
|
32036
|
-
function removeById(list, id) {
|
|
32037
|
-
return list.filter((e) => e.id !== id);
|
|
32038
|
-
}
|
|
32039
|
-
function findByIdOrName(list, idOrName) {
|
|
32040
|
-
const byId = list.find((e) => e.id === idOrName);
|
|
32041
|
-
if (byId) return byId;
|
|
32042
|
-
const slug = slugify(idOrName);
|
|
32043
|
-
return list.find((e) => slugify(e.name) === slug);
|
|
32044
|
-
}
|
|
32045
|
-
|
|
32046
32161
|
// src/core/savedLayouts.ts
|
|
32047
32162
|
function makeLayoutId(name, existingIds = []) {
|
|
32048
32163
|
return mintId(name, existingIds, "layout");
|
|
@@ -32097,87 +32212,6 @@ function sanitizeLayouts(input) {
|
|
|
32097
32212
|
return out;
|
|
32098
32213
|
}
|
|
32099
32214
|
|
|
32100
|
-
// src/core/settingsTemplates.ts
|
|
32101
|
-
function makeSettingsTemplateId(name, existingIds = []) {
|
|
32102
|
-
return mintId(name, existingIds, "settings");
|
|
32103
|
-
}
|
|
32104
|
-
function cloneSettings(s) {
|
|
32105
|
-
return {
|
|
32106
|
-
tradeDisplayFilter: s.tradeDisplayFilter,
|
|
32107
|
-
positionRenderStyle: s.positionRenderStyle,
|
|
32108
|
-
theme: s.theme,
|
|
32109
|
-
showTradeLevelsAlways: s.showTradeLevelsAlways,
|
|
32110
|
-
showPriceAxisCountdown: s.showPriceAxisCountdown,
|
|
32111
|
-
...s.canvasColors ? {
|
|
32112
|
-
canvasColors: {
|
|
32113
|
-
...s.canvasColors.dark ? { dark: { ...s.canvasColors.dark } } : {},
|
|
32114
|
-
...s.canvasColors.light ? { light: { ...s.canvasColors.light } } : {}
|
|
32115
|
-
}
|
|
32116
|
-
} : {},
|
|
32117
|
-
// Each group is one level deep, so a spread copy is a full copy.
|
|
32118
|
-
...s.statusLine ? { statusLine: { ...s.statusLine } } : {},
|
|
32119
|
-
...s.scales ? { scales: { ...s.scales } } : {},
|
|
32120
|
-
...s.canvas ? { canvas: { ...s.canvas } } : {},
|
|
32121
|
-
...s.barColorSource ? { barColorSource: s.barColorSource } : {}
|
|
32122
|
-
};
|
|
32123
|
-
}
|
|
32124
|
-
function captureSettingsTemplate(name, settings, opts = {}) {
|
|
32125
|
-
return {
|
|
32126
|
-
id: opts.id ?? makeSettingsTemplateId(name, opts.existingIds ?? []),
|
|
32127
|
-
name: name.trim(),
|
|
32128
|
-
settings: cloneSettings(settings),
|
|
32129
|
-
...opts.savedAt !== void 0 ? { savedAt: opts.savedAt } : {}
|
|
32130
|
-
};
|
|
32131
|
-
}
|
|
32132
|
-
function upsertSettingsTemplate(list, template) {
|
|
32133
|
-
return upsertById(list, template);
|
|
32134
|
-
}
|
|
32135
|
-
function removeSettingsTemplate(list, id) {
|
|
32136
|
-
return removeById(list, id);
|
|
32137
|
-
}
|
|
32138
|
-
function findSettingsTemplate(list, idOrName) {
|
|
32139
|
-
return findByIdOrName(list, idOrName);
|
|
32140
|
-
}
|
|
32141
|
-
function isGroup(v) {
|
|
32142
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
32143
|
-
}
|
|
32144
|
-
function sanitizeSettingsTemplates(input) {
|
|
32145
|
-
if (!Array.isArray(input)) return [];
|
|
32146
|
-
const out = [];
|
|
32147
|
-
for (const raw of input) {
|
|
32148
|
-
if (typeof raw !== "object" || raw === null) continue;
|
|
32149
|
-
const t = raw;
|
|
32150
|
-
if (typeof t.name !== "string" || t.name.trim() === "") continue;
|
|
32151
|
-
if (typeof t.settings !== "object" || t.settings === null) continue;
|
|
32152
|
-
const s = t.settings;
|
|
32153
|
-
out.push({
|
|
32154
|
-
id: typeof t.id === "string" && t.id !== "" ? t.id : makeSettingsTemplateId(
|
|
32155
|
-
t.name,
|
|
32156
|
-
out.map((x) => x.id)
|
|
32157
|
-
),
|
|
32158
|
-
name: t.name.trim(),
|
|
32159
|
-
settings: {
|
|
32160
|
-
tradeDisplayFilter: s.tradeDisplayFilter ?? "all",
|
|
32161
|
-
positionRenderStyle: s.positionRenderStyle ?? "line",
|
|
32162
|
-
theme: s.theme === "light" ? "light" : "dark",
|
|
32163
|
-
showTradeLevelsAlways: s.showTradeLevelsAlways === true,
|
|
32164
|
-
showPriceAxisCountdown: s.showPriceAxisCountdown === true,
|
|
32165
|
-
...typeof s.canvasColors === "object" && s.canvasColors !== null ? { canvasColors: s.canvasColors } : {},
|
|
32166
|
-
// Groups are carried through when present and dropped when not.
|
|
32167
|
-
// Absent is meaningful: it means "leave the chart as it is",
|
|
32168
|
-
// which is what an old template saved before these existed has
|
|
32169
|
-
// to mean if applying it is not to rearrange the chart.
|
|
32170
|
-
...isGroup(s.statusLine) ? { statusLine: s.statusLine } : {},
|
|
32171
|
-
...isGroup(s.scales) ? { scales: s.scales } : {},
|
|
32172
|
-
...isGroup(s.canvas) ? { canvas: s.canvas } : {},
|
|
32173
|
-
...s.barColorSource === "previousClose" || s.barColorSource === "open" ? { barColorSource: s.barColorSource } : {}
|
|
32174
|
-
},
|
|
32175
|
-
...typeof t.savedAt === "number" ? { savedAt: t.savedAt } : {}
|
|
32176
|
-
});
|
|
32177
|
-
}
|
|
32178
|
-
return out;
|
|
32179
|
-
}
|
|
32180
|
-
|
|
32181
32215
|
// src/core/indicatorTemplates.ts
|
|
32182
32216
|
function makeTemplateId(name, existingIds = []) {
|
|
32183
32217
|
return mintId(name, existingIds);
|
|
@@ -37547,6 +37581,8 @@ var _ChartEngine = class _ChartEngine {
|
|
|
37547
37581
|
this.valueTooltipEnabled = false;
|
|
37548
37582
|
this.valueTooltip = null;
|
|
37549
37583
|
this.compareLegend = null;
|
|
37584
|
+
/** Inputs the compare chips were last computed from (see syncCompareLegend). */
|
|
37585
|
+
this._compareLegendKey = "";
|
|
37550
37586
|
this.comparePickerModal = null;
|
|
37551
37587
|
this.compareDataLoader = null;
|
|
37552
37588
|
/** Crosshair-hovered bar index used to drive live compare percent values. */
|
|
@@ -39326,7 +39362,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
39326
39362
|
this.tradeDisplayFilter = config.tradeDisplayFilter;
|
|
39327
39363
|
}
|
|
39328
39364
|
if (config.canvasColors) {
|
|
39329
|
-
this.settingsCanvasColors = config.canvasColors;
|
|
39365
|
+
this.settingsCanvasColors = cloneCanvasColors(config.canvasColors);
|
|
39330
39366
|
}
|
|
39331
39367
|
this.recomputeRenderStyle();
|
|
39332
39368
|
this.showBidAskLines = config.showBidAskLines ?? false;
|
|
@@ -41851,7 +41887,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
41851
41887
|
* like the dialog's own picks, and `stateChange` fires.
|
|
41852
41888
|
*/
|
|
41853
41889
|
setCanvasColors(colors) {
|
|
41854
|
-
this.settingsCanvasColors = colors ?? {};
|
|
41890
|
+
this.settingsCanvasColors = cloneCanvasColors(colors ?? {});
|
|
41855
41891
|
this.setTheme(this.themeName);
|
|
41856
41892
|
return this;
|
|
41857
41893
|
}
|
|
@@ -42266,11 +42302,15 @@ var _ChartEngine = class _ChartEngine {
|
|
|
42266
42302
|
})),
|
|
42267
42303
|
positionRenderStyle: this.baseRenderStyle(),
|
|
42268
42304
|
tradeDisplayFilter: this.tradeDisplayFilter,
|
|
42269
|
-
canvasColors: Object.keys(this.settingsCanvasColors).length > 0 ?
|
|
42305
|
+
canvasColors: Object.keys(this.settingsCanvasColors).length > 0 ? cloneCanvasColors(this.settingsCanvasColors) : void 0,
|
|
42270
42306
|
tfcEnabled: this.tfcActive,
|
|
42271
42307
|
viewport: this.getViewportTimestamps(),
|
|
42272
42308
|
priceScale: this.priceScaleFactor !== 1 || this.pricePanOffset !== 0 ? { factor: this.priceScaleFactor, panOffset: this.pricePanOffset } : void 0,
|
|
42273
|
-
|
|
42309
|
+
// Always a list when compare is available — `[]` once the last one
|
|
42310
|
+
// is removed. Hosts merge the saved block over the stored one, so
|
|
42311
|
+
// leaving the key out kept the old list on the server and the
|
|
42312
|
+
// compares came back on the trader's other devices.
|
|
42313
|
+
compares: this.compareManager ? this.compareManager.list().map((e) => e.symbol) : void 0
|
|
42274
42314
|
};
|
|
42275
42315
|
}
|
|
42276
42316
|
/**
|
|
@@ -42365,7 +42405,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
42365
42405
|
this.tradeDisplayFilter = state.tradeDisplayFilter;
|
|
42366
42406
|
}
|
|
42367
42407
|
if (state.canvasColors) {
|
|
42368
|
-
this.settingsCanvasColors = state.canvasColors;
|
|
42408
|
+
this.settingsCanvasColors = cloneCanvasColors(state.canvasColors);
|
|
42369
42409
|
}
|
|
42370
42410
|
if (state.tfcEnabled !== void 0 && this.tfcEnabled) {
|
|
42371
42411
|
this.tfcActive = state.tfcEnabled;
|
|
@@ -42390,12 +42430,26 @@ var _ChartEngine = class _ChartEngine {
|
|
|
42390
42430
|
};
|
|
42391
42431
|
}
|
|
42392
42432
|
}
|
|
42393
|
-
if (
|
|
42394
|
-
this.compareManager
|
|
42395
|
-
|
|
42396
|
-
|
|
42433
|
+
if (Array.isArray(state.compares) && this.compareManager) {
|
|
42434
|
+
const cm = this.compareManager;
|
|
42435
|
+
const current = cm.list().map((e) => e.symbol);
|
|
42436
|
+
const same = current.length === state.compares.length && current.every((s, i) => s === state.compares[i]);
|
|
42437
|
+
if (!same) {
|
|
42438
|
+
cm.clear();
|
|
42439
|
+
for (const sym of state.compares) {
|
|
42440
|
+
if (cm.hasRange()) {
|
|
42441
|
+
void cm.add(sym).then(() => {
|
|
42442
|
+
this.recomputeIndicators();
|
|
42443
|
+
this.refreshCompareLegend();
|
|
42444
|
+
this.scheduleRender();
|
|
42445
|
+
});
|
|
42446
|
+
} else {
|
|
42447
|
+
cm.addPending(sym);
|
|
42448
|
+
}
|
|
42449
|
+
}
|
|
42450
|
+
this.recomputeIndicators();
|
|
42451
|
+
this.refreshCompareLegend();
|
|
42397
42452
|
}
|
|
42398
|
-
this.refreshCompareLegend();
|
|
42399
42453
|
}
|
|
42400
42454
|
} finally {
|
|
42401
42455
|
this.isRestoringState = false;
|
|
@@ -42957,6 +43011,20 @@ var _ChartEngine = class _ChartEngine {
|
|
|
42957
43011
|
console.warn("[acttrader-charts] setDraftOrderLabels() is deprecated and is a no-op. DraftOrderQtyInput has been removed.");
|
|
42958
43012
|
return this;
|
|
42959
43013
|
}
|
|
43014
|
+
/**
|
|
43015
|
+
* Choose the glyph the next `icon` drawing places — what a tap in the
|
|
43016
|
+
* Emojis / Stickers / Icons picker does. For hosts that draw their own
|
|
43017
|
+
* picker from the catalog's `glyphTabs` (the native apps): call this, then
|
|
43018
|
+
* `setDrawingTool('icon')`. `kind` sets the size and whether the drawing
|
|
43019
|
+
* colour applies (stickers are larger; icons take the colour). An empty
|
|
43020
|
+
* glyph is ignored.
|
|
43021
|
+
*/
|
|
43022
|
+
setIconGlyph(glyph, kind = "emoji") {
|
|
43023
|
+
if (typeof glyph !== "string" || glyph.length === 0) return this;
|
|
43024
|
+
this.pendingIconGlyph = glyph;
|
|
43025
|
+
this.pendingIconKind = kind === "sticker" || kind === "icon" ? kind : "emoji";
|
|
43026
|
+
return this;
|
|
43027
|
+
}
|
|
42960
43028
|
setDrawingTool(tool) {
|
|
42961
43029
|
this.drawingManager.setActiveTool(tool);
|
|
42962
43030
|
this.leftBar?.setActiveTool(tool);
|
|
@@ -44501,7 +44569,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
44501
44569
|
theme: this.themeName,
|
|
44502
44570
|
showTradeLevelsAlways: this.showTradeLevelsAlways,
|
|
44503
44571
|
showPriceAxisCountdown: this.showPriceAxisCountdown,
|
|
44504
|
-
canvasColors:
|
|
44572
|
+
canvasColors: cloneCanvasColors(this.settingsCanvasColors),
|
|
44505
44573
|
// The resolved sets, not the sparse ones. A template is a thing
|
|
44506
44574
|
// the user means to reproduce exactly; saving the gaps would make
|
|
44507
44575
|
// it reproduce whatever the *next* chart's defaults happen to be.
|
|
@@ -44525,7 +44593,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
44525
44593
|
}
|
|
44526
44594
|
this.positionRenderStyle = settings.positionRenderStyle;
|
|
44527
44595
|
if (settings.canvasColors) {
|
|
44528
|
-
this.settingsCanvasColors = settings.canvasColors;
|
|
44596
|
+
this.settingsCanvasColors = cloneCanvasColors(settings.canvasColors);
|
|
44529
44597
|
}
|
|
44530
44598
|
if (this.showTradeLevelsAlways !== settings.showTradeLevelsAlways) {
|
|
44531
44599
|
this.showTradeLevelsAlways = settings.showTradeLevelsAlways;
|
|
@@ -45570,6 +45638,31 @@ var _ChartEngine = class _ChartEngine {
|
|
|
45570
45638
|
});
|
|
45571
45639
|
this.compareLegend.update(states);
|
|
45572
45640
|
}
|
|
45641
|
+
/**
|
|
45642
|
+
* Called from render while comparing. The lines re-base to the leftmost
|
|
45643
|
+
* visible bar every frame; the chips used to be computed only on
|
|
45644
|
+
* add/remove/crosshair, so after a scroll, zoom, new bar or refetch they
|
|
45645
|
+
* showed a % from an older view. Recompute them whenever anything the
|
|
45646
|
+
* chip % depends on has changed.
|
|
45647
|
+
*/
|
|
45648
|
+
syncCompareLegend(baselineIdx, entries) {
|
|
45649
|
+
const all = this.dataStore.all;
|
|
45650
|
+
const last = all[all.length - 1];
|
|
45651
|
+
const key = [
|
|
45652
|
+
baselineIdx,
|
|
45653
|
+
all.length,
|
|
45654
|
+
last?.time,
|
|
45655
|
+
last?.close,
|
|
45656
|
+
this.compareHoveredIndex,
|
|
45657
|
+
...entries.map((e) => {
|
|
45658
|
+
const b = e.bars[e.bars.length - 1];
|
|
45659
|
+
return `${e.symbol}:${e.status}:${e.bars.length}:${b?.time}:${b?.close}`;
|
|
45660
|
+
})
|
|
45661
|
+
].join("|");
|
|
45662
|
+
if (key === this._compareLegendKey) return;
|
|
45663
|
+
this._compareLegendKey = key;
|
|
45664
|
+
this.refreshCompareLegend();
|
|
45665
|
+
}
|
|
45573
45666
|
/** Returns the index of the leftmost visible bar in the primary store,
|
|
45574
45667
|
* used as the baseline anchor for percent-change normalization. */
|
|
45575
45668
|
compareBaselineIndex(primaryBars) {
|
|
@@ -45683,6 +45776,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
45683
45776
|
(e) => e.status === "ready" && e.bars.length > 0 ? buildComparePctSeries(e.bars, primaryTimes, baselineIdx ?? 0) : []
|
|
45684
45777
|
);
|
|
45685
45778
|
compareColors = entries.map((e) => e.color);
|
|
45779
|
+
this.syncCompareLegend(baselineIdx, entries);
|
|
45686
45780
|
bars = transformBarsToPct(absBars, baseline);
|
|
45687
45781
|
pctTargetRange = combinedPercentRange(
|
|
45688
45782
|
absBars,
|