gloomberb 0.10.0 → 0.10.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/package.json +8 -1
- package/src/api-client/auth.ts +1 -0
- package/src/api-client/index.test.ts +359 -108
- package/src/api-client/index.ts +24 -13
- package/src/api-client/socket.ts +70 -35
- package/src/api-client/types.ts +1 -1
- package/src/app/language-observer.test.tsx +82 -0
- package/src/app/language-observer.tsx +13 -0
- package/src/app/pane-runtime/index.tsx +2 -2
- package/src/app/pane-runtime/pane-settings-runtime.tsx +2 -2
- package/src/app/pane-runtime/pane-template-runtime.ts +2 -2
- package/src/app/pane-runtime/plugin-bindings.ts +2 -2
- package/src/app/pane-runtime/ticker-open-runtime.ts +2 -2
- package/src/app/runtime/broker-import.ts +2 -2
- package/src/app/runtime/plugin-bindings.ts +2 -2
- package/src/app/runtime/startup.ts +2 -2
- package/src/app.tsx +16 -7
- package/src/brokers/account-cache.ts +6 -6
- package/src/brokers/broker-ticker-sync.ts +3 -3
- package/src/brokers/remote-broker-adapter.ts +2 -2
- package/src/brokers/sync-broker-instance.ts +5 -6
- package/src/cli/commands/api.ts +3 -6
- package/src/cli/commands/automation.ts +7 -15
- package/src/cli/commands/market.ts +26 -61
- package/src/cli/commands/overview.ts +34 -45
- package/src/cli/commands/search.ts +25 -27
- package/src/cli/commands/system.ts +27 -57
- package/src/cli/commands/ticker.ts +62 -63
- package/src/cli/context.test.ts +63 -0
- package/src/cli/context.ts +15 -1
- package/src/cli/index.ts +1 -1
- package/src/cli/pane-functions/data.ts +2 -19
- package/src/cli/pane-functions/index.ts +26 -25
- package/src/cli/pane-functions/report.ts +1 -1
- package/src/cli/pane-functions/screenshot.ts +3 -3
- package/src/cli/scoped-context.ts +41 -0
- package/src/components/chart/composite/composite-chart.test.tsx +615 -18
- package/src/components/chart/composite/composite-chart.tsx +179 -79
- package/src/components/chart/composite/format.test.ts +20 -7
- package/src/components/chart/composite/index.ts +0 -21
- package/src/components/chart/composite/interactions.test.ts +90 -3
- package/src/components/chart/composite/interactions.ts +189 -24
- package/src/components/chart/composite/price-series.ts +0 -2
- package/src/components/chart/composite/rasterizer.ts +10 -9
- package/src/components/chart/composite/renderers.test.ts +108 -2
- package/src/components/chart/composite/scene.test.ts +36 -0
- package/src/components/chart/composite/scene.ts +5 -2
- package/src/components/chart/composite/text-renderer.ts +37 -25
- package/src/components/chart/composite/time-axis.test.ts +243 -0
- package/src/components/chart/composite/time-axis.ts +608 -0
- package/src/components/chart/composite/types.ts +12 -1
- package/src/components/chart/core/data.ts +2 -159
- package/src/components/chart/core/date-window.ts +6 -225
- package/src/components/chart/core/palette.ts +1 -12
- package/src/components/chart/core/pointer.ts +0 -142
- package/src/components/chart/core/types.ts +1 -58
- package/src/components/chart/cursor-motion.ts +2 -4
- package/src/components/chart/indicators/types.ts +0 -9
- package/src/components/chart/market-session.ts +0 -11
- package/src/components/chart/native/kitty/protocol.ts +1 -1
- package/src/components/chart/native/raster/layout.ts +3 -10
- package/src/components/chart/static/chart/axis-overlays.test.tsx +64 -0
- package/src/components/chart/static/chart/axis-overlays.tsx +59 -14
- package/src/components/chart/static/chart/surface.tsx +1 -2
- package/src/components/command-bar/action-runtime.ts +2 -2
- package/src/components/command-bar/commands/direct/index.ts +1 -1
- package/src/components/command-bar/panel/body.tsx +2 -2
- package/src/components/command-bar/panel/index.tsx +11 -9
- package/src/components/command-bar/routes/ticker-search/actions.ts +2 -2
- package/src/components/command-bar/surface/index.tsx +2 -2
- package/src/components/command-bar/workflow/body.tsx +3 -3
- package/src/components/command-bar/workflow/collection-actions.ts +2 -2
- package/src/components/command-bar/workflow/coordinator.ts +2 -2
- package/src/components/command-bar/workflow/field-row.tsx +25 -11
- package/src/components/command-bar/workflow/tickers.ts +2 -2
- package/src/components/feed-data-table/stack-view.test.tsx +28 -36
- package/src/components/feed-data-table/stack-view.tsx +3 -1
- package/src/components/layout/detached-pane-shell.tsx +6 -6
- package/src/components/layout/header.tsx +13 -9
- package/src/components/layout/pane/content.test.tsx +42 -3
- package/src/components/layout/pane/content.tsx +3 -1
- package/src/components/layout/pane/footer/index.test.tsx +45 -2
- package/src/components/layout/pane/footer/registration.tsx +3 -1
- package/src/components/layout/shell/index.tsx +1 -2
- package/src/components/layout/shell/native/surfaces.ts +1 -1
- package/src/components/layout/shell/native/window-state.ts +1 -1
- package/src/components/layout/status-bar.tsx +12 -8
- package/src/components/layout/window-controls.tsx +8 -6
- package/src/components/onboarding/onboarding-wizard.tsx +3 -1
- package/src/components/pane-settings-dialog/desktop.tsx +0 -2
- package/src/components/pane-settings-dialog.tsx +0 -2
- package/src/components/pane-template-wizard.tsx +10 -9
- package/src/components/price-performance.tsx +2 -113
- package/src/components/price-sparkline/model.ts +15 -17
- package/src/components/price-sparkline/view.tsx +8 -7
- package/src/components/speedometer-gauge/desktop.tsx +2 -1
- package/src/components/speedometer-gauge/index.tsx +0 -2
- package/src/components/speedometer-gauge/terminal.tsx +24 -5
- package/src/components/ticker/badge/index.tsx +1 -18
- package/src/components/ui/data-table/opentui/index.tsx +3 -3
- package/src/components/ui/frame.tsx +1 -2
- package/src/components/ui/page-stack-view.test.tsx +126 -0
- package/src/components/ui/page-stack-view.tsx +24 -12
- package/src/components/ui/tabs.tsx +7 -3
- package/src/components/ui/ui.test.tsx +23 -2
- package/src/components/use-external-link-footer.ts +4 -2
- package/src/core/app-service-ports.ts +46 -0
- package/src/core/app-services.test.ts +66 -0
- package/src/core/app-services.ts +17 -15
- package/src/data/config/chart-settings.ts +3 -10
- package/src/data/config/store/index.test.ts +99 -2
- package/src/data/config/store/migrations.ts +278 -0
- package/src/data/config/store/normalize.ts +44 -186
- package/src/data/resource-store.ts +73 -12
- package/src/data/sqlite/cache.test.ts +104 -15
- package/src/i18n/index.test.ts +17 -0
- package/src/i18n/index.ts +23 -6
- package/src/i18n/ja.ts +13 -0
- package/src/i18n/ko.ts +13 -0
- package/src/i18n/react.ts +6 -0
- package/src/i18n/zh-cn.ts +13 -0
- package/src/i18n/zh-tw.ts +13 -0
- package/src/market-data/coordinator/auxiliary.ts +7 -3
- package/src/market-data/coordinator/chart.ts +1 -1
- package/src/market-data/coordinator/index.test.ts +26 -0
- package/src/market-data/coordinator/index.ts +11 -5
- package/src/market-data/coordinator/quotes.ts +4 -40
- package/src/market-data/coordinator/subscriptions.test.ts +89 -1
- package/src/market-data/hooks.tsx +47 -1
- package/src/market-data/market/status.ts +6 -6
- package/src/market-data/performance.ts +1 -1
- package/src/market-data/quote-subscription-target.test.ts +37 -0
- package/src/market-data/quote-subscription-target.ts +60 -0
- package/src/market-data/quotes/freshness.ts +1 -0
- package/src/market-data/quotes/resolution.ts +4 -0
- package/src/market-data/request-types.ts +3 -3
- package/src/news/aggregator.test.ts +97 -1
- package/src/news/aggregator.ts +100 -57
- package/src/news/hooks.test.tsx +64 -0
- package/src/news/hooks.ts +18 -25
- package/src/plugins/builtin/account-management/footer.ts +4 -2
- package/src/plugins/builtin/account-management/pane.tsx +9 -4
- package/src/plugins/builtin/ai/agent-history.ts +11 -7
- package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +0 -5
- package/src/plugins/builtin/ai/ask-ai.test.tsx +3 -4
- package/src/plugins/builtin/ai/index.test.ts +2 -2
- package/src/plugins/builtin/ai/index.tsx +10 -11
- package/src/plugins/builtin/ai/pane-settings.ts +1 -1
- package/src/plugins/builtin/ai/pi/runtime.ts +6 -5
- package/src/plugins/builtin/ai/providers.ts +1 -9
- package/src/plugins/builtin/ai/runner.ts +0 -6
- package/src/plugins/builtin/ai/screener/footer.ts +4 -1
- package/src/plugins/builtin/ai/screener/pane.test.tsx +9 -9
- package/src/plugins/builtin/ai/workspace/model.ts +2 -27
- package/src/plugins/builtin/ai/workspace/pane.tsx +2 -2
- package/src/plugins/builtin/analytics/metrics.ts +1 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +3 -2
- package/src/plugins/builtin/broker-manager/index.tsx +4 -2
- package/src/plugins/builtin/chart-composer/editor/controller.ts +643 -0
- package/src/plugins/builtin/chart-composer/editor/desktop-view.tsx +134 -0
- package/src/plugins/builtin/chart-composer/editor/model.test.ts +85 -0
- package/src/plugins/builtin/chart-composer/editor/model.ts +292 -0
- package/src/plugins/builtin/chart-composer/editor/opentui-view.tsx +108 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +65 -947
- package/src/plugins/builtin/chart-composer/index.tsx +0 -6
- package/src/plugins/builtin/chart-composer/pane.tsx +69 -36
- package/src/plugins/builtin/chart-composer/presets.ts +1 -19
- package/src/plugins/builtin/chart-composer/settings.ts +1 -1
- package/src/plugins/builtin/chat/content/composer-runtime.ts +1 -1
- package/src/plugins/builtin/chat/content/shortcuts.ts +0 -1
- package/src/plugins/builtin/chat/controller/fetch.ts +8 -2
- package/src/plugins/builtin/chat/controller/persistence.ts +2 -0
- package/src/plugins/builtin/chat/controller/session.test.ts +25 -0
- package/src/plugins/builtin/chat/controller/session.ts +2 -0
- package/src/plugins/builtin/chat/controller/state.ts +7 -0
- package/src/plugins/builtin/chat/controller.test.ts +73 -20
- package/src/plugins/builtin/chat/message/desktop.tsx +2 -0
- package/src/plugins/builtin/cloud-tweets/footer.ts +9 -28
- package/src/plugins/builtin/cloud-tweets/pane.tsx +0 -5
- package/src/plugins/builtin/cloud-tweets/table.tsx +12 -17
- package/src/plugins/builtin/correlation/compute.ts +4 -10
- package/src/plugins/builtin/econ/index.tsx +17 -19
- package/src/plugins/builtin/holders/types.ts +0 -1
- package/src/plugins/builtin/insider/index.tsx +6 -31
- package/src/plugins/builtin/news/index.tsx +5 -7
- package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +6 -8
- package/src/plugins/builtin/news/wire/news/footer.ts +10 -11
- package/src/plugins/builtin/news/wire/read-state.test.ts +7 -0
- package/src/plugins/builtin/news/wire/read-state.ts +18 -28
- package/src/plugins/builtin/news/wire/rss/parser.ts +1 -11
- package/src/plugins/builtin/options/footer.test.ts +115 -0
- package/src/plugins/builtin/options/footer.ts +110 -0
- package/src/plugins/builtin/options/live-quotes.test.ts +238 -0
- package/src/plugins/builtin/options/live-quotes.ts +147 -0
- package/src/plugins/builtin/options/view.test.tsx +71 -1
- package/src/plugins/builtin/options/view.tsx +39 -24
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +103 -117
- package/src/plugins/builtin/portfolio-list/cli/render.ts +11 -5
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +120 -135
- package/src/plugins/builtin/portfolio-list/header.tsx +9 -4
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +6 -1
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +4 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +24 -52
- package/src/plugins/builtin/sec/filing-content.test.ts +41 -0
- package/src/plugins/builtin/sec/filing-content.ts +94 -0
- package/src/plugins/builtin/sec/index.tsx +21 -104
- package/src/plugins/builtin/sectors/sector-model.ts +1 -7
- package/src/plugins/builtin/shared/cloud-upgrade.ts +1 -0
- package/src/plugins/builtin/shared/pane-footer.ts +82 -0
- package/src/plugins/builtin/shared/read-state.ts +64 -3
- package/src/plugins/builtin/substack/read-state.ts +16 -41
- package/src/plugins/builtin/thirteenf/pane.tsx +24 -73
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +10 -10
- package/src/plugins/builtin/yield-curve/index.tsx +11 -9
- package/src/plugins/ibkr/dialogs.tsx +3 -3
- package/src/plugins/ibkr/gateway/fundamentals.ts +1 -1
- package/src/plugins/ibkr/gateway/history-requests.ts +2 -2
- package/src/plugins/ibkr/gateway/history.ts +2 -2
- package/src/plugins/ibkr/gateway/selection.ts +2 -2
- package/src/plugins/ibkr/gateway/service/index.ts +2 -2
- package/src/plugins/ibkr/gateway/service/native.ts +2 -2
- package/src/plugins/plugin-persistence.ts +3 -4
- package/src/plugins/prediction-markets/controller/keyboard.ts +1 -1
- package/src/plugins/prediction-markets/controller/live-updates.ts +6 -5
- package/src/plugins/prediction-markets/detail/book.tsx +2 -2
- package/src/plugins/prediction-markets/detail/outcomes.tsx +3 -1
- package/src/plugins/prediction-markets/detail/pane.tsx +3 -1
- package/src/plugins/prediction-markets/detail/trades.tsx +3 -3
- package/src/plugins/registry/context.ts +3 -4
- package/src/plugins/registry/index.ts +5 -6
- package/src/react/input.test.ts +126 -3
- package/src/react/input.ts +120 -1
- package/src/remote/app-host.tsx +2 -13
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +44 -60
- package/src/renderers/electrobun/bun/desktop/capability-bridge.ts +15 -13
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +26 -22
- package/src/renderers/electrobun/bun/desktop/http-fetch.ts +5 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +11 -6
- package/src/renderers/electrobun/bun/desktop/media.ts +5 -3
- package/src/renderers/electrobun/bun/desktop/plugin-state.ts +21 -22
- package/src/renderers/electrobun/bun/desktop/window-controls.ts +3 -1
- package/src/renderers/electrobun/bun/desktop/workspace/requests.ts +18 -18
- package/src/renderers/electrobun/bun/index.ts +123 -83
- package/src/renderers/electrobun/shared/protocol.ts +146 -7
- package/src/renderers/electrobun/view/app-services.ts +5 -8
- package/src/renderers/electrobun/view/backend-rpc.ts +18 -8
- package/src/renderers/electrobun/view/broker-remote-client.ts +2 -2
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +5 -22
- package/src/renderers/electrobun/view/config-host.ts +1 -1
- package/src/renderers/electrobun/view/context-menu.ts +1 -17
- package/src/renderers/electrobun/view/data-table/index.tsx +0 -1
- package/src/renderers/electrobun/view/data-table/row.tsx +0 -4
- package/src/renderers/electrobun/view/desktop/control-styles.ts +37 -34
- package/src/renderers/electrobun/view/desktop/controls.tsx +36 -33
- package/src/renderers/electrobun/view/desktop/list-view.tsx +15 -12
- package/src/renderers/electrobun/view/desktop/popover.tsx +2 -3
- package/src/renderers/electrobun/view/dialog-host.tsx +2 -3
- package/src/renderers/electrobun/view/fatal-screen.tsx +0 -1
- package/src/renderers/electrobun/view/host/box.tsx +0 -1
- package/src/renderers/electrobun/view/host/chart-surface.tsx +0 -1
- package/src/renderers/electrobun/view/host/input.tsx +0 -1
- package/src/renderers/electrobun/view/host/media-surface.tsx +0 -1
- package/src/renderers/electrobun/view/host/native.ts +0 -1
- package/src/renderers/electrobun/view/host/scroll-box.tsx +0 -1
- package/src/renderers/electrobun/view/host/tabs.tsx +0 -1
- package/src/renderers/electrobun/view/host/text.tsx +0 -1
- package/src/renderers/electrobun/view/http-fetch.ts +3 -11
- package/src/renderers/electrobun/view/input-host.tsx +10 -53
- package/src/renderers/electrobun/view/key-event.ts +0 -1
- package/src/renderers/electrobun/view/main.tsx +3 -3
- package/src/renderers/electrobun/view/native-renderer.ts +0 -1
- package/src/renderers/electrobun/view/native-stubs/backend-rpc.ts +13 -1
- package/src/renderers/electrobun/view/native-stubs/chart/surface-manager.ts +11 -0
- package/src/renderers/electrobun/view/native-stubs/native-stubs.test.ts +47 -0
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +6 -44
- package/src/renderers/electrobun/view/remote/persistence.ts +6 -2
- package/src/renderers/electrobun/view/remote/quote-subscription-registry.test.ts +86 -0
- package/src/renderers/electrobun/view/remote/quote-subscription-registry.ts +72 -0
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +2 -2
- package/src/renderers/electrobun/view/toast-host.tsx +0 -1
- package/src/renderers/electrobun/view/ui-host.tsx +1 -2
- package/src/renderers/electrobun/view/update-host.ts +1 -1
- package/src/renderers/opentui/chart-surface.tsx +2 -1
- package/src/renderers/opentui/dialog-host.tsx +20 -4
- package/src/renderers/opentui/image/loader.ts +12 -2
- package/src/renderers/opentui/input-host.tsx +7 -49
- package/src/renderers/opentui/start.tsx +2 -0
- package/src/sources/gloomberb-cloud/history-quality.ts +44 -0
- package/src/sources/gloomberb-cloud/index.test.ts +173 -0
- package/src/sources/gloomberb-cloud/index.ts +42 -20
- package/src/sources/gloomberb-cloud/normalizers.ts +9 -3
- package/src/sources/provider-router/cache.ts +1 -1
- package/src/sources/provider-router/document-routes.ts +10 -5
- package/src/sources/provider-router/financials.test.ts +14 -0
- package/src/sources/provider-router/financials.ts +6 -1
- package/src/sources/provider-router/history.test.ts +163 -1
- package/src/sources/provider-router/history.ts +278 -347
- package/src/sources/provider-router/index.ts +2 -2
- package/src/sources/provider-router/routing.test.ts +39 -0
- package/src/sources/provider-router/routing.ts +24 -11
- package/src/sources/provider-router/supplemental.ts +37 -17
- package/src/sources/sec-edgar/content.ts +2 -12
- package/src/sources/sec-edgar.ts +2 -13
- package/src/sources/yahoo-finance/history.ts +2 -2
- package/src/sources/yahoo-finance/options.ts +5 -0
- package/src/sources/yahoo-finance.ts +2 -2
- package/src/state/app/bootstrap.ts +2 -2
- package/src/state/app/context/index.tsx +2 -2
- package/src/state/hooks/quote-streaming.ts +66 -1
- package/src/state/hooks/session-snapshot.ts +2 -2
- package/src/state/selectors.ts +0 -9
- package/src/sync/controller.test.ts +98 -0
- package/src/sync/controller.ts +29 -5
- package/src/sync/react.ts +2 -2
- package/src/sync/types.ts +2 -2
- package/src/theme/colors.test.ts +10 -0
- package/src/theme/colors.ts +75 -78
- package/src/theme/theme-context.test.tsx +67 -0
- package/src/theme/theme-context.tsx +9 -5
- package/src/tickers/open-target.ts +2 -2
- package/src/tickers/search/upsert.ts +2 -2
- package/src/time-series/alignment.ts +0 -7
- package/src/time-series/chart-data.ts +145 -0
- package/src/time-series/date-window.ts +44 -0
- package/src/time-series/history-window.ts +21 -0
- package/src/time-series/hooks.test.ts +59 -0
- package/src/time-series/hooks.ts +33 -12
- package/src/time-series/market-resolution.ts +2 -2
- package/src/time-series/market.ts +1 -1
- package/src/time-series/range.ts +9 -0
- package/src/{components/chart/core → time-series}/resolution.ts +3 -102
- package/src/time-series/resolve.test.ts +203 -1
- package/src/time-series/resolve.ts +96 -18
- package/src/time-series/spec-studies.test.ts +15 -0
- package/src/time-series/spec.ts +2 -2
- package/src/time-series/studies.ts +5 -2
- package/src/time-series/types.ts +3 -1
- package/src/time-series/use-chart-resolution.test.tsx +358 -0
- package/src/time-series/use-chart-resolution.ts +46 -4
- package/src/types/broker.ts +2 -2
- package/src/types/data-provider.ts +3 -3
- package/src/types/financials.ts +12 -0
- package/src/types/plugin.ts +2 -2
- package/src/ui/context-menu.tsx +3 -2
- package/src/ui/dialog.tsx +2 -2
- package/src/utils/html-entities.ts +3 -5
- package/src/utils/main-thread-monitor.ts +1 -1
- package/src/utils/price-history.ts +5 -5
- package/src/version.ts +3 -1
- package/src/components/chart/core/viewport.test.ts +0 -8
- package/src/components/chart/core/viewport.ts +0 -24
- package/src/time-series/index.ts +0 -14
- /package/src/{components/chart/core → time-series}/resolution.test.ts +0 -0
|
@@ -29,21 +29,33 @@ export function PageStackView({
|
|
|
29
29
|
backHint,
|
|
30
30
|
}: PageStackViewProps) {
|
|
31
31
|
const HostPageStackView = useUiHost().PageStackView as ComponentType<PageStackViewProps> | undefined;
|
|
32
|
+
const props = {
|
|
33
|
+
focused,
|
|
34
|
+
detailOpen,
|
|
35
|
+
onBack,
|
|
36
|
+
rootContent,
|
|
37
|
+
detailContent,
|
|
38
|
+
detailTitle,
|
|
39
|
+
backLabel,
|
|
40
|
+
backHint,
|
|
41
|
+
};
|
|
32
42
|
if (HostPageStackView) {
|
|
33
|
-
return
|
|
34
|
-
<HostPageStackView
|
|
35
|
-
focused={focused}
|
|
36
|
-
detailOpen={detailOpen}
|
|
37
|
-
onBack={onBack}
|
|
38
|
-
rootContent={rootContent}
|
|
39
|
-
detailContent={detailContent}
|
|
40
|
-
detailTitle={detailTitle}
|
|
41
|
-
backLabel={backLabel}
|
|
42
|
-
backHint={backHint}
|
|
43
|
-
/>
|
|
44
|
-
);
|
|
43
|
+
return <HostPageStackView {...props} />;
|
|
45
44
|
}
|
|
46
45
|
|
|
46
|
+
return <OpenTuiPageStackView {...props} />;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function OpenTuiPageStackView({
|
|
50
|
+
focused,
|
|
51
|
+
detailOpen,
|
|
52
|
+
onBack,
|
|
53
|
+
rootContent,
|
|
54
|
+
detailContent,
|
|
55
|
+
detailTitle,
|
|
56
|
+
backLabel,
|
|
57
|
+
backHint,
|
|
58
|
+
}: PageStackViewProps) {
|
|
47
59
|
const goBack = useCallback((event?: {
|
|
48
60
|
preventDefault?: () => void;
|
|
49
61
|
stopPropagation?: () => void;
|
|
@@ -2,8 +2,10 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
|
2
2
|
import { useShortcut } from "../../react/input";
|
|
3
3
|
import { Box, ScrollBox, Text, useUiHost } from "../../ui";
|
|
4
4
|
import { TextAttributes, type ScrollBoxRenderable } from "../../ui";
|
|
5
|
-
import {
|
|
5
|
+
import { hoverBg } from "../../theme/colors";
|
|
6
|
+
import { useThemeColors } from "../../theme/theme-context";
|
|
6
7
|
import { t } from "../../i18n";
|
|
8
|
+
import { useAppLanguage } from "../../i18n/react";
|
|
7
9
|
import { displayWidth } from "../../utils/format";
|
|
8
10
|
import { useRemoteUiNode } from "../../remote/semantic-tree";
|
|
9
11
|
|
|
@@ -55,9 +57,11 @@ export function Tabs({
|
|
|
55
57
|
scrollable = true,
|
|
56
58
|
scrollId,
|
|
57
59
|
}: TabsProps) {
|
|
60
|
+
const language = useAppLanguage();
|
|
61
|
+
const colors = useThemeColors();
|
|
58
62
|
const tabs = useMemo(() => rawTabs.map((tab) => (
|
|
59
63
|
{ ...tab, label: t(tab.label) }
|
|
60
|
-
)), [rawTabs]);
|
|
64
|
+
)), [language, rawTabs]);
|
|
61
65
|
const ui = useUiHost();
|
|
62
66
|
const NativeTabs = ui.Tabs;
|
|
63
67
|
const navigationValueRef = useRef<string | null>(activeValue);
|
|
@@ -74,7 +78,7 @@ export function Tabs({
|
|
|
74
78
|
activeUnderline: focused ? colors.textBright : colors.borderFocused,
|
|
75
79
|
inactiveUnderline: colors.bg,
|
|
76
80
|
hoverUnderline: colors.border,
|
|
77
|
-
hoverBg: hoverBg(),
|
|
81
|
+
hoverBg: hoverBg(colors),
|
|
78
82
|
activeBg: colors.selected,
|
|
79
83
|
activePillFg: colors.selectedText,
|
|
80
84
|
closeFg: colors.textMuted,
|
|
@@ -19,6 +19,7 @@ import { Tabs } from "./tabs";
|
|
|
19
19
|
import { AppContext, PaneInstanceProvider, createInitialState } from "../../state/app/context";
|
|
20
20
|
import { createDefaultConfig } from "../../types/config";
|
|
21
21
|
import { DataTableView } from "../data-table/view";
|
|
22
|
+
import { setLanguage } from "../../i18n";
|
|
22
23
|
|
|
23
24
|
let testSetup: Awaited<ReturnType<typeof testRender>> | undefined;
|
|
24
25
|
let setListSelection: ((index: number) => void) | null = null;
|
|
@@ -277,9 +278,9 @@ async function emitKeypress(event: { name?: string; sequence?: string; ctrl?: bo
|
|
|
277
278
|
await testSetup!.renderOnce();
|
|
278
279
|
}
|
|
279
280
|
|
|
280
|
-
afterEach(() => {
|
|
281
|
+
afterEach(async () => {
|
|
281
282
|
if (testSetup) {
|
|
282
|
-
testSetup
|
|
283
|
+
await act(async () => testSetup?.renderer.destroy());
|
|
283
284
|
testSetup = undefined;
|
|
284
285
|
}
|
|
285
286
|
setListSelection = null;
|
|
@@ -290,9 +291,29 @@ afterEach(() => {
|
|
|
290
291
|
addedTab = false;
|
|
291
292
|
resolvedChoice = null;
|
|
292
293
|
multiSelectOpenStates = [];
|
|
294
|
+
setLanguage("en");
|
|
293
295
|
});
|
|
294
296
|
|
|
295
297
|
describe("shared UI kit", () => {
|
|
298
|
+
test("retranslates stable tab items when the app language changes", async () => {
|
|
299
|
+
const tabs = [{ label: "Open", value: "open" }];
|
|
300
|
+
testSetup = await testRender(
|
|
301
|
+
<Tabs tabs={tabs} activeValue="open" onSelect={() => {}} />,
|
|
302
|
+
{ width: 20, height: 2 },
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
await act(async () => testSetup?.renderOnce());
|
|
306
|
+
expect(testSetup.captureCharFrame()).toContain("Open");
|
|
307
|
+
|
|
308
|
+
await act(async () => {
|
|
309
|
+
setLanguage("zh-CN");
|
|
310
|
+
await testSetup?.renderOnce();
|
|
311
|
+
await testSetup?.renderOnce();
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
expect(testSetup.captureCharFrame()).toContain("打开");
|
|
315
|
+
});
|
|
316
|
+
|
|
296
317
|
test("scrolls overflowing tabs horizontally with the mouse wheel", async () => {
|
|
297
318
|
testSetup = await testRender(
|
|
298
319
|
<Tabs
|
|
@@ -15,6 +15,7 @@ interface UseExternalLinkFooterOptions {
|
|
|
15
15
|
info?: PaneFooterSegment[];
|
|
16
16
|
hints?: PaneHint[];
|
|
17
17
|
label?: string;
|
|
18
|
+
showHint?: boolean;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
const EMPTY_INFO: PaneFooterSegment[] = [];
|
|
@@ -33,6 +34,7 @@ export function useExternalLinkFooter({
|
|
|
33
34
|
info = EMPTY_INFO,
|
|
34
35
|
hints = EMPTY_HINTS,
|
|
35
36
|
label = "link",
|
|
37
|
+
showHint = true,
|
|
36
38
|
}: UseExternalLinkFooterOptions) {
|
|
37
39
|
const rendererHost = useRendererHost();
|
|
38
40
|
const url = normalizeUrl(rawUrl);
|
|
@@ -68,10 +70,10 @@ export function useExternalLinkFooter({
|
|
|
68
70
|
info: [...info, ...linkInfo],
|
|
69
71
|
hints: [
|
|
70
72
|
...hints,
|
|
71
|
-
...(url ? [{ id: "open", key: "o", label: "pen", onPress: openUrl }] : []),
|
|
73
|
+
...(url && showHint ? [{ id: "open", key: "o", label: "pen", onPress: openUrl }] : []),
|
|
72
74
|
],
|
|
73
75
|
};
|
|
74
|
-
}, [hints, info, label, openUrl, sourceLabel, url]);
|
|
76
|
+
}, [hints, info, label, openUrl, showHint, sourceLabel, url]);
|
|
75
77
|
|
|
76
78
|
usePaneFooter(registrationId, () => footer, [footer]);
|
|
77
79
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { PluginStateStore } from "../data/plugin-state-store";
|
|
2
|
+
import type { ResourceStore } from "../data/resource-store";
|
|
3
|
+
import type { SessionStore } from "../data/session-store";
|
|
4
|
+
import type { TickerRepository } from "../data/ticker-repository";
|
|
5
|
+
import type { MarketDataCoordinator } from "../market-data/coordinator";
|
|
6
|
+
import type { PluginRegistry } from "../plugins/registry";
|
|
7
|
+
import type { AppConfig } from "../types/config";
|
|
8
|
+
import type { DataProvider } from "../types/data-provider";
|
|
9
|
+
import type { GloomPlugin } from "../types/plugin";
|
|
10
|
+
|
|
11
|
+
export type AppPluginStateStorePort = Pick<
|
|
12
|
+
PluginStateStore,
|
|
13
|
+
"get" | "set" | "delete" | "keys" | "clear"
|
|
14
|
+
>;
|
|
15
|
+
|
|
16
|
+
export type AppResourceStorePort = Pick<ResourceStore, "get" | "list" | "set" | "delete">;
|
|
17
|
+
export type AppSessionStorePort = Pick<SessionStore, "get" | "set" | "delete">;
|
|
18
|
+
|
|
19
|
+
export interface AppPersistencePort {
|
|
20
|
+
pluginState: AppPluginStateStorePort;
|
|
21
|
+
resources: AppResourceStorePort;
|
|
22
|
+
sessions: AppSessionStorePort;
|
|
23
|
+
close(): void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type AppTickerRepositoryPort = Pick<
|
|
27
|
+
TickerRepository,
|
|
28
|
+
"loadAllTickers" | "loadTicker" | "saveTicker" | "createTicker" | "deleteTicker"
|
|
29
|
+
>;
|
|
30
|
+
|
|
31
|
+
export interface AppRuntimeServices {
|
|
32
|
+
persistence: AppPersistencePort;
|
|
33
|
+
tickerRepository: AppTickerRepositoryPort;
|
|
34
|
+
dataProvider: DataProvider;
|
|
35
|
+
marketData: MarketDataCoordinator;
|
|
36
|
+
pluginRegistry: PluginRegistry;
|
|
37
|
+
ready: Promise<void>;
|
|
38
|
+
destroy(): void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface AppServicesFactoryOptions {
|
|
42
|
+
config: AppConfig;
|
|
43
|
+
plugins: readonly GloomPlugin[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type AppServicesFactory = (options: AppServicesFactoryOptions) => AppRuntimeServices;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { afterEach, expect, test } from "bun:test";
|
|
2
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { newsProvider } from "../capabilities";
|
|
6
|
+
import type { NewsArticle } from "../news/types";
|
|
7
|
+
import { createDefaultConfig } from "../types/config";
|
|
8
|
+
import type { GloomPlugin } from "../types/plugin";
|
|
9
|
+
import { createAppServices, type AppServices } from "./app-services";
|
|
10
|
+
|
|
11
|
+
let services: AppServices | null = null;
|
|
12
|
+
let dataDir: string | null = null;
|
|
13
|
+
|
|
14
|
+
afterEach(async () => {
|
|
15
|
+
services?.destroy();
|
|
16
|
+
services = null;
|
|
17
|
+
if (dataDir) await rm(dataDir, { recursive: true, force: true });
|
|
18
|
+
dataDir = null;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("the router does not re-enter the aggregate news facade", async () => {
|
|
22
|
+
dataDir = await mkdtemp(join(tmpdir(), "gloomberb-news-composition-"));
|
|
23
|
+
let fetchCount = 0;
|
|
24
|
+
const article: NewsArticle = {
|
|
25
|
+
id: "composition-story",
|
|
26
|
+
title: "Composition story",
|
|
27
|
+
url: "https://composition.example.com/story",
|
|
28
|
+
source: "Test",
|
|
29
|
+
publishedAt: new Date("2026-07-26T12:00:00.000Z"),
|
|
30
|
+
topic: "markets",
|
|
31
|
+
topics: ["markets"],
|
|
32
|
+
sectors: [],
|
|
33
|
+
categories: ["markets"],
|
|
34
|
+
tickers: [],
|
|
35
|
+
scores: { importance: 50, urgency: 0, marketImpact: 50, novelty: 0, confidence: 100 },
|
|
36
|
+
importance: 50,
|
|
37
|
+
isBreaking: false,
|
|
38
|
+
isDeveloping: false,
|
|
39
|
+
};
|
|
40
|
+
const plugin: GloomPlugin = {
|
|
41
|
+
id: "composition-news",
|
|
42
|
+
name: "Composition News",
|
|
43
|
+
version: "1.0.0",
|
|
44
|
+
capabilities: [newsProvider({
|
|
45
|
+
id: "composition-news",
|
|
46
|
+
name: "Composition News",
|
|
47
|
+
provider: {
|
|
48
|
+
fetchNews: async () => {
|
|
49
|
+
fetchCount++;
|
|
50
|
+
return [article];
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
})],
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
services = createAppServices({
|
|
57
|
+
config: createDefaultConfig(dataDir),
|
|
58
|
+
plugins: [plugin],
|
|
59
|
+
});
|
|
60
|
+
await services.ready;
|
|
61
|
+
|
|
62
|
+
const articles = await services.providerRouter.getNews({ feed: "latest", limit: 20 });
|
|
63
|
+
|
|
64
|
+
expect(fetchCount).toBe(1);
|
|
65
|
+
expect(articles.map((item) => item.id)).toEqual([article.id]);
|
|
66
|
+
});
|
package/src/core/app-services.ts
CHANGED
|
@@ -7,16 +7,15 @@ import { setSharedNewsService } from "../news/hooks";
|
|
|
7
7
|
import { PluginRegistry } from "../plugins/registry";
|
|
8
8
|
import { AssetDataRouter } from "../sources/provider-router";
|
|
9
9
|
import { assetDataProvider, newsProvider } from "../capabilities";
|
|
10
|
-
import type { AppConfig } from "../types/config";
|
|
11
10
|
import type { DataProvider } from "../types/data-provider";
|
|
12
|
-
import type { GloomPlugin } from "../types/plugin";
|
|
13
11
|
import { debugLog } from "../utils/debug-log";
|
|
14
12
|
import { measurePerf, measurePerfAsync } from "../utils/perf-marks";
|
|
15
13
|
import { setIbkrPortfolioPerformanceResourceStore } from "../plugins/ibkr/portfolio-performance";
|
|
14
|
+
import type { AppRuntimeServices, AppServicesFactoryOptions } from "./app-service-ports";
|
|
16
15
|
|
|
17
16
|
const servicesLog = debugLog.createLogger("services");
|
|
18
17
|
|
|
19
|
-
export interface AppServices {
|
|
18
|
+
export interface AppServices extends AppRuntimeServices {
|
|
20
19
|
persistence: AppPersistence;
|
|
21
20
|
tickerRepository: TickerRepository;
|
|
22
21
|
providerRouter: AssetDataRouter;
|
|
@@ -31,10 +30,7 @@ export interface AppServices {
|
|
|
31
30
|
export function createAppServices({
|
|
32
31
|
config,
|
|
33
32
|
plugins,
|
|
34
|
-
}: {
|
|
35
|
-
config: AppConfig;
|
|
36
|
-
plugins: readonly GloomPlugin[];
|
|
37
|
-
}): AppServices {
|
|
33
|
+
}: AppServicesFactoryOptions): AppServices {
|
|
38
34
|
servicesLog.info("create services start", {
|
|
39
35
|
pluginCount: plugins.length,
|
|
40
36
|
brokerInstanceCount: config.brokerInstances.length,
|
|
@@ -49,14 +45,20 @@ export function createAppServices({
|
|
|
49
45
|
const pluginRegistry = new PluginRegistry(dataProvider, tickerRepository, persistence);
|
|
50
46
|
const newsService = new NewsService();
|
|
51
47
|
pluginRegistry.capabilities.register("core", assetDataProvider(providerRouter));
|
|
52
|
-
pluginRegistry.capabilities.register("core",
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
48
|
+
pluginRegistry.capabilities.register("core", {
|
|
49
|
+
...newsProvider({
|
|
50
|
+
id: "core",
|
|
51
|
+
name: "News",
|
|
52
|
+
provider: {
|
|
53
|
+
fetchNews: async (query) => (await newsService.load(query)).articles,
|
|
54
|
+
getCachedNews: (query) => newsService.getQueryState(query).articles,
|
|
55
|
+
},
|
|
56
|
+
}),
|
|
57
|
+
// This capability exposes the aggregate service to remote renderers. Giving
|
|
58
|
+
// it the router source id prevents the router from rediscovering its own
|
|
59
|
+
// aggregate facade alongside the underlying plugin news sources.
|
|
60
|
+
sourceId: providerRouter.id,
|
|
61
|
+
});
|
|
60
62
|
|
|
61
63
|
providerRouter.attachRegistry(pluginRegistry);
|
|
62
64
|
pluginRegistry.getConfigFn = () => config;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChartResolution, TimeRange } from "../../
|
|
1
|
+
import type { ChartResolution, TimeRange } from "../../time-series/range";
|
|
2
2
|
import type { LayoutConfig, PaneBinding } from "../../types/config";
|
|
3
3
|
import type {
|
|
4
4
|
ChartPanelSpec,
|
|
@@ -55,6 +55,7 @@ export interface LegacyChartIndicatorSelection {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
export interface LegacyChartMigrationContext {
|
|
58
|
+
migrateLegacy?: boolean;
|
|
58
59
|
defaultRenderMode?: SeriesStyle;
|
|
59
60
|
indicatorSelection?: LegacyChartIndicatorSelection;
|
|
60
61
|
}
|
|
@@ -362,6 +363,7 @@ export function migrateChartPaneSettings(
|
|
|
362
363
|
settings: Record<string, unknown> | undefined,
|
|
363
364
|
context: LegacyChartMigrationContext = {},
|
|
364
365
|
): Record<string, unknown> | undefined {
|
|
366
|
+
if (context.migrateLegacy === false) return settings;
|
|
365
367
|
if (paneId === "chart-composer") {
|
|
366
368
|
const retained = stripKeys(settings, [
|
|
367
369
|
"chartAxisMode",
|
|
@@ -495,15 +497,6 @@ export function extractLegacyChartIndicatorSelection(
|
|
|
495
497
|
: { ids: normalizedLegacyIndicatorIds(["volume", ...ids]), explicit: true };
|
|
496
498
|
}
|
|
497
499
|
|
|
498
|
-
export function hasLegacyChartIndicatorConfig(
|
|
499
|
-
pluginConfig: Record<string, Record<string, unknown>>,
|
|
500
|
-
): boolean {
|
|
501
|
-
return ["ticker-research", "ticker-detail", "company-research"].some((pluginId) => {
|
|
502
|
-
const state = pluginConfig[pluginId];
|
|
503
|
-
return hasOwn(state, "chartIndicators") || hasOwn(state, "chartIndicatorsVersion");
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
|
|
507
500
|
export function stripLegacyChartPluginConfig(
|
|
508
501
|
pluginConfig: Record<string, Record<string, unknown>>,
|
|
509
502
|
): Record<string, Record<string, unknown>> {
|
|
@@ -648,6 +648,28 @@ describe("loadConfig", () => {
|
|
|
648
648
|
expect(config.disabledPlugins).toEqual(["news"]);
|
|
649
649
|
});
|
|
650
650
|
|
|
651
|
+
test("runs the Cloud-to-Macro split only before its v15 boundary", async () => {
|
|
652
|
+
const beforeDir = await createTempConfigDir();
|
|
653
|
+
await writeConfigJson(beforeDir, createSavedConfig({
|
|
654
|
+
configVersion: 14,
|
|
655
|
+
disabledPlugins: ["gloomberb-cloud"],
|
|
656
|
+
disabledSources: [],
|
|
657
|
+
}));
|
|
658
|
+
const before = await loadConfig(beforeDir);
|
|
659
|
+
expect(before.disabledPlugins).toEqual(["gloomberb-cloud", "macro"]);
|
|
660
|
+
expect(before.disabledSources).toEqual(["gloomberb-cloud"]);
|
|
661
|
+
|
|
662
|
+
const atBoundaryDir = await createTempConfigDir();
|
|
663
|
+
await writeConfigJson(atBoundaryDir, createSavedConfig({
|
|
664
|
+
configVersion: 15,
|
|
665
|
+
disabledPlugins: ["gloomberb-cloud"],
|
|
666
|
+
disabledSources: [],
|
|
667
|
+
}));
|
|
668
|
+
const atBoundary = await loadConfig(atBoundaryDir);
|
|
669
|
+
expect(atBoundary.disabledPlugins).toEqual(["gloomberb-cloud"]);
|
|
670
|
+
expect(atBoundary.disabledSources).toEqual([]);
|
|
671
|
+
});
|
|
672
|
+
|
|
651
673
|
test("preserves IBKR gateway configs without migration rewrites", async () => {
|
|
652
674
|
const dataDir = await createTempConfigDir();
|
|
653
675
|
await writeConfigJson(dataDir, createSavedConfig({
|
|
@@ -679,6 +701,7 @@ describe("loadConfig", () => {
|
|
|
679
701
|
test("migrates legacy saved pane tab IDs and preserves focus metadata", async () => {
|
|
680
702
|
const dataDir = await createTempConfigDir();
|
|
681
703
|
await writeConfigJson(dataDir, createSavedConfig({
|
|
704
|
+
configVersion: CURRENT_CONFIG_VERSION - 1,
|
|
682
705
|
layouts: [{
|
|
683
706
|
name: "Chart",
|
|
684
707
|
layout: DEFAULT_LAYOUT,
|
|
@@ -726,6 +749,80 @@ describe("loadConfig", () => {
|
|
|
726
749
|
expect(persisted.layouts[0]?.activePanel).toBe("right");
|
|
727
750
|
});
|
|
728
751
|
|
|
752
|
+
test("does not replay historical migrations for current configs or saves", async () => {
|
|
753
|
+
const dataDir = await createTempConfigDir();
|
|
754
|
+
const selectedPortfolioColumns = DEFAULT_COLUMNS.map((column) => column.id);
|
|
755
|
+
const currentLayout = {
|
|
756
|
+
...DEFAULT_LAYOUT,
|
|
757
|
+
instances: DEFAULT_LAYOUT.instances.map((instance) => (
|
|
758
|
+
instance.instanceId === "ticker-detail:main"
|
|
759
|
+
? {
|
|
760
|
+
...instance,
|
|
761
|
+
settings: {
|
|
762
|
+
...(instance.settings ?? {}),
|
|
763
|
+
chartRangePreset: "6M",
|
|
764
|
+
chartResolution: "1d",
|
|
765
|
+
},
|
|
766
|
+
}
|
|
767
|
+
: instance.instanceId === "portfolio-list:main"
|
|
768
|
+
? {
|
|
769
|
+
...instance,
|
|
770
|
+
settings: {
|
|
771
|
+
...(instance.settings ?? {}),
|
|
772
|
+
columnIds: selectedPortfolioColumns,
|
|
773
|
+
},
|
|
774
|
+
}
|
|
775
|
+
: instance
|
|
776
|
+
)),
|
|
777
|
+
};
|
|
778
|
+
const legacyPaneState = {
|
|
779
|
+
"ticker-detail:main": {
|
|
780
|
+
activeTabId: "fundamental-graphs",
|
|
781
|
+
pluginState: {
|
|
782
|
+
"ticker-detail": { detailMetric: "revenue", shared: "legacy" },
|
|
783
|
+
"ticker-research": { shared: "canonical" },
|
|
784
|
+
},
|
|
785
|
+
},
|
|
786
|
+
};
|
|
787
|
+
const legacyPluginConfig = {
|
|
788
|
+
"ticker-detail": {
|
|
789
|
+
chartIndicators: ["sma50"],
|
|
790
|
+
chartIndicatorsVersion: 2,
|
|
791
|
+
retainedPreference: "keep",
|
|
792
|
+
},
|
|
793
|
+
};
|
|
794
|
+
await writeConfigJson(dataDir, createSavedConfig({
|
|
795
|
+
layout: currentLayout,
|
|
796
|
+
layouts: [{ name: "Current", layout: currentLayout, paneState: legacyPaneState }],
|
|
797
|
+
pluginConfig: legacyPluginConfig,
|
|
798
|
+
disabledPlugins: ["options", "gloomberb-cloud"],
|
|
799
|
+
disabledSources: [],
|
|
800
|
+
}));
|
|
801
|
+
|
|
802
|
+
const config = await loadConfig(dataDir);
|
|
803
|
+
expect(findPaneInstance(config.layout, "ticker-detail:main")?.settings).toEqual(expect.objectContaining({
|
|
804
|
+
chartRangePreset: "6M",
|
|
805
|
+
chartResolution: "1d",
|
|
806
|
+
}));
|
|
807
|
+
expect(config.layouts[0]?.paneState).toEqual(legacyPaneState);
|
|
808
|
+
expect(config.pluginConfig).toEqual(legacyPluginConfig);
|
|
809
|
+
expect(config.disabledPlugins).toEqual(["options", "gloomberb-cloud"]);
|
|
810
|
+
expect(findPaneInstance(config.layout, "portfolio-list:main")?.settings?.columnIds)
|
|
811
|
+
.toEqual(selectedPortfolioColumns);
|
|
812
|
+
|
|
813
|
+
await saveConfig(config);
|
|
814
|
+
const persisted = JSON.parse(await readFile(join(dataDir, "config.json"), "utf-8")) as typeof config;
|
|
815
|
+
expect(persisted.layouts[0]?.paneState).toEqual(legacyPaneState);
|
|
816
|
+
expect(persisted.pluginConfig).toEqual(legacyPluginConfig);
|
|
817
|
+
expect(persisted.disabledPlugins).toEqual(["options", "gloomberb-cloud"]);
|
|
818
|
+
expect(findPaneInstance(persisted.layout, "portfolio-list:main")?.settings?.columnIds)
|
|
819
|
+
.toEqual(selectedPortfolioColumns);
|
|
820
|
+
expect(findPaneInstance(persisted.layout, "ticker-detail:main")?.settings).toEqual(expect.objectContaining({
|
|
821
|
+
chartRangePreset: "6M",
|
|
822
|
+
chartResolution: "1d",
|
|
823
|
+
}));
|
|
824
|
+
});
|
|
825
|
+
|
|
729
826
|
test("migrates legacy main portfolio panes to the portfolio default columns", async () => {
|
|
730
827
|
const dataDir = await createTempConfigDir();
|
|
731
828
|
const legacyColumnIds = DEFAULT_COLUMNS.map((column) => column.id);
|
|
@@ -757,7 +854,7 @@ describe("loadConfig", () => {
|
|
|
757
854
|
.toEqual(DEFAULT_PORTFOLIO_COLUMN_IDS);
|
|
758
855
|
});
|
|
759
856
|
|
|
760
|
-
test("does not replay the portfolio column migration
|
|
857
|
+
test("does not replay the portfolio column migration at its v17 boundary", async () => {
|
|
761
858
|
const dataDir = await createTempConfigDir();
|
|
762
859
|
const selectedColumnIds = DEFAULT_COLUMNS.map((column) => column.id);
|
|
763
860
|
const layout = {
|
|
@@ -776,7 +873,7 @@ describe("loadConfig", () => {
|
|
|
776
873
|
};
|
|
777
874
|
|
|
778
875
|
await writeConfigJson(dataDir, createSavedConfig({
|
|
779
|
-
configVersion:
|
|
876
|
+
configVersion: 17,
|
|
780
877
|
layout,
|
|
781
878
|
layouts: [{ name: "Default", layout }],
|
|
782
879
|
}));
|