@trdrs/quickcharts 5.0.0 → 5.1.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.
- package/CHANGELOG.md +8 -0
- package/README.md +3 -0
- package/dist/feature-manifest.json +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +75 -31
- package/dist/index.js.map +1 -1
- package/dist/{symbols-DdHLHQFC.d.ts → symbols-CHJGWrf-.d.ts} +3 -1
- package/dist/symbols.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 5.1.0
|
|
6
|
+
|
|
7
|
+
Charts open live subscriptions alongside history, frame the first arriving data, and retain newer streamed prices when history arrives later. A snapshot with `backfill: true` adds missing history without replacing bars already held.
|
|
8
|
+
|
|
9
|
+
## 5.0.1
|
|
10
|
+
|
|
11
|
+
Drawing gestures respect presses handled by host overlays and leave presses outside the plot to the chart scales.
|
|
12
|
+
|
|
5
13
|
## 5.0.0
|
|
6
14
|
|
|
7
15
|
The chart settings, one typed tree saved with the chart in place of the appearance overrides, with a dialog of five pages a host can add its own to, the price labels and scale controls they drive, the plus beside the crosshair's price and its price menu, panes that move and maximize, time-scale marks with icons and lines, the optional prices port, and extension seams for legend rows, captures and settings pages.
|
package/README.md
CHANGED
|
@@ -143,6 +143,9 @@ export const myFeed: ChartDatafeed = {
|
|
|
143
143
|
4. **Live updates mutate the last bar or append.** `subscribeBars` emits `{ kind: 'snapshot', bars }` on
|
|
144
144
|
connect and after every reconnect (the self-healing re-sync), then `{ kind: 'bar', bar }` per update.
|
|
145
145
|
A bar's `t` is its **bucket-open** epoch-seconds time, never the update's wall-clock time.
|
|
146
|
+
The chart opens the subscription alongside history. The first data frames the chart; history
|
|
147
|
+
arriving later adds older bars and retains newer stream readings. Mark a preliminary history
|
|
148
|
+
snapshot with `backfill: true`: it fills missing timestamps without replacing bars already held.
|
|
146
149
|
5. **Bar time is epoch SECONDS.** Not milliseconds.
|
|
147
150
|
6. **No feed for a symbol is terminal.** Throw `FeedUnavailableError` from `history` when no feed serves
|
|
148
151
|
the symbol; the chart shows "market data unavailable" and stops. A transient fetch failure should
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as ChartDatafeed, M as MarkIconId, F as FeedBar, S as SymbolRow, D as DatafeedSearchOptions, a as SearchPage, b as SearchClassNode } from './symbols-
|
|
2
|
-
export { B as BarMark, c as BarsEvent, d as DatafeedConfig, e as FeedUnavailableError, H as HistoryPage, f as MarkColor, g as MarkColorPair, h as MarkColorRole, i as MarkLine, j as MarkLineStyle, k as MarkPlacement, l as MarkShape, O as OlderPageVerdict, P as PricesHandlers, m as SubscribeHandlers, n as SymbolNames, o as SymbolPrices, T as TimescaleMark, p as olderPageVerdict, s as symbolNames } from './symbols-
|
|
1
|
+
import { C as ChartDatafeed, M as MarkIconId, F as FeedBar, S as SymbolRow, D as DatafeedSearchOptions, a as SearchPage, b as SearchClassNode } from './symbols-CHJGWrf-.js';
|
|
2
|
+
export { B as BarMark, c as BarsEvent, d as DatafeedConfig, e as FeedUnavailableError, H as HistoryPage, f as MarkColor, g as MarkColorPair, h as MarkColorRole, i as MarkLine, j as MarkLineStyle, k as MarkPlacement, l as MarkShape, O as OlderPageVerdict, P as PricesHandlers, m as SubscribeHandlers, n as SymbolNames, o as SymbolPrices, T as TimescaleMark, p as olderPageVerdict, s as symbolNames } from './symbols-CHJGWrf-.js';
|
|
3
3
|
import { ao as en, C as ChartMessageKey, G as GlyphSourcePort, M as MagnetMode, d as CursorMode, Q as TextEditFrame, N as TextDraft, L as LineStyle, I as IDrawing, m as DrawingCounts, D as DrawingStyle, $ as VisibilityPreset, X as ToolPreset, Y as ToolTemplate, S as SerializedDrawing, o as DrawingPreferences, l as DrawingAssetPort } from './assets-DF3nozNV.js';
|
|
4
4
|
import { b as ResourceRef, C as ChartSaveLoadAdapter, q as ChartMeta, t as ChartBody, h as DrawingResourceContext, R as ResourceStore, u as DrawingsMeta, j as DrawingsBody, T as TemplateMeta, a as TemplateBody, v as LayoutMeta, w as LayoutBody, e as DrawingContextKind } from './resources-XglhzzW0.js';
|
|
5
5
|
export { c as ChartLocalDrawingContext, D as DRAWING_CONTEXT_VERSION, d as DRAWING_DOCUMENT_VERSION, f as DrawingEntry, g as DrawingGroup, i as DrawingTombstone, L as LayoutSharedDrawingContext, M as MemoryResourcesOptions, x as ResourceAbortError, S as SymbolGlobalDrawingContext, y as TemplateKind, W as WriteOutcome, k as drawingContextKey, l as emptyDrawingDocument, m as liveDrawingEntries, n as liveDrawingGroups, z as memorySaveLoadAdapter, o as mergeDrawingDocuments, p as parseDrawingDocument, r as reviseDrawingDocument, s as sameDrawingContext } from './resources-XglhzzW0.js';
|
package/dist/index.js
CHANGED
|
@@ -4912,6 +4912,17 @@ function layoutChoices(offered) {
|
|
|
4912
4912
|
return (arrangementOf(offered.arrangements[0])?.count ?? 1) > 1 && offered.sync.length > 0;
|
|
4913
4913
|
}
|
|
4914
4914
|
|
|
4915
|
+
// src/widget/barSnapshot.ts
|
|
4916
|
+
function mergeBarSnapshot(held, incoming, backfill = false) {
|
|
4917
|
+
if (backfill) {
|
|
4918
|
+
const merged2 = new Map(incoming.map((bar) => [bar.t, bar]));
|
|
4919
|
+
for (const bar of held) merged2.set(bar.t, bar);
|
|
4920
|
+
return [...merged2.values()].sort((a, b) => a.t - b.t);
|
|
4921
|
+
}
|
|
4922
|
+
const first = incoming[0]?.t;
|
|
4923
|
+
return first === void 0 ? [...incoming] : [...held.filter((bar) => bar.t < first), ...incoming];
|
|
4924
|
+
}
|
|
4925
|
+
|
|
4915
4926
|
// src/widget/chart.ts
|
|
4916
4927
|
import {
|
|
4917
4928
|
CrosshairMode as CrosshairMode2,
|
|
@@ -7021,6 +7032,10 @@ function bindGestures(ctx) {
|
|
|
7021
7032
|
};
|
|
7022
7033
|
ctx.toolChanged = dropPreview;
|
|
7023
7034
|
const onDown = (e) => {
|
|
7035
|
+
if (e.defaultPrevented) return;
|
|
7036
|
+
const press = localXY(e);
|
|
7037
|
+
const plot = viewport();
|
|
7038
|
+
if (!plot || press.x < 0 || press.x > plot.width || press.y < 0 || press.y > plot.height) return;
|
|
7024
7039
|
reopenMenu();
|
|
7025
7040
|
dropPreview();
|
|
7026
7041
|
if (e.button === 2 && ctx.draft) {
|
|
@@ -16649,8 +16664,7 @@ function attachReplayPlane(deps) {
|
|
|
16649
16664
|
if (master === null) return false;
|
|
16650
16665
|
if (event.kind === "snapshot") {
|
|
16651
16666
|
const fresh = event.bars.filter((b) => deps.visible(b.t));
|
|
16652
|
-
|
|
16653
|
-
master = first === void 0 ? fresh : [...master.filter((b) => b.t < first), ...fresh];
|
|
16667
|
+
master = mergeBarSnapshot(master, fresh, event.backfill);
|
|
16654
16668
|
} else if (deps.visible(event.bar.t)) {
|
|
16655
16669
|
const last = master[master.length - 1];
|
|
16656
16670
|
if (!last || event.bar.t > last.t) master = [...master, event.bar];
|
|
@@ -21957,6 +21971,7 @@ function createChartInstance(deps) {
|
|
|
21957
21971
|
let symbolFormatter = createPriceFormatter(UNRESOLVED_PRICE_FORMAT, { locale: i18n.tag() });
|
|
21958
21972
|
let epoch = 0;
|
|
21959
21973
|
let historyPainted = false;
|
|
21974
|
+
let initialHistoryPending = false;
|
|
21960
21975
|
let pendingFrame = null;
|
|
21961
21976
|
let pendingLeftEdge = null;
|
|
21962
21977
|
let menuLevel = null;
|
|
@@ -22991,7 +23006,7 @@ function createChartInstance(deps) {
|
|
|
22991
23006
|
}
|
|
22992
23007
|
function maybePageBack(runway = PAGE_RUNWAY) {
|
|
22993
23008
|
if (replay2.active()) return;
|
|
22994
|
-
if (paging || noMoreHistory || bars.length === 0) return;
|
|
23009
|
+
if (initialHistoryPending || paging || noMoreHistory || bars.length === 0) return;
|
|
22995
23010
|
const range2 = chart.timeScale().getVisibleLogicalRange();
|
|
22996
23011
|
if (!range2 || range2.from > PAGE_TRIGGER_BARS) return;
|
|
22997
23012
|
paging = true;
|
|
@@ -23120,6 +23135,7 @@ function createChartInstance(deps) {
|
|
|
23120
23135
|
bars = [];
|
|
23121
23136
|
paging = false;
|
|
23122
23137
|
historyPainted = false;
|
|
23138
|
+
initialHistoryPending = true;
|
|
23123
23139
|
if (holdFrame !== null) window.cancelAnimationFrame(holdFrame);
|
|
23124
23140
|
holdFrame = null;
|
|
23125
23141
|
mainScale().applyOptions({ autoScale: true });
|
|
@@ -23165,49 +23181,72 @@ function createChartInstance(deps) {
|
|
|
23165
23181
|
}).catch(() => {
|
|
23166
23182
|
});
|
|
23167
23183
|
}
|
|
23168
|
-
|
|
23169
|
-
|
|
23170
|
-
|
|
23171
|
-
|
|
23172
|
-
|
|
23173
|
-
|
|
23174
|
-
|
|
23175
|
-
|
|
23176
|
-
|
|
23177
|
-
|
|
23178
|
-
|
|
23179
|
-
|
|
23180
|
-
|
|
23181
|
-
|
|
23182
|
-
|
|
23183
|
-
|
|
23184
|
+
let firstFrameSize = 0;
|
|
23185
|
+
let frameNavigation = -1;
|
|
23186
|
+
const firstPaint = (complete = false) => {
|
|
23187
|
+
if (complete) initialHistoryPending = false;
|
|
23188
|
+
const grow = complete && firstFrameSize < firstPage && bars.length > firstFrameSize && frameNavigation === navigationEpoch;
|
|
23189
|
+
if (bars.length > 0 && (!historyPainted || grow)) {
|
|
23190
|
+
historyPainted = true;
|
|
23191
|
+
const framing = pendingFrame?.epoch === myEpoch ? pendingFrame : null;
|
|
23192
|
+
pendingFrame = null;
|
|
23193
|
+
const edge = pendingLeftEdge?.epoch === myEpoch ? pendingLeftEdge : null;
|
|
23194
|
+
pendingLeftEdge = null;
|
|
23195
|
+
if (framing) {
|
|
23196
|
+
frameRange(chart, anchor, framing.span, framing.tf);
|
|
23197
|
+
fillSpan(framing.span, framing.tf);
|
|
23198
|
+
} else if (edge) keepLeftEdge(edge);
|
|
23199
|
+
else chart.timeScale().fitContent();
|
|
23200
|
+
firstFrameSize = bars.length;
|
|
23201
|
+
frameNavigation = framing || edge ? -1 : navigationEpoch;
|
|
23202
|
+
holdAxisAfterFrame();
|
|
23203
|
+
refreshMarks();
|
|
23204
|
+
}
|
|
23184
23205
|
if (!ready) {
|
|
23185
23206
|
ready = true;
|
|
23186
23207
|
deps.onReady();
|
|
23187
23208
|
}
|
|
23188
|
-
|
|
23209
|
+
};
|
|
23210
|
+
queueMicrotask(() => openSubscription(myEpoch, firstPaint));
|
|
23211
|
+
void datafeed.history(symbol, tf, { countBack: firstPage }).then((page) => {
|
|
23212
|
+
if (disposed || myEpoch !== epoch) return;
|
|
23213
|
+
const merged2 = new Map(page.bars.map((bar) => [bar.t, bar]));
|
|
23214
|
+
for (const bar of bars) merged2.set(bar.t, bar);
|
|
23215
|
+
const write = () => {
|
|
23216
|
+
bars = [...merged2.values()].sort((a, b) => a.t - b.t);
|
|
23217
|
+
paintAll();
|
|
23218
|
+
};
|
|
23219
|
+
if (historyPainted) maintainTimeline(write);
|
|
23220
|
+
else write();
|
|
23221
|
+
firstPaint(true);
|
|
23189
23222
|
}).catch((e) => {
|
|
23190
23223
|
if (disposed || myEpoch !== epoch) return;
|
|
23191
|
-
historyPainted = true;
|
|
23192
|
-
pendingFrame = null;
|
|
23193
23224
|
if (e instanceof FeedUnavailableError) {
|
|
23225
|
+
unsubscribe?.();
|
|
23226
|
+
unsubscribe = null;
|
|
23227
|
+
bars = [];
|
|
23228
|
+
paintAll();
|
|
23194
23229
|
feedStatus = "feed_unavailable";
|
|
23195
23230
|
events.emit("feedStatus", "feed_unavailable");
|
|
23196
|
-
return;
|
|
23197
23231
|
}
|
|
23198
|
-
|
|
23232
|
+
}).finally(() => {
|
|
23233
|
+
if (myEpoch === epoch) initialHistoryPending = false;
|
|
23199
23234
|
});
|
|
23200
23235
|
}
|
|
23201
|
-
function openSubscription(myEpoch) {
|
|
23236
|
+
function openSubscription(myEpoch, firstPaint) {
|
|
23202
23237
|
if (disposed || myEpoch !== epoch) return;
|
|
23203
|
-
|
|
23238
|
+
let active = true;
|
|
23239
|
+
const off = datafeed.subscribeBars(symbol, tf, {
|
|
23204
23240
|
onBars: (e) => {
|
|
23205
|
-
if (disposed || myEpoch !== epoch) return;
|
|
23241
|
+
if (!active || disposed || myEpoch !== epoch) return;
|
|
23206
23242
|
const replaying = replay2.absorb(e);
|
|
23207
23243
|
if (e.kind === "snapshot") {
|
|
23208
|
-
const
|
|
23209
|
-
|
|
23210
|
-
|
|
23244
|
+
const write = () => {
|
|
23245
|
+
bars = mergeBarSnapshot(bars, e.bars, e.backfill);
|
|
23246
|
+
if (!replaying) paintAll();
|
|
23247
|
+
};
|
|
23248
|
+
if (!replaying && historyPainted) maintainTimeline(write);
|
|
23249
|
+
else write();
|
|
23211
23250
|
} else {
|
|
23212
23251
|
const next = applyBar(bars, e.bar);
|
|
23213
23252
|
if (next) {
|
|
@@ -23215,14 +23254,19 @@ function createChartInstance(deps) {
|
|
|
23215
23254
|
if (!replaying) paintLast(e.bar);
|
|
23216
23255
|
}
|
|
23217
23256
|
}
|
|
23257
|
+
if (!replaying) firstPaint(e.kind === "snapshot" && !e.backfill && e.bars.length >= SNAPSHOT_BARS);
|
|
23218
23258
|
},
|
|
23219
23259
|
onStatus: (status2) => {
|
|
23220
|
-
if (disposed || myEpoch !== epoch) return;
|
|
23260
|
+
if (!active || disposed || myEpoch !== epoch) return;
|
|
23221
23261
|
feedStatus = status2;
|
|
23222
23262
|
events.emit("feedStatus", status2);
|
|
23223
23263
|
countdown?.refresh();
|
|
23224
23264
|
}
|
|
23225
23265
|
});
|
|
23266
|
+
unsubscribe = () => {
|
|
23267
|
+
active = false;
|
|
23268
|
+
off();
|
|
23269
|
+
};
|
|
23226
23270
|
}
|
|
23227
23271
|
const LIVE_EDGE_SLACK_BARS = 1;
|
|
23228
23272
|
let awayFromLive = false;
|