gloomberb 0.15.6 → 0.15.7
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 -2
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/src/api-client/data.ts +37 -10
- package/src/api-client/estimate-revisions.ts +1 -1
- package/src/api-client/futures-curve.ts +1 -1
- package/src/api-client/index.ts +6 -0
- package/src/api-client/market-discovery.ts +84 -0
- package/src/api-client/paths.ts +14 -11
- package/src/api-client/request.ts +12 -2
- package/src/api-client/tape.ts +2 -2
- package/src/api-client/types.ts +2 -0
- package/src/app/dialog-bridge.tsx +33 -0
- package/src/app/pane-runtime/index.tsx +7 -2
- package/src/app/pane-runtime/pane-settings-runtime.tsx +2 -1
- package/src/app/pane-runtime/pane-template-runtime.ts +8 -28
- package/src/app/pane-runtime/plugin-bindings.ts +34 -12
- package/src/app/pane-runtime/ticker-open-runtime.ts +5 -3
- package/src/app/runtime/broker-import.ts +1 -1
- package/src/app/runtime/desktop-menu.ts +9 -6
- package/src/app/runtime/plugin-bindings.ts +7 -1
- package/src/app/runtime/ticker-refresh.ts +1 -1
- package/src/app.tsx +12 -0
- package/src/brokers/directory.ts +11 -2
- package/src/brokers/profile-form.ts +2 -1
- package/src/brokers/remove-profile.ts +61 -0
- package/src/brokers/signed-in/connect.ts +20 -7
- package/src/brokers/signed-in/sign-in-dialog.tsx +68 -30
- package/src/cli/desktop-pane-shot.ts +2 -2
- package/src/cli/history-rows.ts +1 -1
- package/src/cli/pane-functions/function-help.ts +3 -3
- package/src/cli/ticker-resolution.ts +2 -2
- package/src/components/command-bar/action-runtime.ts +7 -67
- package/src/components/command-bar/commands/direct/index.ts +9 -5
- package/src/components/command-bar/commands/direct/runtime.ts +12 -11
- package/src/components/command-bar/commands/plugin/actions.ts +6 -35
- package/src/components/command-bar/helpers.ts +3 -31
- package/src/components/command-bar/keyboard-handlers.ts +7 -219
- package/src/components/command-bar/keyboard-shortcuts.ts +25 -99
- package/src/components/command-bar/layout-items/current-layout.ts +2 -1
- package/src/components/command-bar/layout-items/focused-pane.ts +2 -1
- package/src/components/command-bar/layout-items/types.ts +2 -0
- package/src/components/command-bar/layout-items.ts +3 -0
- package/src/components/command-bar/list/model.ts +1 -1
- package/src/components/command-bar/list/navigation.ts +5 -12
- package/src/components/command-bar/pane-actions.ts +17 -14
- package/src/components/command-bar/pane-settings.ts +7 -250
- package/src/components/command-bar/pane-templates/workflow.ts +6 -13
- package/src/components/command-bar/panel/index.tsx +4 -110
- package/src/components/command-bar/panel/layout.ts +4 -16
- package/src/components/command-bar/panel/runtime.ts +1 -87
- package/src/components/command-bar/panel/state.ts +12 -29
- package/src/components/command-bar/panel/types.ts +2 -26
- package/src/components/command-bar/picker-activation.ts +7 -56
- package/src/components/command-bar/routes/root/plugin-install.ts +3 -3
- package/src/components/command-bar/routes/root/search-report.ts +2 -1
- package/src/components/command-bar/routing/actions.ts +21 -59
- package/src/components/command-bar/routing/confirm.ts +15 -88
- package/src/components/command-bar/routing/effects.ts +1 -20
- package/src/components/command-bar/routing/launch-request.ts +1 -36
- package/src/components/command-bar/routing/list-state.ts +5 -36
- package/src/components/command-bar/selection-runtime.ts +6 -35
- package/src/components/command-bar/surface/environment.ts +10 -6
- package/src/components/command-bar/surface/index.tsx +4 -37
- package/src/components/command-bar/workflow/alert.ts +23 -17
- package/src/components/command-bar/workflow/broker.ts +30 -40
- package/src/components/command-bar/workflow/builtin.ts +33 -38
- package/src/components/command-bar/workflow/collection-actions.ts +18 -21
- package/src/components/command-bar/workflow/collection-ticker.ts +4 -4
- package/src/components/command-bar/workflow/coordinator.ts +27 -156
- package/src/components/command-bar/workflow/fields.ts +6 -22
- package/src/components/command-bar/workflow/submit.ts +28 -43
- package/src/components/command-bar/workflow/ticker-listing-picker.ts +12 -9
- package/src/components/command-bar/workflow/tickers.ts +2 -2
- package/src/components/command-bar/workflow/types.ts +10 -35
- package/src/components/feedback-dialog.tsx +1 -13
- package/src/components/form-modal/broker-step.tsx +119 -0
- package/src/components/form-modal/confirm.tsx +145 -0
- package/src/components/form-modal/content.tsx +698 -0
- package/src/components/form-modal/deps.ts +77 -0
- package/src/components/form-modal/field-row.tsx +318 -0
- package/src/components/form-modal/host.tsx +169 -0
- package/src/components/form-modal/index.ts +2 -0
- package/src/components/form-modal/model.ts +131 -0
- package/src/components/form-modal/request.ts +67 -0
- package/src/components/form-modal/routes.ts +139 -0
- package/src/components/layout/shell/index.tsx +11 -0
- package/src/components/layout/status-bar.tsx +2 -1
- package/src/components/onboarding/first-run-workspace.ts +1 -1
- package/src/components/onboarding/onboarding-frame.tsx +4 -1
- package/src/components/onboarding/onboarding-wizard.tsx +1 -1
- package/src/components/pane-settings-dialog.tsx +7 -1
- package/src/components/pane-template-wizard.tsx +4 -149
- package/src/components/sign-in-code-panel.tsx +22 -9
- package/src/components/textarea-keys.ts +13 -0
- package/src/components/ui/button.tsx +3 -2
- package/src/components/ui/confirm-dialog.tsx +62 -10
- package/src/components/ui/frame.tsx +7 -2
- package/src/components/ui/list-view.tsx +16 -9
- package/src/core/app-runtime.ts +1 -1
- package/src/core/state/app/types.ts +1 -15
- package/src/data/config/home.ts +82 -11
- package/src/data/config/store/node.ts +29 -16
- package/src/i18n/es.ts +53 -7
- package/src/i18n/ja.ts +53 -7
- package/src/i18n/ko.ts +53 -7
- package/src/i18n/zh-cn.ts +53 -7
- package/src/i18n/zh-tw.ts +53 -7
- package/src/layout-marketplace/gallery.tsx +11 -3
- package/src/market-data/coordinator/fx-legs.ts +1 -1
- package/src/market-data/history-session.ts +3 -5
- package/src/market-data/market/freshness.ts +1 -1
- package/src/market-data/market/status.ts +1 -1
- package/src/market-data/quotes/metadata.ts +3 -3
- package/src/market-data/quotes/resolution.ts +3 -3
- package/src/news/types.ts +1 -1
- package/src/plugins/builtin/alerts/alert-engine.ts +2 -0
- package/src/plugins/builtin/alerts/cli.ts +2 -2
- package/src/plugins/builtin/alerts/types.ts +2 -0
- package/src/plugins/builtin/broker-manager/add-flow.tsx +625 -0
- package/src/plugins/builtin/broker-manager/add-request.ts +34 -0
- package/src/plugins/builtin/broker-manager/detail.tsx +128 -50
- package/src/plugins/builtin/broker-manager/footer.ts +6 -5
- package/src/plugins/builtin/broker-manager/index.tsx +62 -21
- package/src/plugins/builtin/broker-manager/model.ts +7 -0
- package/src/plugins/builtin/broker-manager/pane-actions.tsx +47 -44
- package/src/plugins/builtin/cloud/company-picker.tsx +658 -0
- package/src/plugins/builtin/credit-boards/model.ts +1 -1
- package/src/plugins/builtin/dividend-yield/browser.tsx +2 -25
- package/src/plugins/builtin/dividend-yield/client.ts +34 -122
- package/src/plugins/builtin/dividend-yield/index.tsx +9 -9
- package/src/plugins/builtin/dividend-yield/provider-client.ts +1 -1
- package/src/plugins/builtin/estimate-revisions/client.ts +3 -3
- package/src/plugins/builtin/estimate-revisions/pane.tsx +2 -2
- package/src/plugins/builtin/executives/pane.tsx +13 -4
- package/src/plugins/builtin/filing-events/pane.tsx +23 -6
- package/src/plugins/builtin/futures/contracts.ts +3 -3
- package/src/plugins/builtin/futures/returns.ts +1 -1
- package/src/plugins/builtin/futures-curve/client.ts +1 -1
- package/src/plugins/builtin/futures-curve/model.ts +1 -1
- package/src/plugins/builtin/help/index.tsx +8 -3
- package/src/plugins/builtin/market-heatmap/data.ts +13 -256
- package/src/plugins/builtin/market-heatmap/index.tsx +5 -15
- package/src/plugins/builtin/market-movers/client.ts +1 -1
- package/src/plugins/builtin/market-movers/screener.ts +60 -142
- package/src/plugins/builtin/market-valuation/defs.ts +1 -2
- package/src/plugins/builtin/market-valuation/sources.ts +1 -3
- package/src/plugins/builtin/news/wire/default-feeds.ts +2 -8
- package/src/plugins/builtin/news/wire/feed-config.ts +5 -1
- package/src/plugins/builtin/news/wire/persisted-articles.ts +9 -5
- package/src/plugins/builtin/news/wire/rss/cli.ts +1 -1
- package/src/plugins/builtin/news/wire/rss/managed-feed.ts +19 -0
- package/src/plugins/builtin/news/wire/rss/parser.ts +18 -9
- package/src/plugins/builtin/news/wire/rss/source.ts +1 -1
- package/src/plugins/builtin/plugin-marketplace/model.ts +5 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +4 -2
- package/src/plugins/builtin/relative-rotation/client.ts +1 -1
- package/src/plugins/builtin/research/analyst-model.ts +1 -1
- package/src/plugins/builtin/research/event-model.ts +1 -1
- package/src/plugins/builtin/risk-factors/pane.tsx +25 -10
- package/src/plugins/builtin/shared/pro-feature-wall.tsx +47 -0
- package/src/plugins/builtin/short-interest/client.ts +14 -106
- package/src/plugins/builtin/short-interest/headless.ts +2 -2
- package/src/plugins/builtin/short-interest/index.tsx +5 -5
- package/src/plugins/builtin/short-interest/pane.tsx +4 -4
- package/src/plugins/builtin/ticker-detail/pane.tsx +2 -2
- package/src/plugins/builtin/time-sales/client.ts +3 -3
- package/src/plugins/builtin/vol-surface/model.ts +1 -1
- package/src/plugins/builtin/volatility/model.ts +1 -1
- package/src/plugins/bundle.ts +2 -2
- package/src/plugins/catalog-manifest.ts +0 -1
- package/src/plugins/catalog-ui.ts +1 -1
- package/src/plugins/catalog.ts +0 -2
- package/src/plugins/host-link.ts +1 -1
- package/src/plugins/host-resolver.ts +1 -1
- package/src/plugins/loader.ts +6 -5
- package/src/plugins/registry/host-actions.ts +6 -1
- package/src/plugins/registry/index.ts +1 -0
- package/src/plugins/runtime/context.tsx +2 -0
- package/src/plugins/runtime/index.tsx +2 -0
- package/src/public/__snapshots__/public-api.test.ts.snap +0 -1
- package/src/public/market-data.ts +0 -7
- package/src/remote/controller-utils.ts +1 -1
- package/src/remote/controller.ts +27 -3
- package/src/remote/form.ts +20 -0
- package/src/remote/resources.ts +7 -0
- package/src/remote/schema.ts +25 -3
- package/src/remote/semantic-tree.tsx +15 -0
- package/src/remote/types.ts +2 -0
- package/src/renderers/browser/research-entry.ts +2 -2
- package/src/renderers/cloudflare/http-proxy.ts +157 -33
- package/src/renderers/cloudflare/proxy-session.ts +270 -0
- package/src/renderers/cloudflare/worker.ts +4 -1
- package/src/renderers/dom/desktop/controls.tsx +2 -1
- package/src/renderers/dom/dialog-host.tsx +17 -8
- package/src/renderers/dom/styles.css +4 -0
- package/src/renderers/electrobun/bun/application-menu/index.ts +4 -4
- package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -2
- package/src/renderers/electrobun/bun/external-plugins.ts +1 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +3 -6
- package/src/renderers/electrobun/view/cli-pane-shot-health.ts +3 -3
- package/src/renderers/opentui/dialog-host.tsx +14 -2
- package/src/renderers/opentui/start.tsx +2 -2
- package/src/renderers/opentui/terminal-media.ts +2 -2
- package/src/renderers/opentui/test-utils.tsx +9 -2
- package/src/renderers/opentui/toast-host.tsx +1 -1
- package/src/sources/gloomberb-cloud/index.ts +23 -24
- package/src/sources/gloomberb-cloud/news.ts +3 -3
- package/src/sources/history-retention.ts +6 -6
- package/src/sources/{yahoo-finance/symbols.ts → listing-symbols.ts} +16 -41
- package/src/sources/listing-target.ts +4 -4
- package/src/sources/provider-router/cache.ts +16 -6
- package/src/sources/provider-router/financials.ts +1 -1
- package/src/sources/provider-router/history.ts +3 -2
- package/src/state/app/bootstrap.ts +1 -1
- package/src/state/app/context/index.tsx +13 -0
- package/src/sync/core-contributors.ts +163 -16
- package/src/test-support/plugin-runtime.ts +1 -0
- package/src/tickers/instrument-kind.ts +1 -1
- package/src/tickers/open-target.ts +4 -4
- package/src/tickers/search/index.ts +3 -3
- package/src/tickers/search/ranking.ts +4 -4
- package/src/time-series/resolution.ts +1 -1
- package/src/time-series/resolve.ts +1 -1
- package/src/types/financials.ts +5 -5
- package/src/types/instrument.ts +1 -1
- package/src/types/plugin.ts +3 -3
- package/src/types/price-history.ts +1 -1
- package/src/ui/dialog-bridge.tsx +50 -0
- package/src/ui/dialog-context.ts +24 -0
- package/src/ui/dialog-stack.ts +47 -1
- package/src/ui/dialog.tsx +2 -10
- package/src/ui/host.tsx +6 -1
- package/src/ui/index.tsx +7 -2
- package/src/utils/latest-financial-period.ts +1 -1
- package/src/utils/plugin-proxy-hosts.json +1 -5
- package/src/utils/sec.ts +1 -1
- package/src/app/pane-template-dialog-wizard.tsx +0 -67
- package/src/components/command-bar/multi-select-picker.tsx +0 -225
- package/src/components/command-bar/multi-select-runtime.ts +0 -91
- package/src/components/command-bar/workflow/body.tsx +0 -164
- package/src/components/command-bar/workflow/field-row.tsx +0 -231
- package/src/components/command-bar/workflow/route-actions.ts +0 -235
- package/src/components/command-bar/workflow/runtime.ts +0 -247
- package/src/plugins/builtin/yahoo/index.ts +0 -38
- package/src/sources/yahoo-finance/chart-period.ts +0 -138
- package/src/sources/yahoo-finance/financials.ts +0 -370
- package/src/sources/yahoo-finance/history.ts +0 -150
- package/src/sources/yahoo-finance/http.ts +0 -119
- package/src/sources/yahoo-finance/mappers.ts +0 -549
- package/src/sources/yahoo-finance/names.ts +0 -28
- package/src/sources/yahoo-finance/options.ts +0 -190
- package/src/sources/yahoo-finance/quote-summary.ts +0 -176
- package/src/sources/yahoo-finance/requests.ts +0 -245
- package/src/sources/yahoo-finance/snapshots.ts +0 -336
- package/src/sources/yahoo-finance/types.ts +0 -198
- package/src/sources/yahoo-finance/yahoo-chart-interval.ts +0 -18
- package/src/sources/yahoo-finance.ts +0 -339
|
@@ -23,8 +23,9 @@ import {
|
|
|
23
23
|
type TickerOpenTarget,
|
|
24
24
|
} from "../../tickers/open-target";
|
|
25
25
|
import { AmbiguousTickerError, findExactTickerSearchMatch } from "../../tickers/search";
|
|
26
|
+
import { isDialogOpen } from "../../ui/dialog-stack";
|
|
26
27
|
import { parsePublicTickerKey } from "../../utils/exchanges";
|
|
27
|
-
import {
|
|
28
|
+
import { tickerHasListingSuffix } from "../../sources/listing-symbols";
|
|
28
29
|
import { instrumentFromTicker } from "../../market-data/request-types";
|
|
29
30
|
import { tickerInstrumentLabel } from "../../tickers/instrument-label";
|
|
30
31
|
|
|
@@ -75,7 +76,8 @@ export function useAppTickerOpenRuntime({
|
|
|
75
76
|
return target;
|
|
76
77
|
} catch (err) {
|
|
77
78
|
if (!canPresentFeedback()) return null;
|
|
78
|
-
|
|
79
|
+
// Ticker search offers the listings, unless a dialog would sit over the bar.
|
|
80
|
+
if (err instanceof AmbiguousTickerError && !isDialogOpen()) {
|
|
79
81
|
dispatch({ type: "SET_COMMAND_BAR", open: true, query: rawSymbol,
|
|
80
82
|
launch: { kind: "ticker-search", query: rawSymbol } });
|
|
81
83
|
}
|
|
@@ -114,7 +116,7 @@ export function useAppTickerOpenRuntime({
|
|
|
114
116
|
if (!options?.forceNewPane && !existing && instrument === null && !target.ticker.metadata.broker_contracts?.length) {
|
|
115
117
|
existing = findFixedTickerPaneForSymbol(currentLayout, paneType, symbol);
|
|
116
118
|
}
|
|
117
|
-
if (!options?.forceNewPane && !existing && !instrument && (parsePublicTickerKey(symbol).exchange ||
|
|
119
|
+
if (!options?.forceNewPane && !existing && !instrument && (parsePublicTickerKey(symbol).exchange || tickerHasListingSuffix(symbol))) {
|
|
118
120
|
existing = currentLayout.instances.find((instance) => {
|
|
119
121
|
if (instance.paneId !== paneType || instance.binding?.kind !== "fixed" || instance.binding.instrument || !isPaneInLayout(currentLayout, instance.instanceId)) return false;
|
|
120
122
|
const ticker = currentState.tickers.get(instance.binding.symbol);
|
|
@@ -87,7 +87,7 @@ export function useBrokerImportRuntime({
|
|
|
87
87
|
|
|
88
88
|
for (const position of result.positions) {
|
|
89
89
|
throwIfBrokerImportCancelled(options?.signal);
|
|
90
|
-
// Skip
|
|
90
|
+
// Skip market data for broker option symbols; position marks are already available.
|
|
91
91
|
// Position data (markPrice, marketValue, unrealizedPnl) is used directly.
|
|
92
92
|
if (options?.refreshImportedTickers !== false && position.assetCategory !== "OPT") {
|
|
93
93
|
refreshQuote(position.ticker, position.exchange, undefined, 1);
|
|
@@ -4,9 +4,12 @@ import type { PluginRegistry } from "../../plugins/registry";
|
|
|
4
4
|
import type { AppAction, AppState } from "../../state/app/context";
|
|
5
5
|
import type { DesktopApplicationMenuBridge } from "../../types/desktop-menu";
|
|
6
6
|
import type { DesktopWindowBridge } from "../../types/desktop-window";
|
|
7
|
+
import { isDialogOpen } from "../../ui/dialog-stack";
|
|
7
8
|
import type { RendererHost } from "../../ui/host";
|
|
8
9
|
import { recordFunctionOpen } from "../../telemetry/usage-counts";
|
|
9
10
|
|
|
11
|
+
const OPENS_OVER_DIALOGS = new Set<string>(["open-command-bar", "open-plugin-workflow", "open-builtin-workflow"]);
|
|
12
|
+
|
|
10
13
|
export function useDesktopApplicationMenuRuntime({
|
|
11
14
|
desktopApplicationMenuBridge,
|
|
12
15
|
desktopWindowKind,
|
|
@@ -27,6 +30,11 @@ export function useDesktopApplicationMenuRuntime({
|
|
|
27
30
|
useEffect(() => {
|
|
28
31
|
if (desktopWindowKind !== "main" || !desktopApplicationMenuBridge) return;
|
|
29
32
|
return desktopApplicationMenuBridge.subscribe((command) => {
|
|
33
|
+
// The native menu stays reachable over a dialog, accelerators included.
|
|
34
|
+
// A bar opened there would sit over the dialog without its keys, and Esc
|
|
35
|
+
// would close the dialog under it; a form would stack over one the user
|
|
36
|
+
// has not finished. Both wait for the dialog to close.
|
|
37
|
+
if (OPENS_OVER_DIALOGS.has(command.type) && isDialogOpen()) return;
|
|
30
38
|
switch (command.type) {
|
|
31
39
|
case "open-command-bar":
|
|
32
40
|
dispatch({ type: "SET_COMMAND_BAR", open: true, query: command.query });
|
|
@@ -35,12 +43,7 @@ export function useDesktopApplicationMenuRuntime({
|
|
|
35
43
|
pluginRegistry.openPluginCommandWorkflow(command.commandId);
|
|
36
44
|
break;
|
|
37
45
|
case "open-builtin-workflow":
|
|
38
|
-
|
|
39
|
-
type: "SET_COMMAND_BAR",
|
|
40
|
-
open: true,
|
|
41
|
-
query: "",
|
|
42
|
-
launch: { kind: "builtin-workflow", actionId: command.actionId },
|
|
43
|
-
});
|
|
46
|
+
pluginRegistry.openBuiltInWorkflow(command.actionId);
|
|
44
47
|
break;
|
|
45
48
|
case "open-url":
|
|
46
49
|
void rendererHost.openExternal(command.url).catch((error) => {
|
|
@@ -9,6 +9,7 @@ import type { MarketDataCoordinator } from "../../market-data/coordinator";
|
|
|
9
9
|
import { instrumentFromTicker } from "../../market-data/request-types";
|
|
10
10
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
11
11
|
import { saveConfigImmediately } from "../../state/config-save-scheduler";
|
|
12
|
+
import { settleWithin } from "../../utils/async-deadline";
|
|
12
13
|
import type { AppAction, AppState } from "../../state/app/context";
|
|
13
14
|
import type { AppConfig, BrokerInstanceConfig } from "../../types/config";
|
|
14
15
|
import type { DataProvider } from "../../types/data-provider";
|
|
@@ -18,6 +19,9 @@ import {
|
|
|
18
19
|
getBrokerInstance,
|
|
19
20
|
} from "../../utils/broker-instances";
|
|
20
21
|
|
|
22
|
+
/** How long removing a profile waits for its adapter to disconnect before going on without it. */
|
|
23
|
+
const ADAPTER_DISCONNECT_WAIT_MS = 5_000;
|
|
24
|
+
|
|
21
25
|
export function bindPluginRegistryRuntimeAccess({
|
|
22
26
|
dataProvider,
|
|
23
27
|
dispatch,
|
|
@@ -186,8 +190,10 @@ export function bindPluginRegistryRuntimeAccess({
|
|
|
186
190
|
|
|
187
191
|
clearPersistedBrokerAccounts(pluginRegistry.persistence.resources, instance);
|
|
188
192
|
|
|
193
|
+
// Best effort: an adapter that throws, rejects or never answers (a desktop
|
|
194
|
+
// call to a busy Bun process) must not keep the profile.
|
|
189
195
|
const broker = pluginRegistry.brokers.get(instance.brokerType);
|
|
190
|
-
await
|
|
196
|
+
await settleWithin(Promise.resolve().then(() => broker?.disconnect?.(instance)), ADAPTER_DISCONNECT_WAIT_MS);
|
|
191
197
|
|
|
192
198
|
// A portfolio another profile of the account can keep (the account switched
|
|
193
199
|
// to or from sign-in) goes to that profile; only this profile's positions go.
|
|
@@ -96,7 +96,7 @@ export function useTickerRefreshRuntime({
|
|
|
96
96
|
if (quote.currency) void marketData.loadFxRate(quote.currency).catch(() => {});
|
|
97
97
|
void marketData.loadFxRate(baseCurrency).catch(() => {});
|
|
98
98
|
} catch {
|
|
99
|
-
// Silently fail - the list can fall back to stale cache or
|
|
99
|
+
// Silently fail - the list can fall back to stale cache or the backend.
|
|
100
100
|
} finally {
|
|
101
101
|
quoteRefreshInFlight.delete(key);
|
|
102
102
|
}
|
package/src/app.tsx
CHANGED
|
@@ -19,6 +19,7 @@ import { DetachedPaneShell } from "./components/layout/detached-pane-shell";
|
|
|
19
19
|
import { TransientLayoutProvider } from "./components/layout/transient-layout";
|
|
20
20
|
import { CommandBar } from "./components/command-bar/surface";
|
|
21
21
|
import { OnboardingWizard } from "./components/onboarding/onboarding-wizard";
|
|
22
|
+
import { CompanyPickerHost } from "./plugins/builtin/cloud/company-picker";
|
|
22
23
|
import { SignInGate } from "./components/sign-in-gate";
|
|
23
24
|
import { useDialog } from "./ui/dialog";
|
|
24
25
|
import { PluginRegistry } from "./plugins/registry";
|
|
@@ -39,6 +40,7 @@ import { useBrokerImportRuntime } from "./app/runtime/broker-import";
|
|
|
39
40
|
import { useDesktopDeepLinkRuntime } from "./app/runtime/desktop-deeplink";
|
|
40
41
|
import { useDesktopApplicationMenuRuntime } from "./app/runtime/desktop-menu";
|
|
41
42
|
import { useAppGlobalShortcuts } from "./app/global-shortcuts";
|
|
43
|
+
import { AppDialogBridge } from "./app/dialog-bridge";
|
|
42
44
|
import { KeybindingsProvider, useResolvedKeybindings } from "./app/keybindings";
|
|
43
45
|
import { useAppPaneRuntime } from "./app/pane-runtime";
|
|
44
46
|
import { bindPluginRegistryRuntimeAccess } from "./app/runtime/plugin-bindings";
|
|
@@ -181,6 +183,10 @@ function AppInner({
|
|
|
181
183
|
const toast = useToastHost();
|
|
182
184
|
const isDetachedWindow = desktopWindowBridge?.kind === "detached";
|
|
183
185
|
const detachedPaneId = isDetachedWindow ? desktopWindowBridge.paneId ?? null : null;
|
|
186
|
+
const focusDetachedPane = useMemo(() => {
|
|
187
|
+
const focus = desktopWindowBridge?.kind === "main" ? desktopWindowBridge.focusDetachedPane : undefined;
|
|
188
|
+
return focus ? (paneId: string) => { void focus(paneId).catch(() => {}); } : undefined;
|
|
189
|
+
}, [desktopWindowBridge]);
|
|
184
190
|
const [desktopDockPreview, setDesktopDockPreview] = useState<DesktopDockPreviewState | null>(null);
|
|
185
191
|
const [commandBarNativeOccluder, setCommandBarNativeOccluder] = useState<LayoutBounds | null>(null);
|
|
186
192
|
appActiveRef.current = appActive;
|
|
@@ -354,6 +360,7 @@ function AppInner({
|
|
|
354
360
|
dialog,
|
|
355
361
|
dispatch,
|
|
356
362
|
externalPlugins,
|
|
363
|
+
focusDetachedPane,
|
|
357
364
|
isDetachedWindow,
|
|
358
365
|
notify,
|
|
359
366
|
persistConfig,
|
|
@@ -409,6 +416,7 @@ function AppInner({
|
|
|
409
416
|
if (desktopWindowBridge?.kind === "detached" && desktopWindowBridge.paneId) {
|
|
410
417
|
return (
|
|
411
418
|
<KeybindingsProvider value={keybindings}>
|
|
419
|
+
<AppDialogBridge />
|
|
412
420
|
<ContextMenuProvider pluginRegistry={pluginRegistry}>
|
|
413
421
|
<RemoteControlHost
|
|
414
422
|
adapter={remoteControlAdapter}
|
|
@@ -432,6 +440,7 @@ function AppInner({
|
|
|
432
440
|
|
|
433
441
|
return (
|
|
434
442
|
<KeybindingsProvider value={keybindings}>
|
|
443
|
+
<AppDialogBridge />
|
|
435
444
|
<ContextMenuProvider pluginRegistry={pluginRegistry}>
|
|
436
445
|
<RemoteControlHost
|
|
437
446
|
adapter={remoteControlAdapter}
|
|
@@ -448,6 +457,8 @@ function AppInner({
|
|
|
448
457
|
<TransientLayoutProvider>
|
|
449
458
|
<Shell
|
|
450
459
|
pluginRegistry={pluginRegistry}
|
|
460
|
+
dataProvider={dataProvider}
|
|
461
|
+
tickerRepository={tickerRepository}
|
|
451
462
|
desktopWindowBridge={desktopWindowBridge}
|
|
452
463
|
desktopDockPreview={desktopDockPreview}
|
|
453
464
|
commandBarNativeOccluder={commandBarNativeOccluder}
|
|
@@ -460,6 +471,7 @@ function AppInner({
|
|
|
460
471
|
}}
|
|
461
472
|
/>
|
|
462
473
|
</TransientLayoutProvider>
|
|
474
|
+
{!onboardingActive ? <CompanyPickerHost pluginRegistry={pluginRegistry} /> : null}
|
|
463
475
|
{onboardingActive && onOnboardingComplete ? (
|
|
464
476
|
<OnboardingWizard
|
|
465
477
|
pluginRegistry={pluginRegistry}
|
package/src/brokers/directory.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* adapter. One broker offered both ways is one entry with two methods, so Add
|
|
5
5
|
* Broker and onboarding list "Interactive Brokers" once.
|
|
6
6
|
*/
|
|
7
|
+
import { t, tf } from "../i18n";
|
|
7
8
|
import type { BrokerAdapter } from "../types/broker";
|
|
8
9
|
import type { SignedInBroker } from "./signed-in/client";
|
|
9
10
|
import { SIGNED_IN_BROKER_TYPE } from "./signed-in/profile";
|
|
@@ -48,9 +49,17 @@ export function buildBrokerDirectory({
|
|
|
48
49
|
return [...entries.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
/** How a broker connects, beside its name in a list of brokers. */
|
|
53
|
+
export function brokerMethodSummary(entry: BrokerDirectoryEntry): string {
|
|
54
|
+
const signsIn = entry.methods.some((method) => method.kind === "signed-in");
|
|
55
|
+
const onDevice = entry.methods.some((method) => method.kind === "device");
|
|
56
|
+
if (signsIn && onDevice) return t("Sign in, or on this device");
|
|
57
|
+
return signsIn ? t("Sign in") : t("On this device");
|
|
58
|
+
}
|
|
59
|
+
|
|
51
60
|
export function brokerMethodLabel(entry: BrokerDirectoryEntry, method: BrokerMethod): string {
|
|
52
61
|
if (method.kind === "signed-in") {
|
|
53
|
-
return entry.methods.some((candidate) => candidate.kind === "device") ? "Sign in (recommended)" : "Sign in";
|
|
62
|
+
return entry.methods.some((candidate) => candidate.kind === "device") ? t("Sign in (recommended)") : t("Sign in");
|
|
54
63
|
}
|
|
55
|
-
return entry.methods.length > 1 ?
|
|
64
|
+
return entry.methods.length > 1 ? tf("On this device ({broker})", { broker: method.adapter.name }) : method.adapter.name;
|
|
56
65
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BrokerAdapter, BrokerConfigField } from "../types/broker";
|
|
2
2
|
import type { BrokerInstanceConfig } from "../types/config";
|
|
3
3
|
import { resolveBrokerConfigFields } from "../types/broker";
|
|
4
|
+
import { t, tf } from "../i18n";
|
|
4
5
|
|
|
5
6
|
export interface BrokerProfileDraft {
|
|
6
7
|
label: string;
|
|
@@ -93,7 +94,7 @@ export function validateBrokerProfileValues(
|
|
|
93
94
|
for (const field of getVisibleBrokerConfigFields(adapter, resolvedValues)) {
|
|
94
95
|
if (!field.required) continue;
|
|
95
96
|
if (!normalizeFormValue(resolvedValues[field.key]).trim()) {
|
|
96
|
-
return
|
|
97
|
+
return tf("{label} is required.", { label: t(field.label) });
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
100
|
return null;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removing a broker profile, from the Brokers pane or the command bar. A
|
|
3
|
+
* signed-in profile's connection belongs to the Gloom account, so it is
|
|
4
|
+
* disconnected there too, as far as Gloom answers: the profile leaves this
|
|
5
|
+
* device either way, and the message says what the account kept.
|
|
6
|
+
*/
|
|
7
|
+
import type { ConfirmModalOptions } from "../components/form-modal";
|
|
8
|
+
import { t, tf } from "../i18n";
|
|
9
|
+
import type { BrokerInstanceConfig } from "../types/config";
|
|
10
|
+
import { disconnectSignedInProfile, signedInBrokerForProfile } from "./signed-in/connect";
|
|
11
|
+
import { isSignedInBrokerProfile } from "./signed-in/profile";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The confirm before `onConfirm` removes `instance`. `brokerName` names the
|
|
15
|
+
* broker when the connector list does not.
|
|
16
|
+
*/
|
|
17
|
+
export function brokerProfileRemovalConfirm(
|
|
18
|
+
instance: BrokerInstanceConfig,
|
|
19
|
+
brokerName: string,
|
|
20
|
+
onConfirm: () => Promise<void>,
|
|
21
|
+
): ConfirmModalOptions {
|
|
22
|
+
const signedIn = isSignedInBrokerProfile(instance) ? signedInBrokerForProfile(instance, brokerName) : null;
|
|
23
|
+
return {
|
|
24
|
+
confirmId: "remove-broker-profile",
|
|
25
|
+
title: "Disconnect broker?",
|
|
26
|
+
body: [
|
|
27
|
+
tf("Remove \"{label}\" and imported broker data?", { label: instance.label }),
|
|
28
|
+
t("Broker-managed portfolios, positions, and contracts will be removed."),
|
|
29
|
+
...(signedIn
|
|
30
|
+
? [tf("This also disconnects {broker} from your other devices and agents.", { broker: signedIn.name })]
|
|
31
|
+
: []),
|
|
32
|
+
],
|
|
33
|
+
confirmLabel: "Disconnect",
|
|
34
|
+
tone: "danger",
|
|
35
|
+
onConfirm,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface BrokerProfileRemoval {
|
|
40
|
+
message: string;
|
|
41
|
+
/** The Gloom account still holds the broker, so its other devices and agents keep it. */
|
|
42
|
+
accountKept: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function removeBrokerProfile(
|
|
46
|
+
instance: BrokerInstanceConfig,
|
|
47
|
+
brokerName: string,
|
|
48
|
+
removeBrokerInstance: (instanceId: string) => Promise<void>,
|
|
49
|
+
): Promise<BrokerProfileRemoval> {
|
|
50
|
+
const account = await disconnectSignedInProfile(instance);
|
|
51
|
+
await removeBrokerInstance(instance.id);
|
|
52
|
+
const label = instance.label;
|
|
53
|
+
if (!account.stillConnected) return { message: tf("Removed {label}.", { label }), accountKept: false };
|
|
54
|
+
const broker = signedInBrokerForProfile(instance, brokerName).name;
|
|
55
|
+
return {
|
|
56
|
+
accountKept: true,
|
|
57
|
+
message: account.signedOut
|
|
58
|
+
? tf("Removed {label}. {broker} is still connected to your Gloom account. Sign in to Gloom to disconnect {broker} from your account.", { label, broker })
|
|
59
|
+
: tf("Removed {label}. {broker} is still connected to your Gloom account.", { label, broker }),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* connecting again syncs the profile it already has.
|
|
5
5
|
*/
|
|
6
6
|
import { ApiRequestError } from "../../api-client/errors";
|
|
7
|
+
import { withDeadline } from "../../utils/async-deadline";
|
|
7
8
|
import type { AppConfig, BrokerInstanceConfig } from "../../types/config";
|
|
8
9
|
import { findSignedInBroker } from "./catalog";
|
|
9
10
|
import { disconnectSignedInBroker, type SignedInBroker } from "./client";
|
|
@@ -31,7 +32,7 @@ export interface ConnectSignedInBrokerDeps<T> {
|
|
|
31
32
|
getConfig(): AppConfig;
|
|
32
33
|
createBrokerInstance(brokerType: string, label: string, values: Record<string, unknown>): Promise<BrokerInstanceConfig>;
|
|
33
34
|
syncBrokerInstance(instanceId: string): Promise<T>;
|
|
34
|
-
/**
|
|
35
|
+
/** Asks the user to connect: a form's own connect step, or the connect dialog when missing. */
|
|
35
36
|
requestSignIn?: (broker: SignedInBroker) => Promise<boolean>;
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -50,20 +51,32 @@ export async function connectSignedInBrokerProfile<T>(
|
|
|
50
51
|
return { instance, synced: await deps.syncBrokerInstance(instance.id) };
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
/** Long enough for a slow answer, short enough that removing a profile never hangs on one. */
|
|
55
|
+
const DISCONNECT_TIMEOUT_MS = 10_000;
|
|
56
|
+
|
|
57
|
+
/** What became of the Gloom account's connection behind a profile being removed. */
|
|
58
|
+
export type SignedInProfileDisconnect =
|
|
59
|
+
| { stillConnected: false }
|
|
60
|
+
/** The account keeps the broker: signed out of Gloom, or Gloom did not do it. */
|
|
61
|
+
| { stillConnected: true; signedOut: boolean };
|
|
62
|
+
|
|
53
63
|
/**
|
|
54
64
|
* Disconnects the Gloom account's connection behind a signed-in profile before
|
|
55
|
-
* the profile goes. Already gone is fine
|
|
56
|
-
*
|
|
65
|
+
* the profile goes. Already gone is fine. It never throws: the profile leaves
|
|
66
|
+
* this device whatever Gloom answers, and the caller says what the account kept.
|
|
57
67
|
*/
|
|
58
|
-
export async function disconnectSignedInProfile(instance: BrokerInstanceConfig): Promise<
|
|
68
|
+
export async function disconnectSignedInProfile(instance: BrokerInstanceConfig): Promise<SignedInProfileDisconnect> {
|
|
59
69
|
if (!isSignedInBrokerProfile(instance)) return { stillConnected: false };
|
|
60
70
|
try {
|
|
61
|
-
await
|
|
71
|
+
await withDeadline(
|
|
72
|
+
disconnectSignedInBroker(signedInBrokerId(instance)),
|
|
73
|
+
DISCONNECT_TIMEOUT_MS,
|
|
74
|
+
"Gloom did not answer the disconnect.",
|
|
75
|
+
);
|
|
62
76
|
return { stillConnected: false };
|
|
63
77
|
} catch (error) {
|
|
64
78
|
if (error instanceof ApiRequestError && error.status === 404) return { stillConnected: false };
|
|
65
|
-
|
|
66
|
-
throw error;
|
|
79
|
+
return { stillConnected: true, signedOut: error instanceof ApiRequestError && error.status === 401 };
|
|
67
80
|
}
|
|
68
81
|
}
|
|
69
82
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The dialog that connects a signed-in broker, plus the request bridge that
|
|
3
|
-
* lets the
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* lets the Brokers pane and onboarding open it. Those run outside the React
|
|
4
|
+
* tree or in the desktop view, which is also where the browser hand-off has to
|
|
5
|
+
* happen, so the broker adapter never opens it. The Brokers pane's add flow and
|
|
6
|
+
* New Portfolio's form show the same step in place, built from the pieces
|
|
7
|
+
* exported here.
|
|
6
8
|
*/
|
|
7
9
|
import { useEffect, useRef, useState } from "react";
|
|
8
10
|
import { apiClient, type AuthUser } from "../../api-client";
|
|
@@ -12,7 +14,7 @@ import { useAppLanguage } from "../../i18n/react";
|
|
|
12
14
|
import { useViewport } from "../../react/input";
|
|
13
15
|
import { colors } from "../../theme/colors";
|
|
14
16
|
import { Box, Text, TextAttributes } from "../../ui";
|
|
15
|
-
import { useDialog, useDialogKeyboard, type PromptContext } from "../../ui/dialog";
|
|
17
|
+
import { useDialog, useDialogKeyboard, type DialogApi, type PromptContext } from "../../ui/dialog";
|
|
16
18
|
import { isPlainKey } from "../../utils/keyboard";
|
|
17
19
|
import { DeviceSignInDialog } from "../../plugins/builtin/cloud/device-signin-dialog";
|
|
18
20
|
import type { SignedInBroker } from "./client";
|
|
@@ -23,7 +25,7 @@ import {
|
|
|
23
25
|
type BrokerSignInSnapshot,
|
|
24
26
|
} from "./sign-in";
|
|
25
27
|
|
|
26
|
-
function
|
|
28
|
+
export function brokerSignInStatus(snapshot: BrokerSignInSnapshot, broker: SignedInBroker): { text: string; color: string } {
|
|
27
29
|
switch (snapshot.phase) {
|
|
28
30
|
case "connected":
|
|
29
31
|
return { text: t("Connected"), color: colors.positive };
|
|
@@ -39,18 +41,31 @@ function signInStatus(snapshot: BrokerSignInSnapshot, broker: SignedInBroker): {
|
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
/** Under the code: the broker's own note, or what connecting a single-connection broker does to other apps. */
|
|
45
|
+
export function brokerSignInNote(broker: SignedInBroker): string | null {
|
|
46
|
+
return broker.capabilities.signupNote
|
|
47
|
+
?? (broker.capabilities.singleConnection
|
|
48
|
+
? tf("Other AI apps linked to {broker} get disconnected. Connect them to Gloom instead.", { broker: broker.name })
|
|
49
|
+
: null);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* One attempt at connecting `broker`, with a controller of its own: started on
|
|
54
|
+
* mount, cancelled on unmount. A session Gloom refused ends it at once as
|
|
55
|
+
* "signed-out", for a new Gloom sign-in and a fresh attempt; "connected" waits
|
|
56
|
+
* a beat so it is seen, and `finish` skips the wait.
|
|
57
|
+
*/
|
|
58
|
+
export function useBrokerSignInAttempt(
|
|
59
|
+
broker: SignedInBroker,
|
|
60
|
+
write: boolean,
|
|
61
|
+
onOutcome: (outcome: "connected" | "signed-out") => void,
|
|
62
|
+
): { snapshot: BrokerSignInSnapshot; restart(): void; finish(): void } {
|
|
50
63
|
const controllerRef = useRef<BrokerSignInController | null>(null);
|
|
51
64
|
if (!controllerRef.current) controllerRef.current = new BrokerSignInController(broker, write);
|
|
52
65
|
const controller = controllerRef.current;
|
|
53
66
|
const [snapshot, setSnapshot] = useState(controller.getSnapshot());
|
|
67
|
+
const onOutcomeRef = useRef(onOutcome);
|
|
68
|
+
onOutcomeRef.current = onOutcome;
|
|
54
69
|
|
|
55
70
|
useEffect(() => {
|
|
56
71
|
const unsubscribe = controller.subscribe(setSnapshot);
|
|
@@ -61,33 +76,59 @@ function BrokerSignInDialog({
|
|
|
61
76
|
};
|
|
62
77
|
}, [controller]);
|
|
63
78
|
|
|
64
|
-
// Close after a beat so "Connected" is visible; enter skips the wait. Signed
|
|
65
|
-
// out closes at once, for the host to sign in to Gloom.
|
|
66
79
|
useEffect(() => {
|
|
67
80
|
if (snapshot.phase === "signed-out") {
|
|
68
|
-
|
|
81
|
+
onOutcomeRef.current("signed-out");
|
|
69
82
|
return;
|
|
70
83
|
}
|
|
71
84
|
if (snapshot.phase !== "connected") return;
|
|
72
|
-
const closeTimer = setTimeout(() =>
|
|
85
|
+
const closeTimer = setTimeout(() => onOutcomeRef.current("connected"), 900);
|
|
73
86
|
return () => clearTimeout(closeTimer);
|
|
74
|
-
}, [
|
|
87
|
+
}, [snapshot.phase]);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
snapshot,
|
|
91
|
+
restart: () => {
|
|
92
|
+
if (snapshot.phase !== "connected") controller.start();
|
|
93
|
+
},
|
|
94
|
+
finish: () => {
|
|
95
|
+
if (snapshot.phase === "connected") onOutcomeRef.current("connected");
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Gloom's device sign-in, stacked over whatever asked for it. True once signed in. */
|
|
101
|
+
export async function promptGloomSignIn(dialog: DialogApi): Promise<boolean> {
|
|
102
|
+
return !!await dialog.prompt<AuthUser | undefined>({
|
|
103
|
+
size: "full",
|
|
104
|
+
content: (context: unknown) => <DeviceSignInDialog {...(context as PromptContext<AuthUser | undefined>)} />,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function BrokerSignInDialog({
|
|
109
|
+
resolve,
|
|
110
|
+
dismiss,
|
|
111
|
+
broker,
|
|
112
|
+
write,
|
|
113
|
+
}: PromptContext<BrokerSignInOutcome> & { broker: SignedInBroker; write: boolean }) {
|
|
114
|
+
useAppLanguage();
|
|
115
|
+
const { height: termHeight } = useViewport();
|
|
116
|
+
// Close after a beat so "Connected" is visible; enter skips the wait. Signed
|
|
117
|
+
// out closes at once, for the host to sign in to Gloom.
|
|
118
|
+
const { snapshot, restart, finish } = useBrokerSignInAttempt(broker, write, resolve);
|
|
75
119
|
|
|
76
120
|
useDialogKeyboard((event) => {
|
|
77
121
|
event.stopPropagation();
|
|
78
122
|
if (event.name === "enter" || event.name === "return") {
|
|
79
|
-
|
|
80
|
-
} else if (isPlainKey(event, "r")
|
|
81
|
-
|
|
123
|
+
finish();
|
|
124
|
+
} else if (isPlainKey(event, "r")) {
|
|
125
|
+
restart();
|
|
82
126
|
} else if (event.name === "escape") {
|
|
83
127
|
dismiss();
|
|
84
128
|
}
|
|
85
129
|
});
|
|
86
130
|
|
|
87
|
-
const note = broker
|
|
88
|
-
?? (broker.capabilities.singleConnection
|
|
89
|
-
? tf("Other AI apps linked to {broker} get disconnected. Connect them to Gloom instead.", { broker: broker.name })
|
|
90
|
-
: null);
|
|
131
|
+
const note = brokerSignInNote(broker);
|
|
91
132
|
// Title, spacing, note, and footer take about eleven rows around the panel.
|
|
92
133
|
const panelHeight = Math.max(4, termHeight - (note ? 12 : 10));
|
|
93
134
|
|
|
@@ -105,7 +146,7 @@ function BrokerSignInDialog({
|
|
|
105
146
|
<SignInCodePanel
|
|
106
147
|
url={snapshot.connectUrl}
|
|
107
148
|
code={snapshot.code}
|
|
108
|
-
status={
|
|
149
|
+
status={brokerSignInStatus(snapshot, broker)}
|
|
109
150
|
height={panelHeight}
|
|
110
151
|
shortcutScope="broker-signin:browser"
|
|
111
152
|
/>
|
|
@@ -161,10 +202,7 @@ export function BrokerSignInDialogHost() {
|
|
|
161
202
|
// The broker connection belongs to the Gloom account, so there must be one.
|
|
162
203
|
void runBrokerSignIn(request.broker, request.write, {
|
|
163
204
|
isSignedIn: () => apiClient.isSignedIn(),
|
|
164
|
-
signInToGloom:
|
|
165
|
-
size: "full",
|
|
166
|
-
content: (context: unknown) => <DeviceSignInDialog {...(context as PromptContext<AuthUser | undefined>)} />,
|
|
167
|
-
}),
|
|
205
|
+
signInToGloom: () => promptGloomSignIn(dialog),
|
|
168
206
|
connectBroker: async (broker, write) => await dialog.prompt<BrokerSignInOutcome>({
|
|
169
207
|
size: "full",
|
|
170
208
|
content: (context: unknown) => (
|
|
@@ -65,7 +65,7 @@ export interface DesktopPaneShotPayload {
|
|
|
65
65
|
paneState: Record<string, PaneRuntimeState>;
|
|
66
66
|
/**
|
|
67
67
|
* Installed plugins compiled for the browser, the same way the desktop view
|
|
68
|
-
* receives them. Without these a pane from
|
|
68
|
+
* receives them. Without these a pane from the plugins folder resolves in
|
|
69
69
|
* the CLI but is unknown to the page that renders it.
|
|
70
70
|
*/
|
|
71
71
|
externalPlugins?: DesktopExternalPluginBundle[];
|
|
@@ -80,7 +80,7 @@ export interface DesktopPaneShotApiProxy {
|
|
|
80
80
|
/**
|
|
81
81
|
* The page renders panes that fetch their own data. Left alone it can only
|
|
82
82
|
* reach the cloud API, so a pane whose values come from a provider the router
|
|
83
|
-
* merges in (analyst price targets,
|
|
83
|
+
* merges in (analyst price targets, cash dividends) rendered thinner than the
|
|
84
84
|
* same pane in the terminal. The bridge lets the page ask the Bun process to
|
|
85
85
|
* run those requests through the real provider router instead.
|
|
86
86
|
*/
|
package/src/cli/history-rows.ts
CHANGED
|
@@ -22,7 +22,7 @@ function decimalPlaces(value: number): number {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* Providers serve prices as float32:
|
|
25
|
+
* Providers serve prices as float32: The feed sends the exact float
|
|
26
26
|
* (340.33 arrives as 340.3299865722656) and the cloud a float already rounded
|
|
27
27
|
* to a few decimals (254.42999). Return the shortest decimal that is the same
|
|
28
28
|
* float32. Values with fewer than five decimals are left alone, and a precise
|
|
@@ -372,21 +372,21 @@ export const FUNCTION_HELP: Readonly<Record<string, FunctionHelp>> = {
|
|
|
372
372
|
summary: "The 10-K risk factor section grouped by theme, with what was added, dropped or rewritten since the prior year called out.",
|
|
373
373
|
usage: ["RISK TSLA"],
|
|
374
374
|
keys: [key("y", "ear"), key("o", "pen filing")],
|
|
375
|
-
data: AS_FILED,
|
|
375
|
+
data: pro(AS_FILED.pro),
|
|
376
376
|
bloomberg: [],
|
|
377
377
|
},
|
|
378
378
|
EXEC: {
|
|
379
379
|
summary: "Named executive officers and what they were paid, read from the proxy statement and checked against the filing.",
|
|
380
380
|
usage: ["EXEC NVDA"],
|
|
381
381
|
keys: [key("y", "ear"), key("o", "pen filing")],
|
|
382
|
-
data: AS_FILED,
|
|
382
|
+
data: pro(AS_FILED.pro),
|
|
383
383
|
bloomberg: ["MGMT"],
|
|
384
384
|
},
|
|
385
385
|
EK: {
|
|
386
386
|
summary: "The company's 8-Ks classified by item (agreements, executive changes, auditor changes, restructurings) with a summary of what each one said.",
|
|
387
387
|
usage: ["EK NVDA"],
|
|
388
388
|
keys: [key("o", "pen filing")],
|
|
389
|
-
data: AS_FILED,
|
|
389
|
+
data: pro(AS_FILED.pro),
|
|
390
390
|
bloomberg: ["CF"],
|
|
391
391
|
},
|
|
392
392
|
CALLS: {
|
|
@@ -40,8 +40,8 @@ export async function resolveTickerForCli(
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
-
* Search catalogues can file a listing under another company's name
|
|
44
|
-
*
|
|
43
|
+
* Search catalogues can file a listing under another company's name, such as
|
|
44
|
+
* BAE Systems' London line (BA:LSE) under Boeing. The listing's own
|
|
45
45
|
* quote names the security it prices, which is the name the ticker command
|
|
46
46
|
* shows, so a record created here takes it.
|
|
47
47
|
*/
|