gloomberb 0.7.2 → 0.7.4
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 +79 -132
- package/package.json +1 -1
- package/src/app.tsx +73 -67
- package/src/components/chart/chart-data.ts +7 -2
- package/src/components/chart/chart-pane-settings.ts +7 -4
- package/src/components/chart/chart-renderer.ts +158 -47
- package/src/components/chart/chart-types.ts +20 -2
- package/src/components/chart/chart.test.ts +179 -13
- package/src/components/chart/comparison-chart-renderer.test.ts +14 -30
- package/src/components/chart/comparison-chart-renderer.ts +50 -11
- package/src/components/chart/comparison-stock-chart.tsx +129 -129
- package/src/components/chart/market-session.ts +187 -0
- package/src/components/chart/native/chart-rasterizer.ts +108 -19
- package/src/components/chart/price-axis-labels.tsx +25 -9
- package/src/components/chart/stock-chart-renderer-switch.test.tsx +97 -1
- package/src/components/chart/stock-chart.tsx +95 -49
- package/src/components/chart/time-axis-label.tsx +56 -2
- package/src/components/command-bar/command-bar.test.tsx +446 -120
- package/src/components/command-bar/command-bar.tsx +535 -384
- package/src/components/command-bar/command-registry.ts +16 -6
- package/src/components/command-bar/list-model.ts +8 -4
- package/src/components/command-bar/root-shortcuts.ts +4 -5
- package/src/components/command-bar/view-model.test.ts +21 -0
- package/src/components/command-bar/view-model.ts +17 -4
- package/src/components/command-bar/workflow-ops.ts +11 -32
- package/src/components/data-table-view.test.tsx +24 -1
- package/src/components/data-table-view.tsx +3 -2
- package/src/components/feed-data-table-stack-view.tsx +3 -2
- package/src/components/layout/detached-pane-shell.tsx +2 -2
- package/src/components/layout/floating-pane.tsx +9 -2
- package/src/components/layout/pane-footer.tsx +1 -1
- package/src/components/layout/pane-header.tsx +4 -2
- package/src/components/layout/pane-sizing.ts +12 -0
- package/src/components/layout/pane.tsx +24 -2
- package/src/components/layout/shell.tsx +24 -10
- package/src/components/layout/status-bar.test.tsx +4 -1
- package/src/components/layout/status-bar.tsx +4 -24
- package/src/components/markdown-text.tsx +53 -8
- package/src/components/onboarding/onboarding-wizard.tsx +7 -6
- package/src/components/pane-settings-dialog.tsx +9 -6
- package/src/components/pane-template-wizard.tsx +3 -2
- package/src/components/price-selector-dialog.tsx +3 -2
- package/src/components/price-sparkline-view.tsx +463 -0
- package/src/components/price-sparkline.ts +46 -0
- package/src/components/table-view-shared.tsx +21 -3
- package/src/components/ticker-list-table-view.tsx +3 -2
- package/src/components/ticker-list-table.tsx +15 -66
- package/src/components/toggle-list.tsx +3 -3
- package/src/components/ui/choice-dialog.tsx +3 -2
- package/src/components/ui/multi-select-dialog.tsx +3 -2
- package/src/components/ui/native-select.tsx +3 -3
- package/src/core/state/app-state.ts +210 -80
- package/src/core/state/session-persistence.ts +25 -0
- package/src/data/config-store-node.ts +113 -25
- package/src/data/config-store.test.ts +65 -3
- package/src/data/plugin-state-store.ts +39 -13
- package/src/data/sqlite-cache.test.ts +13 -0
- package/src/market-data/coordinator-subscriptions.test.ts +102 -18
- package/src/market-data/coordinator.test.ts +122 -2
- package/src/market-data/coordinator.ts +200 -56
- package/src/market-data/hooks.test.tsx +31 -0
- package/src/market-data/hooks.tsx +1 -0
- package/src/news/aggregator.test.ts +41 -0
- package/src/news/aggregator.ts +61 -9
- package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +25 -6
- package/src/plugins/builtin/ai/screener-pane.tsx +3 -0
- package/src/plugins/builtin/ai/settings.ts +3 -0
- package/src/plugins/builtin/broker-manager/index.tsx +4 -3
- package/src/plugins/builtin/chat-controller.test.ts +398 -17
- package/src/plugins/builtin/chat-controller.ts +263 -48
- package/src/plugins/builtin/chat.test.tsx +312 -6
- package/src/plugins/builtin/chat.tsx +245 -66
- package/src/plugins/builtin/comparison-chart.test.tsx +117 -3
- package/src/plugins/builtin/correlation/colors.test.ts +39 -0
- package/src/plugins/builtin/correlation/colors.ts +87 -0
- package/src/plugins/builtin/correlation/index.test.tsx +151 -0
- package/src/plugins/builtin/correlation/index.tsx +92 -21
- package/src/plugins/builtin/debug.tsx +4 -3
- package/src/plugins/builtin/econ/index.tsx +3 -2
- package/src/plugins/builtin/help.test.tsx +2 -2
- package/src/plugins/builtin/holders/index.tsx +25 -13
- package/src/plugins/builtin/layout-manager.tsx +1 -7
- package/src/plugins/builtin/market-movers/index.tsx +72 -40
- package/src/plugins/builtin/news-wire/breaking-pane.test.tsx +156 -0
- package/src/plugins/builtin/news-wire/breaking-pane.tsx +0 -90
- package/src/plugins/builtin/news-wire/index.tsx +1 -3
- package/src/plugins/builtin/news-wire/news-detail-view.tsx +3 -2
- package/src/plugins/builtin/news-wire/news-preset-pane.tsx +4 -1
- package/src/plugins/builtin/news-wire/persisted-articles.ts +7 -1
- package/src/plugins/builtin/options.tsx +9 -17
- package/src/plugins/builtin/portfolio-list/cli/commands.ts +6 -10
- package/src/plugins/builtin/portfolio-list/metrics.ts +13 -2
- package/src/plugins/builtin/portfolio-list/mutations.ts +17 -0
- package/src/plugins/builtin/portfolio-list/pane.tsx +139 -23
- package/src/plugins/builtin/portfolio-list/quick-add.tsx +422 -0
- package/src/plugins/builtin/portfolio-list/settings.ts +20 -12
- package/src/plugins/builtin/portfolio-list/table.tsx +3 -3
- package/src/plugins/builtin/portfolio-list.test.tsx +311 -9
- package/src/plugins/builtin/quote-monitor.test.tsx +158 -25
- package/src/plugins/builtin/sectors/index.tsx +275 -43
- package/src/plugins/builtin/sectors/sector-data.ts +41 -1
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +3 -2
- package/src/plugins/builtin/ticker-detail/index.tsx +30 -9
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +25 -15
- package/src/plugins/builtin/ticker-detail/pane.tsx +18 -7
- package/src/plugins/builtin/ticker-detail/quote-monitor.tsx +445 -63
- package/src/plugins/builtin/ticker-detail/settings.ts +84 -3
- package/src/plugins/builtin/ticker-detail.test.tsx +2 -2
- package/src/plugins/builtin/world-indices/index.tsx +58 -19
- package/src/plugins/ibkr/trading-pane.tsx +3 -2
- package/src/plugins/plugin-runtime.tsx +2 -2
- package/src/plugins/prediction-markets/settings.ts +4 -0
- package/src/plugins/registry.test.ts +55 -0
- package/src/plugins/registry.ts +11 -4
- package/src/plugins/ticker-navigation.test.ts +35 -5
- package/src/plugins/ticker-navigation.ts +16 -22
- package/src/renderers/electrobun/bun/application-menu-click.test.ts +9 -0
- package/src/renderers/electrobun/bun/application-menu-click.ts +7 -4
- package/src/renderers/electrobun/bun/application-menu.ts +6 -1
- package/src/renderers/electrobun/bun/desktop-workspace.ts +47 -29
- package/src/renderers/electrobun/bun/index.ts +52 -13
- package/src/renderers/electrobun/view/app-services.ts +51 -9
- package/src/renderers/electrobun/view/backend-rpc.ts +5 -8
- package/src/renderers/electrobun/view/desktop-controls.tsx +4 -4
- package/src/renderers/electrobun/view/main.tsx +0 -16
- package/src/renderers/electrobun/view/styles.css +23 -21
- package/src/renderers/electrobun/view/ui-host.tsx +6 -4
- package/src/renderers/opentui/host.tsx +2 -1
- package/src/renderers/opentui/start.tsx +1 -1
- package/src/renderers/opentui/test-utils.tsx +6 -5
- package/src/sources/gloomberb-cloud.test.ts +55 -2
- package/src/sources/gloomberb-cloud.ts +180 -29
- package/src/sources/provider-router.test.ts +567 -10
- package/src/sources/provider-router.ts +323 -93
- package/src/sources/yahoo-finance.test.ts +37 -0
- package/src/sources/yahoo-finance.ts +48 -2
- package/src/state/app-bootstrap.test.ts +50 -23
- package/src/state/app-bootstrap.ts +29 -13
- package/src/state/app-context.test.ts +113 -0
- package/src/state/app-context.tsx +8 -4
- package/src/state/session-persistence.test.ts +5 -3
- package/src/state/use-inline-tickers.ts +3 -0
- package/src/state/use-quote-streaming.ts +11 -0
- package/src/theme/colors.ts +13 -14
- package/src/types/config.ts +9 -4
- package/src/types/data-provider.ts +5 -1
- package/src/types/financials.ts +1 -0
- package/src/types/plugin.ts +8 -1
- package/src/ui/context-menu.tsx +4 -4
- package/src/utils/api-client.test.ts +248 -0
- package/src/utils/api-client.ts +178 -26
- package/src/utils/exchanges.test.ts +16 -0
- package/src/utils/exchanges.ts +33 -0
- package/src/utils/fuzzy-search.ts +12 -1
- package/src/utils/keyboard.ts +31 -0
- package/src/utils/options.ts +1 -1
- package/src/utils/price-history.test.ts +28 -1
- package/src/utils/price-history.ts +16 -0
- package/src/utils/quote-freshness.test.ts +150 -0
- package/src/utils/quote-freshness.ts +42 -1
- package/src/utils/quote-resolution.ts +2 -0
- package/src/utils/quote-time.test.ts +34 -0
- package/src/utils/quote-time.ts +18 -5
- package/src/utils/ticker-search.test.ts +2 -2
- package/src/utils/ticker-search.ts +48 -1
- package/src/version.ts +1 -1
- package/src/plugins/builtin/news-wire/digest-store.ts +0 -44
|
@@ -33,6 +33,43 @@ describe("YahooFinanceClient exchange aliases", () => {
|
|
|
33
33
|
expect(history[0]?.close).toBe(200);
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
+
test("maps IBKR option symbols to Yahoo option quote marks", async () => {
|
|
37
|
+
const provider = new YahooFinanceClient() as any;
|
|
38
|
+
provider.getOptionsChain = async (ticker: string, _exchange: string, expirationDate: number) => ({
|
|
39
|
+
underlyingSymbol: ticker,
|
|
40
|
+
expirationDates: [expirationDate],
|
|
41
|
+
calls: [{
|
|
42
|
+
contractSymbol: "AMD270917C00230000",
|
|
43
|
+
strike: 230,
|
|
44
|
+
currency: "USD",
|
|
45
|
+
lastPrice: 251,
|
|
46
|
+
change: 1.5,
|
|
47
|
+
percentChange: 0.6,
|
|
48
|
+
volume: 10,
|
|
49
|
+
openInterest: 20,
|
|
50
|
+
bid: 250.25,
|
|
51
|
+
ask: 254.5,
|
|
52
|
+
impliedVolatility: 0.4,
|
|
53
|
+
inTheMoney: false,
|
|
54
|
+
expiration: expirationDate,
|
|
55
|
+
lastTradeDate: 1_800_000_000,
|
|
56
|
+
}],
|
|
57
|
+
puts: [],
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const quote = await provider.getQuote("AMD 270917C00230000");
|
|
61
|
+
|
|
62
|
+
expect(quote).toMatchObject({
|
|
63
|
+
symbol: "AMD 270917C00230000",
|
|
64
|
+
price: 252.375,
|
|
65
|
+
mark: 252.375,
|
|
66
|
+
bid: 250.25,
|
|
67
|
+
ask: 254.5,
|
|
68
|
+
providerId: "yahoo",
|
|
69
|
+
});
|
|
70
|
+
expect(quote.lastUpdated).toBe(1_800_000_000_000);
|
|
71
|
+
});
|
|
72
|
+
|
|
36
73
|
test("preserves analyst rating price targets from upgrade history", async () => {
|
|
37
74
|
const provider = new YahooFinanceClient() as any;
|
|
38
75
|
let requestUrl = "";
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type ManualChartResolution,
|
|
8
8
|
} from "../components/chart/chart-resolution";
|
|
9
9
|
import type { InstrumentSearchResult } from "../types/instrument";
|
|
10
|
+
import { parseOptionSymbol } from "../utils/options";
|
|
10
11
|
import { SecEdgarClient } from "./sec-edgar";
|
|
11
12
|
|
|
12
13
|
// Exchange suffix mapping for Yahoo Finance ticker symbols
|
|
@@ -755,7 +756,7 @@ export class YahooFinanceClient implements DataProvider {
|
|
|
755
756
|
const history: PricePoint[] = [];
|
|
756
757
|
for (let i = 0; i < result.timestamp.length; i++) {
|
|
757
758
|
const close = quote.close?.[i];
|
|
758
|
-
if (close == null || Number.
|
|
759
|
+
if (close == null || !Number.isFinite(close) || close <= 0) continue;
|
|
759
760
|
history.push({
|
|
760
761
|
date: new Date((result.timestamp[i]!) * 1000),
|
|
761
762
|
open: quote.open?.[i] ?? undefined,
|
|
@@ -1047,8 +1048,53 @@ export class YahooFinanceClient implements DataProvider {
|
|
|
1047
1048
|
};
|
|
1048
1049
|
}
|
|
1049
1050
|
|
|
1051
|
+
private async getOptionQuote(ticker: string, context?: MarketDataRequestContext): Promise<Quote> {
|
|
1052
|
+
const parsed = parseOptionSymbol(ticker);
|
|
1053
|
+
if (!parsed) throw new Error(`Unsupported option symbol ${ticker}`);
|
|
1054
|
+
const chain = await this.getOptionsChain(parsed.underlying, "", parsed.expTs, context);
|
|
1055
|
+
const contracts = parsed.side === "C" ? chain.calls : chain.puts;
|
|
1056
|
+
const contract = contracts.find((candidate) =>
|
|
1057
|
+
Math.abs(candidate.strike - parsed.strike) < 0.001 &&
|
|
1058
|
+
candidate.expiration === parsed.expTs
|
|
1059
|
+
);
|
|
1060
|
+
if (!contract) throw new Error(`No option contract for ${ticker}`);
|
|
1061
|
+
|
|
1062
|
+
const mark = contract.bid > 0 && contract.ask > 0
|
|
1063
|
+
? (contract.bid + contract.ask) / 2
|
|
1064
|
+
: contract.bid > 0
|
|
1065
|
+
? contract.bid
|
|
1066
|
+
: contract.ask > 0
|
|
1067
|
+
? contract.ask
|
|
1068
|
+
: undefined;
|
|
1069
|
+
return {
|
|
1070
|
+
symbol: ticker,
|
|
1071
|
+
providerId: this.id,
|
|
1072
|
+
price: mark ?? contract.lastPrice,
|
|
1073
|
+
currency: contract.currency || "USD",
|
|
1074
|
+
change: contract.change,
|
|
1075
|
+
changePercent: contract.percentChange,
|
|
1076
|
+
volume: contract.volume,
|
|
1077
|
+
bid: contract.bid,
|
|
1078
|
+
ask: contract.ask,
|
|
1079
|
+
mark,
|
|
1080
|
+
name: contract.contractSymbol,
|
|
1081
|
+
lastUpdated: contract.lastTradeDate > 0
|
|
1082
|
+
? contract.lastTradeDate * 1000
|
|
1083
|
+
: Date.now(),
|
|
1084
|
+
exchangeName: "OPTIONS",
|
|
1085
|
+
fullExchangeName: "OPTIONS",
|
|
1086
|
+
listingExchangeName: "OPTIONS",
|
|
1087
|
+
listingExchangeFullName: "OPTIONS",
|
|
1088
|
+
dataSource: "delayed",
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1050
1092
|
/** Fetch just a quote (lighter weight) */
|
|
1051
|
-
async getQuote(ticker: string, exchange = "",
|
|
1093
|
+
async getQuote(ticker: string, exchange = "", context?: MarketDataRequestContext): Promise<Quote> {
|
|
1094
|
+
if (parseOptionSymbol(ticker)) {
|
|
1095
|
+
return this.getOptionQuote(ticker, context);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1052
1098
|
const symbolsToTry = this.getSymbolsToTry(ticker, exchange);
|
|
1053
1099
|
let lastError: any;
|
|
1054
1100
|
|
|
@@ -408,8 +408,28 @@ describe("initializeAppState", () => {
|
|
|
408
408
|
custom: {},
|
|
409
409
|
tags: [],
|
|
410
410
|
});
|
|
411
|
+
const hydrationSymbols = [
|
|
412
|
+
"AAPL",
|
|
413
|
+
...Array.from({ length: 13 }, (_, index) => `T${String(index + 1).padStart(2, "0")}`),
|
|
414
|
+
];
|
|
415
|
+
for (const symbol of hydrationSymbols.slice(1)) {
|
|
416
|
+
await tickerRepository.createTicker({
|
|
417
|
+
ticker: symbol,
|
|
418
|
+
exchange: "NASDAQ",
|
|
419
|
+
currency: "USD",
|
|
420
|
+
name: symbol,
|
|
421
|
+
portfolios: [],
|
|
422
|
+
watchlists: ["main"],
|
|
423
|
+
positions: [],
|
|
424
|
+
broker_contracts: [],
|
|
425
|
+
custom: {},
|
|
426
|
+
tags: [],
|
|
427
|
+
});
|
|
428
|
+
}
|
|
411
429
|
|
|
412
430
|
const events: string[] = [];
|
|
431
|
+
const cachedTargetRequests: string[] = [];
|
|
432
|
+
const financialRefreshes: string[] = [];
|
|
413
433
|
|
|
414
434
|
await initializeAppState({
|
|
415
435
|
config: quoteOnlyConfig,
|
|
@@ -417,23 +437,26 @@ describe("initializeAppState", () => {
|
|
|
417
437
|
dataProvider: {
|
|
418
438
|
id: "test-provider",
|
|
419
439
|
name: "Test Provider",
|
|
420
|
-
getCachedFinancialsForTargets: () =>
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
440
|
+
getCachedFinancialsForTargets: (targets: Array<{ symbol: string }>) => {
|
|
441
|
+
cachedTargetRequests.push(...targets.map((target) => target.symbol));
|
|
442
|
+
return new Map(targets.map((target) => [target.symbol, {
|
|
443
|
+
annualStatements: [],
|
|
444
|
+
quarterlyStatements: [],
|
|
445
|
+
priceHistory: [],
|
|
446
|
+
quote: {
|
|
447
|
+
symbol: target.symbol,
|
|
448
|
+
price: 200,
|
|
449
|
+
currency: "USD",
|
|
450
|
+
change: 1,
|
|
451
|
+
changePercent: 0.5,
|
|
452
|
+
marketCap: 2_000_000_000,
|
|
453
|
+
lastUpdated: Date.now(),
|
|
454
|
+
},
|
|
455
|
+
fundamentals: {
|
|
456
|
+
trailingPE: 25,
|
|
457
|
+
},
|
|
458
|
+
}]));
|
|
459
|
+
},
|
|
437
460
|
} as any,
|
|
438
461
|
sessionSnapshot: {
|
|
439
462
|
paneState: {
|
|
@@ -446,11 +469,11 @@ describe("initializeAppState", () => {
|
|
|
446
469
|
activePanel: "left",
|
|
447
470
|
statusBarVisible: true,
|
|
448
471
|
openPaneIds: ["portfolio-list:main"],
|
|
449
|
-
hydrationTargets:
|
|
450
|
-
symbol
|
|
472
|
+
hydrationTargets: hydrationSymbols.map((symbol) => ({
|
|
473
|
+
symbol,
|
|
451
474
|
exchange: "NASDAQ",
|
|
452
475
|
instrument: null,
|
|
453
|
-
}
|
|
476
|
+
})),
|
|
454
477
|
exchangeCurrencies: [],
|
|
455
478
|
savedAt: Date.now(),
|
|
456
479
|
},
|
|
@@ -458,13 +481,17 @@ describe("initializeAppState", () => {
|
|
|
458
481
|
primeCachedFinancials: (entries) => {
|
|
459
482
|
events.push(`prime:${entries.map((entry) => entry.ticker.metadata.ticker).join(",")}`);
|
|
460
483
|
},
|
|
461
|
-
refreshTicker: () => {},
|
|
484
|
+
refreshTicker: (symbol) => { financialRefreshes.push(symbol); },
|
|
462
485
|
refreshQuote: () => {},
|
|
463
486
|
autoImportBrokerPositions: async () => {},
|
|
464
487
|
});
|
|
465
488
|
|
|
466
|
-
|
|
467
|
-
expect(
|
|
489
|
+
const primeEvent = events.find((event) => event.startsWith("prime:"));
|
|
490
|
+
expect(primeEvent).toContain("AAPL");
|
|
491
|
+
expect(primeEvent).toContain("T13");
|
|
492
|
+
expect(cachedTargetRequests).toContain("T13");
|
|
493
|
+
expect(financialRefreshes).not.toContain("T13");
|
|
494
|
+
expect(events.indexOf(primeEvent!)).toBeLessThan(events.indexOf("SET_INITIALIZED"));
|
|
468
495
|
|
|
469
496
|
persistence.close();
|
|
470
497
|
});
|
|
@@ -37,11 +37,11 @@ interface RefreshPlanEntry {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const MAX_BACKGROUND_WARMUP_TICKERS = 12;
|
|
40
|
-
const
|
|
41
|
-
"market_cap",
|
|
40
|
+
const PORTFOLIO_FULL_SNAPSHOT_COLUMN_IDS = new Set([
|
|
42
41
|
"pe",
|
|
43
42
|
"forward_pe",
|
|
44
43
|
"dividend_yield",
|
|
44
|
+
"sparkline",
|
|
45
45
|
]);
|
|
46
46
|
const startupLog = debugLog.createLogger("startup");
|
|
47
47
|
|
|
@@ -157,7 +157,7 @@ function buildRefreshPlan(
|
|
|
157
157
|
const columnIds = Array.isArray(instance.settings?.columnIds)
|
|
158
158
|
? instance.settings.columnIds.filter((value): value is string => typeof value === "string")
|
|
159
159
|
: [];
|
|
160
|
-
return columnIds.some((columnId) =>
|
|
160
|
+
return columnIds.some((columnId) => PORTFOLIO_FULL_SNAPSHOT_COLUMN_IDS.has(columnId))
|
|
161
161
|
? "financials"
|
|
162
162
|
: "quote";
|
|
163
163
|
};
|
|
@@ -216,26 +216,39 @@ function buildCachedFinancialTarget(ticker: TickerRecord): CachedFinancialsTarge
|
|
|
216
216
|
async function resolveCachedFinancialPrimeEntries(
|
|
217
217
|
refreshPlan: RefreshPlanEntry[],
|
|
218
218
|
sessionSnapshot: AppSessionSnapshot | null | undefined,
|
|
219
|
+
tickerMap: Map<string, TickerRecord>,
|
|
219
220
|
dataProvider: DataProvider,
|
|
220
221
|
): Promise<Array<{ ticker: TickerRecord; financials: TickerFinancials }>> {
|
|
221
222
|
if (!dataProvider.getCachedFinancialsForTargets) return [];
|
|
222
223
|
|
|
223
|
-
const
|
|
224
|
+
const targetEntriesBySymbol = new Map<string, { ticker: TickerRecord; target: CachedFinancialsTarget }>();
|
|
224
225
|
for (const target of sessionSnapshot?.hydrationTargets ?? []) {
|
|
225
|
-
|
|
226
|
+
const symbol = target.symbol.trim().toUpperCase();
|
|
227
|
+
const ticker = tickerMap.get(symbol);
|
|
228
|
+
if (ticker) {
|
|
229
|
+
targetEntriesBySymbol.set(symbol, { ticker, target });
|
|
230
|
+
}
|
|
226
231
|
}
|
|
227
232
|
|
|
228
|
-
const
|
|
229
|
-
|
|
233
|
+
for (const entry of refreshPlan.filter((entry) => entry.mode === "financials")) {
|
|
234
|
+
const symbol = entry.ticker.metadata.ticker.trim().toUpperCase();
|
|
235
|
+
if (!targetEntriesBySymbol.has(symbol)) {
|
|
236
|
+
targetEntriesBySymbol.set(symbol, {
|
|
237
|
+
ticker: entry.ticker,
|
|
238
|
+
target: buildCachedFinancialTarget(entry.ticker),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
230
242
|
|
|
243
|
+
const targetEntries = [...targetEntriesBySymbol.values()];
|
|
244
|
+
if (targetEntries.length === 0) return [];
|
|
231
245
|
const cachedFinancials = await dataProvider.getCachedFinancialsForTargets(
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
)),
|
|
246
|
+
targetEntries.map((entry) => entry.target),
|
|
247
|
+
{ includeStaleQuotes: true },
|
|
235
248
|
);
|
|
236
249
|
const primedEntries: Array<{ ticker: TickerRecord; financials: TickerFinancials }> = [];
|
|
237
250
|
|
|
238
|
-
for (const entry of
|
|
251
|
+
for (const entry of targetEntries) {
|
|
239
252
|
const cached = cachedFinancials.get(entry.ticker.metadata.ticker.trim().toUpperCase());
|
|
240
253
|
if (cached) {
|
|
241
254
|
primedEntries.push({ ticker: entry.ticker, financials: cached });
|
|
@@ -347,8 +360,11 @@ export async function initializeAppState({
|
|
|
347
360
|
if (primeCachedFinancials) {
|
|
348
361
|
const cachedPrimeEntries = await measurePerfAsync(
|
|
349
362
|
"startup.resolve-cached-financial-prime",
|
|
350
|
-
() => resolveCachedFinancialPrimeEntries(refreshPlan, sessionSnapshot, dataProvider),
|
|
351
|
-
{
|
|
363
|
+
() => resolveCachedFinancialPrimeEntries(refreshPlan, sessionSnapshot, tickerMap, dataProvider),
|
|
364
|
+
{
|
|
365
|
+
financialRefreshCount: refreshPlan.filter((entry) => entry.mode === "financials").length,
|
|
366
|
+
sessionHydrationTargetCount: sessionSnapshot?.hydrationTargets.length ?? 0,
|
|
367
|
+
},
|
|
352
368
|
);
|
|
353
369
|
if (cachedPrimeEntries.length > 0) {
|
|
354
370
|
measurePerf("startup.prime-cached-financials", () => {
|
|
@@ -329,6 +329,64 @@ describe("pane state updates", () => {
|
|
|
329
329
|
|
|
330
330
|
expect(next).toBe(initial);
|
|
331
331
|
});
|
|
332
|
+
|
|
333
|
+
test("keeps pane runtime state scoped to each saved layout", () => {
|
|
334
|
+
let state = createInitialState(createDefaultConfig("/tmp/gloomberb-test"));
|
|
335
|
+
const originalLayoutIndex = state.config.activeLayoutIndex;
|
|
336
|
+
|
|
337
|
+
state = appReducer(state, {
|
|
338
|
+
type: "UPDATE_PANE_STATE",
|
|
339
|
+
paneId: "ticker-detail:main",
|
|
340
|
+
patch: { activeTabId: "chart" },
|
|
341
|
+
});
|
|
342
|
+
state = appReducer(state, { type: "DUPLICATE_LAYOUT", index: originalLayoutIndex });
|
|
343
|
+
const duplicateLayoutIndex = state.config.activeLayoutIndex;
|
|
344
|
+
|
|
345
|
+
state = appReducer(state, {
|
|
346
|
+
type: "UPDATE_PANE_STATE",
|
|
347
|
+
paneId: "ticker-detail:main",
|
|
348
|
+
patch: { activeTabId: "financials" },
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
expect(state.config.layouts[duplicateLayoutIndex]?.paneState?.["ticker-detail:main"]?.activeTabId).toBe("financials");
|
|
352
|
+
|
|
353
|
+
state = appReducer(state, { type: "SWITCH_LAYOUT", index: originalLayoutIndex });
|
|
354
|
+
expect(state.paneState["ticker-detail:main"]?.activeTabId).toBe("chart");
|
|
355
|
+
expect(state.config.layouts[originalLayoutIndex]?.paneState?.["ticker-detail:main"]?.activeTabId).toBe("chart");
|
|
356
|
+
|
|
357
|
+
state = appReducer(state, { type: "SWITCH_LAYOUT", index: duplicateLayoutIndex });
|
|
358
|
+
expect(state.paneState["ticker-detail:main"]?.activeTabId).toBe("financials");
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
test("hydrates saved layout pane state before legacy session pane state", () => {
|
|
362
|
+
const config = createDefaultConfig("/tmp/gloomberb-test");
|
|
363
|
+
config.layouts[config.activeLayoutIndex] = {
|
|
364
|
+
...config.layouts[config.activeLayoutIndex]!,
|
|
365
|
+
paneState: {
|
|
366
|
+
"ticker-detail:main": { activeTabId: "chart" },
|
|
367
|
+
},
|
|
368
|
+
focusedPaneId: "ticker-detail:main",
|
|
369
|
+
activePanel: "right",
|
|
370
|
+
};
|
|
371
|
+
const sessionSnapshot: AppSessionSnapshot = {
|
|
372
|
+
paneState: {
|
|
373
|
+
"ticker-detail:main": { activeTabId: "overview" },
|
|
374
|
+
},
|
|
375
|
+
focusedPaneId: "portfolio-list:main",
|
|
376
|
+
activePanel: "left",
|
|
377
|
+
statusBarVisible: true,
|
|
378
|
+
openPaneIds: ["portfolio-list:main", "ticker-detail:main"],
|
|
379
|
+
hydrationTargets: [],
|
|
380
|
+
exchangeCurrencies: ["USD"],
|
|
381
|
+
savedAt: Date.now(),
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
const state = createInitialState(config, sessionSnapshot);
|
|
385
|
+
|
|
386
|
+
expect(state.paneState["ticker-detail:main"]?.activeTabId).toBe("chart");
|
|
387
|
+
expect(state.focusedPaneId).toBe("ticker-detail:main");
|
|
388
|
+
expect(state.activePanel).toBe("right");
|
|
389
|
+
});
|
|
332
390
|
});
|
|
333
391
|
|
|
334
392
|
describe("quote merging", () => {
|
|
@@ -487,6 +545,61 @@ describe("quote merging", () => {
|
|
|
487
545
|
});
|
|
488
546
|
|
|
489
547
|
describe("layout focus fallback", () => {
|
|
548
|
+
test("switching to an old layout without focused metadata uses that layout's top floating pane", () => {
|
|
549
|
+
const config = createDefaultConfig("/tmp/gloomberb-test");
|
|
550
|
+
const targetLayout = {
|
|
551
|
+
...cloneLayout(config.layout),
|
|
552
|
+
dockRoot: null,
|
|
553
|
+
floating: [
|
|
554
|
+
{ instanceId: "chat:main", x: 4, y: 2, width: 36, height: 10, zIndex: 70 },
|
|
555
|
+
{ instanceId: "ticker-detail:main", x: 12, y: 4, width: 36, height: 10, zIndex: 95 },
|
|
556
|
+
],
|
|
557
|
+
};
|
|
558
|
+
config.layouts = [
|
|
559
|
+
{ name: "Current", layout: cloneLayout(config.layout), paneState: {} },
|
|
560
|
+
{ name: "Floating", layout: cloneLayout(targetLayout), paneState: {} },
|
|
561
|
+
];
|
|
562
|
+
config.activeLayoutIndex = 0;
|
|
563
|
+
|
|
564
|
+
let state = createInitialState(config);
|
|
565
|
+
state = appReducer(state, { type: "FOCUS_PANE", paneId: "chat:main" });
|
|
566
|
+
state = appReducer(state, { type: "SWITCH_LAYOUT", index: 1 });
|
|
567
|
+
|
|
568
|
+
const chatEntry = state.config.layout.floating.find((entry) => entry.instanceId === "chat:main");
|
|
569
|
+
const tickerEntry = state.config.layout.floating.find((entry) => entry.instanceId === "ticker-detail:main");
|
|
570
|
+
expect(state.focusedPaneId).toBe("ticker-detail:main");
|
|
571
|
+
expect((tickerEntry?.zIndex ?? 0) > (chatEntry?.zIndex ?? 0)).toBe(true);
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
test("switching layouts raises the saved focused floating pane above stale z-order", () => {
|
|
575
|
+
const config = createDefaultConfig("/tmp/gloomberb-test");
|
|
576
|
+
const targetLayout = {
|
|
577
|
+
...cloneLayout(config.layout),
|
|
578
|
+
dockRoot: null,
|
|
579
|
+
floating: [
|
|
580
|
+
{ instanceId: "chat:main", x: 4, y: 2, width: 36, height: 10, zIndex: 70 },
|
|
581
|
+
{ instanceId: "ticker-detail:main", x: 12, y: 4, width: 36, height: 10, zIndex: 95 },
|
|
582
|
+
],
|
|
583
|
+
};
|
|
584
|
+
config.layouts = [
|
|
585
|
+
{ name: "Current", layout: cloneLayout(config.layout), paneState: {} },
|
|
586
|
+
{
|
|
587
|
+
name: "Floating",
|
|
588
|
+
layout: cloneLayout(targetLayout),
|
|
589
|
+
paneState: {},
|
|
590
|
+
focusedPaneId: "chat:main",
|
|
591
|
+
},
|
|
592
|
+
];
|
|
593
|
+
config.activeLayoutIndex = 0;
|
|
594
|
+
|
|
595
|
+
const state = appReducer(createInitialState(config), { type: "SWITCH_LAYOUT", index: 1 });
|
|
596
|
+
|
|
597
|
+
const chatEntry = state.config.layout.floating.find((entry) => entry.instanceId === "chat:main");
|
|
598
|
+
const tickerEntry = state.config.layout.floating.find((entry) => entry.instanceId === "ticker-detail:main");
|
|
599
|
+
expect(state.focusedPaneId).toBe("chat:main");
|
|
600
|
+
expect((chatEntry?.zIndex ?? 0) > (tickerEntry?.zIndex ?? 0)).toBe(true);
|
|
601
|
+
});
|
|
602
|
+
|
|
490
603
|
test("starts with the top floating pane focused when no session pane is active", () => {
|
|
491
604
|
const config = createDefaultConfig("/tmp/gloomberb-test");
|
|
492
605
|
const layout = {
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
type PaneRuntimeState,
|
|
38
38
|
resolveCollectionForPane,
|
|
39
39
|
resolveTickerForPane,
|
|
40
|
+
syncConfigActiveLayoutState,
|
|
40
41
|
} from "../core/state/app-state";
|
|
41
42
|
import type { AppAction, AppState } from "../core/state/app-state";
|
|
42
43
|
import { scheduleConfigSave } from "./config-save-scheduler";
|
|
@@ -50,6 +51,7 @@ export {
|
|
|
50
51
|
getPaneState,
|
|
51
52
|
resolveCollectionForPane,
|
|
52
53
|
resolveFinancialsForPane,
|
|
54
|
+
syncConfigActiveLayoutState,
|
|
53
55
|
resolveTickerFileForPane,
|
|
54
56
|
resolveTickerForPane,
|
|
55
57
|
} from "../core/state/app-state";
|
|
@@ -283,10 +285,12 @@ export function usePaneSettingValue<T>(
|
|
|
283
285
|
? (nextValue as (previousValue: T) => T)(currentValue)
|
|
284
286
|
: nextValue;
|
|
285
287
|
const layout = setPaneSetting(currentState.config.layout, scopedPaneId, key, resolved);
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
288
|
+
const nextConfig = syncConfigActiveLayoutState(
|
|
289
|
+
{ ...currentState.config, layout },
|
|
290
|
+
currentState.paneState,
|
|
291
|
+
currentState.focusedPaneId,
|
|
292
|
+
currentState.activePanel,
|
|
293
|
+
);
|
|
290
294
|
dispatch({ type: "SET_CONFIG", config: nextConfig });
|
|
291
295
|
scheduleConfigSave(nextConfig);
|
|
292
296
|
}, [dispatch, fallback, key, scopedPaneId, stateRef]);
|
|
@@ -24,7 +24,10 @@ function createTicker(symbol: string, exchange = "NASDAQ"): TickerRecord {
|
|
|
24
24
|
describe("session persistence", () => {
|
|
25
25
|
test("builds a working-set snapshot from runtime state", () => {
|
|
26
26
|
const config = createDefaultConfig("/tmp/gloomberb-test");
|
|
27
|
-
const tickers = new Map<string, TickerRecord>([
|
|
27
|
+
const tickers = new Map<string, TickerRecord>([
|
|
28
|
+
["AAPL", createTicker("AAPL")],
|
|
29
|
+
["MSFT", createTicker("MSFT")],
|
|
30
|
+
]);
|
|
28
31
|
const financials = new Map<string, TickerFinancials>([["AAPL", {
|
|
29
32
|
annualStatements: [],
|
|
30
33
|
quarterlyStatements: [],
|
|
@@ -61,8 +64,7 @@ describe("session persistence", () => {
|
|
|
61
64
|
});
|
|
62
65
|
|
|
63
66
|
expect(snapshot.focusedPaneId).toBe("ticker-detail:main");
|
|
64
|
-
expect(snapshot.hydrationTargets).
|
|
65
|
-
expect(snapshot.hydrationTargets[0]?.symbol).toBe("AAPL");
|
|
67
|
+
expect(snapshot.hydrationTargets.map((target) => target.symbol)).toEqual(["AAPL", "MSFT"]);
|
|
66
68
|
expect(snapshot.exchangeCurrencies).toContain("JPY");
|
|
67
69
|
expect(snapshot.paneState["portfolio-list:main"]).toEqual({
|
|
68
70
|
cursorSymbol: "AAPL",
|
|
@@ -152,6 +152,9 @@ export function useInlineTickers(texts: readonly string[]): {
|
|
|
152
152
|
return {
|
|
153
153
|
symbol: ticker.metadata.ticker,
|
|
154
154
|
exchange: ticker.metadata.exchange,
|
|
155
|
+
surface: "inline" as const,
|
|
156
|
+
visible: true,
|
|
157
|
+
weight: 40,
|
|
155
158
|
context: instrument
|
|
156
159
|
? {
|
|
157
160
|
brokerId: instrument.brokerId,
|
|
@@ -22,6 +22,7 @@ export function buildQuoteStreamSubscriptionKey(target: QuoteSubscriptionTarget)
|
|
|
22
22
|
?? target.context?.instrument?.localSymbol
|
|
23
23
|
?? target.context?.instrument?.symbol
|
|
24
24
|
?? "";
|
|
25
|
+
const weight = Number.isFinite(target.weight) ? String(target.weight) : "";
|
|
25
26
|
return [
|
|
26
27
|
target.symbol,
|
|
27
28
|
target.exchange ?? "",
|
|
@@ -29,6 +30,10 @@ export function buildQuoteStreamSubscriptionKey(target: QuoteSubscriptionTarget)
|
|
|
29
30
|
target.context?.brokerInstanceId ?? "",
|
|
30
31
|
contractKey,
|
|
31
32
|
target.route ?? "auto",
|
|
33
|
+
target.surface ?? "",
|
|
34
|
+
target.visible ? "visible" : "",
|
|
35
|
+
target.selected ? "selected" : "",
|
|
36
|
+
weight,
|
|
32
37
|
].join("|");
|
|
33
38
|
}
|
|
34
39
|
|
|
@@ -68,6 +73,12 @@ export function useQuoteStreaming(targets: QuoteSubscriptionTarget[]): void {
|
|
|
68
73
|
brokerInstanceId: target.context?.brokerInstanceId,
|
|
69
74
|
instrument: target.context?.instrument ?? null,
|
|
70
75
|
},
|
|
76
|
+
priority: {
|
|
77
|
+
surface: target.surface,
|
|
78
|
+
visible: target.visible,
|
|
79
|
+
selected: target.selected,
|
|
80
|
+
weight: target.weight,
|
|
81
|
+
},
|
|
71
82
|
})));
|
|
72
83
|
return () => {
|
|
73
84
|
quoteStreamLog.info("unsubscribe", {
|
package/src/theme/colors.ts
CHANGED
|
@@ -73,21 +73,20 @@ export function syncTheme(id: string): void {
|
|
|
73
73
|
|
|
74
74
|
export { blendHex } from "./color-utils";
|
|
75
75
|
|
|
76
|
-
const COMPARISON_SERIES_COLORS = [
|
|
77
|
-
"#5bc0eb",
|
|
78
|
-
"#f6ae2d",
|
|
79
|
-
"#f26419",
|
|
80
|
-
"#6f2dbd",
|
|
81
|
-
"#00a896",
|
|
82
|
-
"#ef476f",
|
|
83
|
-
"#118ab2",
|
|
84
|
-
"#8ac926",
|
|
85
|
-
"#ff7f51",
|
|
86
|
-
"#4361ee",
|
|
87
|
-
] as const;
|
|
88
|
-
|
|
89
76
|
export function getComparisonSeriesColor(index: number): string {
|
|
90
|
-
|
|
77
|
+
const seriesColors = [
|
|
78
|
+
colors.borderFocused,
|
|
79
|
+
colors.warning,
|
|
80
|
+
colors.positive,
|
|
81
|
+
colors.negative,
|
|
82
|
+
colors.textBright,
|
|
83
|
+
colors.neutral,
|
|
84
|
+
blendHex(colors.borderFocused, colors.positive, 0.45),
|
|
85
|
+
blendHex(colors.warning, colors.negative, 0.35),
|
|
86
|
+
blendHex(colors.textBright, colors.positive, 0.35),
|
|
87
|
+
blendHex(colors.borderFocused, colors.negative, 0.35),
|
|
88
|
+
];
|
|
89
|
+
return seriesColors[((index % seriesColors.length) + seriesColors.length) % seriesColors.length]!;
|
|
91
90
|
}
|
|
92
91
|
|
|
93
92
|
/** Returns a hover background color derived from bg and selected */
|
package/src/types/config.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Portfolio, Watchlist } from "./ticker";
|
|
2
2
|
|
|
3
|
-
export const CURRENT_CONFIG_VERSION =
|
|
3
|
+
export const CURRENT_CONFIG_VERSION = 16;
|
|
4
4
|
|
|
5
|
-
export type DefaultChartRenderMode = "area" | "line" | "candles" | "ohlc";
|
|
5
|
+
export type DefaultChartRenderMode = "area" | "line" | "candles" | "ohlc" | "hlc";
|
|
6
6
|
export type ChartRendererPreference = "auto" | "kitty" | "braille";
|
|
7
7
|
|
|
8
8
|
export interface ChartPreferences {
|
|
@@ -108,8 +108,12 @@ export interface LayoutConfig {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
export interface SavedLayout {
|
|
111
|
+
id?: string;
|
|
111
112
|
name: string;
|
|
112
113
|
layout: LayoutConfig;
|
|
114
|
+
paneState?: Record<string, Record<string, unknown>>;
|
|
115
|
+
focusedPaneId?: string | null;
|
|
116
|
+
activePanel?: "left" | "right";
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
export interface AppConfig {
|
|
@@ -154,6 +158,7 @@ export const DEFAULT_COLUMNS: ColumnConfig[] = [
|
|
|
154
158
|
|
|
155
159
|
export const DEFAULT_PORTFOLIO_COLUMN_IDS = [
|
|
156
160
|
...DEFAULT_COLUMNS.map((column) => column.id),
|
|
161
|
+
"sparkline",
|
|
157
162
|
"shares",
|
|
158
163
|
"avg_cost",
|
|
159
164
|
"cost_basis",
|
|
@@ -585,8 +590,8 @@ export function createDefaultConfig(dataDir: string): AppConfig {
|
|
|
585
590
|
watchlists: [{ id: "watchlist", name: "Watchlist" }],
|
|
586
591
|
layout,
|
|
587
592
|
layouts: [
|
|
588
|
-
{ name: "Home", layout: cloneLayout(layout) },
|
|
589
|
-
{ name: "Monitor", layout: cloneLayout(DEFAULT_MONITOR_LAYOUT) },
|
|
593
|
+
{ name: "Home", layout: cloneLayout(layout), paneState: {} },
|
|
594
|
+
{ name: "Monitor", layout: cloneLayout(DEFAULT_MONITOR_LAYOUT), paneState: {} },
|
|
590
595
|
],
|
|
591
596
|
activeLayoutIndex: 0,
|
|
592
597
|
brokerInstances: [],
|
|
@@ -90,6 +90,10 @@ export interface QuoteSubscriptionTarget {
|
|
|
90
90
|
exchange?: string;
|
|
91
91
|
context?: MarketDataRequestContext;
|
|
92
92
|
route?: "auto" | "provider" | "broker";
|
|
93
|
+
surface?: "portfolio" | "watchlist" | "detail" | "monitor" | "inline" | "screener" | "unknown";
|
|
94
|
+
visible?: boolean;
|
|
95
|
+
selected?: boolean;
|
|
96
|
+
weight?: number;
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
export interface QuoteBatchResult {
|
|
@@ -111,7 +115,7 @@ export interface AssetDataProvider {
|
|
|
111
115
|
readonly cachePolicy?: CachePolicyMap;
|
|
112
116
|
|
|
113
117
|
canProvide?(ticker: string, exchange?: string, context?: MarketDataRequestContext): Promise<boolean> | boolean;
|
|
114
|
-
getCachedFinancialsForTargets?(targets: CachedFinancialsTarget[], options?: { allowExpired?: boolean }): Map<string, TickerFinancials> | Promise<Map<string, TickerFinancials>>;
|
|
118
|
+
getCachedFinancialsForTargets?(targets: CachedFinancialsTarget[], options?: { allowExpired?: boolean; includeStaleQuotes?: boolean }): Map<string, TickerFinancials> | Promise<Map<string, TickerFinancials>>;
|
|
115
119
|
getQuotesBatch?(targets: QuoteSubscriptionTarget[], options?: { forceRefresh?: boolean }): Promise<QuoteBatchResult[]>;
|
|
116
120
|
getTickerFinancialsBatch?(targets: CachedFinancialsTarget[], options?: { forceRefresh?: boolean }): Promise<TickerFinancialsBatchResult[]>;
|
|
117
121
|
getTickerFinancials(ticker: string, exchange?: string, context?: MarketDataRequestContext): Promise<TickerFinancials>;
|
package/src/types/financials.ts
CHANGED
|
@@ -32,6 +32,7 @@ export interface Quote {
|
|
|
32
32
|
volume?: number;
|
|
33
33
|
name?: string;
|
|
34
34
|
lastUpdated: number; // timestamp ms
|
|
35
|
+
receivedAt?: number; // local receipt timestamp ms for streamed/display freshness
|
|
35
36
|
exchangeName?: string;
|
|
36
37
|
fullExchangeName?: string;
|
|
37
38
|
listingExchangeName?: string;
|
package/src/types/plugin.ts
CHANGED
|
@@ -110,6 +110,7 @@ export type PaneSettingField =
|
|
|
110
110
|
|
|
111
111
|
export interface PaneSettingsDef {
|
|
112
112
|
title?: string;
|
|
113
|
+
values?: Record<string, unknown>;
|
|
113
114
|
fields: PaneSettingField[];
|
|
114
115
|
}
|
|
115
116
|
|
|
@@ -406,6 +407,12 @@ export interface BrokerInstanceUpdateOptions {
|
|
|
406
407
|
replaceConfig?: boolean;
|
|
407
408
|
}
|
|
408
409
|
|
|
410
|
+
export interface PinTickerOptions {
|
|
411
|
+
floating?: boolean;
|
|
412
|
+
paneType?: string;
|
|
413
|
+
forceNewPane?: boolean;
|
|
414
|
+
}
|
|
415
|
+
|
|
409
416
|
export interface GloomPluginContext {
|
|
410
417
|
registerPane(pane: PaneDef): void;
|
|
411
418
|
registerPaneTemplate(template: PaneTemplateDef): void;
|
|
@@ -448,7 +455,7 @@ export interface GloomPluginContext {
|
|
|
448
455
|
createPaneFromTemplate(templateId: string, options?: PaneTemplateCreateOptions): void;
|
|
449
456
|
hidePane(paneId: string): void;
|
|
450
457
|
focusPane(paneId: string): void;
|
|
451
|
-
pinTicker(symbol: string, options?:
|
|
458
|
+
pinTicker(symbol: string, options?: PinTickerOptions): void;
|
|
452
459
|
navigateTicker(symbol: string, options?: { sourcePaneId?: string | null }): void;
|
|
453
460
|
openPaneSettings(paneId?: string): void;
|
|
454
461
|
|