acttrader-charts 1.4.0-beta.12 → 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.
@@ -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",
@@ -27198,7 +27315,7 @@ var ChartSettingsDialog = class {
27198
27315
  }
27199
27316
  show(current, onChange, onReset) {
27200
27317
  const wasVisible = this.isVisible;
27201
- this.current = { ...current };
27318
+ this.current = { ...current, canvasColors: cloneCanvasColors(current.canvasColors) };
27202
27319
  this.onChange = onChange;
27203
27320
  this.onReset = onReset ?? null;
27204
27321
  this.renderContent();
@@ -27258,7 +27375,7 @@ var ChartSettingsDialog = class {
27258
27375
  }
27259
27376
  // ── Private – emit ────────────────────────────────────────────────────────
27260
27377
  emit() {
27261
- this.onChange?.({ ...this.current });
27378
+ this.onChange?.({ ...this.current, canvasColors: cloneCanvasColors(this.current.canvasColors) });
27262
27379
  }
27263
27380
  // ── Private – panel theme ─────────────────────────────────────────────────
27264
27381
  applyPanelTheme() {
@@ -28038,9 +28155,11 @@ var ChartSettingsDialog = class {
28038
28155
  }
28039
28156
  ];
28040
28157
  const makeSwatch = (def) => this.makeColorSwatch(current[def.field] ?? def.fallback, (hex) => {
28041
- if (!this.current.canvasColors) this.current.canvasColors = {};
28042
- if (!this.current.canvasColors[themeKey]) this.current.canvasColors[themeKey] = {};
28043
- this.current.canvasColors[themeKey][def.field] = hex;
28158
+ const cc = this.current.canvasColors ?? {};
28159
+ this.current.canvasColors = {
28160
+ ...cc,
28161
+ [themeKey]: { ...cc[themeKey] ?? {}, [def.field]: hex }
28162
+ };
28044
28163
  this.emit();
28045
28164
  });
28046
28165
  for (const { label, s1, s2 } of rows) {
@@ -32039,34 +32158,6 @@ function formatMeasureDate(time, timeframe, timezone) {
32039
32158
  return `${date} ${pick(cp, "hour")}:${pick(cp, "minute")}`;
32040
32159
  }
32041
32160
 
32042
- // src/core/templateStore.ts
32043
- function slugify(name) {
32044
- return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
32045
- }
32046
- function mintId(name, existingIds = [], fallback = "template") {
32047
- const base = slugify(name) || fallback;
32048
- if (!existingIds.includes(base)) return base;
32049
- let n = 2;
32050
- while (existingIds.includes(`${base}-${n}`)) n++;
32051
- return `${base}-${n}`;
32052
- }
32053
- function upsertById(list, entry) {
32054
- const idx = list.findIndex((e) => e.id === entry.id);
32055
- if (idx === -1) return [...list, entry];
32056
- const next = [...list];
32057
- next[idx] = entry;
32058
- return next;
32059
- }
32060
- function removeById(list, id) {
32061
- return list.filter((e) => e.id !== id);
32062
- }
32063
- function findByIdOrName(list, idOrName) {
32064
- const byId = list.find((e) => e.id === idOrName);
32065
- if (byId) return byId;
32066
- const slug = slugify(idOrName);
32067
- return list.find((e) => slugify(e.name) === slug);
32068
- }
32069
-
32070
32161
  // src/core/savedLayouts.ts
32071
32162
  function makeLayoutId(name, existingIds = []) {
32072
32163
  return mintId(name, existingIds, "layout");
@@ -32121,87 +32212,6 @@ function sanitizeLayouts(input) {
32121
32212
  return out;
32122
32213
  }
32123
32214
 
32124
- // src/core/settingsTemplates.ts
32125
- function makeSettingsTemplateId(name, existingIds = []) {
32126
- return mintId(name, existingIds, "settings");
32127
- }
32128
- function cloneSettings(s) {
32129
- return {
32130
- tradeDisplayFilter: s.tradeDisplayFilter,
32131
- positionRenderStyle: s.positionRenderStyle,
32132
- theme: s.theme,
32133
- showTradeLevelsAlways: s.showTradeLevelsAlways,
32134
- showPriceAxisCountdown: s.showPriceAxisCountdown,
32135
- ...s.canvasColors ? {
32136
- canvasColors: {
32137
- ...s.canvasColors.dark ? { dark: { ...s.canvasColors.dark } } : {},
32138
- ...s.canvasColors.light ? { light: { ...s.canvasColors.light } } : {}
32139
- }
32140
- } : {},
32141
- // Each group is one level deep, so a spread copy is a full copy.
32142
- ...s.statusLine ? { statusLine: { ...s.statusLine } } : {},
32143
- ...s.scales ? { scales: { ...s.scales } } : {},
32144
- ...s.canvas ? { canvas: { ...s.canvas } } : {},
32145
- ...s.barColorSource ? { barColorSource: s.barColorSource } : {}
32146
- };
32147
- }
32148
- function captureSettingsTemplate(name, settings, opts = {}) {
32149
- return {
32150
- id: opts.id ?? makeSettingsTemplateId(name, opts.existingIds ?? []),
32151
- name: name.trim(),
32152
- settings: cloneSettings(settings),
32153
- ...opts.savedAt !== void 0 ? { savedAt: opts.savedAt } : {}
32154
- };
32155
- }
32156
- function upsertSettingsTemplate(list, template) {
32157
- return upsertById(list, template);
32158
- }
32159
- function removeSettingsTemplate(list, id) {
32160
- return removeById(list, id);
32161
- }
32162
- function findSettingsTemplate(list, idOrName) {
32163
- return findByIdOrName(list, idOrName);
32164
- }
32165
- function isGroup(v) {
32166
- return typeof v === "object" && v !== null && !Array.isArray(v);
32167
- }
32168
- function sanitizeSettingsTemplates(input) {
32169
- if (!Array.isArray(input)) return [];
32170
- const out = [];
32171
- for (const raw of input) {
32172
- if (typeof raw !== "object" || raw === null) continue;
32173
- const t = raw;
32174
- if (typeof t.name !== "string" || t.name.trim() === "") continue;
32175
- if (typeof t.settings !== "object" || t.settings === null) continue;
32176
- const s = t.settings;
32177
- out.push({
32178
- id: typeof t.id === "string" && t.id !== "" ? t.id : makeSettingsTemplateId(
32179
- t.name,
32180
- out.map((x) => x.id)
32181
- ),
32182
- name: t.name.trim(),
32183
- settings: {
32184
- tradeDisplayFilter: s.tradeDisplayFilter ?? "all",
32185
- positionRenderStyle: s.positionRenderStyle ?? "line",
32186
- theme: s.theme === "light" ? "light" : "dark",
32187
- showTradeLevelsAlways: s.showTradeLevelsAlways === true,
32188
- showPriceAxisCountdown: s.showPriceAxisCountdown === true,
32189
- ...typeof s.canvasColors === "object" && s.canvasColors !== null ? { canvasColors: s.canvasColors } : {},
32190
- // Groups are carried through when present and dropped when not.
32191
- // Absent is meaningful: it means "leave the chart as it is",
32192
- // which is what an old template saved before these existed has
32193
- // to mean if applying it is not to rearrange the chart.
32194
- ...isGroup(s.statusLine) ? { statusLine: s.statusLine } : {},
32195
- ...isGroup(s.scales) ? { scales: s.scales } : {},
32196
- ...isGroup(s.canvas) ? { canvas: s.canvas } : {},
32197
- ...s.barColorSource === "previousClose" || s.barColorSource === "open" ? { barColorSource: s.barColorSource } : {}
32198
- },
32199
- ...typeof t.savedAt === "number" ? { savedAt: t.savedAt } : {}
32200
- });
32201
- }
32202
- return out;
32203
- }
32204
-
32205
32215
  // src/core/indicatorTemplates.ts
32206
32216
  function makeTemplateId(name, existingIds = []) {
32207
32217
  return mintId(name, existingIds);
@@ -39352,7 +39362,7 @@ var _ChartEngine = class _ChartEngine {
39352
39362
  this.tradeDisplayFilter = config.tradeDisplayFilter;
39353
39363
  }
39354
39364
  if (config.canvasColors) {
39355
- this.settingsCanvasColors = config.canvasColors;
39365
+ this.settingsCanvasColors = cloneCanvasColors(config.canvasColors);
39356
39366
  }
39357
39367
  this.recomputeRenderStyle();
39358
39368
  this.showBidAskLines = config.showBidAskLines ?? false;
@@ -41877,7 +41887,7 @@ var _ChartEngine = class _ChartEngine {
41877
41887
  * like the dialog's own picks, and `stateChange` fires.
41878
41888
  */
41879
41889
  setCanvasColors(colors) {
41880
- this.settingsCanvasColors = colors ?? {};
41890
+ this.settingsCanvasColors = cloneCanvasColors(colors ?? {});
41881
41891
  this.setTheme(this.themeName);
41882
41892
  return this;
41883
41893
  }
@@ -42292,7 +42302,7 @@ var _ChartEngine = class _ChartEngine {
42292
42302
  })),
42293
42303
  positionRenderStyle: this.baseRenderStyle(),
42294
42304
  tradeDisplayFilter: this.tradeDisplayFilter,
42295
- canvasColors: Object.keys(this.settingsCanvasColors).length > 0 ? { ...this.settingsCanvasColors } : void 0,
42305
+ canvasColors: Object.keys(this.settingsCanvasColors).length > 0 ? cloneCanvasColors(this.settingsCanvasColors) : void 0,
42296
42306
  tfcEnabled: this.tfcActive,
42297
42307
  viewport: this.getViewportTimestamps(),
42298
42308
  priceScale: this.priceScaleFactor !== 1 || this.pricePanOffset !== 0 ? { factor: this.priceScaleFactor, panOffset: this.pricePanOffset } : void 0,
@@ -42395,7 +42405,7 @@ var _ChartEngine = class _ChartEngine {
42395
42405
  this.tradeDisplayFilter = state.tradeDisplayFilter;
42396
42406
  }
42397
42407
  if (state.canvasColors) {
42398
- this.settingsCanvasColors = state.canvasColors;
42408
+ this.settingsCanvasColors = cloneCanvasColors(state.canvasColors);
42399
42409
  }
42400
42410
  if (state.tfcEnabled !== void 0 && this.tfcEnabled) {
42401
42411
  this.tfcActive = state.tfcEnabled;
@@ -44559,7 +44569,7 @@ var _ChartEngine = class _ChartEngine {
44559
44569
  theme: this.themeName,
44560
44570
  showTradeLevelsAlways: this.showTradeLevelsAlways,
44561
44571
  showPriceAxisCountdown: this.showPriceAxisCountdown,
44562
- canvasColors: { ...this.settingsCanvasColors },
44572
+ canvasColors: cloneCanvasColors(this.settingsCanvasColors),
44563
44573
  // The resolved sets, not the sparse ones. A template is a thing
44564
44574
  // the user means to reproduce exactly; saving the gaps would make
44565
44575
  // it reproduce whatever the *next* chart's defaults happen to be.
@@ -44583,7 +44593,7 @@ var _ChartEngine = class _ChartEngine {
44583
44593
  }
44584
44594
  this.positionRenderStyle = settings.positionRenderStyle;
44585
44595
  if (settings.canvasColors) {
44586
- this.settingsCanvasColors = settings.canvasColors;
44596
+ this.settingsCanvasColors = cloneCanvasColors(settings.canvasColors);
44587
44597
  }
44588
44598
  if (this.showTradeLevelsAlways !== settings.showTradeLevelsAlways) {
44589
44599
  this.showTradeLevelsAlways = settings.showTradeLevelsAlways;