gloomberb 0.11.0 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -15
- package/README.zh-CN.md +12 -12
- package/package.json +10 -3
- package/src/api-client/auth.ts +14 -10
- package/src/api-client/data.ts +54 -0
- package/src/api-client/index.test.ts +124 -0
- package/src/api-client/index.ts +139 -4
- package/src/api-client/paths.ts +89 -0
- package/src/api-client/request.ts +15 -2
- package/src/api-client/socket-health.test.ts +1 -0
- package/src/api-client/socket.ts +2 -1
- package/src/api-client/types.ts +163 -0
- package/src/app/pane-runtime/index.tsx +4 -1
- package/src/app/pane-runtime/plugin-bindings.test.ts +117 -0
- package/src/app/pane-runtime/plugin-bindings.ts +25 -4
- package/src/app/pane-runtime/ticker-inspector-runtime.ts +25 -14
- package/src/app/runtime/desktop-deeplink.test.ts +16 -0
- package/src/app/runtime/desktop-deeplink.ts +66 -2
- package/src/app/runtime/desktop-menu.ts +3 -0
- package/src/app/runtime/plugin-bindings.ts +30 -28
- package/src/app/runtime/update.ts +8 -6
- package/src/app.tsx +21 -7
- package/src/architecture/import-boundaries.test.ts +5 -1
- package/src/capabilities/factories.ts +4 -0
- package/src/cli/commands/automation.test.ts +9 -0
- package/src/cli/commands/automation.ts +22 -73
- package/src/cli/index.ts +10 -6
- package/src/cli/registry.ts +1 -1
- package/src/components/chart/composite/composite-chart.test.tsx +45 -2
- package/src/components/chart/composite/composite-chart.tsx +16 -12
- package/src/components/chart/composite/format.test.ts +34 -0
- package/src/components/chart/composite/format.ts +33 -10
- package/src/components/chart/composite/scene.ts +0 -21
- package/src/components/chart/composite/tools.ts +49 -0
- package/src/components/chart/core/axis-format.ts +13 -3
- package/src/components/chart/core/index.test.ts +7 -0
- package/src/components/chart/core/renderer.ts +3 -4
- package/src/components/command-bar/commands/direct/index.ts +4 -0
- package/src/components/command-bar/commands/registry.ts +8 -1
- package/src/components/command-bar/layout-items/current-layout.ts +1 -1
- package/src/components/command-bar/pane-templates/workflow.ts +4 -0
- package/src/components/command-bar/routes/root/shortcut-feedback.ts +3 -0
- package/src/components/command-bar/routes/root/shortcuts.test.ts +19 -0
- package/src/components/command-bar/routes/root/shortcuts.ts +3 -1
- package/src/components/command-bar/surface/index.test.tsx +6 -6
- package/src/components/command-bar/surface/index.tsx +4 -1
- package/src/components/command-bar/surface/pane.test.tsx +66 -2
- package/src/components/command-bar/view-model.test.ts +2 -1
- package/src/components/command-bar/workflow/ops.test.ts +54 -0
- package/src/components/command-bar/workflow/ops.ts +1 -1
- package/src/components/company-logo.test.ts +19 -0
- package/src/components/company-logo.tsx +60 -0
- package/src/components/feed-data-table/stack-view.tsx +7 -1
- package/src/components/index.ts +1 -0
- package/src/components/layout/detached-pane-shell.tsx +85 -15
- package/src/components/layout/header.tsx +5 -5
- package/src/components/layout/pane/title.ts +31 -3
- package/src/components/layout/shell/active-drag.ts +4 -11
- package/src/components/layout/shell/drag/index.ts +4 -20
- package/src/components/layout/shell/drag/runtime.ts +3 -4
- package/src/components/layout/shell/index.test.tsx +17 -3
- package/src/components/layout/shell/index.tsx +89 -31
- package/src/components/layout/shell/menu.test.ts +37 -1
- package/src/components/layout/shell/menu.ts +14 -0
- package/src/components/layout/shell/native/pointer-runtime.ts +22 -8
- package/src/components/layout/shell/pane/actions.ts +0 -5
- package/src/components/layout/shell/pane/management-shortcuts.ts +9 -4
- package/src/components/layout/shell/shortcuts.ts +6 -3
- package/src/components/layout/shell/ticker-link-menu.test.ts +82 -0
- package/src/components/layout/shell/ticker-link-menu.ts +78 -0
- package/src/components/layout/status-bar.test.tsx +62 -61
- package/src/components/layout/status-bar.tsx +53 -75
- package/src/components/onboarding/account-step/form-panel.tsx +4 -2
- package/src/components/onboarding/account-step/index.tsx +1 -1
- package/src/components/onboarding/account-step/signed-in-panel.tsx +1 -1
- package/src/components/onboarding/onboarding-wizard.test.tsx +44 -1
- package/src/components/onboarding/onboarding-wizard.tsx +13 -5
- package/src/components/onboarding/wizard-account.ts +4 -23
- package/src/components/table-view-shared.test.ts +11 -0
- package/src/components/table-view-shared.tsx +26 -0
- package/src/components/ui/data-table/index.tsx +19 -1
- package/src/components/ui/fields.tsx +5 -1
- package/src/core/state/app/layout-reducer.ts +36 -0
- package/src/core/state/app/layout.ts +10 -5
- package/src/core/state/app/state.ts +0 -13
- package/src/core/state/app/types.ts +6 -4
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/config/layout.ts +2 -0
- package/src/data/config/store/index.test.ts +27 -6
- package/src/data/config/store/normalize.ts +3 -5
- package/src/data/plugin-state-store.ts +11 -0
- package/src/data/sqlite/cache.test.ts +10 -0
- package/src/i18n/es.ts +13 -5
- package/src/i18n/ja.ts +13 -5
- package/src/i18n/ko.ts +13 -5
- package/src/i18n/zh-cn.ts +13 -5
- package/src/i18n/zh-tw.ts +13 -5
- package/src/layout-marketplace/api.test.ts +54 -0
- package/src/layout-marketplace/api.ts +65 -0
- package/src/layout-marketplace/gallery-desktop.test.tsx +262 -0
- package/src/layout-marketplace/gallery-desktop.tsx +375 -0
- package/src/layout-marketplace/gallery-terminal.tsx +376 -0
- package/src/layout-marketplace/gallery.test.tsx +84 -0
- package/src/layout-marketplace/gallery.tsx +323 -0
- package/src/layout-marketplace/geometry.test.ts +47 -0
- package/src/layout-marketplace/geometry.ts +80 -0
- package/src/layout-marketplace/mini-workspace.test.tsx +122 -0
- package/src/layout-marketplace/mini-workspace.tsx +428 -0
- package/src/layout-marketplace/model.test.ts +118 -0
- package/src/layout-marketplace/model.ts +171 -0
- package/src/layout-marketplace/name-dialog.tsx +60 -0
- package/src/layout-marketplace/pane-imagery.ts +85 -0
- package/src/layout-marketplace/pane.tsx +20 -0
- package/src/layout-marketplace/payload.test.ts +246 -0
- package/src/layout-marketplace/payload.ts +624 -0
- package/src/layout-marketplace/use-marketplace.ts +63 -0
- package/src/market-data/coordinator/index.test.ts +2 -2
- package/src/market-data/coordinator/index.ts +1 -1
- package/src/market-data/hooks.tsx +0 -41
- package/src/market-data/market/format.test.ts +7 -0
- package/src/market-data/market/freshness.ts +5 -2
- package/src/market-data/quotes/freshness.test.ts +16 -0
- package/src/news/aggregator.test.ts +92 -3
- package/src/news/aggregator.ts +98 -18
- package/src/news/hooks.test.tsx +2 -0
- package/src/news/hooks.ts +27 -1
- package/src/news/news-model.ts +5 -2
- package/src/news/types.ts +7 -0
- package/src/plugins/broker-sync/broker-sync.test.ts +58 -0
- package/src/plugins/broker-sync/normalize.ts +42 -5
- package/src/plugins/builtin/account-management/pane.tsx +5 -5
- package/src/plugins/builtin/ai/index.tsx +18 -2
- package/src/plugins/builtin/ai/screener/pane.test.tsx +47 -4
- package/src/plugins/builtin/ai/screener/pane.tsx +35 -12
- package/src/plugins/builtin/alerts/alert-engine.test.ts +53 -1
- package/src/plugins/builtin/alerts/alert-engine.ts +34 -1
- package/src/plugins/builtin/alerts/index.test.tsx +49 -6
- package/src/plugins/builtin/alerts/index.tsx +11 -6
- package/src/plugins/builtin/alerts/pane.tsx +51 -9
- package/src/plugins/builtin/alerts/quotes.ts +11 -3
- package/src/plugins/builtin/alerts/types.ts +1 -0
- package/src/plugins/builtin/analytics/index.tsx +11 -1
- package/src/plugins/builtin/analytics/pane-model.ts +6 -4
- package/src/plugins/builtin/broker-manager/index.tsx +3 -0
- package/src/plugins/builtin/buildout/model/index.ts +1 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +17 -18
- package/src/plugins/builtin/cds/client.test.ts +83 -0
- package/src/plugins/builtin/cds/client.ts +67 -0
- package/src/plugins/builtin/cds/index.tsx +41 -0
- package/src/plugins/builtin/cds/model.test.ts +232 -0
- package/src/plugins/builtin/cds/model.ts +401 -0
- package/src/plugins/builtin/cds/pane.test.tsx +132 -0
- package/src/plugins/builtin/cds/pane.tsx +358 -0
- package/src/plugins/builtin/chart-composer/chart-spec.ts +31 -0
- package/src/plugins/builtin/chart-composer/index.tsx +88 -2
- package/src/plugins/builtin/chart-composer/pane-share.test.ts +71 -0
- package/src/plugins/builtin/chart-composer/pane.tsx +103 -15
- package/src/plugins/builtin/chat/content/index.tsx +4 -1
- package/src/plugins/builtin/chat/controller/channels.ts +7 -1
- package/src/plugins/builtin/chat/controller/index.ts +14 -9
- package/src/plugins/builtin/chat/controller/notifications.ts +13 -3
- package/src/plugins/builtin/chat/controller/persistence.ts +0 -3
- package/src/plugins/builtin/chat/controller/realtime.ts +3 -3
- package/src/plugins/builtin/chat/controller/send.ts +1 -3
- package/src/plugins/builtin/chat/controller/session-runtime.ts +18 -11
- package/src/plugins/builtin/chat/controller/state.ts +1 -3
- package/src/plugins/builtin/chat/controller/storage.ts +0 -2
- package/src/plugins/builtin/chat/controller/utils.ts +4 -6
- package/src/plugins/builtin/chat/controller/view.ts +2 -2
- package/src/plugins/builtin/chat/controller.test.ts +142 -14
- package/src/plugins/builtin/chat/message/desktop.test.tsx +88 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +3 -1
- package/src/plugins/builtin/chat/message/profile-popover.test.tsx +1 -29
- package/src/plugins/builtin/chat/profile-popover.ts +53 -22
- package/src/plugins/builtin/chat/sidebar.test.tsx +81 -0
- package/src/plugins/builtin/chat/sidebar.tsx +63 -0
- package/src/plugins/builtin/chat/test-harness.tsx +29 -1
- package/src/plugins/builtin/cloud/auth-actions.tsx +14 -7
- package/src/plugins/builtin/cloud/auth-commands.ts +21 -51
- package/src/plugins/builtin/cloud/auth-dialog.tsx +296 -0
- package/src/{components/onboarding/account-step/model.test.ts → plugins/builtin/cloud/auth-model.test.ts} +1 -1
- package/src/{components/onboarding/account-step/model.ts → plugins/builtin/cloud/auth-model.ts} +37 -3
- package/src/plugins/builtin/cloud/browser.tsx +9 -0
- package/src/plugins/builtin/cloud/buildout-module.tsx +23 -0
- package/src/plugins/builtin/cloud/device-signin.ts +2 -8
- package/src/plugins/builtin/cloud/index.tsx +2 -0
- package/src/plugins/builtin/cloud/plugin.tsx +14 -24
- package/src/plugins/builtin/cloud/upgrade-command.ts +13 -6
- package/src/plugins/builtin/cloud-tweets/pane-share.test.ts +56 -0
- package/src/plugins/builtin/cloud-tweets/pane.tsx +20 -5
- package/src/plugins/builtin/cloud-tweets/registration.ts +50 -6
- package/src/plugins/builtin/composite-plugins.ts +3 -1
- package/src/plugins/builtin/congress-trades/detail.tsx +43 -1
- package/src/plugins/builtin/congress-trades/model.test.ts +85 -0
- package/src/plugins/builtin/congress-trades/model.ts +69 -1
- package/src/plugins/builtin/congress-trades/pane.tsx +51 -2
- package/src/plugins/builtin/connections/index.ts +3 -0
- package/src/plugins/builtin/dividend-yield/client.ts +18 -0
- package/src/plugins/builtin/dividend-yield/pane.tsx +7 -6
- package/src/plugins/builtin/earnings/index.tsx +5 -3
- package/src/plugins/builtin/earnings/model.test.ts +19 -1
- package/src/plugins/builtin/earnings/model.ts +17 -15
- package/src/plugins/builtin/econ/calendar-model.ts +0 -5
- package/src/plugins/builtin/econ/index.test.tsx +6 -1
- package/src/plugins/builtin/fear-greed/charts.tsx +12 -8
- package/src/plugins/builtin/help/index.tsx +2 -2
- package/src/plugins/builtin/holders/thirteenf-match.ts +1 -2
- package/src/plugins/builtin/insider/index.tsx +27 -11
- package/src/plugins/builtin/kelly-sizer/index.tsx +4 -1
- package/src/plugins/builtin/layout-manager/index.tsx +17 -4
- package/src/plugins/builtin/news/index.tsx +11 -6
- package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +2 -0
- package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +2 -0
- package/src/plugins/builtin/news/wire/breaking/pane.tsx +4 -1
- package/src/plugins/builtin/news/wire/feed-config.test.ts +15 -1
- package/src/plugins/builtin/news/wire/feed-config.ts +25 -5
- package/src/plugins/builtin/news/wire/index.tsx +3 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +7 -2
- package/src/plugins/builtin/news/wire/news/footer.ts +34 -1
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +4 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +8 -2
- package/src/plugins/builtin/news/wire/rss/source.test.ts +12 -0
- package/src/plugins/builtin/news/wire/rss/source.ts +1 -1
- package/src/plugins/builtin/notes/files.ts +80 -0
- package/src/plugins/builtin/notes/index.tsx +3 -0
- package/src/plugins/builtin/notes/sync.test.ts +49 -0
- package/src/plugins/builtin/notes/sync.ts +98 -0
- package/src/plugins/builtin/options/index.tsx +1 -0
- package/src/plugins/builtin/options/view.test.tsx +25 -3
- package/src/plugins/builtin/options/view.tsx +1 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.test.ts +22 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +12 -7
- package/src/plugins/builtin/portfolio-list/cli/render.ts +5 -3
- package/src/plugins/builtin/portfolio-list/column-values.ts +15 -4
- package/src/plugins/builtin/portfolio-list/header.tsx +13 -3
- package/src/plugins/builtin/portfolio-list/index.tsx +8 -0
- package/src/plugins/builtin/portfolio-list/metrics.test.ts +31 -1
- package/src/plugins/builtin/portfolio-list/pane/index.test.tsx +1 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +10 -7
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +6 -1
- package/src/plugins/builtin/portfolio-list/position-metrics.ts +25 -6
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +35 -21
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +20 -6
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +5 -12
- package/src/plugins/builtin/research/equity-diagnostic-pane.test.tsx +329 -0
- package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +582 -0
- package/src/plugins/builtin/research/index.test.ts +14 -0
- package/src/plugins/builtin/research/index.tsx +29 -0
- package/src/plugins/builtin/sec/index.tsx +24 -9
- package/src/plugins/builtin/sectors/index.tsx +14 -0
- package/src/plugins/builtin/shared/cloud-upgrade.ts +18 -11
- package/src/plugins/builtin/shared/public-pane.test.ts +12 -0
- package/src/plugins/builtin/shared/public-pane.ts +9 -0
- package/src/plugins/builtin/shared/public-share.ts +23 -0
- package/src/plugins/builtin/shared/ticker-source.ts +32 -0
- package/src/plugins/builtin/shared/ticker-surface.test.ts +27 -0
- package/src/plugins/builtin/shared/ticker-surface.ts +17 -0
- package/src/plugins/builtin/substack/api/cache.ts +7 -3
- package/src/plugins/builtin/substack/api/loaders.ts +36 -12
- package/src/plugins/builtin/substack/api/types.ts +2 -0
- package/src/plugins/builtin/substack/pane-state.ts +1 -1
- package/src/plugins/builtin/substack/pane.tsx +55 -12
- package/src/plugins/builtin/thirteenf/api.test.ts +1 -1
- package/src/plugins/builtin/thirteenf/api.ts +40 -18
- package/src/plugins/builtin/thirteenf/data.ts +15 -7
- package/src/plugins/builtin/thirteenf/pane.tsx +45 -10
- package/src/plugins/builtin/ticker-detail/index.tsx +10 -0
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +12 -4
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +8 -2
- package/src/plugins/builtin/ticker-detail/settings.ts +12 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +2 -0
- package/src/plugins/builtin/world-indices/index.tsx +2 -0
- package/src/plugins/catalog-browser.test.ts +78 -0
- package/src/plugins/catalog-browser.ts +136 -0
- package/src/plugins/gridlock-notification.ts +2 -1
- package/src/plugins/ibkr/flex/index.test.ts +19 -0
- package/src/plugins/ibkr/flex/index.ts +1 -1
- package/src/plugins/pane-manager/gridlock.ts +87 -3
- package/src/plugins/pane-manager.test.ts +30 -0
- package/src/plugins/pane-manager.ts +6 -1
- package/src/plugins/prediction-markets/chart.tsx +2 -2
- package/src/plugins/prediction-markets/controller/catalog.ts +77 -2
- package/src/plugins/prediction-markets/controller/data.ts +7 -1
- package/src/plugins/prediction-markets/controller/index.ts +9 -2
- package/src/plugins/prediction-markets/controller/keyboard.ts +10 -14
- package/src/plugins/prediction-markets/detail/overview.tsx +21 -0
- package/src/plugins/prediction-markets/detail/pane.tsx +43 -34
- package/src/plugins/prediction-markets/detail/trades.tsx +34 -9
- package/src/plugins/prediction-markets/detail.test.tsx +29 -2
- package/src/plugins/prediction-markets/index.tsx +8 -4
- package/src/plugins/prediction-markets/navigation.ts +0 -1
- package/src/plugins/prediction-markets/pane.test.tsx +77 -0
- package/src/plugins/prediction-markets/pane.tsx +12 -0
- package/src/plugins/prediction-markets/plugin.test.tsx +36 -0
- package/src/plugins/prediction-markets/services/kalshi/adapter.ts +49 -9
- package/src/plugins/prediction-markets/services/polymarket/adapter.ts +31 -0
- package/src/plugins/prediction-markets/test-helpers.tsx +9 -0
- package/src/plugins/registry/index.ts +2 -0
- package/src/plugins/ticker-navigation.test.ts +24 -0
- package/src/plugins/ticker-navigation.ts +40 -0
- package/src/remote/schema.ts +1 -1
- package/src/renderers/browser/app-services.ts +61 -0
- package/src/renderers/browser/cloud-transport.test.ts +68 -0
- package/src/renderers/browser/cloud-transport.ts +48 -0
- package/src/renderers/browser/config-host.ts +58 -0
- package/src/renderers/browser/deeplink-bridge.test.ts +41 -0
- package/src/renderers/browser/deeplink-bridge.ts +22 -0
- package/src/renderers/browser/error-boundary.tsx +23 -0
- package/src/renderers/browser/main.tsx +64 -0
- package/src/renderers/browser/persistence.ts +74 -0
- package/src/renderers/browser/storage.test.ts +66 -0
- package/src/renderers/browser/storage.ts +50 -0
- package/src/renderers/browser/ticker-repository.ts +59 -0
- package/src/renderers/browser/ui-host.tsx +40 -0
- package/src/renderers/cloudflare/worker.test.ts +136 -0
- package/src/renderers/cloudflare/worker.ts +116 -0
- package/src/renderers/electrobun/bun/application-menu/index.ts +3 -2
- package/src/renderers/electrobun/bun/desktop/initialization.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/plugin-state.ts +7 -1
- package/src/renderers/electrobun/view/app-services.ts +14 -3
- package/src/renderers/electrobun/view/build-assets.ts +12 -10
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/data-table/index.tsx +10 -1
- package/src/renderers/electrobun/view/desktop/controls.tsx +2 -0
- package/src/renderers/electrobun/view/dom-ui-host.tsx +143 -0
- package/src/renderers/electrobun/view/host/box.test.tsx +33 -0
- package/src/renderers/electrobun/view/host/box.tsx +11 -6
- package/src/renderers/electrobun/view/host/chart-surface.tsx +7 -1
- package/src/renderers/electrobun/view/host/input.tsx +11 -7
- package/src/renderers/electrobun/view/host/native.ts +0 -5
- package/src/renderers/electrobun/view/main.tsx +2 -0
- package/src/renderers/electrobun/view/remote/persistence.ts +5 -0
- package/src/renderers/electrobun/view/styles.css +107 -7
- package/src/renderers/electrobun/view/toast-host.test.tsx +78 -0
- package/src/renderers/electrobun/view/toast-host.tsx +69 -43
- package/src/renderers/electrobun/view/ui-host.tsx +6 -137
- package/src/renderers/opentui/start.tsx +2 -0
- package/src/renderers/share/layout-view.test.tsx +58 -0
- package/src/renderers/share/layout-view.tsx +133 -0
- package/src/renderers/share/main.tsx +102 -0
- package/src/renderers/share/styles.css +54 -0
- package/src/renderers/share/view.test.tsx +100 -0
- package/src/renderers/share/view.tsx +150 -0
- package/src/shares/api.test.ts +105 -0
- package/src/shares/api.ts +127 -0
- package/src/shares/chart-snapshot.test.ts +34 -0
- package/src/shares/chart-snapshot.ts +28 -0
- package/src/shares/location.test.ts +19 -0
- package/src/shares/location.ts +11 -0
- package/src/shares/pane.test.ts +110 -0
- package/src/shares/pane.ts +54 -0
- package/src/shares/payload.ts +177 -0
- package/src/sources/gloomberb-cloud/index.test.ts +61 -1
- package/src/sources/gloomberb-cloud/index.ts +86 -14
- package/src/sources/gloomberb-cloud/normalizers.test.ts +34 -0
- package/src/sources/gloomberb-cloud/normalizers.ts +5 -5
- package/src/sources/provider-router/history.test.ts +2 -2
- package/src/sources/provider-router/news.test.ts +34 -0
- package/src/sources/provider-router/news.ts +15 -1
- package/src/sources/sec-edgar.test.ts +59 -0
- package/src/sources/sec-edgar.ts +63 -23
- package/src/sources/yahoo-finance/mappers.ts +3 -0
- package/src/sources/yahoo-finance/snapshots.ts +10 -2
- package/src/sources/yahoo-finance/symbols.test.ts +10 -0
- package/src/sources/yahoo-finance/symbols.ts +13 -8
- package/src/sources/yahoo-finance.test.ts +1 -1
- package/src/sources/yahoo-finance.ts +6 -2
- package/src/state/app/bootstrap.ts +6 -7
- package/src/state/app/context/index.test.ts +41 -1
- package/src/state/app/context/index.test.tsx +34 -16
- package/src/state/selectors-ui.ts +0 -8
- package/src/sync/baseline.ts +32 -0
- package/src/sync/controller.test.ts +124 -1
- package/src/sync/controller.ts +54 -12
- package/src/sync/core-contributors.test.ts +44 -0
- package/src/sync/core-contributors.ts +59 -10
- package/src/sync/react.ts +22 -2
- package/src/sync/types.ts +12 -0
- package/src/theme/font-scale.ts +0 -11
- package/src/tickers/search/index.test.ts +27 -0
- package/src/tickers/search/upsert.ts +10 -0
- package/src/types/capability-route-source.ts +2 -1
- package/src/types/config.ts +35 -5
- package/src/types/desktop-menu.ts +1 -0
- package/src/types/plugin.ts +36 -0
- package/src/ui/context-menu.tsx +1 -1
- package/src/ui/host.tsx +4 -0
- package/src/ui/toast.tsx +2 -0
- package/src/utils/back-navigation.ts +0 -4
- package/src/utils/price-history.ts +11 -10
- package/src/utils/sec.test.ts +6 -0
- package/src/utils/sec.ts +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
listThirteenFFormHoldings,
|
|
3
|
+
listThirteenFFormHoldingsPage,
|
|
3
4
|
listThirteenFForms,
|
|
4
5
|
listThirteenFFilings,
|
|
5
6
|
listTopThirteenFFunds,
|
|
@@ -81,8 +82,17 @@ export async function loadBrowserRows(
|
|
|
81
82
|
const ticker = query.trim().toUpperCase();
|
|
82
83
|
if (!ticker) return { rows: [], quarter };
|
|
83
84
|
const tickers = await lookupThirteenFTickers([ticker], signal, apiOptions);
|
|
84
|
-
const
|
|
85
|
-
|
|
85
|
+
const exact = tickers.filter((item) => item.ticker === ticker);
|
|
86
|
+
const candidates = exact.length > 0 ? exact : tickers;
|
|
87
|
+
const cusips = [...new Set(candidates.map((item) => item.cusip).filter(Boolean))];
|
|
88
|
+
const cusip = cusips.length === 1 ? cusips[0] : undefined;
|
|
89
|
+
if (!cusip) {
|
|
90
|
+
return {
|
|
91
|
+
rows: [],
|
|
92
|
+
quarter,
|
|
93
|
+
warning: cusips.length === 0 ? `No CUSIP found for ${ticker}` : `Ambiguous CUSIP for ${ticker}`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
86
96
|
const periodOfReport = quarterToPeriod(quarter);
|
|
87
97
|
try {
|
|
88
98
|
const holders = await lookupThirteenFHoldersByCusip(cusip, periodOfReport, signal, apiOptions);
|
|
@@ -186,11 +196,9 @@ export async function loadFilingPositions(
|
|
|
186
196
|
cik: string,
|
|
187
197
|
accessionNumber: string,
|
|
188
198
|
signal?: AbortSignal,
|
|
189
|
-
options: { forceRefresh?: boolean } = {},
|
|
190
|
-
): Promise<ThirteenFHoldingRecord[]> {
|
|
191
|
-
return
|
|
192
|
-
forceRefresh: options.forceRefresh,
|
|
193
|
-
});
|
|
199
|
+
options: { forceRefresh?: boolean; offset?: number; limit?: number } = {},
|
|
200
|
+
): Promise<{ rows: ThirteenFHoldingRecord[]; hasMore: boolean }> {
|
|
201
|
+
return listThirteenFFormHoldingsPage(cik, accessionNumber, signal, options);
|
|
194
202
|
}
|
|
195
203
|
|
|
196
204
|
function quarterToPeriod(quarter: string): string {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
DataTableStackView,
|
|
11
11
|
DataTableView,
|
|
12
12
|
EmptyState,
|
|
13
|
+
useTableLoadMore,
|
|
13
14
|
InputSearchBar,
|
|
14
15
|
Spinner,
|
|
15
16
|
Tabs,
|
|
@@ -238,13 +239,7 @@ export function ThirteenFPane({ focused, width, height }: PaneProps) {
|
|
|
238
239
|
setSelectedId(sortedRows[0]?.id ?? null);
|
|
239
240
|
}, [selectedId, setSelectedId, sortedRows]);
|
|
240
241
|
|
|
241
|
-
const loadMoreFromScroll =
|
|
242
|
-
const scrollBox = tableScrollRef.current;
|
|
243
|
-
if (!scrollBox?.viewport) return;
|
|
244
|
-
const viewportHeight = Math.max(1, scrollBox.viewport.height);
|
|
245
|
-
if (scrollBox.scrollTop + viewportHeight < scrollBox.scrollHeight - LOAD_MORE_THRESHOLD) return;
|
|
246
|
-
loadMore();
|
|
247
|
-
}, [loadMore]);
|
|
242
|
+
const loadMoreFromScroll = useTableLoadMore(tableScrollRef, hasMore && !loadingMore && status === "loaded", loadMore, LOAD_MORE_THRESHOLD);
|
|
248
243
|
|
|
249
244
|
const refresh = useCallback(() => load(true), [load]);
|
|
250
245
|
const focusSearch = useCallback(() => {
|
|
@@ -672,19 +667,29 @@ function FilingDetailView({
|
|
|
672
667
|
const [holdings, setHoldings] = useState<ThirteenFHoldingRecord[]>([]);
|
|
673
668
|
const [status, setStatus] = useState<LoadStatus>("loading");
|
|
674
669
|
const [error, setError] = useState<string | null>(null);
|
|
670
|
+
const [hasMore, setHasMore] = useState(false);
|
|
671
|
+
const [nextOffset, setNextOffset] = useState(0);
|
|
672
|
+
const [loadingMore, setLoadingMore] = useState(false);
|
|
675
673
|
const abortRef = useRef<AbortController | null>(null);
|
|
674
|
+
const moreAbortRef = useRef<AbortController | null>(null);
|
|
675
|
+
const positionScrollRef = useRef<ScrollBoxRenderable | null>(null);
|
|
676
676
|
|
|
677
677
|
const load = useCallback((refresh = false) => {
|
|
678
678
|
abortRef.current?.abort();
|
|
679
|
+
moreAbortRef.current?.abort();
|
|
679
680
|
const controller = new AbortController();
|
|
680
681
|
abortRef.current = controller;
|
|
681
682
|
setStatus("loading");
|
|
682
683
|
setError(null);
|
|
683
684
|
setHoldings([]);
|
|
684
|
-
|
|
685
|
-
|
|
685
|
+
setHasMore(false);
|
|
686
|
+
setNextOffset(0);
|
|
687
|
+
void loadFilingPositions(filing.cik, filing.accessionNumber, controller.signal, { forceRefresh: refresh, offset: 0 })
|
|
688
|
+
.then((result) => {
|
|
686
689
|
if (abortRef.current !== controller) return;
|
|
687
|
-
setHoldings(
|
|
690
|
+
setHoldings(result.rows);
|
|
691
|
+
setHasMore(result.hasMore);
|
|
692
|
+
setNextOffset(result.rows.length);
|
|
688
693
|
setStatus("loaded");
|
|
689
694
|
})
|
|
690
695
|
.catch((loadError) => {
|
|
@@ -695,11 +700,39 @@ function FilingDetailView({
|
|
|
695
700
|
});
|
|
696
701
|
}, [filing.accessionNumber, filing.cik]);
|
|
697
702
|
|
|
703
|
+
const loadMore = useCallback(() => {
|
|
704
|
+
if (loadingMore || !hasMore || status !== "loaded") return;
|
|
705
|
+
moreAbortRef.current?.abort();
|
|
706
|
+
const controller = new AbortController();
|
|
707
|
+
moreAbortRef.current = controller;
|
|
708
|
+
setLoadingMore(true);
|
|
709
|
+
void loadFilingPositions(filing.cik, filing.accessionNumber, controller.signal, { offset: nextOffset })
|
|
710
|
+
.then((result) => {
|
|
711
|
+
if (moreAbortRef.current !== controller) return;
|
|
712
|
+
setHoldings((current) => [...current, ...result.rows]);
|
|
713
|
+
setHasMore(result.hasMore);
|
|
714
|
+
setNextOffset(nextOffset + result.rows.length);
|
|
715
|
+
})
|
|
716
|
+
.catch((loadError) => {
|
|
717
|
+
if (moreAbortRef.current !== controller) return;
|
|
718
|
+
if (loadError instanceof Error && loadError.name === "AbortError") return;
|
|
719
|
+
setError(loadError instanceof Error ? loadError.message : String(loadError));
|
|
720
|
+
})
|
|
721
|
+
.finally(() => {
|
|
722
|
+
if (moreAbortRef.current !== controller) return;
|
|
723
|
+
setLoadingMore(false);
|
|
724
|
+
});
|
|
725
|
+
}, [filing.accessionNumber, filing.cik, hasMore, loadingMore, nextOffset, status]);
|
|
726
|
+
|
|
727
|
+
const onBodyScrollActivity = useTableLoadMore(positionScrollRef, hasMore && !loadingMore && status === "loaded", loadMore);
|
|
728
|
+
|
|
698
729
|
useEffect(() => {
|
|
699
730
|
load(false);
|
|
700
731
|
return () => {
|
|
701
732
|
abortRef.current?.abort();
|
|
702
733
|
abortRef.current = null;
|
|
734
|
+
moreAbortRef.current?.abort();
|
|
735
|
+
moreAbortRef.current = null;
|
|
703
736
|
};
|
|
704
737
|
}, [load]);
|
|
705
738
|
|
|
@@ -769,6 +802,8 @@ function FilingDetailView({
|
|
|
769
802
|
onRootKeyDown={handlePositionKeyDown}
|
|
770
803
|
rootWidth={width}
|
|
771
804
|
rootBefore={summary}
|
|
805
|
+
scrollRef={positionScrollRef}
|
|
806
|
+
onBodyScrollActivity={onBodyScrollActivity}
|
|
772
807
|
resetScrollKey={filing.accessionNumber}
|
|
773
808
|
columns={columns}
|
|
774
809
|
items={positionRows}
|
|
@@ -100,6 +100,14 @@ export const tickerDetailModule: PluginModule = {
|
|
|
100
100
|
}
|
|
101
101
|
: {}
|
|
102
102
|
),
|
|
103
|
+
publicShare: {
|
|
104
|
+
serialize: ({ pane }) => pane.binding?.kind === "fixed" && pane.binding.symbol.trim()
|
|
105
|
+
? { title: pane.title?.trim() || pane.binding.symbol, data: { symbol: pane.binding.symbol.trim().toUpperCase() } }
|
|
106
|
+
: null,
|
|
107
|
+
restore: (data) => Object.keys(data).length === 1 && typeof data.symbol === "string" && data.symbol.trim()
|
|
108
|
+
? { symbol: data.symbol.trim().toUpperCase() }
|
|
109
|
+
: null,
|
|
110
|
+
},
|
|
103
111
|
},
|
|
104
112
|
{
|
|
105
113
|
id: "quote-monitor-pane",
|
|
@@ -147,6 +155,7 @@ export const tickerDetailModule: PluginModule = {
|
|
|
147
155
|
description: "Open a historical OHLCV table for a ticker.",
|
|
148
156
|
keywords: ["historical", "prices", "hp", "ohlc", "volume"],
|
|
149
157
|
shortcut: "HP",
|
|
158
|
+
publicShare: true,
|
|
150
159
|
}),
|
|
151
160
|
createProviderSearchPaneTemplate(),
|
|
152
161
|
createTickerSurfacePaneTemplate({
|
|
@@ -156,6 +165,7 @@ export const tickerDetailModule: PluginModule = {
|
|
|
156
165
|
description: "Open financial statements for a ticker.",
|
|
157
166
|
keywords: ["fa", "financial", "analysis", "statements"],
|
|
158
167
|
shortcut: "FA",
|
|
168
|
+
publicShare: true,
|
|
159
169
|
titlePrefix: "FA",
|
|
160
170
|
}),
|
|
161
171
|
],
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from "../../../market-data/market/status";
|
|
15
15
|
import { selectEffectiveExchangeRates } from "../../../utils/exchange-rate-map";
|
|
16
16
|
import { EmptyState } from "../../../components";
|
|
17
|
+
import { CompanyLogo } from "../../../components/company-logo";
|
|
17
18
|
import {
|
|
18
19
|
CompositeChart,
|
|
19
20
|
pricePointsToResolvedSeries,
|
|
@@ -48,7 +49,7 @@ export function OverviewTab({
|
|
|
48
49
|
const baseCurrency = useAppSelector((state) => state.config.baseCurrency);
|
|
49
50
|
const exchangeRatesState = useAppSelector((state) => state.exchangeRates);
|
|
50
51
|
const { width: termWidth } = useViewport();
|
|
51
|
-
const { fractionalViewport = false } = useUiCapabilities();
|
|
52
|
+
const { fractionalViewport = false, nativePaneChrome } = useUiCapabilities();
|
|
52
53
|
|
|
53
54
|
if (!ticker) return <EmptyState title={t("No ticker selected.")} />;
|
|
54
55
|
|
|
@@ -136,14 +137,20 @@ export function OverviewTab({
|
|
|
136
137
|
|
|
137
138
|
return (
|
|
138
139
|
<ScrollBox flexGrow={1} flexBasis={0} scrollY focusable={false}>
|
|
139
|
-
<Box flexDirection="column" paddingX={1} paddingBottom={1} gap={1}>
|
|
140
|
+
<Box flexDirection="column" paddingX={1} paddingTop={nativePaneChrome ? 1 : 0} paddingBottom={1} gap={1}>
|
|
140
141
|
<Box flexDirection={quoteBookInline ? "row" : "column"} gap={quoteBookInline ? 2 : 0} width={contentWidth}>
|
|
141
|
-
<Box flexDirection="
|
|
142
|
+
<Box flexDirection="row" width={quoteSummaryWidth}>
|
|
143
|
+
<CompanyLogo
|
|
144
|
+
symbol={ticker.metadata.ticker}
|
|
145
|
+
assetCategory={ticker.metadata.assetCategory}
|
|
146
|
+
name={ticker.metadata.name || quote?.name}
|
|
147
|
+
/>
|
|
148
|
+
<Box flexDirection="column" flexGrow={1} flexShrink={1} minWidth={0}>
|
|
142
149
|
<Box flexDirection="row">
|
|
143
150
|
<Text attributes={TextAttributes.BOLD} fg={colors.textBright}>
|
|
144
151
|
{ticker.metadata.ticker}
|
|
145
152
|
</Text>
|
|
146
|
-
{ticker.metadata.name && ticker.metadata.name !== ticker.metadata.ticker && (
|
|
153
|
+
{(ticker.metadata.name || quote?.name) && (ticker.metadata.name || quote?.name) !== ticker.metadata.ticker && (
|
|
147
154
|
<Text fg={colors.textDim}>
|
|
148
155
|
{" "}- {ticker.metadata.name || quote?.name || ""}
|
|
149
156
|
</Text>
|
|
@@ -193,6 +200,7 @@ export function OverviewTab({
|
|
|
193
200
|
{metadataParts.length > 0 && (
|
|
194
201
|
<Text fg={colors.textDim}>{metadataParts.join(" | ")}</Text>
|
|
195
202
|
)}
|
|
203
|
+
</Box>
|
|
196
204
|
</Box>
|
|
197
205
|
|
|
198
206
|
{quote && hasBidAsk && (
|
|
@@ -82,7 +82,8 @@ export function QuoteMonitorCard({
|
|
|
82
82
|
const flashDirection = useQuoteFlashDirection(flashFinancials, valueFlashingEnabled);
|
|
83
83
|
const display = getActiveQuoteDisplay(quote);
|
|
84
84
|
const quoteStatus = resolveQuoteStatus(quoteEntry, symbol);
|
|
85
|
-
const
|
|
85
|
+
const quoteFailed = quoteStatus.failed && !!display;
|
|
86
|
+
const changeColor = quoteFailed ? colors.textDim : priceColor(display?.change ?? 0);
|
|
86
87
|
const priceAttributes = flashDirection ? TextAttributes.DIM : TextAttributes.BOLD;
|
|
87
88
|
const changeAttributes = flashDirection ? TextAttributes.DIM : TextAttributes.NONE;
|
|
88
89
|
const currency = quote?.currency ?? ticker?.metadata.currency ?? "USD";
|
|
@@ -192,7 +193,12 @@ export function QuoteMonitorCard({
|
|
|
192
193
|
<Text attributes={TextAttributes.BOLD} fg={colors.textBright} style={desktopSymbolStyle}>
|
|
193
194
|
{symbol}
|
|
194
195
|
</Text>
|
|
195
|
-
{
|
|
196
|
+
{quoteFailed && (
|
|
197
|
+
<Text fg={colors.negative} style={{ fontSize: "12px", lineHeight: "14px" }}>
|
|
198
|
+
{quoteStatus.text}
|
|
199
|
+
</Text>
|
|
200
|
+
)}
|
|
201
|
+
{!quoteFailed && ticker?.metadata.name && (
|
|
196
202
|
<Text
|
|
197
203
|
fg={colors.textDim}
|
|
198
204
|
style={{
|
|
@@ -73,6 +73,18 @@ export function buildTickerResearchSettingsDef(settings: TickerResearchPaneSetti
|
|
|
73
73
|
options: tabs.map((tab) => ({ value: tab.id, label: tab.name })),
|
|
74
74
|
}]
|
|
75
75
|
: []),
|
|
76
|
+
{
|
|
77
|
+
key: "chainRefreshMinutes",
|
|
78
|
+
label: "Options chain refresh",
|
|
79
|
+
description: "How often the Options tab refetches the whole chain snapshot.",
|
|
80
|
+
type: "select" as const,
|
|
81
|
+
options: [
|
|
82
|
+
{ value: "1", label: "Every minute" },
|
|
83
|
+
{ value: "5", label: "Every 5 minutes" },
|
|
84
|
+
{ value: "10", label: "Every 10 minutes" },
|
|
85
|
+
{ value: "30", label: "Every 30 minutes" },
|
|
86
|
+
],
|
|
87
|
+
},
|
|
76
88
|
],
|
|
77
89
|
};
|
|
78
90
|
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "./cache";
|
|
8
8
|
import { TreasuryAuctionsPane } from "./pane";
|
|
9
9
|
import { TREASURY_AUCTIONS_PANE_ID } from "./types";
|
|
10
|
+
import { createPublicPaneShare } from "../shared/public-pane";
|
|
10
11
|
|
|
11
12
|
let disposeConnection: (() => void) | null = null;
|
|
12
13
|
|
|
@@ -85,6 +86,7 @@ export const treasuryAuctionsModule: PluginModule = {
|
|
|
85
86
|
],
|
|
86
87
|
shortcut: { prefix: "AUCT" },
|
|
87
88
|
createInstance: () => ({ placement: "floating" }),
|
|
89
|
+
publicShare: createPublicPaneShare("Treasury Auctions"),
|
|
88
90
|
},
|
|
89
91
|
],
|
|
90
92
|
};
|
|
@@ -8,6 +8,7 @@ import { useAssetData, usePluginTickerActions } from "../../runtime";
|
|
|
8
8
|
import { useQuoteBoard } from "../shared/use-quote-board";
|
|
9
9
|
import { WORLD_INDICES, REGION_LABELS, getIndicesByRegion, resolveIndexEntries } from "./indices";
|
|
10
10
|
import { useWorldIndicesFooter } from "./footer";
|
|
11
|
+
import { createPublicPaneShare } from "../shared/public-pane";
|
|
11
12
|
import {
|
|
12
13
|
buildFlatRows,
|
|
13
14
|
DEFAULT_SORT_PREFERENCE,
|
|
@@ -158,6 +159,7 @@ export const worldIndicesModule: PluginModule = {
|
|
|
158
159
|
description: "Monitor global equity indices grouped by region.",
|
|
159
160
|
keywords: ["world", "indices", "global", "equity", "markets", "international"],
|
|
160
161
|
shortcut: { prefix: "WEI" },
|
|
162
|
+
publicShare: createPublicPaneShare("World Equity Indices"),
|
|
161
163
|
},
|
|
162
164
|
],
|
|
163
165
|
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { browserBuiltinPlugins } from "./catalog-browser";
|
|
3
|
+
import { getLoadablePlugins } from "./catalog";
|
|
4
|
+
|
|
5
|
+
const ids = browserBuiltinPlugins.map((plugin) => plugin.id);
|
|
6
|
+
const paneIds = browserBuiltinPlugins.flatMap((plugin) => plugin.panes?.map((pane) => pane.id) ?? []);
|
|
7
|
+
const templateIds = new Set(browserBuiltinPlugins.flatMap((plugin) => plugin.paneTemplates?.map((template) => template.id) ?? []));
|
|
8
|
+
|
|
9
|
+
describe("browser plugin catalog", () => {
|
|
10
|
+
test("contains the reviewed cloud, local, market, and research plugins", () => {
|
|
11
|
+
expect(ids).toEqual([
|
|
12
|
+
"gloomberb-cloud",
|
|
13
|
+
"portfolio",
|
|
14
|
+
"ticker-research",
|
|
15
|
+
"application",
|
|
16
|
+
"news",
|
|
17
|
+
"market-overview",
|
|
18
|
+
"macro",
|
|
19
|
+
"alerts",
|
|
20
|
+
]);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("excludes native, filesystem, local AI, debug, updater, and external plugins", () => {
|
|
24
|
+
for (const forbidden of [
|
|
25
|
+
"broker",
|
|
26
|
+
"ibkr",
|
|
27
|
+
"public",
|
|
28
|
+
"robinhood",
|
|
29
|
+
"simplefin",
|
|
30
|
+
"notes",
|
|
31
|
+
"substack",
|
|
32
|
+
"ai",
|
|
33
|
+
"debug",
|
|
34
|
+
"yahoo",
|
|
35
|
+
"prediction-markets",
|
|
36
|
+
"polls",
|
|
37
|
+
"updater",
|
|
38
|
+
]) {
|
|
39
|
+
expect(ids).not.toContain(forbidden);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("keeps every public pane handoff restorable in the hosted browser", () => {
|
|
44
|
+
const unavailable = getLoadablePlugins()
|
|
45
|
+
.flatMap((plugin) => plugin.paneTemplates ?? [])
|
|
46
|
+
.filter((template) => template.publicShare && !templateIds.has(template.id))
|
|
47
|
+
.map((template) => template.id);
|
|
48
|
+
expect(unavailable).toEqual([]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("omits unsupported modules inside otherwise browser-safe product areas", () => {
|
|
52
|
+
expect(paneIds).toEqual(expect.arrayContaining([
|
|
53
|
+
"ticker-research",
|
|
54
|
+
"chart-composer",
|
|
55
|
+
"options-calculator",
|
|
56
|
+
"ticker-news",
|
|
57
|
+
"world-indices",
|
|
58
|
+
"econ-calendar",
|
|
59
|
+
"treasury-auctions",
|
|
60
|
+
]));
|
|
61
|
+
for (const forbidden of [
|
|
62
|
+
"buildout",
|
|
63
|
+
"market-heatmap",
|
|
64
|
+
"market-movers",
|
|
65
|
+
"market-halts",
|
|
66
|
+
"earnings-calendar",
|
|
67
|
+
"ipo-calendar",
|
|
68
|
+
"tv",
|
|
69
|
+
"dividend-yield",
|
|
70
|
+
"short-interest",
|
|
71
|
+
"thirteenf",
|
|
72
|
+
"sec",
|
|
73
|
+
"insider",
|
|
74
|
+
]) {
|
|
75
|
+
expect(paneIds).not.toContain(forbidden);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import {
|
|
2
|
+
attachFredSeriesPersistence,
|
|
3
|
+
resetFredSeriesPersistence,
|
|
4
|
+
} from "../data/fred-series";
|
|
5
|
+
import type { GloomPlugin } from "../types/plugin";
|
|
6
|
+
import { portfolioAnalyticsModule } from "./builtin/analytics";
|
|
7
|
+
import { alertsPlugin } from "./builtin/alerts";
|
|
8
|
+
import { browserGloomberbCloudPlugin } from "./builtin/cloud/browser";
|
|
9
|
+
import { changelogModule } from "./builtin/changelog";
|
|
10
|
+
import { chartComposerModule } from "./builtin/chart-composer";
|
|
11
|
+
import { connectionsModule } from "./builtin/connections";
|
|
12
|
+
import { correlationModule } from "./builtin/correlation";
|
|
13
|
+
import { cdsModule } from "./builtin/cds";
|
|
14
|
+
import { creditConditionsModule } from "./builtin/credit-conditions";
|
|
15
|
+
import { economicCalendarModule } from "./builtin/econ";
|
|
16
|
+
import { futuresModule } from "./builtin/futures";
|
|
17
|
+
import { fxMatrixModule } from "./builtin/fx-matrix";
|
|
18
|
+
import { helpModule } from "./builtin/help";
|
|
19
|
+
import { positionSizerModule } from "./builtin/kelly-sizer";
|
|
20
|
+
import { layoutManagerModule } from "./builtin/layout-manager";
|
|
21
|
+
import { tickerNewsModule } from "./builtin/news";
|
|
22
|
+
import { optionsModule } from "./builtin/options";
|
|
23
|
+
import { optionsCalculatorModule } from "./builtin/options-calculator";
|
|
24
|
+
import { composeBuiltinPlugin, type PluginModule } from "./builtin/plugin-module";
|
|
25
|
+
import { portfolioListModule } from "./builtin/portfolio-list";
|
|
26
|
+
import { researchModule } from "./builtin/research";
|
|
27
|
+
import { scannerModule } from "./builtin/scanner";
|
|
28
|
+
import { sectorsModule } from "./builtin/sectors";
|
|
29
|
+
import { tickerDetailModule } from "./builtin/ticker-detail";
|
|
30
|
+
import { treasuryAuctionsModule } from "./builtin/treasury-auctions";
|
|
31
|
+
import { volatilityModule } from "./builtin/volatility";
|
|
32
|
+
import { worldIndicesModule } from "./builtin/world-indices";
|
|
33
|
+
import { yieldCurveModule } from "./builtin/yield-curve";
|
|
34
|
+
|
|
35
|
+
const browserApplicationPlugin = composeBuiltinPlugin({
|
|
36
|
+
id: "application",
|
|
37
|
+
name: "Application",
|
|
38
|
+
version: "1.0.0",
|
|
39
|
+
description: "Core layout, help, and release information.",
|
|
40
|
+
modules: [layoutManagerModule, helpModule, changelogModule, connectionsModule],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const browserPortfolioPlugin = composeBuiltinPlugin({
|
|
44
|
+
id: "portfolio",
|
|
45
|
+
name: "Portfolio",
|
|
46
|
+
version: "1.0.0",
|
|
47
|
+
description: "Portfolio and watchlist management, analytics, and position sizing.",
|
|
48
|
+
toggleable: true,
|
|
49
|
+
modules: [portfolioListModule, portfolioAnalyticsModule, positionSizerModule],
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const browserTickerResearchPlugin = composeBuiltinPlugin({
|
|
53
|
+
id: "ticker-research",
|
|
54
|
+
name: "Ticker Research",
|
|
55
|
+
version: "1.0.0",
|
|
56
|
+
description: "Company overview, charts, financials, options, and research.",
|
|
57
|
+
toggleable: true,
|
|
58
|
+
modules: [
|
|
59
|
+
tickerDetailModule,
|
|
60
|
+
chartComposerModule,
|
|
61
|
+
optionsModule,
|
|
62
|
+
optionsCalculatorModule,
|
|
63
|
+
researchModule,
|
|
64
|
+
],
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const browserNewsPlugin = composeBuiltinPlugin({
|
|
68
|
+
id: "news",
|
|
69
|
+
name: "News",
|
|
70
|
+
version: "1.0.0",
|
|
71
|
+
description: "View latest news for each ticker.",
|
|
72
|
+
toggleable: true,
|
|
73
|
+
modules: [tickerNewsModule],
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const browserMarketOverviewPlugin = composeBuiltinPlugin({
|
|
77
|
+
id: "market-overview",
|
|
78
|
+
name: "Market Overview",
|
|
79
|
+
version: "1.0.0",
|
|
80
|
+
description: "Global indices, scanners, sectors, FX, futures, and correlations.",
|
|
81
|
+
toggleable: true,
|
|
82
|
+
modules: [
|
|
83
|
+
correlationModule,
|
|
84
|
+
worldIndicesModule,
|
|
85
|
+
scannerModule,
|
|
86
|
+
sectorsModule,
|
|
87
|
+
fxMatrixModule,
|
|
88
|
+
futuresModule,
|
|
89
|
+
],
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const browserFredResourcesModule: PluginModule = {
|
|
93
|
+
setup(ctx) {
|
|
94
|
+
attachFredSeriesPersistence(ctx.persistence);
|
|
95
|
+
},
|
|
96
|
+
dispose() {
|
|
97
|
+
resetFredSeriesPersistence();
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const browserMacroPlugin = composeBuiltinPlugin({
|
|
102
|
+
id: "macro",
|
|
103
|
+
name: "Macro",
|
|
104
|
+
version: "1.0.0",
|
|
105
|
+
description: "Economic calendar, rates, volatility, credit spreads, single-name CDS, and Treasury auctions.",
|
|
106
|
+
toggleable: true,
|
|
107
|
+
modules: [
|
|
108
|
+
browserFredResourcesModule,
|
|
109
|
+
economicCalendarModule,
|
|
110
|
+
yieldCurveModule,
|
|
111
|
+
volatilityModule,
|
|
112
|
+
creditConditionsModule,
|
|
113
|
+
cdsModule,
|
|
114
|
+
treasuryAuctionsModule,
|
|
115
|
+
],
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Reviewed browser catalog. Native brokers, filesystem/local-process plugins,
|
|
120
|
+
* and modules whose data path is not available through Gloom Cloud or a
|
|
121
|
+
* browser-safe public API are absent rather than registered behind stubs.
|
|
122
|
+
*/
|
|
123
|
+
export const browserBuiltinPlugins: readonly GloomPlugin[] = [
|
|
124
|
+
browserGloomberbCloudPlugin,
|
|
125
|
+
browserPortfolioPlugin,
|
|
126
|
+
browserTickerResearchPlugin,
|
|
127
|
+
browserApplicationPlugin,
|
|
128
|
+
browserNewsPlugin,
|
|
129
|
+
browserMarketOverviewPlugin,
|
|
130
|
+
browserMacroPlugin,
|
|
131
|
+
alertsPlugin,
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
export function getBrowserBuiltinPlugins(): readonly GloomPlugin[] {
|
|
135
|
+
return browserBuiltinPlugins;
|
|
136
|
+
}
|
|
@@ -3,9 +3,10 @@ import type { AppNotificationRequest } from "../types/plugin";
|
|
|
3
3
|
export function notifyGridlockComplete(
|
|
4
4
|
notify: (notification: AppNotificationRequest) => void,
|
|
5
5
|
onRevert: () => void,
|
|
6
|
+
body = "Windows tidied",
|
|
6
7
|
): void {
|
|
7
8
|
notify({
|
|
8
|
-
body
|
|
9
|
+
body,
|
|
9
10
|
type: "success",
|
|
10
11
|
action: {
|
|
11
12
|
label: "Revert",
|
|
@@ -27,6 +27,7 @@ describe("parseFlexPositions", () => {
|
|
|
27
27
|
const positions = parseFlexPositions(xml);
|
|
28
28
|
expect(positions).toHaveLength(1);
|
|
29
29
|
expect(positions[0]?.ticker).toBe("SPY 260619C00500000");
|
|
30
|
+
expect(positions[0]?.side).toBe("long");
|
|
30
31
|
expect(positions[0]?.brokerContract).toEqual({
|
|
31
32
|
brokerId: "ibkr",
|
|
32
33
|
conId: 123456,
|
|
@@ -43,6 +44,24 @@ describe("parseFlexPositions", () => {
|
|
|
43
44
|
tradingClass: "SPY",
|
|
44
45
|
});
|
|
45
46
|
});
|
|
47
|
+
|
|
48
|
+
test("treats a negative Flex position without a side attribute as a short", () => {
|
|
49
|
+
const xml = `
|
|
50
|
+
<FlexQueryResponse>
|
|
51
|
+
<OpenPositions>
|
|
52
|
+
<OpenPosition accountId="DU12345" symbol="TSLA" assetCategory="STK" position="-50" costBasisPrice="200" currency="USD" listingExchange="NASDAQ" />
|
|
53
|
+
</OpenPositions>
|
|
54
|
+
</FlexQueryResponse>
|
|
55
|
+
`;
|
|
56
|
+
|
|
57
|
+
const positions = parseFlexPositions(xml);
|
|
58
|
+
expect(positions).toHaveLength(1);
|
|
59
|
+
expect(positions[0]).toMatchObject({
|
|
60
|
+
ticker: "TSLA",
|
|
61
|
+
shares: 50,
|
|
62
|
+
side: "short",
|
|
63
|
+
});
|
|
64
|
+
});
|
|
46
65
|
});
|
|
47
66
|
|
|
48
67
|
describe("parseFlexAccounts", () => {
|
|
@@ -162,7 +162,7 @@ export function parseFlexPositions(xml: string): BrokerPosition[] {
|
|
|
162
162
|
name: description || undefined,
|
|
163
163
|
assetCategory: assetCategory || undefined,
|
|
164
164
|
isin: isin || undefined,
|
|
165
|
-
side: side === "long" || side === "short" ? side :
|
|
165
|
+
side: side === "long" || side === "short" ? side : quantity < 0 ? "short" : "long",
|
|
166
166
|
markPrice: numAttr("markPrice"),
|
|
167
167
|
marketValue: numAttr("positionValue"),
|
|
168
168
|
unrealizedPnl: numAttr("fifoPnlUnrealized") ?? numAttr("unrealizedCapitalGainsPnl"),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LayoutConfig } from "../../types/config";
|
|
1
|
+
import type { FloatingPaneEntry, LayoutConfig } from "../../types/config";
|
|
2
2
|
import {
|
|
3
3
|
boundsForRects,
|
|
4
4
|
inferDockTreeFromRects,
|
|
@@ -14,9 +14,85 @@ import {
|
|
|
14
14
|
type PaneTypeAvailability,
|
|
15
15
|
} from "./layout-state";
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
const BURIED_VISIBLE_RATIO = 0.2;
|
|
18
|
+
const TITLE_BAR_HEIGHT_RATIO = 0.12;
|
|
19
|
+
const TIDY_FLOATING_THRESHOLD = 3;
|
|
20
|
+
|
|
21
|
+
type Rect = Pick<FloatingPaneEntry, "x" | "y" | "width" | "height">;
|
|
22
|
+
|
|
23
|
+
export interface FloatingPaneVisibility {
|
|
24
|
+
instanceId: string;
|
|
25
|
+
buried: boolean;
|
|
26
|
+
visibleRatio: number;
|
|
27
|
+
titleBarVisibleRatio: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function rectArea(rect: Rect): number {
|
|
31
|
+
return Math.max(0, rect.width) * Math.max(0, rect.height);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function subtractRect(rect: Rect, cover: Rect): Rect[] {
|
|
35
|
+
const left = Math.max(rect.x, cover.x);
|
|
36
|
+
const top = Math.max(rect.y, cover.y);
|
|
37
|
+
const right = Math.min(rect.x + rect.width, cover.x + cover.width);
|
|
38
|
+
const bottom = Math.min(rect.y + rect.height, cover.y + cover.height);
|
|
39
|
+
if (left >= right || top >= bottom) return [rect];
|
|
40
|
+
|
|
41
|
+
return [
|
|
42
|
+
{ x: rect.x, y: rect.y, width: rect.width, height: top - rect.y },
|
|
43
|
+
{ x: rect.x, y: bottom, width: rect.width, height: rect.y + rect.height - bottom },
|
|
44
|
+
{ x: rect.x, y: top, width: left - rect.x, height: bottom - top },
|
|
45
|
+
{ x: right, y: top, width: rect.x + rect.width - right, height: bottom - top },
|
|
46
|
+
].filter((piece) => piece.width > 0 && piece.height > 0);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function visibleRatio(rect: Rect, covers: Rect[]): number {
|
|
50
|
+
const area = rectArea(rect);
|
|
51
|
+
if (area === 0) return 0;
|
|
52
|
+
let pieces = [rect];
|
|
53
|
+
for (const cover of covers) {
|
|
54
|
+
pieces = pieces.flatMap((piece) => subtractRect(piece, cover));
|
|
55
|
+
if (pieces.length === 0) return 0;
|
|
56
|
+
}
|
|
57
|
+
return Math.min(1, pieces.reduce((sum, piece) => sum + rectArea(piece), 0) / area);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function analyzeFloatingPaneVisibility(layout: LayoutConfig): FloatingPaneVisibility[] {
|
|
61
|
+
const frontToBack = layout.floating
|
|
62
|
+
.map((entry, index) => ({ entry, index }))
|
|
63
|
+
.sort((left, right) => (
|
|
64
|
+
(right.entry.zIndex ?? 50) - (left.entry.zIndex ?? 50)
|
|
65
|
+
|| right.index - left.index
|
|
66
|
+
));
|
|
67
|
+
const covers: Rect[] = [];
|
|
68
|
+
|
|
69
|
+
return frontToBack.map(({ entry }) => {
|
|
70
|
+
const paneVisibleRatio = visibleRatio(entry, covers);
|
|
71
|
+
const titleBar = {
|
|
72
|
+
x: entry.x,
|
|
73
|
+
y: entry.y,
|
|
74
|
+
width: entry.width,
|
|
75
|
+
height: Math.min(entry.height, Math.max(1, entry.height * TITLE_BAR_HEIGHT_RATIO)),
|
|
76
|
+
};
|
|
77
|
+
const titleBarVisibleRatio = visibleRatio(titleBar, covers);
|
|
78
|
+
covers.push(entry);
|
|
79
|
+
return {
|
|
80
|
+
instanceId: entry.instanceId,
|
|
81
|
+
buried: paneVisibleRatio < BURIED_VISIBLE_RATIO || titleBarVisibleRatio < BURIED_VISIBLE_RATIO,
|
|
82
|
+
visibleRatio: paneVisibleRatio,
|
|
83
|
+
titleBarVisibleRatio,
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function shouldShowTidyWindows(layout: LayoutConfig): boolean {
|
|
89
|
+
return layout.floating.length >= TIDY_FLOATING_THRESHOLD
|
|
90
|
+
|| analyzeFloatingPaneVisibility(layout).some((pane) => pane.buried);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function gridlockPanes(
|
|
18
94
|
layout: LayoutConfig,
|
|
19
|
-
bounds: LayoutBounds
|
|
95
|
+
bounds: LayoutBounds,
|
|
20
96
|
paneTypes?: PaneTypeAvailability,
|
|
21
97
|
): LayoutConfig {
|
|
22
98
|
const visibleLayout = paneTypes
|
|
@@ -40,3 +116,11 @@ export function gridlockAllPanes(
|
|
|
40
116
|
floating: [],
|
|
41
117
|
});
|
|
42
118
|
}
|
|
119
|
+
|
|
120
|
+
export function gridlockAllPanes(
|
|
121
|
+
layout: LayoutConfig,
|
|
122
|
+
bounds: LayoutBounds = { x: 0, y: 0, width: 120, height: 40 },
|
|
123
|
+
paneTypes?: PaneTypeAvailability,
|
|
124
|
+
): LayoutConfig {
|
|
125
|
+
return gridlockPanes(layout, bounds, paneTypes);
|
|
126
|
+
}
|