gloomberb 0.12.0 → 0.13.2
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 +2 -0
- package/package.json +1 -2
- package/src/api-client/auth.ts +2 -1
- package/src/api-client/index.test.ts +34 -0
- package/src/api-client/index.ts +10 -2
- package/src/api-client/request.ts +4 -1
- package/src/api-client/research-activity.ts +61 -0
- package/src/api-client/types.ts +2 -1
- package/src/cli/commands/plugins.ts +4 -3
- package/src/cli/desktop-pane-shot.ts +79 -10
- package/src/cli/pane-functions/capabilities.ts +76 -23
- package/src/cli/pane-functions/cloud-session.test.ts +54 -0
- package/src/cli/pane-functions/cloud-session.ts +38 -0
- package/src/cli/pane-functions/dom.test.ts +41 -0
- package/src/cli/pane-functions/dom.ts +199 -0
- package/src/cli/pane-functions/index.test.ts +13 -2
- package/src/cli/pane-functions/index.ts +9 -5
- package/src/cli/pane-functions/intraday-shot.test.ts +307 -0
- package/src/cli/pane-functions/intraday-shot.ts +334 -0
- package/src/cli/pane-functions/report.test.ts +29 -1
- package/src/cli/pane-functions/report.ts +68 -7
- package/src/cli/pane-functions/screenshot.ts +195 -42
- package/src/cli/restore-plugins.ts +10 -3
- package/src/components/chart/composite/composite-chart.test.tsx +68 -1
- package/src/components/chart/composite/composite-chart.tsx +87 -21
- package/src/components/chart/composite/rasterizer.ts +33 -0
- package/src/components/chart/composite/renderers.test.ts +51 -4
- package/src/components/chart/composite/scene.test.ts +128 -2
- package/src/components/chart/composite/scene.ts +92 -16
- package/src/components/chart/composite/text-renderer.ts +13 -0
- package/src/components/chart/composite/time-scale.ts +30 -10
- package/src/components/chart/composite/types.ts +26 -0
- package/src/components/command-bar/assist/runtime.ts +38 -30
- package/src/components/command-bar/surface/assist.test.tsx +63 -9
- package/src/components/command-bar/surface/test-harness.tsx +23 -4
- package/src/components/onboarding/account-step/chooser-panel.tsx +2 -2
- package/src/components/onboarding/account-step/qr-panel.tsx +2 -10
- package/src/components/onboarding/onboarding-wizard.test.tsx +38 -12
- package/src/components/onboarding/onboarding-wizard.tsx +49 -14
- package/src/data/config/store/index.test.ts +23 -0
- package/src/data/config/store/node.ts +7 -1
- package/src/data/config/store/normalize.ts +4 -0
- package/src/news/types.ts +1 -0
- package/src/plugins/builtin/cds/client.test.ts +20 -0
- package/src/plugins/builtin/cds/client.ts +13 -3
- package/src/plugins/builtin/cds/headless.test.ts +30 -0
- package/src/plugins/builtin/cds/headless.ts +113 -0
- package/src/plugins/builtin/cds/index.tsx +4 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -1
- package/src/plugins/builtin/chart-composer/index.tsx +1 -1
- package/src/plugins/builtin/chat/controller/channels.ts +2 -1
- package/src/plugins/builtin/chat/controller/index.ts +13 -5
- package/src/plugins/builtin/chat/controller/realtime.ts +24 -0
- package/src/plugins/builtin/chat/controller/session-runtime.ts +76 -8
- package/src/plugins/builtin/chat/controller/state.ts +1 -0
- package/src/plugins/builtin/chat/controller.test.ts +260 -4
- package/src/plugins/builtin/cloud/askg/executor.test.ts +160 -0
- package/src/plugins/builtin/cloud/askg/executor.ts +429 -0
- package/src/plugins/builtin/cloud/askg/manifest.test.ts +153 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +314 -0
- package/src/plugins/builtin/cloud/askg/protocol.test.ts +190 -0
- package/src/plugins/builtin/cloud/askg/protocol.ts +286 -0
- package/src/plugins/builtin/cloud/askg/undo.test.ts +44 -0
- package/src/plugins/builtin/cloud/askg/undo.ts +277 -0
- package/src/plugins/builtin/cloud/askg/verify.ts +130 -0
- package/src/plugins/builtin/cloud/auth-dialog.tsx +7 -1
- package/src/plugins/builtin/cloud/device-signin-dialog.tsx +21 -8
- package/src/plugins/builtin/cloud/plugin.tsx +2 -0
- package/src/plugins/builtin/cloud/verification-panel.tsx +56 -0
- package/src/plugins/builtin/congress-trades/client.ts +11 -0
- package/src/plugins/builtin/congress-trades/detail.tsx +3 -3
- package/src/plugins/builtin/congress-trades/headless.test.ts +98 -0
- package/src/plugins/builtin/congress-trades/headless.ts +145 -0
- package/src/plugins/builtin/congress-trades/pane.tsx +3 -3
- package/src/plugins/builtin/credit-conditions/client.ts +1 -1
- package/src/plugins/builtin/credit-conditions/headless.test.ts +31 -0
- package/src/plugins/builtin/credit-conditions/headless.ts +55 -0
- package/src/plugins/builtin/credit-conditions/index.tsx +4 -0
- package/src/plugins/builtin/dividend-yield/headless.test.ts +88 -0
- package/src/plugins/builtin/dividend-yield/headless.ts +135 -0
- package/src/plugins/builtin/dividend-yield/index.tsx +12 -8
- package/src/plugins/builtin/dividend-yield/model.ts +4 -22
- package/src/plugins/builtin/dividend-yield/view.ts +17 -0
- package/src/plugins/builtin/earnings/headless.test.ts +82 -0
- package/src/plugins/builtin/earnings/headless.ts +162 -0
- package/src/plugins/builtin/earnings/index.tsx +2 -0
- package/src/plugins/builtin/earnings-calls/data.ts +29 -8
- package/src/plugins/builtin/earnings-calls/headless.test.ts +192 -0
- package/src/plugins/builtin/earnings-calls/headless.ts +300 -0
- package/src/plugins/builtin/earnings-calls/index.tsx +19 -9
- package/src/plugins/builtin/earnings-calls/model.ts +238 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +4 -0
- package/src/plugins/builtin/earnings-calls/transcript-view.tsx +13 -11
- package/src/plugins/builtin/fear-greed/headless.test.ts +53 -0
- package/src/plugins/builtin/fear-greed/headless.ts +84 -0
- package/src/plugins/builtin/fear-greed/index.tsx +4 -0
- package/src/plugins/builtin/holders/client.ts +29 -0
- package/src/plugins/builtin/holders/headless.test.ts +58 -0
- package/src/plugins/builtin/holders/headless.ts +162 -0
- package/src/plugins/builtin/holders/index.tsx +14 -8
- package/src/plugins/builtin/holders/pane.tsx +7 -1
- package/src/plugins/builtin/insider/client.ts +50 -0
- package/src/plugins/builtin/insider/headless.test.ts +122 -0
- package/src/plugins/builtin/insider/headless.ts +117 -0
- package/src/plugins/builtin/insider/index.tsx +21 -50
- package/src/plugins/builtin/insider/insider-data.ts +1 -1
- package/src/plugins/builtin/insider/model.ts +72 -0
- package/src/plugins/builtin/ipo-calendar/client.ts +15 -0
- package/src/plugins/builtin/ipo-calendar/headless.test.ts +89 -0
- package/src/plugins/builtin/ipo-calendar/headless.ts +127 -0
- package/src/plugins/builtin/ipo-calendar/index.tsx +2 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +3 -15
- package/src/plugins/builtin/market-movers/client.ts +76 -0
- package/src/plugins/builtin/market-movers/headless.test.ts +34 -0
- package/src/plugins/builtin/market-movers/headless.ts +73 -0
- package/src/plugins/builtin/market-movers/index.tsx +11 -51
- package/src/plugins/builtin/news/headless.test.ts +138 -0
- package/src/plugins/builtin/news/headless.ts +190 -0
- package/src/plugins/builtin/news/index.tsx +13 -9
- package/src/plugins/builtin/news/wire/index.tsx +2 -1
- package/src/plugins/builtin/research/analyst-headless.test.ts +60 -0
- package/src/plugins/builtin/research/analyst-headless.ts +172 -0
- package/src/plugins/builtin/research/analyst-model.ts +219 -0
- package/src/plugins/builtin/research/analyst-pane.tsx +31 -221
- package/src/plugins/builtin/research/client.ts +12 -0
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -237
- package/src/plugins/builtin/research/event-model.ts +245 -0
- package/src/plugins/builtin/research/events-headless.test.ts +32 -0
- package/src/plugins/builtin/research/events-headless.ts +87 -0
- package/src/plugins/builtin/research/headless.test.ts +93 -0
- package/src/plugins/builtin/research/headless.ts +161 -0
- package/src/plugins/builtin/research/index.tsx +32 -18
- package/src/plugins/builtin/research-search/pane.tsx +4 -0
- package/src/plugins/builtin/scanner/headless.test.ts +59 -0
- package/src/plugins/builtin/scanner/headless.ts +125 -0
- package/src/plugins/builtin/scanner/index.ts +4 -0
- package/src/plugins/builtin/sec/client.ts +16 -0
- package/src/plugins/builtin/sec/headless.test.ts +72 -0
- package/src/plugins/builtin/sec/headless.ts +69 -0
- package/src/plugins/builtin/sec/index.tsx +20 -75
- package/src/plugins/builtin/sec/model.ts +89 -0
- package/src/plugins/builtin/sectors/client.ts +54 -0
- package/src/plugins/builtin/sectors/headless.test.ts +28 -0
- package/src/plugins/builtin/sectors/headless.ts +80 -0
- package/src/plugins/builtin/sectors/index.tsx +6 -51
- package/src/plugins/builtin/shared/cloud-upgrade.ts +8 -4
- package/src/plugins/builtin/short-interest/client.ts +5 -2
- package/src/plugins/builtin/short-interest/headless.test.ts +96 -0
- package/src/plugins/builtin/short-interest/headless.ts +111 -0
- package/src/plugins/builtin/short-interest/index.tsx +15 -9
- package/src/plugins/builtin/thirteenf/data.ts +13 -11
- package/src/plugins/builtin/thirteenf/headless.test.ts +112 -0
- package/src/plugins/builtin/thirteenf/headless.ts +286 -0
- package/src/plugins/builtin/thirteenf/index.tsx +4 -0
- package/src/plugins/builtin/thirteenf/model.ts +13 -0
- package/src/plugins/builtin/thirteenf/table.ts +3 -15
- package/src/plugins/builtin/ticker-detail/pane.tsx +14 -0
- package/src/plugins/builtin/treasury-auctions/headless.test.ts +34 -0
- package/src/plugins/builtin/treasury-auctions/headless.ts +95 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +4 -0
- package/src/plugins/builtin/volatility/client.ts +16 -3
- package/src/plugins/builtin/volatility/headless.test.ts +35 -0
- package/src/plugins/builtin/volatility/headless.ts +75 -0
- package/src/plugins/builtin/volatility/index.tsx +4 -0
- package/src/plugins/builtin/world-indices/client.ts +43 -0
- package/src/plugins/builtin/world-indices/headless.test.ts +32 -0
- package/src/plugins/builtin/world-indices/headless.ts +109 -0
- package/src/plugins/builtin/world-indices/index.tsx +4 -0
- package/src/plugins/builtin/yield-curve/headless.test.ts +24 -0
- package/src/plugins/builtin/yield-curve/headless.ts +67 -0
- package/src/plugins/builtin/yield-curve/index.tsx +6 -8
- package/src/plugins/builtin/yield-curve/treasury-data.ts +16 -6
- package/src/plugins/bundle.test.ts +58 -1
- package/src/plugins/bundle.ts +4 -2
- package/src/plugins/catalog-backend.test.ts +26 -0
- package/src/plugins/catalog-backend.ts +9 -2
- package/src/plugins/catalog-browser.test.ts +0 -13
- package/src/plugins/catalog-browser.ts +4 -0
- package/src/plugins/catalog-manifest.test.ts +15 -0
- package/src/plugins/catalog-manifest.ts +12 -5
- package/src/plugins/catalog-ui.ts +0 -6
- package/src/plugins/host-contract.ts +9 -0
- package/src/plugins/host-modules.ts +12 -0
- package/src/plugins/loader.ts +63 -15
- package/src/plugins/prediction-markets/headless.test.ts +119 -0
- package/src/plugins/prediction-markets/headless.ts +255 -0
- package/src/plugins/prediction-markets/index.tsx +2 -0
- package/src/plugins/seed.ts +3 -0
- package/src/public/broker.ts +6 -0
- package/src/remote/schema.test.ts +65 -0
- package/src/remote/schema.ts +322 -39
- package/src/remote/types.ts +49 -2
- package/src/renderers/browser/app-services.ts +3 -0
- package/src/renderers/browser/config-host.ts +24 -8
- package/src/renderers/browser/deeplink-bridge.test.ts +11 -0
- package/src/renderers/browser/deeplink-bridge.ts +15 -3
- package/src/renderers/browser/main.tsx +3 -1
- package/src/renderers/browser/research-entry.test.ts +8 -0
- package/src/renderers/browser/research-entry.ts +7 -0
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +2 -2
- package/src/renderers/electrobun/bun/desktop/initialization.ts +10 -3
- package/src/renderers/electrobun/bun/external-plugins.ts +20 -4
- package/src/renderers/electrobun/view/build-assets.ts +0 -2
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +63 -10
- package/src/sources/gloomberb-cloud/news.ts +1 -0
- package/src/sync/core-contributors.test.ts +111 -0
- package/src/sync/core-contributors.ts +155 -1
- package/src/time-series/resolve.test.ts +118 -1
- package/src/time-series/resolve.ts +14 -16
- package/src/types/config.ts +2 -0
- package/src/plugins/broker-sync/broker-sync.test.ts +0 -188
- package/src/plugins/broker-sync/native-loader.ts +0 -10
- package/src/plugins/broker-sync/normalize.ts +0 -308
- package/src/plugins/broker-sync/public.ts +0 -122
- package/src/plugins/broker-sync/robinhood-native.ts +0 -367
- package/src/plugins/broker-sync/robinhood.ts +0 -114
- package/src/plugins/broker-sync/simplefin-native.ts +0 -201
- package/src/plugins/broker-sync/simplefin.ts +0 -70
- package/src/renderers/electrobun/view/native-stubs/broker-sync-native-loader.ts +0 -7
|
@@ -11,6 +11,7 @@ const DEFAULT_SHOT_DEVICE_SCALE_FACTOR = 2;
|
|
|
11
11
|
import {
|
|
12
12
|
renderDesktopPaneScreenshot,
|
|
13
13
|
type DesktopPaneShotApiProxy,
|
|
14
|
+
type DesktopPaneShotIntradayHistory,
|
|
14
15
|
type DesktopPaneShotPayload,
|
|
15
16
|
type DesktopPaneShotRenderResult,
|
|
16
17
|
} from "../desktop-pane-shot";
|
|
@@ -18,6 +19,7 @@ import { optionPaneState } from "./options";
|
|
|
18
19
|
import type { ResolvedPaneFunction } from "./resolver";
|
|
19
20
|
import type { MarketContext } from "../types";
|
|
20
21
|
import { capabilityPluginState } from "./capabilities";
|
|
22
|
+
import { resolvePersistedCloudSessionToken } from "./cloud-session";
|
|
21
23
|
import type { RemoteUiNodeSnapshot } from "../../remote/types";
|
|
22
24
|
import {
|
|
23
25
|
graphRowsForFinancials,
|
|
@@ -65,14 +67,16 @@ import {
|
|
|
65
67
|
isFinancialAnalysisFunction,
|
|
66
68
|
withShotPriceHistory,
|
|
67
69
|
} from "./data";
|
|
70
|
+
import {
|
|
71
|
+
loadShotIntradayWindow,
|
|
72
|
+
resolveShotIntradayRequest,
|
|
73
|
+
} from "./intraday-shot";
|
|
68
74
|
|
|
69
75
|
const DESKTOP_CELL_WIDTH_PX = 8;
|
|
70
76
|
const DESKTOP_CELL_HEIGHT_PX = 18;
|
|
71
77
|
const OPTIONS_PANE_ID = "options";
|
|
72
78
|
const MARKET_VALUATION_PANE_ID = "market-valuation";
|
|
73
79
|
const ECON_STATISTICS_PANE_ID = "econ-statistics";
|
|
74
|
-
const CLOUD_PLUGIN_ID = "gloomberb-cloud";
|
|
75
|
-
const CLOUD_SESSION_KEYS = ["resume:session", "session"] as const;
|
|
76
80
|
const CREDENTIAL_FIELD_NAMES = new Set([
|
|
77
81
|
"accesstoken",
|
|
78
82
|
"accessurl",
|
|
@@ -91,10 +95,6 @@ const CREDENTIAL_FIELD_NAMES = new Set([
|
|
|
91
95
|
"token",
|
|
92
96
|
]);
|
|
93
97
|
|
|
94
|
-
interface PersistedShotCloudSession {
|
|
95
|
-
sessionToken?: unknown;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
98
|
/**
|
|
99
99
|
* The proxy receives the desktop session out of band. Keeping this separate
|
|
100
100
|
* from DesktopPaneShotPayload prevents the token from entering the page,
|
|
@@ -133,21 +133,9 @@ export function stripDesktopShotCredentials<T>(value: T): T {
|
|
|
133
133
|
export function resolveDesktopShotApiProxy(
|
|
134
134
|
context: Pick<MarketContext, "persistence">,
|
|
135
135
|
): DesktopPaneShotApiProxy {
|
|
136
|
-
let sessionToken: string | null = null;
|
|
137
|
-
for (const key of CLOUD_SESSION_KEYS) {
|
|
138
|
-
const value = context.persistence.pluginState.get<PersistedShotCloudSession>(
|
|
139
|
-
CLOUD_PLUGIN_ID,
|
|
140
|
-
key,
|
|
141
|
-
1,
|
|
142
|
-
)?.value;
|
|
143
|
-
if (typeof value?.sessionToken === "string" && value.sessionToken.length > 0) {
|
|
144
|
-
sessionToken = value.sessionToken;
|
|
145
|
-
break;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
136
|
return {
|
|
149
137
|
baseUrl: getCloudApiBaseUrl(),
|
|
150
|
-
sessionToken,
|
|
138
|
+
sessionToken: resolvePersistedCloudSessionToken(context),
|
|
151
139
|
};
|
|
152
140
|
}
|
|
153
141
|
|
|
@@ -289,6 +277,8 @@ export interface PaneScreenshotPriceSeriesEvidence {
|
|
|
289
277
|
pointCount: number;
|
|
290
278
|
first: PaneScreenshotChartPointEvidence;
|
|
291
279
|
last: PaneScreenshotChartPointEvidence;
|
|
280
|
+
resolution?: string;
|
|
281
|
+
sessionDates?: string[];
|
|
292
282
|
}
|
|
293
283
|
|
|
294
284
|
export interface PaneScreenshotFundamentalSeriesEvidence {
|
|
@@ -362,6 +352,7 @@ export interface PaneScreenshotResult {
|
|
|
362
352
|
unavailableSymbols: string[];
|
|
363
353
|
semanticMismatch: boolean;
|
|
364
354
|
usable: boolean;
|
|
355
|
+
unusableReason: string | null;
|
|
365
356
|
dataEvidence: PaneScreenshotDataEvidence | null;
|
|
366
357
|
outputPath: string;
|
|
367
358
|
render: DesktopPaneShotRenderResult & {
|
|
@@ -379,7 +370,7 @@ export function defaultScreenshotPath(resolved: ResolvedPaneFunction, rawArg: st
|
|
|
379
370
|
return resolve(process.cwd(), `gloomberb-${suffix}.png`);
|
|
380
371
|
}
|
|
381
372
|
|
|
382
|
-
async function buildDesktopShotPayload(
|
|
373
|
+
export async function buildDesktopShotPayload(
|
|
383
374
|
resolved: ResolvedPaneFunction,
|
|
384
375
|
context: MarketContext,
|
|
385
376
|
rawArg: string,
|
|
@@ -414,9 +405,10 @@ async function buildDesktopShotPayload(
|
|
|
414
405
|
const paneState = stripDesktopShotCredentials<Record<string, PaneRuntimeState>>({
|
|
415
406
|
[resolved.instance.instanceId]: initialPaneState,
|
|
416
407
|
});
|
|
408
|
+
let shotInstance = stripDesktopShotCredentials<typeof resolved.instance>(resolved.instance);
|
|
417
409
|
const layout = {
|
|
418
410
|
dockRoot: null,
|
|
419
|
-
instances: [
|
|
411
|
+
instances: [shotInstance],
|
|
420
412
|
floating: [{
|
|
421
413
|
instanceId: resolved.instance.instanceId,
|
|
422
414
|
x: 0,
|
|
@@ -444,6 +436,7 @@ async function buildDesktopShotPayload(
|
|
|
444
436
|
|
|
445
437
|
const tickers: TickerRecord[] = [];
|
|
446
438
|
const financials: Array<[string, TickerFinancials]> = [];
|
|
439
|
+
const intradayHistories: DesktopPaneShotIntradayHistory[] = [];
|
|
447
440
|
const optionsChains: Array<[string, OptionsChain]> = [];
|
|
448
441
|
const [fredSeries, capabilitySeries, valuationSeries, statSeries] = await Promise.all([
|
|
449
442
|
collectShotFredSeries(resolved),
|
|
@@ -452,16 +445,64 @@ async function buildDesktopShotPayload(
|
|
|
452
445
|
collectShotStatSeries(resolved),
|
|
453
446
|
]);
|
|
454
447
|
const includeOptionsChains = resolved.pane.id === OPTIONS_PANE_ID || resolved.template?.paneId === OPTIONS_PANE_ID;
|
|
448
|
+
const shotNow = new Date();
|
|
455
449
|
for (const symbol of collectShotSymbols(resolved, rawArg)) {
|
|
456
450
|
const entry = await fetchTickerFinancials(context, symbol);
|
|
457
451
|
const requestedRange = shotPriceHistoryRange(resolved);
|
|
458
452
|
let data = entry.financials;
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
453
|
+
const exchange = entry.instrument.exchange
|
|
454
|
+
?? entry.tickerFile?.metadata.exchange
|
|
455
|
+
?? data.quote?.listingExchangeName
|
|
456
|
+
?? data.quote?.exchangeName
|
|
457
|
+
?? "";
|
|
458
|
+
if (resolved.capability.id === "intraday-price-chart") {
|
|
459
|
+
const request = resolveShotIntradayRequest(resolved.options);
|
|
460
|
+
const intraday = await loadShotIntradayWindow({
|
|
461
|
+
provider: context.dataProvider,
|
|
462
|
+
symbol: entry.instrument.symbol,
|
|
463
|
+
exchange,
|
|
464
|
+
request,
|
|
465
|
+
now: shotNow,
|
|
466
|
+
});
|
|
467
|
+
data = { ...data, priceHistory: intraday.points };
|
|
468
|
+
intradayHistories.push({
|
|
469
|
+
symbol: entry.instrument.symbol,
|
|
470
|
+
exchange,
|
|
471
|
+
rangePreset: request.rangePreset,
|
|
472
|
+
resolution: request.resolution,
|
|
473
|
+
requestedSession: request.session,
|
|
474
|
+
sessionDates: intraday.sessionDates,
|
|
475
|
+
points: intraday.points,
|
|
476
|
+
start: intraday.start?.toISOString() ?? null,
|
|
477
|
+
end: intraday.end?.toISOString() ?? null,
|
|
478
|
+
unavailableReason: intraday.unavailableReason,
|
|
479
|
+
});
|
|
480
|
+
const presetStart = subtractTimeRange(shotNow, request.rangePreset);
|
|
481
|
+
const needsExplicitWindow = request.session !== null
|
|
482
|
+
|| (intraday.start !== null && intraday.start.getTime() < presetStart.getTime())
|
|
483
|
+
|| (intraday.end !== null && intraday.end.getTime() > shotNow.getTime());
|
|
484
|
+
if (needsExplicitWindow && intraday.start && intraday.end) {
|
|
485
|
+
const chartSpec = parseChartSpec(shotInstance.settings?.chartSpec);
|
|
486
|
+
if (chartSpec) {
|
|
487
|
+
shotInstance = {
|
|
488
|
+
...shotInstance,
|
|
489
|
+
settings: {
|
|
490
|
+
...shotInstance.settings,
|
|
491
|
+
chartSpec: {
|
|
492
|
+
...chartSpec,
|
|
493
|
+
viewport: {
|
|
494
|
+
...chartSpec.viewport,
|
|
495
|
+
dateWindow: {
|
|
496
|
+
start: intraday.start.toISOString(),
|
|
497
|
+
end: intraday.end.toISOString(),
|
|
498
|
+
},
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
} else if (requestedRange) {
|
|
465
506
|
try {
|
|
466
507
|
const priceHistory = await context.dataProvider.getPriceHistory(entry.instrument.symbol, exchange, requestedRange);
|
|
467
508
|
data = { ...data, priceHistory: clipPriceHistoryToRange(priceHistory, requestedRange) };
|
|
@@ -472,15 +513,13 @@ async function buildDesktopShotPayload(
|
|
|
472
513
|
tickers.push(entry.tickerFile ?? createFallbackTicker(symbol, data, context));
|
|
473
514
|
financials.push([symbol, data]);
|
|
474
515
|
if (includeOptionsChains && context.dataProvider.getOptionsChain) {
|
|
475
|
-
const exchange = entry.instrument.exchange
|
|
476
|
-
?? entry.tickerFile?.metadata.exchange
|
|
477
|
-
?? data.quote?.listingExchangeName
|
|
478
|
-
?? data.quote?.exchangeName
|
|
479
|
-
?? "";
|
|
480
516
|
const chain = await context.dataProvider.getOptionsChain(entry.instrument.symbol, exchange);
|
|
481
517
|
optionsChains.push([symbol, chain]);
|
|
482
518
|
}
|
|
483
519
|
}
|
|
520
|
+
layout.instances[0] = shotInstance;
|
|
521
|
+
config.layout.instances[0] = shotInstance;
|
|
522
|
+
if (config.layouts[0]) config.layouts[0].layout.instances[0] = shotInstance;
|
|
484
523
|
|
|
485
524
|
return {
|
|
486
525
|
config,
|
|
@@ -493,6 +532,7 @@ async function buildDesktopShotPayload(
|
|
|
493
532
|
watermark,
|
|
494
533
|
tickers,
|
|
495
534
|
financials,
|
|
535
|
+
intradayHistories,
|
|
496
536
|
optionsChains,
|
|
497
537
|
fredSeries,
|
|
498
538
|
valuationSeries,
|
|
@@ -525,7 +565,7 @@ export function shotPriceHistoryRange(resolved: ResolvedPaneFunction): TimeRange
|
|
|
525
565
|
case "return-correlation":
|
|
526
566
|
return (resolved.options.rangePreset ?? "1Y") as TimeRange;
|
|
527
567
|
case "intraday-price-chart":
|
|
528
|
-
return "1D";
|
|
568
|
+
return (resolved.options.rangePreset ?? "1D") as TimeRange;
|
|
529
569
|
case "historical-prices":
|
|
530
570
|
case "security-relationship":
|
|
531
571
|
return (resolved.options.range ?? "1Y") as TimeRange;
|
|
@@ -545,6 +585,7 @@ export async function renderDesktopShot({
|
|
|
545
585
|
scale,
|
|
546
586
|
watermark,
|
|
547
587
|
options,
|
|
588
|
+
captureImage = true,
|
|
548
589
|
}: {
|
|
549
590
|
resolved: ResolvedPaneFunction;
|
|
550
591
|
context: MarketContext;
|
|
@@ -556,8 +597,9 @@ export async function renderDesktopShot({
|
|
|
556
597
|
scale?: number;
|
|
557
598
|
watermark?: string | null;
|
|
558
599
|
options: Record<string, string | true>;
|
|
600
|
+
captureImage?: boolean;
|
|
559
601
|
}): Promise<PaneScreenshotResult> {
|
|
560
|
-
await mkdir(dirname(outputPath), { recursive: true });
|
|
602
|
+
if (captureImage) await mkdir(dirname(outputPath), { recursive: true });
|
|
561
603
|
const apiProxy = resolveDesktopShotApiProxy(context);
|
|
562
604
|
const previousSessionToken = apiClient.getSessionToken();
|
|
563
605
|
let payload: DesktopPaneShotPayload;
|
|
@@ -575,7 +617,7 @@ export async function renderDesktopShot({
|
|
|
575
617
|
scale ?? 1,
|
|
576
618
|
watermark ?? null,
|
|
577
619
|
);
|
|
578
|
-
render = await renderDesktopPaneScreenshot(payload, outputPath, apiProxy);
|
|
620
|
+
render = await renderDesktopPaneScreenshot(payload, outputPath, apiProxy, { captureImage });
|
|
579
621
|
} finally {
|
|
580
622
|
apiClient.setSessionToken(previousSessionToken);
|
|
581
623
|
}
|
|
@@ -598,9 +640,10 @@ export async function renderDesktopShot({
|
|
|
598
640
|
);
|
|
599
641
|
const expectedChart = shotExpectedChart(resolved, payload);
|
|
600
642
|
const dataEvidence = shotDataEvidenceFor(resolved, payload);
|
|
601
|
-
const chartEvidenceMismatches =
|
|
602
|
-
? chartEvidenceMismatchesFor(render.semanticUi, expectedChart)
|
|
603
|
-
|
|
643
|
+
const chartEvidenceMismatches = [
|
|
644
|
+
...(expectedChart ? chartEvidenceMismatchesFor(render.semanticUi, expectedChart) : []),
|
|
645
|
+
...intradayChartEvidenceMismatchesFor(resolved, payload, render.semanticUi),
|
|
646
|
+
];
|
|
604
647
|
const semanticMismatch = missingExpectedText.length > 0
|
|
605
648
|
|| missingExpectedSelections.length > 0
|
|
606
649
|
|| chartEvidenceMismatches.length > 0;
|
|
@@ -619,6 +662,9 @@ export async function renderDesktopShot({
|
|
|
619
662
|
requiresStructuredDataEvidence: requiresStructuredDataEvidence(resolved),
|
|
620
663
|
hasStructuredDataEvidence: dataEvidence !== null,
|
|
621
664
|
});
|
|
665
|
+
const unusableReason = usable
|
|
666
|
+
? null
|
|
667
|
+
: shotUnusableReasonFor(resolved, payload, render, unavailableSymbols, semanticMismatch);
|
|
622
668
|
return {
|
|
623
669
|
kind: "pane-screenshot",
|
|
624
670
|
target: resolved.token,
|
|
@@ -637,6 +683,7 @@ export async function renderDesktopShot({
|
|
|
637
683
|
unavailableSymbols,
|
|
638
684
|
semanticMismatch,
|
|
639
685
|
usable,
|
|
686
|
+
unusableReason,
|
|
640
687
|
dataEvidence,
|
|
641
688
|
outputPath,
|
|
642
689
|
render: {
|
|
@@ -652,8 +699,37 @@ export async function renderDesktopShot({
|
|
|
652
699
|
}
|
|
653
700
|
|
|
654
701
|
function requiresStructuredDataEvidence(resolved: ResolvedPaneFunction): boolean {
|
|
655
|
-
return [
|
|
656
|
-
|
|
702
|
+
return [
|
|
703
|
+
"price-chart",
|
|
704
|
+
"intraday-price-chart",
|
|
705
|
+
"price-comparison",
|
|
706
|
+
"fundamental-series",
|
|
707
|
+
"financial-statements",
|
|
708
|
+
].includes(resolved.capability.id);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
export function shotUnusableReasonFor(
|
|
712
|
+
resolved: ResolvedPaneFunction,
|
|
713
|
+
payload: DesktopPaneShotPayload,
|
|
714
|
+
render: Pick<DesktopPaneShotRenderResult, "loadingStateDetected" | "errorStateDetected" | "emptyStateDetected">,
|
|
715
|
+
unavailableSymbols: string[],
|
|
716
|
+
semanticMismatch: boolean,
|
|
717
|
+
): string {
|
|
718
|
+
if (resolved.capability.id === "intraday-price-chart") {
|
|
719
|
+
const intraday = payload.intradayHistories[0];
|
|
720
|
+
if (intraday?.unavailableReason) return intraday.unavailableReason;
|
|
721
|
+
if (intraday && intraday.points.length > 0) {
|
|
722
|
+
return `Intraday bars were loaded for ${intraday.symbol}, but the chart did not render them.`;
|
|
723
|
+
}
|
|
724
|
+
const symbol = payload.financials[0]?.[0] ?? resolved.createOptions?.symbol ?? "the ticker";
|
|
725
|
+
return `No intraday price history is available for ${symbol} for the requested session window.`;
|
|
726
|
+
}
|
|
727
|
+
if (render.loadingStateDetected) return "The pane was still loading when the screenshot was captured.";
|
|
728
|
+
if (render.errorStateDetected) return "The pane rendered an error state.";
|
|
729
|
+
if (render.emptyStateDetected) return "The pane rendered an empty state.";
|
|
730
|
+
if (unavailableSymbols.length > 0) return `Data is unavailable for ${unavailableSymbols.join(", ")}.`;
|
|
731
|
+
if (semanticMismatch) return "The rendered content did not match the requested capability.";
|
|
732
|
+
return "The pane did not produce verifiable screenshot evidence.";
|
|
657
733
|
}
|
|
658
734
|
|
|
659
735
|
const STATEMENT_EVIDENCE_KEYS: Record<string, ReadonlySet<string>> = {
|
|
@@ -685,6 +761,23 @@ export function shotDataEvidenceFor(
|
|
|
685
761
|
resolved: ResolvedPaneFunction,
|
|
686
762
|
payload: DesktopPaneShotPayload,
|
|
687
763
|
): PaneScreenshotDataEvidence | null {
|
|
764
|
+
if (resolved.capability.id === "intraday-price-chart") {
|
|
765
|
+
const intraday = payload.intradayHistories[0];
|
|
766
|
+
if (!intraday || intraday.points.length === 0) return null;
|
|
767
|
+
const evidence = chartSeriesEvidence(intraday.symbol, intraday.points);
|
|
768
|
+
if (!evidence.first || !evidence.last) return null;
|
|
769
|
+
return {
|
|
770
|
+
kind: "price-series",
|
|
771
|
+
symbol: intraday.symbol,
|
|
772
|
+
range: intraday.rangePreset,
|
|
773
|
+
pointCount: evidence.pointCount,
|
|
774
|
+
first: evidence.first,
|
|
775
|
+
last: evidence.last,
|
|
776
|
+
resolution: intraday.resolution,
|
|
777
|
+
sessionDates: intraday.sessionDates,
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
|
|
688
781
|
if (resolved.capability.id === "price-chart") {
|
|
689
782
|
const range = String(resolved.options.rangePreset ?? "5Y") as TimeRange;
|
|
690
783
|
const [symbol, financials] = payload.financials[0] ?? [];
|
|
@@ -1140,6 +1233,48 @@ export function chartEvidenceMismatchesFor(
|
|
|
1140
1233
|
return mismatches;
|
|
1141
1234
|
}
|
|
1142
1235
|
|
|
1236
|
+
export function intradayChartEvidenceMismatchesFor(
|
|
1237
|
+
resolved: ResolvedPaneFunction,
|
|
1238
|
+
payload: DesktopPaneShotPayload,
|
|
1239
|
+
semanticUi: RemoteUiNodeSnapshot[],
|
|
1240
|
+
): string[] {
|
|
1241
|
+
if (resolved.capability.id !== "intraday-price-chart") return [];
|
|
1242
|
+
const intraday = payload.intradayHistories[0];
|
|
1243
|
+
if (!intraday || intraday.points.length === 0) return [];
|
|
1244
|
+
const metadata = semanticUi.find((node) => (
|
|
1245
|
+
node.role === "chart-data" && node.metadata?.kind === "chart-composer"
|
|
1246
|
+
))?.metadata;
|
|
1247
|
+
if (!metadata) return ["rendered intraday chart-data semantic evidence is missing"];
|
|
1248
|
+
const baseSeries = Array.isArray(metadata.baseSeries) ? metadata.baseSeries : [];
|
|
1249
|
+
const actual = baseSeries[0];
|
|
1250
|
+
if (!isRecord(actual)) return ["rendered intraday base series is missing"];
|
|
1251
|
+
|
|
1252
|
+
const expected = chartSeriesEvidence(intraday.symbol, intraday.points);
|
|
1253
|
+
const mismatches: string[] = [];
|
|
1254
|
+
if (actual.pointCount !== expected.pointCount) {
|
|
1255
|
+
mismatches.push("rendered intraday point count does not match");
|
|
1256
|
+
}
|
|
1257
|
+
if (metadata.projectedPointCount !== expected.pointCount) {
|
|
1258
|
+
mismatches.push("rendered intraday projection point count does not match");
|
|
1259
|
+
}
|
|
1260
|
+
const pointMatches = (
|
|
1261
|
+
actualPoint: unknown,
|
|
1262
|
+
expectedPoint: PaneScreenshotChartPointEvidence | null,
|
|
1263
|
+
): boolean => {
|
|
1264
|
+
if (!isRecord(actualPoint) || !expectedPoint) return false;
|
|
1265
|
+
return actualPoint.date === expectedPoint.date
|
|
1266
|
+
&& typeof actualPoint.value === "number"
|
|
1267
|
+
&& closeEnough(actualPoint.value, expectedPoint.close);
|
|
1268
|
+
};
|
|
1269
|
+
if (!pointMatches(actual.first, expected.first)) {
|
|
1270
|
+
mismatches.push("rendered intraday first bar does not match");
|
|
1271
|
+
}
|
|
1272
|
+
if (!pointMatches(actual.last, expected.last)) {
|
|
1273
|
+
mismatches.push("rendered intraday last bar does not match");
|
|
1274
|
+
}
|
|
1275
|
+
return mismatches;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1143
1278
|
export function shotUnavailableSymbols(
|
|
1144
1279
|
resolved: ResolvedPaneFunction,
|
|
1145
1280
|
payload: DesktopPaneShotPayload,
|
|
@@ -1162,6 +1297,16 @@ export function shotUnavailableSymbols(
|
|
|
1162
1297
|
return [];
|
|
1163
1298
|
});
|
|
1164
1299
|
}
|
|
1300
|
+
if (resolved.capability.id === "intraday-price-chart") {
|
|
1301
|
+
const symbol = payload.intradayHistories[0]?.symbol ?? payload.financials[0]?.[0];
|
|
1302
|
+
const metadata = semanticUi.find((node) => (
|
|
1303
|
+
node.role === "chart-data" && node.metadata?.kind === "chart-composer"
|
|
1304
|
+
))?.metadata;
|
|
1305
|
+
const renderedPoints = typeof metadata?.projectedPointCount === "number"
|
|
1306
|
+
? metadata.projectedPointCount
|
|
1307
|
+
: 0;
|
|
1308
|
+
return symbol && renderedPoints <= 0 ? [symbol] : [];
|
|
1309
|
+
}
|
|
1165
1310
|
const graphKind = shotGraphKind(resolved);
|
|
1166
1311
|
if (graphKind) {
|
|
1167
1312
|
const metric = resolved.options.metric as GraphMetricKey;
|
|
@@ -1174,7 +1319,7 @@ export function shotUnavailableSymbols(
|
|
|
1174
1319
|
).length === 0 ? [symbol] : []
|
|
1175
1320
|
));
|
|
1176
1321
|
}
|
|
1177
|
-
if (["price-chart", "
|
|
1322
|
+
if (["price-chart", "historical-prices"].includes(resolved.capability.id)) {
|
|
1178
1323
|
return payload.financials.flatMap(([symbol, financials]) => financials.priceHistory.length > 0 ? [] : [symbol]);
|
|
1179
1324
|
}
|
|
1180
1325
|
if (["price-comparison", "return-correlation", "security-relationship"].includes(resolved.capability.id)) {
|
|
@@ -1211,6 +1356,14 @@ export function shotSemanticRowCount(
|
|
|
1211
1356
|
count + (isRecord(entry) && typeof entry.pointCount === "number" ? Math.max(0, entry.pointCount) : 0)
|
|
1212
1357
|
), 0);
|
|
1213
1358
|
}
|
|
1359
|
+
if (resolved.capability.id === "intraday-price-chart") {
|
|
1360
|
+
const metadata = semanticUi.find((node) => (
|
|
1361
|
+
node.role === "chart-data" && node.metadata?.kind === "chart-composer"
|
|
1362
|
+
))?.metadata;
|
|
1363
|
+
return typeof metadata?.projectedPointCount === "number"
|
|
1364
|
+
? Math.max(0, metadata.projectedPointCount)
|
|
1365
|
+
: 0;
|
|
1366
|
+
}
|
|
1214
1367
|
const graphKind = shotGraphKind(resolved);
|
|
1215
1368
|
if (graphKind) {
|
|
1216
1369
|
const metric = resolved.options.metric as GraphMetricKey;
|
|
@@ -1223,7 +1376,7 @@ export function shotSemanticRowCount(
|
|
|
1223
1376
|
).length
|
|
1224
1377
|
), 0);
|
|
1225
1378
|
}
|
|
1226
|
-
if (["price-chart", "
|
|
1379
|
+
if (["price-chart", "historical-prices"].includes(resolved.capability.id)) {
|
|
1227
1380
|
return payload.financials[0]?.[1].priceHistory.length ?? 0;
|
|
1228
1381
|
}
|
|
1229
1382
|
if (["price-comparison", "return-correlation", "security-relationship"].includes(resolved.capability.id)) {
|
|
@@ -11,24 +11,31 @@ const log = debugLog.createLogger("plugin-seed");
|
|
|
11
11
|
* Runs before the plugin catalog is read so a restored plugin is available in
|
|
12
12
|
* the same session, and stays quiet on failure: an offline launch should start
|
|
13
13
|
* normally and try again next time, not block on a network request.
|
|
14
|
+
*
|
|
15
|
+
* Returns the seeded ids so a caller that already holds the config in memory
|
|
16
|
+
* can adopt them. The desktop loads its config before restoring, and without
|
|
17
|
+
* this it would keep an object whose `seededPlugins` is one launch stale and
|
|
18
|
+
* overwrite the record on the next save.
|
|
14
19
|
*/
|
|
15
|
-
export async function restoreExtractedPlugins(): Promise<
|
|
20
|
+
export async function restoreExtractedPlugins(): Promise<string[] | null> {
|
|
16
21
|
try {
|
|
17
22
|
const config = await loadCliConfigIfAvailable();
|
|
18
23
|
// No data directory yet means a first run: there is nothing to restore.
|
|
19
|
-
if (!config) return;
|
|
24
|
+
if (!config) return null;
|
|
20
25
|
const { installPlugin } = await import("./commands/plugins");
|
|
21
26
|
const result = await seedExtractedPlugins(config, installPlugin);
|
|
22
27
|
|
|
23
28
|
const seeded = [...new Set(result.seeded)].sort();
|
|
24
29
|
const existing = [...new Set(config.seededPlugins ?? [])].sort();
|
|
25
|
-
if (seeded.join() === existing.join()) return;
|
|
30
|
+
if (seeded.join() === existing.join()) return seeded;
|
|
26
31
|
|
|
27
32
|
await saveConfig({ ...config, seededPlugins: seeded });
|
|
28
33
|
if (result.installed.length > 0) {
|
|
29
34
|
log.info(`Restored ${result.installed.join(", ")} into ~/.gloomberb/plugins`);
|
|
30
35
|
}
|
|
36
|
+
return seeded;
|
|
31
37
|
} catch (error) {
|
|
32
38
|
log.error(`Plugin restore skipped: ${error}`);
|
|
39
|
+
return null;
|
|
33
40
|
}
|
|
34
41
|
}
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
buildCompositeNavigationFrame,
|
|
32
32
|
zoomCompositeViewport,
|
|
33
33
|
} from "./interactions";
|
|
34
|
+
import { COMPOSITE_RIGHT_OFFSET_RATIO } from "./time-scale";
|
|
34
35
|
import { getThemeColors, syncTheme } from "../../../theme/colors";
|
|
35
36
|
import { ThemeProvider } from "../../../theme/theme-context";
|
|
36
37
|
import { DEFAULT_THEME } from "../../../theme/themes";
|
|
@@ -304,6 +305,70 @@ describe("CompositeChart", () => {
|
|
|
304
305
|
expect(testSetup.captureCharFrame()).toContain("ACME Price $110 +10.65%");
|
|
305
306
|
});
|
|
306
307
|
|
|
308
|
+
test("holds a lower panel's rows while its series has no observations in view", async () => {
|
|
309
|
+
const price: ResolvedSeries = {
|
|
310
|
+
...series("price", "main", "left", "USD", []),
|
|
311
|
+
points: [
|
|
312
|
+
point("2025-01-01", 100),
|
|
313
|
+
point("2025-01-02", 103),
|
|
314
|
+
point("2025-01-03", 101),
|
|
315
|
+
point("2025-01-04", 104),
|
|
316
|
+
point("2025-01-05", 102),
|
|
317
|
+
],
|
|
318
|
+
};
|
|
319
|
+
const volume: ResolvedSeries = {
|
|
320
|
+
...series("volume", "volume", "left", "shares", []),
|
|
321
|
+
label: "Volume",
|
|
322
|
+
unitGroup: "volume",
|
|
323
|
+
style: "columns",
|
|
324
|
+
points: [point("2025-01-01", 4_000), point("2025-01-02", 6_000)],
|
|
325
|
+
};
|
|
326
|
+
const panels = [{ id: "main", height: 3 }, { id: "volume", height: 1 }];
|
|
327
|
+
const renderChart = async (
|
|
328
|
+
volumeSeries: ResolvedSeries,
|
|
329
|
+
viewport?: { start: Date; end: Date },
|
|
330
|
+
) => {
|
|
331
|
+
testSetup = await testRender(
|
|
332
|
+
<CompositeChart
|
|
333
|
+
width={78}
|
|
334
|
+
height={18}
|
|
335
|
+
series={[price, volumeSeries]}
|
|
336
|
+
panels={panels}
|
|
337
|
+
viewport={viewport}
|
|
338
|
+
/>,
|
|
339
|
+
{ width: 80, height: 20 },
|
|
340
|
+
);
|
|
341
|
+
await act(async () => {
|
|
342
|
+
await testSetup!.renderOnce();
|
|
343
|
+
await testSetup!.renderOnce();
|
|
344
|
+
});
|
|
345
|
+
const frame = testSetup!.captureCharFrame();
|
|
346
|
+
await act(async () => testSetup!.renderer.destroy());
|
|
347
|
+
testSetup = undefined;
|
|
348
|
+
return frame;
|
|
349
|
+
};
|
|
350
|
+
// The lowest price-axis label sits on the boundary between the two panels,
|
|
351
|
+
// so it moves down the moment the lower panel gives up its rows.
|
|
352
|
+
const priceAxisFloor = (frame: string) => frame
|
|
353
|
+
.split("\n")
|
|
354
|
+
.reduce((row, line, index) => line.includes("$") ? index : row, -1);
|
|
355
|
+
|
|
356
|
+
const loaded = await renderChart(volume);
|
|
357
|
+
// Panned to a window the upper panel covers and the lower panel has no bars in.
|
|
358
|
+
const panned = await renderChart(volume, {
|
|
359
|
+
start: new Date("2025-01-03T00:00:00.000Z"),
|
|
360
|
+
end: new Date("2025-01-05T00:00:00.000Z"),
|
|
361
|
+
});
|
|
362
|
+
// The same chart before the lower panel's data has loaded at all.
|
|
363
|
+
const loading = await renderChart({ ...volume, points: [] });
|
|
364
|
+
|
|
365
|
+
expect(priceAxisFloor(loaded)).toBeGreaterThan(0);
|
|
366
|
+
expect(priceAxisFloor(panned)).toBe(priceAxisFloor(loaded));
|
|
367
|
+
expect(priceAxisFloor(loading)).toBe(priceAxisFloor(loaded));
|
|
368
|
+
// A panel with loaded history keeps its axis while the window holds no bars.
|
|
369
|
+
expect(panned).toMatch(/\dK/);
|
|
370
|
+
});
|
|
371
|
+
|
|
307
372
|
test("lays out mixed panels with one shared legend and time axis", async () => {
|
|
308
373
|
testSetup = await testRender(
|
|
309
374
|
<CompositeChart
|
|
@@ -1429,7 +1494,9 @@ describe("CompositeChart", () => {
|
|
|
1429
1494
|
frame,
|
|
1430
1495
|
viewport,
|
|
1431
1496
|
1 + 4 * 0.04,
|
|
1432
|
-
|
|
1497
|
+
// The pointer reads in plot space, which is wider than the viewport by
|
|
1498
|
+
// the reserved right offset.
|
|
1499
|
+
pointerX / Math.max(plotWidth - 1, 1) * (1 + COMPOSITE_RIGHT_OFFSET_RATIO),
|
|
1433
1500
|
);
|
|
1434
1501
|
|
|
1435
1502
|
await act(async () => {
|