@coderyo/core 1.0.3 → 1.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/dist/index.d.ts +132 -8
- package/dist/index.js +620 -94
- package/dist/index.js.map +1 -1
- package/package.json +21 -20
package/dist/index.js
CHANGED
|
@@ -1,11 +1,402 @@
|
|
|
1
1
|
// src/bridge-wire.ts
|
|
2
2
|
import {
|
|
3
3
|
BRIDGE_SCHEMA_VERSION,
|
|
4
|
-
isBridgeInbound
|
|
4
|
+
isBridgeInbound,
|
|
5
|
+
isBridgeLayerInboundType as isBridgeLayerInboundType2,
|
|
6
|
+
LAYER_API_READY
|
|
5
7
|
} from "@coderyo/bridge";
|
|
6
8
|
|
|
9
|
+
// src/bridge-layer-wire.ts
|
|
10
|
+
import {
|
|
11
|
+
bridgeLayerPayloadHasDeprecatedLayerId,
|
|
12
|
+
isBridgeLayerInboundType
|
|
13
|
+
} from "@coderyo/bridge";
|
|
14
|
+
|
|
15
|
+
// src/merge-layer-bridge-preset.ts
|
|
16
|
+
function upsertById(current, incoming) {
|
|
17
|
+
if (!incoming?.length) return current.map((x) => ({ ...x }));
|
|
18
|
+
const map = new Map(current.map((x) => [x.id, { ...x }]));
|
|
19
|
+
for (const item of incoming) {
|
|
20
|
+
if (!item?.id) continue;
|
|
21
|
+
map.set(item.id, { ...item });
|
|
22
|
+
}
|
|
23
|
+
return [...map.values()];
|
|
24
|
+
}
|
|
25
|
+
function mergeLayers(current, incoming) {
|
|
26
|
+
if (!incoming?.length) return current.map((l) => ({ ...l }));
|
|
27
|
+
const map = new Map(current.map((l) => [l.id, { ...l }]));
|
|
28
|
+
for (const raw of incoming) {
|
|
29
|
+
if (!raw?.id) continue;
|
|
30
|
+
const prev = map.get(raw.id);
|
|
31
|
+
map.set(raw.id, { ...prev ?? raw, ...raw });
|
|
32
|
+
}
|
|
33
|
+
return [...map.values()];
|
|
34
|
+
}
|
|
35
|
+
function mergeGroups(current, incoming) {
|
|
36
|
+
if (!incoming?.length) {
|
|
37
|
+
return current.map((g) => ({ ...g, layerIds: [...g.layerIds ?? []] }));
|
|
38
|
+
}
|
|
39
|
+
const map = new Map(
|
|
40
|
+
current.map((g) => [g.id, { ...g, layerIds: [...g.layerIds ?? []] }])
|
|
41
|
+
);
|
|
42
|
+
for (const raw of incoming) {
|
|
43
|
+
if (!raw?.id) continue;
|
|
44
|
+
map.set(raw.id, {
|
|
45
|
+
id: raw.id,
|
|
46
|
+
name: raw.name ?? map.get(raw.id)?.name,
|
|
47
|
+
layerIds: [...raw.layerIds ?? map.get(raw.id)?.layerIds ?? []]
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return [...map.values()];
|
|
51
|
+
}
|
|
52
|
+
function mergeLayerBridgePreset(current, partial) {
|
|
53
|
+
return {
|
|
54
|
+
...current,
|
|
55
|
+
...partial,
|
|
56
|
+
pages: upsertById(current.pages, partial.pages),
|
|
57
|
+
layers: mergeLayers(current.layers, partial.layers),
|
|
58
|
+
groups: mergeGroups(current.groups, partial.groups)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/resolve-pane-sync-groups.ts
|
|
63
|
+
function resolvePaneSyncId(layer) {
|
|
64
|
+
if (layer == null) return void 0;
|
|
65
|
+
const raw = layer.syncTimeScaleGroupId;
|
|
66
|
+
if (raw == null) return null;
|
|
67
|
+
const trimmed = String(raw).trim();
|
|
68
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
69
|
+
}
|
|
70
|
+
function resolvePaneSyncGroupsFromLayers(layers, pageId) {
|
|
71
|
+
const scoped = pageId != null ? layers.filter((l) => l.pageId === pageId) : layers;
|
|
72
|
+
const pick = (type) => scoped.find((l) => l.type === type);
|
|
73
|
+
return {
|
|
74
|
+
main: resolvePaneSyncId(pick("chart.main")),
|
|
75
|
+
volume: resolvePaneSyncId(pick("chart.volume")),
|
|
76
|
+
indicator: resolvePaneSyncId(pick("chart.indicator"))
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/bridge-layer-wire.ts
|
|
81
|
+
var PANE_LAYER_TYPES = {
|
|
82
|
+
main: "chart.main",
|
|
83
|
+
volume: "chart.volume",
|
|
84
|
+
indicator: "chart.indicator"
|
|
85
|
+
};
|
|
86
|
+
var registry = /* @__PURE__ */ new Map();
|
|
87
|
+
function registerChartLayerBridge(reg) {
|
|
88
|
+
const state = {
|
|
89
|
+
...reg,
|
|
90
|
+
mergePreset: reg.mergePreset ?? mergeLayerBridgePreset,
|
|
91
|
+
visitedPageIds: /* @__PURE__ */ new Set(),
|
|
92
|
+
pendingAllPagesApply: false
|
|
93
|
+
};
|
|
94
|
+
registry.set(reg.chartId, state);
|
|
95
|
+
return () => {
|
|
96
|
+
registry.delete(reg.chartId);
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function unregisterChartLayerBridge(chartId) {
|
|
100
|
+
registry.delete(chartId);
|
|
101
|
+
}
|
|
102
|
+
function clearLayerBridgeVisitedPages(chartId) {
|
|
103
|
+
const state = registry.get(chartId);
|
|
104
|
+
if (!state) return;
|
|
105
|
+
state.visitedPageIds.clear();
|
|
106
|
+
state.pendingAllPagesApply = false;
|
|
107
|
+
}
|
|
108
|
+
function hasLayerBridgeRegistration(chartId) {
|
|
109
|
+
if (chartId != null) return registry.has(chartId);
|
|
110
|
+
return registry.size > 0;
|
|
111
|
+
}
|
|
112
|
+
function isValidLayerBridgePane(pane) {
|
|
113
|
+
return pane === "main" || pane === "volume" || pane === "indicator";
|
|
114
|
+
}
|
|
115
|
+
function resolvePaneLayerIds(preset, pane, opts) {
|
|
116
|
+
const layerType = PANE_LAYER_TYPES[pane];
|
|
117
|
+
const allPages = opts?.allPages === true;
|
|
118
|
+
const activePageId = opts?.activePageId;
|
|
119
|
+
const pageIds = allPages ? preset.pages.map((p) => p.id) : activePageId ? [activePageId] : [];
|
|
120
|
+
if (pageIds.length === 0) return [];
|
|
121
|
+
return preset.layers.filter((l) => pageIds.includes(l.pageId) && l.type === layerType).map((l) => l.id);
|
|
122
|
+
}
|
|
123
|
+
function postLayerError(chartId, code, message, post) {
|
|
124
|
+
post("chart.error", { chartId, code, message });
|
|
125
|
+
}
|
|
126
|
+
function applyTimeScaleForPage(state, pageId) {
|
|
127
|
+
state.chart.applyTimeScaleSyncFromLayers(state.layerController.getPreset().layers, pageId);
|
|
128
|
+
state.visitedPageIds.add(pageId);
|
|
129
|
+
}
|
|
130
|
+
function lazyApplyActivePage(state) {
|
|
131
|
+
applyTimeScaleForPage(state, state.layerController.activePageId);
|
|
132
|
+
}
|
|
133
|
+
function invalidateNonActivePageVisits(state) {
|
|
134
|
+
const activeId = state.layerController.activePageId;
|
|
135
|
+
for (const p of state.layerController.getPreset().pages) {
|
|
136
|
+
if (p.id !== activeId) state.visitedPageIds.delete(p.id);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function afterPresetMutation(state) {
|
|
140
|
+
state.compositorApply?.();
|
|
141
|
+
state.syncCompositorShellVisibility?.();
|
|
142
|
+
lazyApplyActivePage(state);
|
|
143
|
+
}
|
|
144
|
+
function requirePayloadChartId(payload) {
|
|
145
|
+
const id = typeof payload.chartId === "string" ? payload.chartId.trim() : "";
|
|
146
|
+
return id.length > 0 ? id : null;
|
|
147
|
+
}
|
|
148
|
+
function handleSetSyncGroup(state, payload, post, postError) {
|
|
149
|
+
if (bridgeLayerPayloadHasDeprecatedLayerId(payload)) {
|
|
150
|
+
postError("INVALID_PAYLOAD", "host.layer.setSyncGroup does not accept layerId (use pane)");
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (!("pane" in payload)) {
|
|
154
|
+
postError("INVALID_PAYLOAD", "pane is required");
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (!isValidLayerBridgePane(payload.pane)) {
|
|
158
|
+
postError("INVALID_PANE", `Invalid pane: ${String(payload.pane ?? "")}`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const pane = payload.pane;
|
|
162
|
+
const allPages = payload.allPages === true;
|
|
163
|
+
const groupId = payload.groupId == null ? "" : String(payload.groupId);
|
|
164
|
+
const preset = state.layerController.getPreset();
|
|
165
|
+
const layerIds = resolvePaneLayerIds(preset, pane, {
|
|
166
|
+
allPages,
|
|
167
|
+
activePageId: state.layerController.activePageId
|
|
168
|
+
});
|
|
169
|
+
if (layerIds.length === 0) {
|
|
170
|
+
postError("PANE_NOT_FOUND", `No ${pane} pane layers in scope`);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
for (const layerId of layerIds) {
|
|
174
|
+
state.layerController.setLayerSyncGroup(layerId, groupId);
|
|
175
|
+
}
|
|
176
|
+
lazyApplyActivePage(state);
|
|
177
|
+
post("chart.layerSyncGroupChanged", {
|
|
178
|
+
chartId: state.chartId,
|
|
179
|
+
pane,
|
|
180
|
+
groupId,
|
|
181
|
+
allPages,
|
|
182
|
+
activePageId: state.layerController.activePageId
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function handleSetVisible(state, payload, post, postError) {
|
|
186
|
+
if (!("pane" in payload)) {
|
|
187
|
+
postError("INVALID_PAYLOAD", "pane is required");
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (!isValidLayerBridgePane(payload.pane)) {
|
|
191
|
+
postError("INVALID_PANE", `Invalid pane: ${String(payload.pane ?? "")}`);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (typeof payload.visible !== "boolean") {
|
|
195
|
+
postError("INVALID_PAYLOAD", "visible must be boolean");
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const pane = payload.pane;
|
|
199
|
+
const allPages = payload.allPages === true;
|
|
200
|
+
const visible = payload.visible;
|
|
201
|
+
const preset = state.layerController.getPreset();
|
|
202
|
+
const layerIds = resolvePaneLayerIds(preset, pane, {
|
|
203
|
+
allPages,
|
|
204
|
+
activePageId: state.layerController.activePageId
|
|
205
|
+
});
|
|
206
|
+
if (layerIds.length === 0) {
|
|
207
|
+
postError("PANE_NOT_FOUND", `No ${pane} pane layers in scope`);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
for (const layerId of layerIds) {
|
|
211
|
+
state.layerController.setLayerVisible(layerId, visible);
|
|
212
|
+
}
|
|
213
|
+
state.compositorApply?.();
|
|
214
|
+
state.syncCompositorShellVisibility?.();
|
|
215
|
+
post("chart.layerVisibleChanged", {
|
|
216
|
+
chartId: state.chartId,
|
|
217
|
+
pane,
|
|
218
|
+
visible,
|
|
219
|
+
allPages
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
function maybeClearPendingAllPages(state) {
|
|
223
|
+
if (!state.pendingAllPagesApply) return;
|
|
224
|
+
const allVisited = state.layerController.getPreset().pages.every((p) => state.visitedPageIds.has(p.id));
|
|
225
|
+
if (allVisited) state.pendingAllPagesApply = false;
|
|
226
|
+
}
|
|
227
|
+
function handleSetActivePage(state, payload, post, postError) {
|
|
228
|
+
const pageId = typeof payload.pageId === "string" ? payload.pageId : "";
|
|
229
|
+
if (!pageId) {
|
|
230
|
+
postError("INVALID_PAYLOAD", "pageId is required");
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const preset = state.layerController.getPreset();
|
|
234
|
+
if (!preset.pages.some((p) => p.id === pageId)) {
|
|
235
|
+
postError("INVALID_PAYLOAD", `Unknown pageId: ${pageId}`);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const previousPageId = state.layerController.activePageId;
|
|
239
|
+
if (previousPageId === pageId) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (!state.layerController.setActivePage(pageId)) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
state.compositorApply?.();
|
|
246
|
+
state.syncCompositorShellVisibility?.();
|
|
247
|
+
if (!state.visitedPageIds.has(pageId) || state.pendingAllPagesApply) {
|
|
248
|
+
applyTimeScaleForPage(state, pageId);
|
|
249
|
+
}
|
|
250
|
+
maybeClearPendingAllPages(state);
|
|
251
|
+
post("chart.layerPageChanged", {
|
|
252
|
+
chartId: state.chartId,
|
|
253
|
+
pageId,
|
|
254
|
+
previousPageId
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
function handleSetPreset(state, payload, postError, emitLayerDeltas) {
|
|
258
|
+
const rawPreset = payload.preset;
|
|
259
|
+
if (!rawPreset || typeof rawPreset !== "object") {
|
|
260
|
+
postError("INVALID_PRESET", "preset object is required");
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const incoming = rawPreset;
|
|
264
|
+
const revisionRaw = Number(incoming.revision);
|
|
265
|
+
if (!Number.isFinite(revisionRaw) || revisionRaw < 1) {
|
|
266
|
+
postError("INVALID_PRESET", "preset.revision must be an integer \u2265 1");
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const revision = Math.floor(revisionRaw);
|
|
270
|
+
if (revision < state.layerController.presetRevision) {
|
|
271
|
+
postError(
|
|
272
|
+
"STALE_PRESET_REVISION",
|
|
273
|
+
`Host revision ${revision} < current ${state.layerController.presetRevision}`
|
|
274
|
+
);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const replace = payload.replace === true;
|
|
278
|
+
const normalize = state.normalizePreset ?? ((p) => p);
|
|
279
|
+
const mergeFn = state.mergePreset ?? mergeLayerBridgePreset;
|
|
280
|
+
const current = state.layerController.getPreset();
|
|
281
|
+
let merged;
|
|
282
|
+
try {
|
|
283
|
+
merged = replace ? normalize({ ...incoming, revision }) : normalize(mergeFn(current, { ...incoming, revision }));
|
|
284
|
+
} catch (err) {
|
|
285
|
+
postError("INVALID_PRESET", err instanceof Error ? err.message : String(err));
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const prev = state.layerController.getPreset();
|
|
289
|
+
if (!state.layerController.setPreset(merged)) {
|
|
290
|
+
postError("INVALID_PRESET", "setPreset rejected (interaction in progress)");
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
state.layerController.presetRevision = revision;
|
|
294
|
+
afterPresetMutation(state);
|
|
295
|
+
emitLayerDeltas(prev, state.layerController.getPreset());
|
|
296
|
+
}
|
|
297
|
+
function handleApplyTimeScaleSync(state, payload, postError) {
|
|
298
|
+
const allPages = payload.allPages === true;
|
|
299
|
+
const activeId = state.layerController.activePageId;
|
|
300
|
+
const pageId = typeof payload.pageId === "string" ? payload.pageId : activeId;
|
|
301
|
+
if (!state.layerController.getPreset().pages.some((p) => p.id === pageId)) {
|
|
302
|
+
postError("INVALID_PAYLOAD", `Unknown pageId: ${pageId}`);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (allPages) {
|
|
306
|
+
state.pendingAllPagesApply = true;
|
|
307
|
+
invalidateNonActivePageVisits(state);
|
|
308
|
+
applyTimeScaleForPage(state, activeId);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (pageId === activeId) {
|
|
312
|
+
applyTimeScaleForPage(state, pageId);
|
|
313
|
+
} else {
|
|
314
|
+
state.visitedPageIds.delete(pageId);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function emitPresetLayerDeltas(state, prev, next, postSync, postVisible, _postPage) {
|
|
318
|
+
const active = state.layerController.activePageId;
|
|
319
|
+
for (const pane of ["main", "volume", "indicator"]) {
|
|
320
|
+
const prevIds = resolvePaneLayerIds(prev, pane, { activePageId: active });
|
|
321
|
+
const nextIds = resolvePaneLayerIds(next, pane, { activePageId: active });
|
|
322
|
+
if (prevIds.length === 0 && nextIds.length === 0) continue;
|
|
323
|
+
const prevLayer = prev.layers.find((l) => l.id === prevIds[0]);
|
|
324
|
+
const nextLayer = next.layers.find((l) => l.id === nextIds[0]);
|
|
325
|
+
const prevGroup = prevLayer?.syncTimeScaleGroupId ?? "";
|
|
326
|
+
const nextGroup = nextLayer?.syncTimeScaleGroupId ?? "";
|
|
327
|
+
if (prevGroup !== nextGroup && nextIds.length > 0) {
|
|
328
|
+
postSync("chart.layerSyncGroupChanged", {
|
|
329
|
+
chartId: state.chartId,
|
|
330
|
+
pane,
|
|
331
|
+
groupId: nextGroup,
|
|
332
|
+
allPages: false,
|
|
333
|
+
activePageId: active
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
if (prevLayer && nextLayer && prevLayer.visible !== nextLayer.visible) {
|
|
337
|
+
postVisible("chart.layerVisibleChanged", {
|
|
338
|
+
chartId: state.chartId,
|
|
339
|
+
pane,
|
|
340
|
+
visible: nextLayer.visible !== false,
|
|
341
|
+
allPages: false
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function handleLayerBridgeMessage(type, payload, opts) {
|
|
347
|
+
if (!isBridgeLayerInboundType(type)) return false;
|
|
348
|
+
const chartId = requirePayloadChartId(payload);
|
|
349
|
+
if (!chartId) {
|
|
350
|
+
opts.post("chart.error", {
|
|
351
|
+
chartId: "",
|
|
352
|
+
code: "MISSING_CHART_ID",
|
|
353
|
+
message: "chartId is required in payload for host.layer.*"
|
|
354
|
+
});
|
|
355
|
+
return true;
|
|
356
|
+
}
|
|
357
|
+
const state = registry.get(chartId);
|
|
358
|
+
if (!state) {
|
|
359
|
+
const code = registry.size === 0 ? "LAYER_BRIDGE_NOT_REGISTERED" : "CHART_NOT_FOUND";
|
|
360
|
+
const message = code === "LAYER_BRIDGE_NOT_REGISTERED" ? "Layer bridge is not registered (pass layerBridge to wireChartBridge)" : `No layer bridge registered for chartId: ${chartId}`;
|
|
361
|
+
opts.post("chart.error", { chartId, code, message });
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
const postError = (code, message) => postLayerError(chartId, code, message, opts.post);
|
|
365
|
+
const postSync = (_type, payload2) => opts.post("chart.layerSyncGroupChanged", payload2);
|
|
366
|
+
const postVisible = (_type, payload2) => opts.post("chart.layerVisibleChanged", payload2);
|
|
367
|
+
const postPage = (_type, payload2) => opts.post("chart.layerPageChanged", payload2);
|
|
368
|
+
switch (type) {
|
|
369
|
+
case "host.layer.setSyncGroup":
|
|
370
|
+
handleSetSyncGroup(state, payload, postSync, postError);
|
|
371
|
+
break;
|
|
372
|
+
case "host.layer.setVisible":
|
|
373
|
+
handleSetVisible(state, payload, postVisible, postError);
|
|
374
|
+
break;
|
|
375
|
+
case "host.layer.setActivePage":
|
|
376
|
+
handleSetActivePage(state, payload, postPage, postError);
|
|
377
|
+
break;
|
|
378
|
+
case "host.layer.setPreset":
|
|
379
|
+
handleSetPreset(
|
|
380
|
+
state,
|
|
381
|
+
payload,
|
|
382
|
+
postError,
|
|
383
|
+
(prev, next) => emitPresetLayerDeltas(state, prev, next, postSync, postVisible, postPage)
|
|
384
|
+
);
|
|
385
|
+
break;
|
|
386
|
+
case "host.layer.applyTimeScaleSync":
|
|
387
|
+
handleApplyTimeScaleSync(state, payload, postError);
|
|
388
|
+
break;
|
|
389
|
+
default:
|
|
390
|
+
postError("SCHEMA_MISMATCH", `Unknown host.layer.* event: ${type}`);
|
|
391
|
+
}
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
function resolvePaneSyncGroupsForBridge(layers, pageId) {
|
|
395
|
+
return resolvePaneSyncGroupsFromLayers(layers, pageId);
|
|
396
|
+
}
|
|
397
|
+
|
|
7
398
|
// src/version.ts
|
|
8
|
-
var TRADVIEW_VERSION = "1.0
|
|
399
|
+
var TRADVIEW_VERSION = "1.1.0";
|
|
9
400
|
|
|
10
401
|
// src/bridge-wire.ts
|
|
11
402
|
var TRADVIEW_API_VERSION = 1;
|
|
@@ -41,7 +432,8 @@ function wireChartBridge(opts) {
|
|
|
41
432
|
chartId,
|
|
42
433
|
bridgeSchemaVersion: BRIDGE_SCHEMA_VERSION,
|
|
43
434
|
apiVersion: TRADVIEW_API_VERSION,
|
|
44
|
-
version: TRADVIEW_VERSION
|
|
435
|
+
version: TRADVIEW_VERSION,
|
|
436
|
+
layerApi: LAYER_API_READY
|
|
45
437
|
});
|
|
46
438
|
const postResize = () => {
|
|
47
439
|
const el = controller.getContainer();
|
|
@@ -124,15 +516,37 @@ function wireChartBridge(opts) {
|
|
|
124
516
|
chart.on(ev, fn);
|
|
125
517
|
}
|
|
126
518
|
}
|
|
519
|
+
const unregisterLayer = opts.layerBridge ? registerChartLayerBridge({ ...opts.layerBridge, chartId }) : void 0;
|
|
127
520
|
const offHost = bridge.onMessage((msg) => {
|
|
128
521
|
if (!isBridgeInbound(msg)) return;
|
|
129
522
|
const p = msg.payload ?? {};
|
|
523
|
+
if (msg.type.startsWith("host.layer.")) {
|
|
524
|
+
if (!isBridgeLayerInboundType2(msg.type)) {
|
|
525
|
+
post("chart.error", {
|
|
526
|
+
chartId: typeof p.chartId === "string" ? p.chartId : "",
|
|
527
|
+
code: "SCHEMA_MISMATCH",
|
|
528
|
+
message: `Unknown host.layer.* event: ${msg.type}`
|
|
529
|
+
});
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
handleLayerBridgeMessage(msg.type, p, {
|
|
533
|
+
bridge,
|
|
534
|
+
post: (type, payload) => post(type, payload)
|
|
535
|
+
});
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
130
538
|
switch (msg.type) {
|
|
131
539
|
case "host.setSymbol":
|
|
132
|
-
if (typeof p.symbol === "string")
|
|
540
|
+
if (typeof p.symbol === "string") {
|
|
541
|
+
chart.setSymbol(p.symbol);
|
|
542
|
+
clearLayerBridgeVisitedPages(chartId);
|
|
543
|
+
}
|
|
133
544
|
break;
|
|
134
545
|
case "host.setInterval":
|
|
135
|
-
if (typeof p.interval === "string")
|
|
546
|
+
if (typeof p.interval === "string") {
|
|
547
|
+
chart.setInterval(p.interval);
|
|
548
|
+
clearLayerBridgeVisitedPages(chartId);
|
|
549
|
+
}
|
|
136
550
|
break;
|
|
137
551
|
case "host.setTheme":
|
|
138
552
|
if (p.theme === "dark" || p.theme === "light") chart.setTheme(p.theme);
|
|
@@ -199,6 +613,17 @@ function wireChartBridge(opts) {
|
|
|
199
613
|
chart.setDrawingTool(p.tool);
|
|
200
614
|
}
|
|
201
615
|
break;
|
|
616
|
+
case "host.setChartPaneResizeFocus":
|
|
617
|
+
if (p.pane === "main" || p.pane === "volume" || p.pane === "indicator" || p.pane === "all") {
|
|
618
|
+
chart.setChartPaneResizeFocus(p.pane);
|
|
619
|
+
} else {
|
|
620
|
+
post("chart.error", {
|
|
621
|
+
chartId,
|
|
622
|
+
code: "INVALID_PANE",
|
|
623
|
+
message: `Invalid pane: ${String(p.pane ?? "")}`
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
break;
|
|
202
627
|
case "host.destroy":
|
|
203
628
|
chart.destroy();
|
|
204
629
|
break;
|
|
@@ -209,12 +634,17 @@ function wireChartBridge(opts) {
|
|
|
209
634
|
return () => {
|
|
210
635
|
if (crosshairTimer) clearTimeout(crosshairTimer);
|
|
211
636
|
offHost();
|
|
637
|
+
unregisterLayer?.();
|
|
212
638
|
for (const [ev, fn] of handlers) chart.off(ev, fn);
|
|
213
639
|
};
|
|
214
640
|
}
|
|
215
641
|
|
|
216
642
|
// src/chart-controller.ts
|
|
217
|
-
import {
|
|
643
|
+
import {
|
|
644
|
+
clearedIndicatorConfig,
|
|
645
|
+
disableIndicatorLayer as applyDisableIndicatorLayer,
|
|
646
|
+
listActiveIndicatorLayers
|
|
647
|
+
} from "@coderyo/indicators";
|
|
218
648
|
import { floorBarOpenTime, intervalMs as intervalMs2 } from "@coderyo/data";
|
|
219
649
|
import { parseInterval } from "@coderyo/data";
|
|
220
650
|
import { BarStore, computeGapStartTimes, TickAggregator } from "@coderyo/series";
|
|
@@ -260,7 +690,9 @@ import {
|
|
|
260
690
|
runPineLiteAsync,
|
|
261
691
|
terminatePineWorker
|
|
262
692
|
} from "@coderyo/pine-lite";
|
|
263
|
-
import {
|
|
693
|
+
import {
|
|
694
|
+
PaneOrchestrator
|
|
695
|
+
} from "@coderyo/renderer-lite";
|
|
264
696
|
|
|
265
697
|
// src/chart-features.ts
|
|
266
698
|
var DEFAULT_CHART_FEATURES = {
|
|
@@ -382,11 +814,12 @@ var ChartController = class {
|
|
|
382
814
|
});
|
|
383
815
|
const symbol = options.symbol?.trim() || PENDING_SYMBOL;
|
|
384
816
|
const interval = parseInterval(options.interval ?? "1h");
|
|
385
|
-
|
|
817
|
+
this.fetchPolicy = this.features.gaps.fillVisibleHoles ? "fill-visible-holes" : this.features.fetchPolicy;
|
|
386
818
|
this.store = new BarStore(symbol || PENDING_SYMBOL, interval);
|
|
387
|
-
this.virtualWindow = new VirtualWindow(this.store, { fetchPolicy });
|
|
388
819
|
this.orchestrator = new PaneOrchestrator({
|
|
389
820
|
container,
|
|
821
|
+
volumeMount: options.volumeMount,
|
|
822
|
+
listenPaneResizeEvents: false,
|
|
390
823
|
indicatorRoot: options.indicatorHost,
|
|
391
824
|
theme: options.theme ?? "dark",
|
|
392
825
|
scaleMode: options.scaleMode ?? "linear",
|
|
@@ -402,20 +835,27 @@ var ChartController = class {
|
|
|
402
835
|
if (options.width) container.style.width = `${options.width}px`;
|
|
403
836
|
if (options.height) container.style.height = `${options.height}px`;
|
|
404
837
|
this.resizeObserver = new ResizeObserver(() => {
|
|
405
|
-
if (
|
|
838
|
+
if (this.destroyed) return;
|
|
839
|
+
this.orchestrator.setResizeFocusPanes(null);
|
|
406
840
|
});
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
841
|
+
const resizeTargets = [container, options.volumeMount, options.indicatorHost].filter(
|
|
842
|
+
Boolean
|
|
843
|
+
);
|
|
844
|
+
for (const el of resizeTargets) this.resizeObserver.observe(el);
|
|
845
|
+
window.addEventListener("tradview:pane-resize", this.onPaneResize);
|
|
846
|
+
this.orchestrator.busRegistry.forEachBus((key, bus) => {
|
|
847
|
+
bus.subscribeTransform(() => {
|
|
848
|
+
if (key !== this.orchestrator.busRegistry.getActiveBusKey()) return;
|
|
849
|
+
if (bus.visibleToMs > bus.visibleFromMs) {
|
|
850
|
+
this.activeVirtualWindow().setVisibleRange({
|
|
851
|
+
fromMs: bus.visibleFromMs,
|
|
852
|
+
toMs: bus.visibleToMs
|
|
853
|
+
});
|
|
854
|
+
this.visibleRangeInitialized = true;
|
|
855
|
+
}
|
|
856
|
+
void this.maybeLoadMore();
|
|
857
|
+
this.drawingManager?.redraw();
|
|
858
|
+
});
|
|
419
859
|
});
|
|
420
860
|
const overlay = this.orchestrator.getOverlayCanvas();
|
|
421
861
|
if (overlay) {
|
|
@@ -452,7 +892,9 @@ var ChartController = class {
|
|
|
452
892
|
container;
|
|
453
893
|
options;
|
|
454
894
|
store;
|
|
455
|
-
|
|
895
|
+
fetchPolicy;
|
|
896
|
+
/** Per sync-group viewport for loadMore / render slicing (active bus drives IChart APIs). */
|
|
897
|
+
virtualWindows = /* @__PURE__ */ new Map();
|
|
456
898
|
orchestrator;
|
|
457
899
|
handlers = /* @__PURE__ */ new Map();
|
|
458
900
|
subscriptionId = null;
|
|
@@ -473,6 +915,10 @@ var ChartController = class {
|
|
|
473
915
|
/** After clearAllIndicators(); blocks Pine replot until script/features change. */
|
|
474
916
|
pinePlotsSuppressed = false;
|
|
475
917
|
chartStorage;
|
|
918
|
+
onPaneResize = () => {
|
|
919
|
+
if (this.destroyed) return;
|
|
920
|
+
this.orchestrator.setResizeFocusPanes(null);
|
|
921
|
+
};
|
|
476
922
|
getFeatures() {
|
|
477
923
|
return { ...this.features };
|
|
478
924
|
}
|
|
@@ -489,9 +935,21 @@ var ChartController = class {
|
|
|
489
935
|
const s = this.store.symbol;
|
|
490
936
|
return s.length > 0 && s !== PENDING_SYMBOL;
|
|
491
937
|
}
|
|
938
|
+
activeVirtualWindow() {
|
|
939
|
+
const key = this.orchestrator.busRegistry.getActiveBusKey();
|
|
940
|
+
let vw = this.virtualWindows.get(key);
|
|
941
|
+
if (!vw) {
|
|
942
|
+
vw = new VirtualWindow(this.store, { fetchPolicy: this.fetchPolicy });
|
|
943
|
+
const range = this.orchestrator.busRegistry.getOrCreateBus(key).getVisibleRange();
|
|
944
|
+
if (range) vw.setVisibleRange(range);
|
|
945
|
+
this.virtualWindows.set(key, vw);
|
|
946
|
+
}
|
|
947
|
+
return vw;
|
|
948
|
+
}
|
|
492
949
|
applyFeatures() {
|
|
493
950
|
const fetchPolicy = this.features.gaps.fillVisibleHoles ? "fill-visible-holes" : this.features.fetchPolicy;
|
|
494
|
-
this.
|
|
951
|
+
this.fetchPolicy = fetchPolicy;
|
|
952
|
+
for (const vw of this.virtualWindows.values()) vw.setFetchPolicy(fetchPolicy);
|
|
495
953
|
this.orchestrator.setIndicatorConfig(this.features.indicators);
|
|
496
954
|
this.orchestrator.setBarSpacingPolicy({
|
|
497
955
|
autoBarSpacingOnInterval: this.features.autoBarSpacingOnInterval,
|
|
@@ -520,7 +978,7 @@ var ChartController = class {
|
|
|
520
978
|
}
|
|
521
979
|
this.pineIr = compiled.ir;
|
|
522
980
|
if (this.hasActiveSymbol()) {
|
|
523
|
-
const bars = this.
|
|
981
|
+
const bars = this.activeVirtualWindow().getBarsForRender();
|
|
524
982
|
if (bars.length > 0) this.applyPinePlots(bars);
|
|
525
983
|
}
|
|
526
984
|
}
|
|
@@ -590,7 +1048,7 @@ var ChartController = class {
|
|
|
590
1048
|
async applyRealtimeBar(bar, partial, loadGen = this.loadGeneration) {
|
|
591
1049
|
await this.store.mergeRealtime({ bar, partial });
|
|
592
1050
|
if (!this.isLoadGenerationCurrent(loadGen)) return;
|
|
593
|
-
const bars = this.
|
|
1051
|
+
const bars = this.activeVirtualWindow().getBarsForRender();
|
|
594
1052
|
const last = bars[bars.length - 1];
|
|
595
1053
|
if (last && this.features.smoothPriceUpdate) {
|
|
596
1054
|
this.orchestrator.updateLastBar(last, {
|
|
@@ -657,6 +1115,21 @@ var ChartController = class {
|
|
|
657
1115
|
this.orchestrator.resize();
|
|
658
1116
|
return this;
|
|
659
1117
|
}
|
|
1118
|
+
/** P2: limit LWC resize to focused panes; also selects that pane's time-scale sync group for IChart APIs. */
|
|
1119
|
+
setChartPaneResizeFocus(pane) {
|
|
1120
|
+
if (pane !== "all") {
|
|
1121
|
+
this.orchestrator.setActiveSyncPane(pane);
|
|
1122
|
+
this.orchestrator.setResizeFocusPanes([pane]);
|
|
1123
|
+
} else {
|
|
1124
|
+
this.orchestrator.setResizeFocusPanes(null);
|
|
1125
|
+
}
|
|
1126
|
+
return this;
|
|
1127
|
+
}
|
|
1128
|
+
/** Apply `syncTimeScaleGroupId` from layout layers to pane buses (empty = independent). */
|
|
1129
|
+
applyTimeScaleSyncFromLayers(layers, pageId) {
|
|
1130
|
+
this.orchestrator.setPaneSyncGroups(resolvePaneSyncGroupsFromLayers(layers, pageId));
|
|
1131
|
+
return this;
|
|
1132
|
+
}
|
|
660
1133
|
setDrawingTool(tool) {
|
|
661
1134
|
this.drawingManager?.setTool(tool);
|
|
662
1135
|
this.syncOverlayPointerEvents();
|
|
@@ -707,6 +1180,20 @@ var ChartController = class {
|
|
|
707
1180
|
this.orchestrator.setIndicatorConfig(loaded);
|
|
708
1181
|
this.emit("featuresChange", this.getFeatures());
|
|
709
1182
|
}
|
|
1183
|
+
/** @public List built-in indicator layers currently enabled on the chart. */
|
|
1184
|
+
listIndicatorLayers() {
|
|
1185
|
+
if (this.features.indicators === null) return [];
|
|
1186
|
+
return listActiveIndicatorLayers(this.features.indicators);
|
|
1187
|
+
}
|
|
1188
|
+
/** @public Disable a single built-in indicator layer by id. */
|
|
1189
|
+
disableIndicatorLayer(id) {
|
|
1190
|
+
if (this.features.indicators === null) {
|
|
1191
|
+
return clearedIndicatorConfig();
|
|
1192
|
+
}
|
|
1193
|
+
const next = applyDisableIndicatorLayer(this.features.indicators, id);
|
|
1194
|
+
this.setIndicatorConfig(next);
|
|
1195
|
+
return next;
|
|
1196
|
+
}
|
|
710
1197
|
clearAllIndicators() {
|
|
711
1198
|
const config = clearedIndicatorConfig(this.features.indicators ?? void 0);
|
|
712
1199
|
this.setIndicatorConfig(config);
|
|
@@ -774,7 +1261,7 @@ var ChartController = class {
|
|
|
774
1261
|
this.orchestrator.setVisibleRange(range);
|
|
775
1262
|
const { fromMs, toMs } = range;
|
|
776
1263
|
if (toMs > fromMs) {
|
|
777
|
-
this.
|
|
1264
|
+
this.activeVirtualWindow().setVisibleRange({ fromMs, toMs });
|
|
778
1265
|
this.visibleRangeInitialized = true;
|
|
779
1266
|
}
|
|
780
1267
|
return this;
|
|
@@ -846,6 +1333,7 @@ var ChartController = class {
|
|
|
846
1333
|
this.offCrosshair?.();
|
|
847
1334
|
this.offCrosshair = null;
|
|
848
1335
|
this.resizeObserver.disconnect();
|
|
1336
|
+
window.removeEventListener("tradview:pane-resize", this.onPaneResize);
|
|
849
1337
|
this.drawingManager?.destroy();
|
|
850
1338
|
void this.teardownSubscription();
|
|
851
1339
|
this.orchestrator.destroy();
|
|
@@ -856,7 +1344,7 @@ var ChartController = class {
|
|
|
856
1344
|
beginDataContextChange() {
|
|
857
1345
|
this.loadGeneration += 1;
|
|
858
1346
|
this.visibleRangeInitialized = false;
|
|
859
|
-
this.
|
|
1347
|
+
this.virtualWindows.clear();
|
|
860
1348
|
this.orchestrator.resetViewState();
|
|
861
1349
|
this.orchestrator.clearBars();
|
|
862
1350
|
return this.loadGeneration;
|
|
@@ -868,74 +1356,86 @@ var ChartController = class {
|
|
|
868
1356
|
if (!this.isLoadGenerationCurrent(loadGen)) return;
|
|
869
1357
|
const symbol = this.store.symbol;
|
|
870
1358
|
const interval = this.store.interval;
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
this.
|
|
874
|
-
|
|
875
|
-
|
|
1359
|
+
try {
|
|
1360
|
+
const endTime = Date.now();
|
|
1361
|
+
if (this.features.protobuf) {
|
|
1362
|
+
this.emit("error", {
|
|
1363
|
+
code: "PROTOBUF_UNAVAILABLE",
|
|
1364
|
+
message: "Protobuf encoding requires protocol v1.1 (not in 1.0.0)"
|
|
1365
|
+
});
|
|
1366
|
+
}
|
|
1367
|
+
const history = await fetchChartHistory(this.options.dataProvider, {
|
|
1368
|
+
mode: "loadMore",
|
|
1369
|
+
symbol,
|
|
1370
|
+
interval,
|
|
1371
|
+
endTime,
|
|
1372
|
+
limit: 500
|
|
876
1373
|
});
|
|
877
|
-
}
|
|
878
|
-
const history = await fetchChartHistory(this.options.dataProvider, {
|
|
879
|
-
mode: "loadMore",
|
|
880
|
-
symbol,
|
|
881
|
-
interval,
|
|
882
|
-
endTime,
|
|
883
|
-
limit: 500
|
|
884
|
-
});
|
|
885
|
-
if (!this.isLoadGenerationCurrent(loadGen)) return;
|
|
886
|
-
if (this.store.symbol !== symbol || this.store.interval !== interval) return;
|
|
887
|
-
await this.store.mergeBars(history.bars.map((bar) => ({ bar })));
|
|
888
|
-
if (!this.isLoadGenerationCurrent(loadGen)) return;
|
|
889
|
-
this.refreshRender(loadGen);
|
|
890
|
-
void this.resolveSymbol(symbol).then((info) => {
|
|
891
|
-
if (!this.isLoadGenerationCurrent(loadGen)) return;
|
|
892
|
-
this.emit("symbolChange", info ?? { symbol });
|
|
893
|
-
});
|
|
894
|
-
this.emit("intervalChange", interval);
|
|
895
|
-
const streamMode = this.features.tickStream ? "bar+tick" : this.features.streamMode;
|
|
896
|
-
const tickOnly = streamMode === "tick";
|
|
897
|
-
const params = {
|
|
898
|
-
symbol,
|
|
899
|
-
interval,
|
|
900
|
-
channels: tickOnly ? ["tick"] : this.features.tickStream ? ["bar", "tick"] : ["bar"],
|
|
901
|
-
streamMode
|
|
902
|
-
};
|
|
903
|
-
this.tickAggregator = tickOnly ? new TickAggregator(interval, (bar, partial) => {
|
|
904
1374
|
if (!this.isLoadGenerationCurrent(loadGen)) return;
|
|
905
1375
|
if (this.store.symbol !== symbol || this.store.interval !== interval) return;
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
1376
|
+
if (history.bars.length === 0) {
|
|
1377
|
+
this.emit("error", {
|
|
1378
|
+
kind: "history",
|
|
1379
|
+
message: `No bars returned for ${symbol} (${interval})`
|
|
1380
|
+
});
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
await this.store.mergeBars(history.bars.map((bar) => ({ bar })));
|
|
1384
|
+
if (!this.isLoadGenerationCurrent(loadGen)) return;
|
|
1385
|
+
this.refreshRender(loadGen);
|
|
1386
|
+
void this.resolveSymbol(symbol).then((info) => {
|
|
912
1387
|
if (!this.isLoadGenerationCurrent(loadGen)) return;
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
1388
|
+
this.emit("symbolChange", info ?? { symbol });
|
|
1389
|
+
});
|
|
1390
|
+
this.emit("intervalChange", interval);
|
|
1391
|
+
const streamMode = this.features.tickStream ? "bar+tick" : this.features.streamMode;
|
|
1392
|
+
const tickOnly = streamMode === "tick";
|
|
1393
|
+
const params = {
|
|
1394
|
+
symbol,
|
|
1395
|
+
interval,
|
|
1396
|
+
channels: tickOnly ? ["tick"] : this.features.tickStream ? ["bar", "tick"] : ["bar"],
|
|
1397
|
+
streamMode
|
|
1398
|
+
};
|
|
1399
|
+
this.tickAggregator = tickOnly ? new TickAggregator(interval, (bar, partial) => {
|
|
917
1400
|
if (!this.isLoadGenerationCurrent(loadGen)) return;
|
|
918
1401
|
if (this.store.symbol !== symbol || this.store.interval !== interval) return;
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
void
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1402
|
+
void this.applyRealtimeBar(bar, partial, loadGen);
|
|
1403
|
+
}) : null;
|
|
1404
|
+
await this.options.dataProvider.connect?.();
|
|
1405
|
+
if (!this.isLoadGenerationCurrent(loadGen)) return;
|
|
1406
|
+
const sub = await this.options.dataProvider.subscribe(params, {
|
|
1407
|
+
onBar: tickOnly ? void 0 : (bar, meta) => {
|
|
1408
|
+
if (!this.isLoadGenerationCurrent(loadGen)) return;
|
|
1409
|
+
if (this.store.symbol !== symbol || this.store.interval !== interval) return;
|
|
1410
|
+
void this.applyRealtimeBar(bar, meta.partial, loadGen);
|
|
1411
|
+
},
|
|
1412
|
+
onTick: (tick) => {
|
|
1413
|
+
if (!this.isLoadGenerationCurrent(loadGen)) return;
|
|
1414
|
+
if (this.store.symbol !== symbol || this.store.interval !== interval) return;
|
|
1415
|
+
if (this.tickAggregator) {
|
|
1416
|
+
this.tickAggregator.ingest(tick);
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
const bar = this.buildBarFromPrice(tick.price, tick.t);
|
|
1420
|
+
void this.applyRealtimeBar(bar, true, loadGen);
|
|
1421
|
+
},
|
|
1422
|
+
onConnectionChange: (state) => {
|
|
1423
|
+
this.emit("connectionChange", state);
|
|
1424
|
+
if (state === "connected" && this.subscriptionId != null) {
|
|
1425
|
+
void this.catchUpMissedBars();
|
|
1426
|
+
}
|
|
1427
|
+
},
|
|
1428
|
+
onError: (err) => this.emit("error", err)
|
|
1429
|
+
});
|
|
1430
|
+
if (!this.isLoadGenerationCurrent(loadGen)) {
|
|
1431
|
+
await this.options.dataProvider.unsubscribe(sub.id);
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
this.subscriptionId = sub.id;
|
|
1435
|
+
} catch (err) {
|
|
1436
|
+
this.emit("error", err);
|
|
1437
|
+
this.emit("connectionChange", "disconnected");
|
|
937
1438
|
}
|
|
938
|
-
this.subscriptionId = sub.id;
|
|
939
1439
|
}
|
|
940
1440
|
async teardownSubscription() {
|
|
941
1441
|
this.tickAggregator?.flush();
|
|
@@ -1008,7 +1508,7 @@ var ChartController = class {
|
|
|
1008
1508
|
async maybeLoadMore() {
|
|
1009
1509
|
if (this.destroyed || this.loadingMore) return;
|
|
1010
1510
|
const loadGen = this.loadGeneration;
|
|
1011
|
-
const reqs = this.
|
|
1511
|
+
const reqs = this.activeVirtualWindow().planFetches();
|
|
1012
1512
|
if (reqs.length === 0) return;
|
|
1013
1513
|
this.loadingMore = true;
|
|
1014
1514
|
try {
|
|
@@ -1038,19 +1538,20 @@ var ChartController = class {
|
|
|
1038
1538
|
const times = this.store.sortedTimes;
|
|
1039
1539
|
if (times.length === 0) return;
|
|
1040
1540
|
if (!this.visibleRangeInitialized) {
|
|
1041
|
-
this.
|
|
1541
|
+
this.activeVirtualWindow().setVisibleRange({
|
|
1042
1542
|
fromMs: times[0],
|
|
1043
1543
|
toMs: times[times.length - 1]
|
|
1044
1544
|
});
|
|
1045
1545
|
this.visibleRangeInitialized = true;
|
|
1046
1546
|
}
|
|
1047
|
-
const bars = this.
|
|
1547
|
+
const bars = this.activeVirtualWindow().getBarsForRender();
|
|
1048
1548
|
if (bars.length === 0) return;
|
|
1049
1549
|
const gaps = this.features.gaps.whitespace ? computeGapStartTimes(
|
|
1050
1550
|
bars.map((b) => b.t),
|
|
1051
1551
|
this.store.interval
|
|
1052
1552
|
) : void 0;
|
|
1053
1553
|
this.orchestrator.setBars(bars, gaps);
|
|
1554
|
+
this.orchestrator.resizeAllPanes();
|
|
1054
1555
|
this.applyPinePlots(bars);
|
|
1055
1556
|
this.drawingManager?.redraw();
|
|
1056
1557
|
this.emit("visibleRangeChange", {
|
|
@@ -1165,6 +1666,14 @@ function wrap(controller, beforeDestroy) {
|
|
|
1165
1666
|
controller.resize(s);
|
|
1166
1667
|
return wrap(controller, beforeDestroy);
|
|
1167
1668
|
},
|
|
1669
|
+
setChartPaneResizeFocus: (pane) => {
|
|
1670
|
+
controller.setChartPaneResizeFocus(pane);
|
|
1671
|
+
return wrap(controller, beforeDestroy);
|
|
1672
|
+
},
|
|
1673
|
+
applyTimeScaleSyncFromLayers: (layers, pageId) => {
|
|
1674
|
+
controller.applyTimeScaleSyncFromLayers(layers, pageId);
|
|
1675
|
+
return wrap(controller, beforeDestroy);
|
|
1676
|
+
},
|
|
1168
1677
|
setFullscreen: (e) => {
|
|
1169
1678
|
controller.setFullscreen(e);
|
|
1170
1679
|
return wrap(controller, beforeDestroy);
|
|
@@ -1198,6 +1707,8 @@ function wrap(controller, beforeDestroy) {
|
|
|
1198
1707
|
controller.setIndicatorConfig(c);
|
|
1199
1708
|
return wrap(controller, beforeDestroy);
|
|
1200
1709
|
},
|
|
1710
|
+
listIndicatorLayers: () => controller.listIndicatorLayers(),
|
|
1711
|
+
disableIndicatorLayer: (id) => controller.disableIndicatorLayer(id),
|
|
1201
1712
|
clearAllIndicators: () => controller.clearAllIndicators(),
|
|
1202
1713
|
clearAllDrawings: () => controller.clearAllDrawings(),
|
|
1203
1714
|
setReturnToCursorAfterDraw: (v) => {
|
|
@@ -1238,7 +1749,8 @@ function createChart(target, options) {
|
|
|
1238
1749
|
bridge: options.bridge,
|
|
1239
1750
|
chartId: options.chartId,
|
|
1240
1751
|
outboundEvents: options.bridgeOutboundEvents,
|
|
1241
|
-
crosshairThrottleMs: options.bridgeCrosshairThrottleMs
|
|
1752
|
+
crosshairThrottleMs: options.bridgeCrosshairThrottleMs,
|
|
1753
|
+
layerBridge: options.layerBridge
|
|
1242
1754
|
});
|
|
1243
1755
|
}
|
|
1244
1756
|
return chart;
|
|
@@ -1251,7 +1763,9 @@ import {
|
|
|
1251
1763
|
hasVisibleIndicatorPanes,
|
|
1252
1764
|
hasMainChartOverlays,
|
|
1253
1765
|
hasAnyActiveIndicators,
|
|
1254
|
-
indicatorConfigStorageKey as indicatorConfigStorageKey2
|
|
1766
|
+
indicatorConfigStorageKey as indicatorConfigStorageKey2,
|
|
1767
|
+
listActiveIndicatorLayers as listActiveIndicatorLayers2,
|
|
1768
|
+
disableIndicatorLayer
|
|
1255
1769
|
} from "@coderyo/indicators";
|
|
1256
1770
|
import {
|
|
1257
1771
|
compilePineLite as compilePineLite2,
|
|
@@ -1268,6 +1782,7 @@ export {
|
|
|
1268
1782
|
PINE_SAMPLE_SCRIPT2 as PINE_SAMPLE_SCRIPT,
|
|
1269
1783
|
TRADVIEW_API_VERSION,
|
|
1270
1784
|
TRADVIEW_VERSION,
|
|
1785
|
+
clearLayerBridgeVisitedPages,
|
|
1271
1786
|
clearedIndicatorConfig2 as clearedIndicatorConfig,
|
|
1272
1787
|
compilePineLite2 as compilePineLite,
|
|
1273
1788
|
createChart,
|
|
@@ -1275,14 +1790,25 @@ export {
|
|
|
1275
1790
|
createDemoChartOptions,
|
|
1276
1791
|
createLocalChartStorage,
|
|
1277
1792
|
defaultChartStorage,
|
|
1793
|
+
disableIndicatorLayer,
|
|
1794
|
+
handleLayerBridgeMessage,
|
|
1278
1795
|
hasAnyActiveIndicators,
|
|
1796
|
+
hasLayerBridgeRegistration,
|
|
1279
1797
|
hasMainChartOverlays,
|
|
1280
1798
|
hasVisibleIndicatorPanes,
|
|
1281
1799
|
indicatorConfigStorageKey2 as indicatorConfigStorageKey,
|
|
1800
|
+
isValidLayerBridgePane,
|
|
1801
|
+
listActiveIndicatorLayers2 as listActiveIndicatorLayers,
|
|
1282
1802
|
loadIndicatorConfig,
|
|
1803
|
+
mergeLayerBridgePreset,
|
|
1804
|
+
registerChartLayerBridge,
|
|
1283
1805
|
resolveChartFeatures,
|
|
1806
|
+
resolvePaneLayerIds,
|
|
1807
|
+
resolvePaneSyncGroupsForBridge,
|
|
1808
|
+
resolvePaneSyncGroupsFromLayers,
|
|
1284
1809
|
runPineLite,
|
|
1285
1810
|
saveIndicatorConfig,
|
|
1811
|
+
unregisterChartLayerBridge,
|
|
1286
1812
|
wireChartBridge
|
|
1287
1813
|
};
|
|
1288
1814
|
//# sourceMappingURL=index.js.map
|