gloomberb 0.16.6 → 0.17.1
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 +2 -1
- package/src/api-client/chat-pane-link.ts +47 -0
- package/src/api-client/chat.ts +9 -1
- package/src/api-client/data.ts +42 -2
- package/src/api-client/doe.ts +19 -0
- package/src/api-client/equity-screener.ts +3 -0
- package/src/api-client/europe-energy.ts +91 -0
- package/src/api-client/index.ts +7 -0
- package/src/api-client/letters.ts +41 -0
- package/src/api-client/normalizers.ts +36 -6
- package/src/api-client/organization-tickers.ts +44 -0
- package/src/api-client/request.ts +3 -1
- package/src/api-client/types.ts +27 -1
- package/src/app/runtime/desktop-deeplink.ts +48 -15
- package/src/app/runtime/update.ts +16 -0
- package/src/architecture/core-builtin-allowlist.json +1 -1
- package/src/architecture/dom-cell-metrics-allowlist.json +102 -0
- package/src/ask-gloom/pane-context.ts +23 -19
- package/src/ask-gloom/pane-request.ts +2 -2
- package/src/cli/commands/fred-series.ts +77 -11
- package/src/cli/commands/market.ts +134 -25
- package/src/cli/commands/options-chain.ts +75 -1
- package/src/cli/commands/overview.ts +29 -20
- package/src/cli/commands/ticker.ts +82 -20
- package/src/cli/desktop-pane-shot-view.ts +114 -0
- package/src/cli/desktop-pane-shot.ts +30 -31
- package/src/cli/entry.ts +7 -0
- package/src/cli/export-source.ts +43 -0
- package/src/cli/freshness.ts +18 -3
- package/src/cli/glossary.ts +17 -0
- package/src/cli/history-rows.ts +174 -13
- package/src/cli/index.ts +14 -6
- package/src/cli/known-gaps.ts +14 -3
- package/src/cli/listing-arg.ts +20 -2
- package/src/cli/listing-suggestion.ts +159 -0
- package/src/cli/native-smoke.ts +13 -0
- package/src/cli/not-a-ticker.ts +13 -1
- package/src/cli/pane-functions/capabilities.ts +16 -8
- package/src/cli/pane-functions/catalog.ts +39 -7
- package/src/cli/pane-functions/dom.ts +39 -13
- package/src/cli/pane-functions/function-help.ts +54 -7
- package/src/cli/pane-functions/headless.ts +38 -8
- package/src/cli/pane-functions/index.ts +85 -3
- package/src/cli/pane-functions/lock-note.ts +56 -0
- package/src/cli/pane-functions/report-notices.ts +6 -3
- package/src/cli/pane-functions/report.ts +16 -2
- package/src/cli/pane-functions/resolver.ts +7 -4
- package/src/cli/report-tables.ts +31 -7
- package/src/cli/result.ts +54 -11
- package/src/cli/symbol-rows.ts +18 -1
- package/src/components/chart/composite/axis-overlays.tsx +2 -0
- package/src/components/chart/composite/composite-chart.tsx +4 -21
- package/src/components/chart/composite/legend.tsx +10 -6
- package/src/components/chart/composite/price-axis-labels.tsx +1 -0
- package/src/components/command-bar/list/model.ts +2 -0
- package/src/components/command-bar/list/view.tsx +2 -1
- package/src/components/command-bar/pane-templates/items.ts +1 -1
- package/src/components/command-bar/routes/ticker-search/actions.ts +1 -0
- package/src/components/command-bar/routes/ticker-search/results.ts +3 -1
- package/src/components/command-bar/workflow/ops.ts +8 -97
- package/src/components/command-bar/workflow/pane-reuse.ts +177 -0
- package/src/components/data-table/view.tsx +1 -0
- package/src/components/feed-data-table/stack-view.tsx +18 -42
- package/src/components/index.ts +3 -2
- package/src/components/layout/header.tsx +58 -14
- package/src/components/layout/missing-pane.tsx +2 -0
- package/src/components/layout/shell/chrome.ts +10 -4
- package/src/components/layout/shell/index.tsx +1 -0
- package/src/components/layout/shell/pane/ask.ts +12 -7
- package/src/components/layout/shell/pane/capture.ts +8 -34
- package/src/components/markdown-text.tsx +37 -6
- package/src/components/reader/article-reader.tsx +91 -266
- package/src/components/reader/detail-reader.tsx +210 -0
- package/src/components/reader/reader-text.tsx +239 -0
- package/src/components/table-view-shared.tsx +8 -4
- package/src/components/ticker/badge/list.tsx +15 -4
- package/src/components/ui/choice-dialog.tsx +3 -0
- package/src/components/ui/column-grid.tsx +206 -0
- package/src/components/ui/data-table/types.ts +7 -0
- package/src/components/ui/detail-scroll-body.tsx +31 -4
- package/src/components/ui/display.tsx +86 -4
- package/src/components/ui/fill.ts +8 -0
- package/src/components/ui/icon.tsx +11 -1
- package/src/components/ui/index.ts +2 -0
- package/src/components/ui/list-view.tsx +3 -1
- package/src/components/ui/query-bar-keys.tsx +2 -0
- package/src/components/ui/query-bar.tsx +9 -1
- package/src/components/ui/select-button.tsx +3 -0
- package/src/data/config/store/fold-panes.ts +202 -11
- package/src/data/config/store/migrations.ts +13 -1
- package/src/docs/snapshot.json +29 -0
- package/src/i18n/es.ts +14 -0
- package/src/i18n/ja.ts +14 -0
- package/src/i18n/ko.ts +14 -0
- package/src/i18n/zh-cn.ts +14 -0
- package/src/i18n/zh-tw.ts +14 -0
- package/src/layout/desk-keys.ts +7 -0
- package/src/layout/desks.ts +2 -2
- package/src/market-data/market/status.ts +67 -6
- package/src/market-data/quotes/contributions.ts +15 -0
- package/src/market-data/quotes/regular-session-valuation.ts +131 -0
- package/src/market-data/quotes/resolution.ts +4 -0
- package/src/market-data/quotes/screener-live-quotes.ts +31 -7
- package/src/market-data/snapshot-provider.ts +6 -3
- package/src/notifications/app-badge.ts +30 -0
- package/src/plugins/builtin/account-management/calendar-tab.tsx +1 -1
- package/src/plugins/builtin/account-management/form-components.tsx +57 -8
- package/src/plugins/builtin/account-management/keyboard.ts +5 -0
- package/src/plugins/builtin/account-management/model.ts +27 -0
- package/src/plugins/builtin/account-management/pane.tsx +62 -2
- package/src/plugins/builtin/account-management/password-dialog.tsx +3 -2
- package/src/plugins/builtin/alerts/cli.ts +6 -4
- package/src/plugins/builtin/analytics/index.tsx +1 -1
- package/src/plugins/builtin/awards/pane.tsx +29 -18
- package/src/plugins/builtin/backtest/pane.tsx +11 -4
- package/src/plugins/builtin/bond-calculator/index.tsx +3 -5
- package/src/plugins/builtin/buildout/detail/company.tsx +11 -7
- package/src/plugins/builtin/buildout/detail/ui.tsx +9 -12
- package/src/plugins/builtin/builtin-plugin-meta.ts +5 -4
- package/src/plugins/builtin/catalysts/model.ts +92 -10
- package/src/plugins/builtin/catalysts/pane.tsx +6 -4
- package/src/plugins/builtin/cds/index.tsx +21 -30
- package/src/plugins/builtin/changelog/index.tsx +11 -12
- package/src/plugins/builtin/chart-composer/headless-schema.ts +2 -0
- package/src/plugins/builtin/chart-composer/headless.ts +87 -5
- package/src/plugins/builtin/chart-composer/pane.tsx +3 -2
- package/src/plugins/builtin/chart-composer/session-history.ts +20 -5
- package/src/plugins/builtin/chat/command-bar.ts +6 -8
- package/src/plugins/builtin/chat/content/composer-runtime.ts +2 -2
- package/src/plugins/builtin/chat/content/composer.tsx +13 -4
- package/src/plugins/builtin/chat/content/footer.tsx +15 -5
- package/src/plugins/builtin/chat/content/handle-prompt.tsx +326 -0
- package/src/plugins/builtin/chat/content/index.tsx +113 -9
- package/src/plugins/builtin/chat/content/layout-metrics.ts +4 -0
- package/src/plugins/builtin/chat/content/open-targets.ts +20 -4
- package/src/plugins/builtin/chat/content/scroll.ts +6 -1
- package/src/plugins/builtin/chat/content/transcript.tsx +11 -2
- package/src/plugins/builtin/chat/ghost-user.ts +1 -1
- package/src/plugins/builtin/chat/handle-prompt.ts +128 -0
- package/src/plugins/builtin/chat/index.tsx +44 -21
- package/src/plugins/builtin/chat/layout.ts +7 -14
- package/src/plugins/builtin/chat/message/desktop.tsx +59 -11
- package/src/plugins/builtin/chat/message/header.tsx +69 -21
- package/src/plugins/builtin/chat/message/pane-chip.tsx +223 -0
- package/src/plugins/builtin/chat/message/person-icons.tsx +53 -0
- package/src/plugins/builtin/chat/message/profile-popover.tsx +73 -9
- package/src/plugins/builtin/chat/message/render-state.ts +15 -2
- package/src/plugins/builtin/chat/message/terminal.tsx +33 -8
- package/src/plugins/builtin/chat/message/types.ts +3 -0
- package/src/plugins/builtin/chat/pane-links/model.ts +149 -0
- package/src/plugins/builtin/chat/pane-links/open.ts +29 -0
- package/src/plugins/builtin/chat/pane-links/previews.ts +119 -0
- package/src/plugins/builtin/chat/pane-state.ts +5 -8
- package/src/plugins/builtin/chat/people.ts +202 -0
- package/src/plugins/builtin/chat/profile-popover.ts +18 -2
- package/src/plugins/builtin/chat/status-widget.tsx +3 -8
- package/src/plugins/builtin/chat/unread-badge.ts +65 -0
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +9 -6
- package/src/plugins/builtin/clinical-trials/client.ts +26 -1
- package/src/plugins/builtin/clinical-trials/index.tsx +10 -2
- package/src/plugins/builtin/clinical-trials/model.ts +269 -0
- package/src/plugins/builtin/clinical-trials/pane.tsx +247 -62
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +156 -0
- package/src/plugins/builtin/cloud/askg/attached-pane.tsx +3 -3
- package/src/plugins/builtin/cloud/askg/pane-attachment.ts +3 -3
- package/src/plugins/builtin/cloud/askg/pane.tsx +2 -1
- package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +3 -2
- package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +1 -0
- package/src/plugins/builtin/cloud/thesis/detail.tsx +11 -9
- package/src/plugins/builtin/comment-letters/pane.tsx +30 -11
- package/src/plugins/builtin/commodities-energy/index.tsx +179 -0
- package/src/plugins/builtin/commodity-balances/client.ts +208 -0
- package/src/plugins/builtin/commodity-balances/headless.ts +66 -0
- package/src/plugins/builtin/commodity-balances/index.tsx +32 -0
- package/src/plugins/builtin/commodity-balances/model.ts +261 -0
- package/src/plugins/builtin/commodity-balances/pane.tsx +227 -0
- package/src/plugins/builtin/composite-plugins.ts +26 -1
- package/src/plugins/builtin/congress-trades/detail.tsx +8 -4
- package/src/plugins/builtin/connections/pane.tsx +16 -5
- package/src/plugins/builtin/cpi/pane.tsx +5 -4
- package/src/plugins/builtin/credit-boards/index.tsx +21 -42
- package/src/plugins/builtin/credit-boards/model.ts +0 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +10 -19
- package/src/plugins/builtin/credit-documents/index.tsx +27 -10
- package/src/plugins/builtin/debt-maturities/index.tsx +3 -17
- package/src/plugins/builtin/debug/index.tsx +2 -2
- package/src/plugins/builtin/dividend-yield/client.ts +3 -1
- package/src/plugins/builtin/dividend-yield/headless.ts +3 -1
- package/src/plugins/builtin/dividend-yield/pane.tsx +3 -1
- package/src/plugins/builtin/dividend-yield/provider-client.ts +3 -1
- package/src/plugins/builtin/docs/blocks.tsx +4 -3
- package/src/plugins/builtin/docs/pane.tsx +16 -8
- package/src/plugins/builtin/doe/index.tsx +11 -4
- package/src/plugins/builtin/doe/pane.tsx +5 -4
- package/src/plugins/builtin/doe/prices.ts +84 -0
- package/src/plugins/builtin/doe/series.ts +4 -1
- package/src/plugins/builtin/earnings/index.tsx +2 -0
- package/src/plugins/builtin/earnings-calls/headless.ts +7 -21
- package/src/plugins/builtin/earnings-calls/index.tsx +2 -0
- package/src/plugins/builtin/earnings-calls/lock-note.ts +17 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +2 -12
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/headless.ts +95 -3
- package/src/plugins/builtin/equity-screener/live.ts +82 -31
- package/src/plugins/builtin/equity-screener/model.ts +121 -68
- package/src/plugins/builtin/equity-screener/pane.tsx +60 -23
- package/src/plugins/builtin/estimate-revisions/client.ts +1 -1
- package/src/plugins/builtin/estimate-revisions/headless.ts +9 -2
- package/src/plugins/builtin/estimate-revisions/index.tsx +3 -0
- package/src/plugins/builtin/estimate-revisions/lock-note.ts +23 -0
- package/src/plugins/builtin/executives/index.tsx +18 -0
- package/src/plugins/builtin/executives/pane.tsx +3 -1
- package/src/plugins/builtin/exposure/charts.tsx +4 -3
- package/src/plugins/builtin/filing-events/index.tsx +18 -0
- package/src/plugins/builtin/fixed-income/index.tsx +46 -0
- package/src/plugins/builtin/fixed-income/model.ts +27 -0
- package/src/plugins/builtin/fixed-income/pane.tsx +163 -0
- package/src/plugins/builtin/fixed-income/shortcut.ts +52 -0
- package/src/plugins/builtin/futures/index.tsx +24 -7
- package/src/plugins/builtin/futures/series.ts +23 -2
- package/src/plugins/builtin/futures/use-front-returns.ts +3 -2
- package/src/plugins/builtin/futures-curve/pane.tsx +6 -4
- package/src/plugins/builtin/gpu/equities.tsx +9 -6
- package/src/plugins/builtin/help/shortcut-table.tsx +4 -2
- package/src/plugins/builtin/insider/detail.tsx +20 -12
- package/src/plugins/builtin/insider/index.tsx +2 -2
- package/src/plugins/builtin/iv-history/format.ts +3 -0
- package/src/plugins/builtin/iv-history/headless.ts +3 -1
- package/src/plugins/builtin/iv-history/index.tsx +6 -1
- package/src/plugins/builtin/iv-history/model.ts +3 -1
- package/src/plugins/builtin/iv-history/screen-pane.tsx +16 -6
- package/src/plugins/builtin/iv-history/universe.ts +1 -1
- package/src/plugins/builtin/jobs/headless.ts +6 -20
- package/src/plugins/builtin/jobs/index.tsx +2 -0
- package/src/plugins/builtin/jobs/lock-note.ts +16 -0
- package/src/plugins/builtin/jodi/client.ts +220 -0
- package/src/plugins/builtin/jodi/headless.ts +63 -0
- package/src/plugins/builtin/jodi/index.tsx +32 -0
- package/src/plugins/builtin/jodi/model.ts +75 -0
- package/src/plugins/builtin/jodi/pane.tsx +141 -0
- package/src/plugins/builtin/market-heatmap/evidence.ts +13 -4
- package/src/plugins/builtin/market-heatmap/headless.ts +3 -3
- package/src/plugins/builtin/market-heatmap/index.tsx +41 -23
- package/src/plugins/builtin/market-heatmap/live.ts +2 -1
- package/src/plugins/builtin/market-heatmap/portfolio.ts +67 -21
- package/src/plugins/builtin/market-movers/table.ts +2 -2
- package/src/plugins/builtin/market-valuation/align.ts +10 -9
- package/src/plugins/builtin/mna/distress/model.ts +52 -1
- package/src/plugins/builtin/mna/distress/pane.tsx +17 -12
- package/src/plugins/builtin/mna/index.tsx +2 -0
- package/src/plugins/builtin/news/wire/index.tsx +2 -2
- package/src/plugins/builtin/news/wire/industry-pane.tsx +28 -14
- package/src/plugins/builtin/news/wire/letters/client.ts +108 -0
- package/src/plugins/builtin/news/wire/letters/model.ts +69 -0
- package/src/plugins/builtin/news/wire/letters/pane.tsx +155 -0
- package/src/plugins/builtin/news/wire/mutes.ts +2 -1
- package/src/plugins/builtin/news/wire/news/query-presets.ts +4 -1
- package/src/plugins/builtin/openfda/client.ts +123 -15
- package/src/plugins/builtin/openfda/model.ts +163 -0
- package/src/plugins/builtin/openfda/pane.tsx +109 -65
- package/src/plugins/builtin/openfda/types.ts +25 -0
- package/src/plugins/builtin/options/index.tsx +2 -1
- package/src/plugins/builtin/options/model-spot.ts +94 -0
- package/src/plugins/builtin/options/open-interest-as-of.ts +63 -0
- package/src/plugins/builtin/options/view.tsx +56 -10
- package/src/plugins/builtin/options-scenario/client.ts +33 -9
- package/src/plugins/builtin/options-scenario/csp.ts +168 -0
- package/src/plugins/builtin/options-scenario/evidence.ts +3 -0
- package/src/plugins/builtin/options-scenario/headless.ts +24 -8
- package/src/plugins/builtin/options-scenario/index.tsx +3 -1
- package/src/plugins/builtin/options-scenario/pane.tsx +32 -6
- package/src/plugins/builtin/pe-band/model.ts +3 -2
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +1 -1
- package/src/plugins/builtin/plugin-module.ts +3 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +1 -1
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +19 -4
- package/src/plugins/builtin/portfolio-list/cli/render.ts +2 -2
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +39 -0
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +18 -5
- package/src/plugins/builtin/portfolio-list/live-valuation.ts +25 -13
- package/src/plugins/builtin/power/evidence.ts +32 -3
- package/src/plugins/builtin/power/gas-client.ts +110 -0
- package/src/plugins/builtin/power/gas-model.ts +194 -0
- package/src/plugins/builtin/power/gas.tsx +145 -0
- package/src/plugins/builtin/power/headless.ts +4 -1
- package/src/plugins/builtin/power/index.tsx +19 -9
- package/src/plugins/builtin/power/pane.tsx +2 -3
- package/src/plugins/builtin/power/prices-headless.ts +75 -7
- package/src/plugins/builtin/power/prices.tsx +36 -13
- package/src/plugins/builtin/primary-dealers/client.ts +108 -0
- package/src/plugins/builtin/primary-dealers/headless.ts +53 -0
- package/src/plugins/builtin/primary-dealers/index.tsx +17 -0
- package/src/plugins/builtin/primary-dealers/model.ts +206 -0
- package/src/plugins/builtin/primary-dealers/pane.tsx +149 -0
- package/src/plugins/builtin/relative-rotation/index.tsx +3 -0
- package/src/plugins/builtin/research/analyst-model.ts +9 -5
- package/src/plugins/builtin/research/headless-schema.ts +2 -1
- package/src/plugins/builtin/research/relative-valuation-headless.ts +68 -32
- package/src/plugins/builtin/research/relative-valuation-model.ts +45 -24
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +39 -4
- package/src/plugins/builtin/risk-factors/index.tsx +18 -0
- package/src/plugins/builtin/risk-factors/pane.tsx +2 -1
- package/src/plugins/builtin/scanner/flow-pane.tsx +8 -1
- package/src/plugins/builtin/scanner/headless.ts +9 -2
- package/src/plugins/builtin/scanner/index.ts +3 -0
- package/src/plugins/builtin/scanner/lock-note.ts +17 -0
- package/src/plugins/builtin/sec/filing-display.tsx +3 -36
- package/src/plugins/builtin/sec/index.tsx +131 -43
- package/src/plugins/builtin/sec/model.ts +148 -2
- package/src/plugins/builtin/sec/row-summaries.ts +105 -0
- package/src/plugins/builtin/sectors/index.tsx +2 -0
- package/src/plugins/builtin/sectors/sector-model.ts +2 -2
- package/src/plugins/builtin/series-inventory.ts +2 -1
- package/src/plugins/builtin/shared/cloud-denied.ts +39 -8
- package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
- package/src/plugins/builtin/shared/pro-lock-note.ts +58 -0
- package/src/plugins/builtin/shared/series-list-detail.tsx +10 -5
- package/src/plugins/builtin/shared/use-quote-board.ts +179 -43
- package/src/plugins/builtin/shared/wall-teaser.tsx +2 -2
- package/src/plugins/builtin/shared/zip-text.ts +46 -0
- package/src/plugins/builtin/short-interest/watch-model.ts +11 -2
- package/src/plugins/builtin/social-mentions/client.ts +3 -3
- package/src/plugins/builtin/social-mentions/headless.ts +6 -2
- package/src/plugins/builtin/social-mentions/index.tsx +2 -1
- package/src/plugins/builtin/social-mentions/lock-note.ts +15 -0
- package/src/plugins/builtin/supply-chain/flow.tsx +15 -13
- package/src/plugins/builtin/supply-chain/graph.tsx +6 -4
- package/src/plugins/builtin/supply-chain/pane.tsx +8 -5
- package/src/plugins/builtin/themes/model.ts +2 -2
- package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +25 -1
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +87 -23
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/headless.ts +6 -3
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +105 -104
- package/src/plugins/builtin/ticker-detail/overview/model.ts +12 -3
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +41 -13
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +8 -6
- package/src/plugins/builtin/time-sales/headless.ts +22 -0
- package/src/plugins/builtin/trace-bonds/client.ts +196 -0
- package/src/plugins/builtin/trace-bonds/headless.ts +69 -0
- package/src/plugins/builtin/trace-bonds/index.tsx +33 -0
- package/src/plugins/builtin/trace-bonds/model.ts +115 -0
- package/src/plugins/builtin/trace-bonds/pane.tsx +191 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +33 -47
- package/src/plugins/builtin/treasury-daily/client.ts +96 -0
- package/src/plugins/builtin/treasury-daily/headless.ts +69 -0
- package/src/plugins/builtin/treasury-daily/index.tsx +17 -0
- package/src/plugins/builtin/treasury-daily/model.ts +69 -0
- package/src/plugins/builtin/treasury-daily/pane.tsx +140 -0
- package/src/plugins/builtin/vol-surface/pane.tsx +6 -3
- package/src/plugins/builtin/world-venue-map/entity-detail.tsx +15 -2
- package/src/plugins/builtin/world-venue-map/map.tsx +1 -0
- package/src/plugins/builtin-editorial.ts +1 -1
- package/src/plugins/catalog-browser.ts +25 -2
- package/src/plugins/marketplace/model.ts +57 -21
- package/src/public/__snapshots__/public-api.test.ts.snap +2 -0
- package/src/renderers/dom/build-assets.ts +14 -5
- package/src/renderers/dom/data-table/index.tsx +53 -18
- package/src/renderers/dom/data-table/row.tsx +1 -1
- package/src/renderers/dom/desktop/controls.tsx +2 -1
- package/src/renderers/dom/desktop/icons.tsx +25 -0
- package/src/renderers/dom/desktop/list-view.tsx +1 -1
- package/src/renderers/dom/desktop/menu.tsx +1 -1
- package/src/renderers/dom/desktop/popover.tsx +6 -15
- package/src/renderers/dom/desktop/query-bar.tsx +3 -1
- package/src/renderers/dom/host/chart-surface.tsx +7 -2
- package/src/renderers/dom/host/input.tsx +27 -3
- package/src/renderers/dom/host/resize-rows.ts +111 -0
- package/src/renderers/dom/host/sticky-scroll.ts +22 -3
- package/src/renderers/dom/host/style.ts +14 -0
- package/src/renderers/dom/host/surface-3d.tsx +8 -5
- package/src/renderers/dom/host/surface-fit.ts +10 -6
- package/src/renderers/dom/host/text.tsx +2 -2
- package/src/renderers/dom/styles.css +85 -10
- package/src/renderers/electrobun/bun/desktop/app-badge.ts +37 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +8 -0
- package/src/renderers/electrobun/bun/desktop/macos-dock-badge.ts +37 -0
- package/src/renderers/electrobun/bun/desktop/macos-dock-icon.ts +2 -40
- package/src/renderers/electrobun/bun/desktop/macos-objc.ts +41 -0
- package/src/renderers/electrobun/bun/tui-entry.ts +18 -1
- package/src/renderers/electrobun/shared/protocol.ts +2 -0
- package/src/renderers/electrobun/view/app-badge-host.ts +17 -0
- package/src/renderers/electrobun/view/app-services.ts +2 -22
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +8 -3
- package/src/renderers/electrobun/view/main.tsx +2 -0
- package/src/renderers/electrobun/view/news-source.ts +42 -0
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +5 -1
- package/src/series/validate.ts +6 -1
- package/src/shares/api.ts +5 -1
- package/src/shares/dialog.tsx +2 -1
- package/src/shares/location.ts +31 -0
- package/src/shares/open.ts +26 -0
- package/src/shares/pane-image.ts +18 -6
- package/src/shares/personal-pane.ts +42 -54
- package/src/sources/gloomberb-cloud/index.ts +7 -2
- package/src/sources/gloomberb-cloud/normalizers.ts +5 -0
- package/src/sources/history-coverage.ts +22 -4
- package/src/sources/history-result.ts +3 -1
- package/src/sources/provider-errors.ts +2 -1
- package/src/sources/provider-router/batches.ts +18 -2
- package/src/sources/provider-router/financials.ts +5 -0
- package/src/sources/provider-router/index.ts +2 -1
- package/src/state/app/context/index.tsx +3 -2
- package/src/test-support/builtin-panes.ts +31 -0
- package/src/tickers/listing-venues.ts +22 -0
- package/src/tickers/search/index.ts +7 -2
- package/src/tickers/search/ranking.ts +19 -0
- package/src/time-series/chart-data.ts +2 -0
- package/src/time-series/parsed-history-cache.ts +41 -11
- package/src/time-series/resolve.ts +47 -15
- package/src/types/config.ts +1 -1
- package/src/types/data-provider.ts +13 -1
- package/src/types/financials.ts +35 -0
- package/src/types/headless.ts +22 -0
- package/src/types/plugin.ts +46 -6
- package/src/types/price-history.ts +2 -0
- package/src/ui/host.tsx +6 -0
- package/src/utils/crypto-pair.ts +1 -1
- package/src/utils/currency-pair.ts +66 -0
- package/src/utils/decimal-comma.ts +57 -0
- package/src/utils/financial-statements.ts +25 -0
- package/src/utils/fundamentals.ts +41 -1
- package/src/utils/guards.ts +25 -0
- package/src/utils/local-utc-offset.ts +37 -0
- package/src/utils/plugin-proxy-hosts.json +8 -1
- package/src/components/chart/composite/crosshair-readout.tsx +0 -150
- package/src/plugins/builtin/chat/message/ai-badge.tsx +0 -18
- package/src/plugins/builtin/credit-boards/pane.tsx +0 -30
|
@@ -6,16 +6,21 @@ import { getSharedMarketDataCoordinator } from "../../../market-data/coordinator
|
|
|
6
6
|
import { useShortcut } from "../../../react/input";
|
|
7
7
|
import { isPlainKey } from "../../../utils/keyboard";
|
|
8
8
|
import { instrumentFromTicker } from "../../../market-data/request-types";
|
|
9
|
-
import { useDebouncedPluginPaneState } from "../../runtime";
|
|
9
|
+
import { useDebouncedPluginPaneState, usePluginPaneState } from "../../runtime";
|
|
10
10
|
import { usePaneSettingValue } from "../../../state/app/context";
|
|
11
11
|
import type { ScrollBoxRenderable } from "../../../ui";
|
|
12
|
-
import { EmptyState, FeedDataTableStackView, Spinner,
|
|
12
|
+
import { EmptyState, FeedDataTableStackView, QueryBar, Spinner, TickerBadgeList, usePaneNoticeFooter, useTableLoadMore, type FeedDataTableItem, type PaneHint, type QueryBarFilter } from "../../../components";
|
|
13
|
+
import { displayWidth } from "../../../utils/format";
|
|
14
|
+
import type { DetailFact } from "../../../components/reader/detail-reader";
|
|
15
|
+
import type { ThemeColors } from "../../../theme/colors";
|
|
16
|
+
import { useThemeColors } from "../../../theme/theme-context";
|
|
17
|
+
import { useOpenDescription } from "../shared/open-description";
|
|
18
|
+
import { toneColor } from "../shared/research-cells";
|
|
13
19
|
import { mayBeUsEquityOrFundTicker, mayBeUsEquityTicker, secFilingItemCodes } from "../../../utils/sec";
|
|
14
|
-
import { parseForm4Xml
|
|
20
|
+
import { parseForm4Xml } from "../insider/insider-data";
|
|
15
21
|
import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
|
|
16
22
|
import {
|
|
17
23
|
formatFilingMetaDate,
|
|
18
|
-
buildInsiderTransactionTitle,
|
|
19
24
|
buildInsiderTransactionDetailBody,
|
|
20
25
|
renderFilingNotice,
|
|
21
26
|
} from "./filing-display";
|
|
@@ -37,6 +42,11 @@ import { SignInWall } from "../cloud/auth-actions";
|
|
|
37
42
|
import { ETF_FILING_FORMS, ETF_FORMS_SETTING, filterFilingsByForms, parseFormsSetting, SEC_FILING_FETCH_LIMIT } from "./forms";
|
|
38
43
|
import { createSecHeadless, fundFilingsEmpty, secHeadless } from "./headless";
|
|
39
44
|
import {
|
|
45
|
+
ALL_FORMS,
|
|
46
|
+
COMPANY_FORM_GROUPS,
|
|
47
|
+
filingTone,
|
|
48
|
+
filterFilingsByGroup,
|
|
49
|
+
FUND_FORM_GROUPS,
|
|
40
50
|
getFilingDisplayTitle,
|
|
41
51
|
getFilingColumnText,
|
|
42
52
|
getFormDescription,
|
|
@@ -45,6 +55,7 @@ import {
|
|
|
45
55
|
secIssuerLabel,
|
|
46
56
|
secReportedAcceptance,
|
|
47
57
|
} from "./model";
|
|
58
|
+
import { rowSummary, rowSummaryTarget } from "./row-summaries";
|
|
48
59
|
import { usePaneTickerIdentity } from "../../../state/hooks/pane-ticker";
|
|
49
60
|
import { createSecFilingSearchProvider, resetSecFilingFocusRequests, useSecFilingFocusRequest } from "./command-bar-search";
|
|
50
61
|
|
|
@@ -53,6 +64,8 @@ export { secHeadless } from "./headless";
|
|
|
53
64
|
const SEC_FILING_PAGE_SIZE = 50;
|
|
54
65
|
const NO_FILINGS: SecFilingItem[] = [];
|
|
55
66
|
const OWNERSHIP_FORMS = new Set(["3", "4", "5"]);
|
|
67
|
+
/** At most this many rows on screen have their insider forms and fund flow notices read for their row text. */
|
|
68
|
+
const OWNERSHIP_ROWS_READ = 40;
|
|
56
69
|
|
|
57
70
|
function formatFiledAt(filing: SecFilingItem): string {
|
|
58
71
|
return formatFilingMetaDate(filing.filingDate);
|
|
@@ -122,16 +135,6 @@ function buildDetailBodyWithDocuments({
|
|
|
122
135
|
return lines.join("\n");
|
|
123
136
|
}
|
|
124
137
|
|
|
125
|
-
function buildForm4Preview(content: string | null): string | null {
|
|
126
|
-
if (!content) return null;
|
|
127
|
-
const transactions = parseForm4Xml(content);
|
|
128
|
-
const tx = transactions[0];
|
|
129
|
-
if (!tx) return null;
|
|
130
|
-
if (transactions.length === 1) return `${tx.reportedName} — ${buildInsiderTransactionTitle(tx)}`;
|
|
131
|
-
const types = [...new Set(transactions.map((transaction) => transactionTypeLabel(transaction.transactionType)))];
|
|
132
|
-
return `${tx.reportedName} — ${transactions.length} transactions: ${types.join(", ")}`;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
138
|
function buildForm4Detail(content: string | null, filing: SecFilingItem): string {
|
|
136
139
|
if (!content) return buildDetailBody(filing);
|
|
137
140
|
const transactions = parseForm4Xml(content);
|
|
@@ -149,6 +152,9 @@ function buildForm4Detail(content: string | null, filing: SecFilingItem): string
|
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
function toFeedItems(
|
|
155
|
+
colors: ThemeColors,
|
|
156
|
+
issuerTicker: { symbol: string; open: () => void } | null,
|
|
157
|
+
showIssuer: boolean,
|
|
152
158
|
filings: SecFilingItem[],
|
|
153
159
|
selectedAccessionNumber: string | undefined,
|
|
154
160
|
contentCache: Map<string, string | null>,
|
|
@@ -169,10 +175,10 @@ function toFeedItems(
|
|
|
169
175
|
? `${buildDetailBody(filing)}\n\nReadable filing content was not available for this document.`
|
|
170
176
|
: buildDetailBody(filing);
|
|
171
177
|
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
178
|
+
// An insider form read once says who traded and how, a fund's 24F-2NT its
|
|
179
|
+
// year's flows; the row says it too.
|
|
180
|
+
const summaryTarget = rowSummaryTarget(filing);
|
|
181
|
+
const readSummary = summaryTarget ? rowSummary(filing.form, contentCache.get(summaryTarget.accessionNumber)) : null;
|
|
176
182
|
const form4Detail = isOwnership && hasFetchedContent
|
|
177
183
|
? buildForm4Detail(fetchedContent ?? null, filing)
|
|
178
184
|
: null;
|
|
@@ -197,13 +203,28 @@ function toFeedItems(
|
|
|
197
203
|
const enrichedTitle = formDesc
|
|
198
204
|
? `${displayTitle} — ${formDesc}`
|
|
199
205
|
: displayTitle;
|
|
206
|
+
const summary = readSummary?.title ?? getFilingColumnText(filing);
|
|
207
|
+
const items = secFilingItemCodes(filing.items);
|
|
208
|
+
const facts: DetailFact[] = [
|
|
209
|
+
{ label: "Issuer", value: filing.companyName || "Name unavailable" },
|
|
210
|
+
...(issuerTicker ? [{ label: "Ticker", value: issuerTicker.symbol, link: { label: `Open ${issuerTicker.symbol}`, onOpen: issuerTicker.open } }] : []),
|
|
211
|
+
{ label: "CIK", value: filing.cik },
|
|
212
|
+
{ label: "Form", value: formDesc ? `${filing.form} · ${formDesc}` : filing.form, color: toneColor(filingTone(filing, readSummary?.direction), colors) },
|
|
213
|
+
{ label: "Filed", value: formatFiledAt(filing) },
|
|
214
|
+
...(acceptedAt ? [{ label: "SEC-reported acceptance", value: acceptedAt }] : []),
|
|
215
|
+
...(items ? [{ label: "Items", value: items }] : []),
|
|
216
|
+
{ label: "Accession", value: filing.accessionNumber },
|
|
217
|
+
];
|
|
200
218
|
|
|
201
219
|
return {
|
|
202
220
|
id: filing.accessionNumber,
|
|
203
221
|
eyebrow: filing.form,
|
|
204
|
-
|
|
222
|
+
eyebrowColor: toneColor(filingTone(filing, readSummary?.direction), colors),
|
|
223
|
+
// A ticker shared by several filers names the filer on each row.
|
|
224
|
+
title: showIssuer && filing.companyName ? `${filing.companyName}: ${summary}` : summary,
|
|
205
225
|
timestamp: filing.filingDate,
|
|
206
226
|
detailTitle: enrichedTitle,
|
|
227
|
+
detailFacts: facts,
|
|
207
228
|
detailMeta: [
|
|
208
229
|
secIssuerLabel(filing),
|
|
209
230
|
`Filed ${formatFiledAt(filing)}`,
|
|
@@ -218,8 +239,11 @@ function toFeedItems(
|
|
|
218
239
|
});
|
|
219
240
|
}
|
|
220
241
|
|
|
221
|
-
|
|
242
|
+
/** `paneType` is set for the standalone pane and absent in the DES tab, whose header already names the security. */
|
|
243
|
+
function SecView({ width, height, focused, paneType }: { width: number; height: number; focused: boolean; paneType?: string }) {
|
|
222
244
|
const { ticker } = usePaneTickerIdentity();
|
|
245
|
+
const colors = useThemeColors();
|
|
246
|
+
const openDescription = useOpenDescription();
|
|
223
247
|
const selectionKey = `selectedIdx:${ticker?.metadata.ticker ?? "none"}`;
|
|
224
248
|
const [selectedIdx, setSelectedIdx] = useDebouncedPluginPaneState<number>(selectionKey, 0);
|
|
225
249
|
// The open filing is what the pane shows, so it is pane state: it restores
|
|
@@ -251,10 +275,22 @@ function SecView({ width, height, focused }: { width: number; height: number; fo
|
|
|
251
275
|
const issuerFilings = useResolvedEntryValue(filingsEntry) ?? NO_FILINGS;
|
|
252
276
|
// The filter runs over every filing the service returned for the issuer,
|
|
253
277
|
// the same list the unfiltered pane shows, not over a first page of it.
|
|
254
|
-
const
|
|
278
|
+
const formFilings = useMemo(
|
|
255
279
|
() => (forms ? filterFilingsByForms(issuerFilings, forms) : issuerFilings),
|
|
256
280
|
[forms, issuerFilings],
|
|
257
281
|
);
|
|
282
|
+
// The fund view (ETF) has its own groups; each view keeps its own choice.
|
|
283
|
+
const groups = forms ? FUND_FORM_GROUPS : COMPANY_FORM_GROUPS;
|
|
284
|
+
const [storedGroup, setFormGroup] = usePluginPaneState<string>(forms ? "fundFormGroup" : "formGroup", ALL_FORMS);
|
|
285
|
+
const formGroup = groups.some((group) => group.value === storedGroup) ? storedGroup : ALL_FORMS;
|
|
286
|
+
// A ticker can map to more than one filer (CIK); each can be listed alone.
|
|
287
|
+
const issuers = useMemo(() => secFilingIssuers(formFilings), [formFilings]);
|
|
288
|
+
const [storedIssuer, setIssuer] = usePluginPaneState<string>("issuerCik", ALL_FORMS);
|
|
289
|
+
const issuerCik = issuers.some((issuer) => issuer.cik === storedIssuer) ? storedIssuer : ALL_FORMS;
|
|
290
|
+
const filings = useMemo(() => {
|
|
291
|
+
const grouped = filterFilingsByGroup(formFilings, groups, formGroup);
|
|
292
|
+
return issuerCik === ALL_FORMS ? grouped : grouped.filter((filing) => filing.cik === issuerCik);
|
|
293
|
+
}, [formFilings, formGroup, groups, issuerCik]);
|
|
258
294
|
const filterHistoryCapped = !!forms && issuerFilings.length >= SEC_FILING_FETCH_LIMIT;
|
|
259
295
|
const [visibleCount, setVisibleCount] = useState(SEC_FILING_PAGE_SIZE);
|
|
260
296
|
const visibleFilings = useMemo(() => filings.slice(0, visibleCount), [filings, visibleCount]);
|
|
@@ -266,7 +302,7 @@ function SecView({ width, height, focused }: { width: number; height: number; fo
|
|
|
266
302
|
);
|
|
267
303
|
useEffect(() => {
|
|
268
304
|
setVisibleCount(SEC_FILING_PAGE_SIZE);
|
|
269
|
-
}, [ticker?.metadata.ticker, ticker?.metadata.exchange, formsSetting]);
|
|
305
|
+
}, [ticker?.metadata.ticker, ticker?.metadata.exchange, formsSetting, formGroup, issuerCik]);
|
|
270
306
|
const loading = filingsEntry?.phase === "loading" || filingsEntry?.phase === "refreshing";
|
|
271
307
|
// A restored or shared open filing can sit past the first page, or be gone
|
|
272
308
|
// from the feed entirely.
|
|
@@ -284,7 +320,7 @@ function SecView({ width, height, focused }: { width: number; height: number; fo
|
|
|
284
320
|
const error = filingsEntry?.error && filingsEntry.error.reasonCode !== "NO_DATA" ? filingsEntry.error.message : null;
|
|
285
321
|
// Hosted, the only filings source is Gloom Cloud, which needs an account.
|
|
286
322
|
const cloudSession = useResearchCloudSession();
|
|
287
|
-
const authWall =
|
|
323
|
+
const authWall = formFilings.length === 0 && isCloudSessionRequired(error);
|
|
288
324
|
|
|
289
325
|
const openFiling = openItemId
|
|
290
326
|
? filings.find((filing) => filing.accessionNumber === openItemId) ?? null
|
|
@@ -298,14 +334,30 @@ function SecView({ width, height, focused }: { width: number; height: number; fo
|
|
|
298
334
|
|| documentsEntry?.phase === "refreshing"
|
|
299
335
|
);
|
|
300
336
|
|
|
301
|
-
//
|
|
302
|
-
//
|
|
337
|
+
// The open filing's content comes first. Insider forms say who traded only
|
|
338
|
+
// in their text, so the ones around the cursor are read after it, one at a
|
|
339
|
+
// time, for their rows; the rest of the list is never queued, which would
|
|
340
|
+
// fire dozens of requests for rows nobody looks at. Read text is cached.
|
|
303
341
|
const selectedFiling = visibleFilings[selectedIdx];
|
|
304
|
-
|
|
342
|
+
// The rows on screen, as a share of the list so it holds for terminal rows and desktop pixels alike.
|
|
343
|
+
const [onScreen, setOnScreen] = useState({ first: 0, count: height });
|
|
344
|
+
const trackScroll = useCallback(() => {
|
|
345
|
+
loadMore();
|
|
346
|
+
const box = scrollRef.current;
|
|
347
|
+
if (!box?.viewport || !(box.scrollHeight > 0) || visibleFilings.length === 0) return;
|
|
348
|
+
const first = Math.floor((box.scrollTop / box.scrollHeight) * visibleFilings.length);
|
|
349
|
+
const count = Math.ceil((box.viewport.height / box.scrollHeight) * visibleFilings.length) + 1;
|
|
350
|
+
setOnScreen((current) => (current.first === first && current.count === count ? current : { first, count }));
|
|
351
|
+
}, [loadMore, visibleFilings.length]);
|
|
352
|
+
const nearbyOwnership = useMemo(() => [
|
|
353
|
+
...(selectedFiling ? [selectedFiling] : []),
|
|
354
|
+
...visibleFilings.slice(onScreen.first, onScreen.first + Math.min(OWNERSHIP_ROWS_READ, onScreen.count)),
|
|
355
|
+
].map(rowSummaryTarget).filter((target): target is SecFilingItem => !!target), [onScreen, selectedFiling, visibleFilings]);
|
|
356
|
+
const viewedTargets = useMemo(() => [
|
|
305
357
|
...(openFiling ? [openFiling] : []),
|
|
306
358
|
...buildInlineFilingContentTargets(openFiling, openDocuments),
|
|
307
|
-
|
|
308
|
-
], [
|
|
359
|
+
], [openDocuments, openFiling]);
|
|
360
|
+
const contentTargets = useMemo(() => [...viewedTargets, ...nearbyOwnership], [nearbyOwnership, viewedTargets]);
|
|
309
361
|
const { contentCache, contentErrors, retry: retryContent, loadingKey } = useSecFilingContentCache({
|
|
310
362
|
scopeKey: `${ticker?.metadata.ticker ?? "none"}:${ticker?.metadata.exchange ?? ""}:${eligibleTicker}`,
|
|
311
363
|
targets: contentTargets,
|
|
@@ -352,14 +404,24 @@ function SecView({ width, height, focused }: { width: number; height: number; fo
|
|
|
352
404
|
}
|
|
353
405
|
}, [selectedIdx, setSelectedIdx, visibleFilings.length]);
|
|
354
406
|
|
|
407
|
+
// The pane's own ticker opens its description; a fund or equity both have one.
|
|
408
|
+
const issuerTicker = useMemo(() => (ticker && paneType ? {
|
|
409
|
+
symbol: ticker.metadata.ticker,
|
|
410
|
+
open: () => openDescription(ticker.metadata.ticker, ticker.metadata.exchange || undefined),
|
|
411
|
+
} : null), [openDescription, paneType, ticker]);
|
|
412
|
+
const hints = useMemo<PaneHint[]>(() => (issuerTicker
|
|
413
|
+
? [{ id: "sec-description", key: "d", label: "es", title: `Open ${issuerTicker.symbol}`, onPress: issuerTicker.open }]
|
|
414
|
+
: []), [issuerTicker]);
|
|
355
415
|
usePaneStatusLinkFooter({
|
|
356
416
|
registrationId: "sec",
|
|
357
417
|
focused,
|
|
358
418
|
url: openFiling?.filingUrl,
|
|
359
419
|
source: openFiling?.form,
|
|
360
420
|
label: "filing",
|
|
361
|
-
|
|
421
|
+
// Rows read in the background for their insiders do not hold up the pane.
|
|
422
|
+
loading: loading || loadingDocuments || (!!loadingKey && viewedTargets.some((target) => target.accessionNumber === loadingKey)),
|
|
362
423
|
error: authWall ? null : error ?? detailError,
|
|
424
|
+
hints,
|
|
363
425
|
showOpenHint: !!openFiling?.filingUrl,
|
|
364
426
|
});
|
|
365
427
|
|
|
@@ -373,25 +435,48 @@ function SecView({ width, height, focused }: { width: number; height: number; fo
|
|
|
373
435
|
title: "Fund filings",
|
|
374
436
|
});
|
|
375
437
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
438
|
+
const queryFilters = useMemo<QueryBarFilter[]>(() => [
|
|
439
|
+
{
|
|
440
|
+
id: "form",
|
|
441
|
+
label: "Form",
|
|
442
|
+
value: formGroup,
|
|
443
|
+
defaultValue: ALL_FORMS,
|
|
444
|
+
options: [{ value: ALL_FORMS, label: forms ? "All fund forms" : "All forms" }, ...groups.map((group) => ({ value: group.value, label: group.label }))],
|
|
445
|
+
onChange: (value: string) => { setFormGroup(value); setSelectedIdx(0, { immediate: true }); setOpenItemId(null); },
|
|
446
|
+
},
|
|
447
|
+
...(issuers.length > 1 ? [{
|
|
448
|
+
id: "issuer",
|
|
449
|
+
label: "Filer",
|
|
450
|
+
value: issuerCik,
|
|
451
|
+
defaultValue: ALL_FORMS,
|
|
452
|
+
options: [{ value: ALL_FORMS, label: "All filers" }, ...issuers.map((issuer) => ({ value: issuer.cik, label: issuer.companyName || `CIK ${issuer.cik}` }))],
|
|
453
|
+
onChange: (value: string) => { setIssuer(value); setSelectedIdx(0, { immediate: true }); setOpenItemId(null); },
|
|
454
|
+
}] : []),
|
|
455
|
+
], [formGroup, forms, groups, issuerCik, issuers, setFormGroup, setIssuer, setOpenItemId, setSelectedIdx]);
|
|
456
|
+
// The ticker and the filer once, beside the filters: every row is their filing.
|
|
457
|
+
const issuerMeta = issuers.length === 1 ? secIssuerLabel(issuers[0]!).replace(/CIK 0+/, "CIK ") : undefined;
|
|
458
|
+
const queryBar = (
|
|
459
|
+
<QueryBar
|
|
460
|
+
width={width}
|
|
461
|
+
filters={queryFilters}
|
|
462
|
+
meta={issuerMeta}
|
|
463
|
+
leading={issuerTicker
|
|
464
|
+
? <TickerBadgeList symbols={[issuerTicker.symbol]} width={displayWidth(issuerTicker.symbol) + 2} listed onOpen={issuerTicker.open} />
|
|
465
|
+
: undefined}
|
|
466
|
+
/>
|
|
467
|
+
);
|
|
383
468
|
|
|
384
469
|
if (!ticker) {
|
|
385
470
|
return <EmptyState title="No ticker selected." message="Select a ticker to view SEC filings." />;
|
|
386
471
|
}
|
|
387
472
|
if (!eligibleTicker) {
|
|
388
|
-
return renderFilingNotice(forms ? "Fund filings are only shown for US-listed funds and equities." : "SEC filings are only shown for US equities."
|
|
473
|
+
return renderFilingNotice(forms ? "Fund filings are only shown for US-listed funds and equities." : "SEC filings are only shown for US equities.");
|
|
389
474
|
}
|
|
390
475
|
if (authWall) return <SignInWall placement="sec-signin" action="view SEC filings" needsVerification={cloudSession.needsVerification} />;
|
|
391
476
|
if (loading && filings.length === 0) return <Spinner label="Loading SEC filings..." />;
|
|
392
|
-
if (error &&
|
|
393
|
-
if (
|
|
394
|
-
return renderFilingNotice(forms ? `No fund filings for ${ticker.metadata.ticker}.` : `No recent SEC filings for ${ticker.metadata.ticker}
|
|
477
|
+
if (error && formFilings.length === 0) return <EmptyState title="SEC filings unavailable." message={error} />;
|
|
478
|
+
if (formFilings.length === 0) {
|
|
479
|
+
return renderFilingNotice(forms ? `No fund filings for ${ticker.metadata.ticker}.` : `No recent SEC filings for ${ticker.metadata.ticker}.`);
|
|
395
480
|
}
|
|
396
481
|
|
|
397
482
|
return (
|
|
@@ -400,6 +485,9 @@ function SecView({ width, height, focused }: { width: number; height: number; fo
|
|
|
400
485
|
height={height}
|
|
401
486
|
focused={focused}
|
|
402
487
|
items={toFeedItems(
|
|
488
|
+
colors,
|
|
489
|
+
issuerTicker,
|
|
490
|
+
issuers.length > 1 && issuerCik === ALL_FORMS,
|
|
403
491
|
visibleFilings,
|
|
404
492
|
openFiling?.accessionNumber,
|
|
405
493
|
contentCache,
|
|
@@ -414,12 +502,12 @@ function SecView({ width, height, focused }: { width: number; height: number; fo
|
|
|
414
502
|
openItemId={openItemId}
|
|
415
503
|
onOpenItemIdChange={setOpenItemId}
|
|
416
504
|
prefetchDetail={prefetchDocuments}
|
|
417
|
-
rootBefore={
|
|
505
|
+
rootBefore={queryBar}
|
|
418
506
|
sourceLabel="Form"
|
|
419
507
|
titleLabel="Filing"
|
|
420
|
-
emptyStateTitle="No SEC filings."
|
|
508
|
+
emptyStateTitle={formGroup === ALL_FORMS ? "No SEC filings." : "No filings of these forms."}
|
|
421
509
|
scrollRef={scrollRef}
|
|
422
|
-
onBodyScrollActivity={
|
|
510
|
+
onBodyScrollActivity={trackScroll}
|
|
423
511
|
/>
|
|
424
512
|
);
|
|
425
513
|
}
|
|
@@ -46,11 +46,77 @@ export function getFilingDisplayTitle(filing: SecFilingItem): string {
|
|
|
46
46
|
return description ? `${formLabel} | ${description}` : formLabel;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* 8-K items in a few words, as a reader scans them: what happened, not the
|
|
51
|
+
* SEC's full caption. 9.01 (exhibits) comes with most reports and says little.
|
|
52
|
+
*/
|
|
53
|
+
const CURRENT_REPORT_ITEMS: Record<string, string> = {
|
|
54
|
+
"1.01": "Material agreement",
|
|
55
|
+
"1.02": "Agreement terminated",
|
|
56
|
+
"1.03": "Bankruptcy or receivership",
|
|
57
|
+
"1.04": "Mine safety",
|
|
58
|
+
"1.05": "Cybersecurity incident",
|
|
59
|
+
"2.01": "Acquisition or disposal",
|
|
60
|
+
"2.02": "Results of operations",
|
|
61
|
+
"2.03": "New financial obligation",
|
|
62
|
+
"2.04": "Obligation accelerated",
|
|
63
|
+
"2.05": "Exit or restructuring costs",
|
|
64
|
+
"2.06": "Impairment",
|
|
65
|
+
"3.01": "Listing standard notice",
|
|
66
|
+
"3.02": "Unregistered equity sale",
|
|
67
|
+
"3.03": "Shareholder rights modified",
|
|
68
|
+
"4.01": "Auditor change",
|
|
69
|
+
"4.02": "Non-reliance on financials",
|
|
70
|
+
"5.01": "Change in control",
|
|
71
|
+
"5.02": "Officer or director change",
|
|
72
|
+
"5.03": "Charter or bylaws amended",
|
|
73
|
+
"5.04": "Benefit plan trading suspension",
|
|
74
|
+
"5.05": "Code of ethics change",
|
|
75
|
+
"5.06": "Shell company status change",
|
|
76
|
+
"5.07": "Shareholder vote results",
|
|
77
|
+
"5.08": "Director nominations",
|
|
78
|
+
"7.01": "Regulation FD disclosure",
|
|
79
|
+
"8.01": "Other events",
|
|
80
|
+
"9.01": "Exhibits",
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const isCurrentReport = (form: string) => /^(?:8-K|6-K)(?:\/A)?$/i.test(form.trim());
|
|
84
|
+
|
|
85
|
+
/** `Results of operations; Exhibits` from `2.02,9.01`; exhibits only when nothing else is reported. */
|
|
86
|
+
function currentReportItemsText(items: string | null | undefined): string | null {
|
|
87
|
+
const codes = (secFilingItemCodes(items) ?? "").split(",").filter(Boolean);
|
|
88
|
+
const named = codes.filter((code) => code !== "9.01" || codes.length === 1).map((code) => CURRENT_REPORT_ITEMS[code] ?? `Item ${code}`);
|
|
89
|
+
return named.length > 0 ? named.join("; ") : null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The period a 10-K or 10-Q covers, read from its inline XBRL document name
|
|
94
|
+
* (`aapl-20260627.htm`), which by convention carries the period end. A date
|
|
95
|
+
* after the filing, or more than 15 months before it, is not taken.
|
|
96
|
+
*/
|
|
97
|
+
function periodicReportPeriod(filing: Pick<SecFilingItem, "form" | "primaryDocument" | "filingDate">): string | null {
|
|
98
|
+
if (!/^(?:10-K|10-Q|10-KT|10-QT|20-F|40-F)(?:\/A)?$/i.test(filing.form.trim())) return null;
|
|
99
|
+
const match = /^[a-z0-9.]+-(\d{4})(\d{2})(\d{2})(?:x\d+)?\.html?$/i.exec(filing.primaryDocument?.split("/").pop() ?? "");
|
|
100
|
+
if (!match) return null;
|
|
101
|
+
const end = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])));
|
|
102
|
+
const iso = `${match[1]}-${match[2]}-${match[3]}`;
|
|
103
|
+
const filed = new Date(filing.filingDate).getTime();
|
|
104
|
+
if (!Number.isFinite(end.getTime()) || end.toISOString().slice(0, 10) !== iso || !Number.isFinite(filed)) return null;
|
|
105
|
+
const days = (filed - end.getTime()) / 86_400_000;
|
|
106
|
+
return days >= 0 && days <= 460 ? iso : null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** The adjacent FORM column already identifies the form. Keep its meaning here: what the filing reports. */
|
|
50
110
|
export function getFilingColumnText(filing: SecFilingItem): string {
|
|
51
111
|
const description = getMeaningfulPrimaryDescription(filing);
|
|
52
112
|
const meaning = getFormDescription(filing.form);
|
|
53
|
-
if (!description)
|
|
113
|
+
if (!description) {
|
|
114
|
+
const items = isCurrentReport(filing.form) ? currentReportItemsText(filing.items) : null;
|
|
115
|
+
if (items) return /\/A$/i.test(filing.form.trim()) ? `${items} (amended)` : items;
|
|
116
|
+
const period = periodicReportPeriod(filing);
|
|
117
|
+
if (period) return `${meaning || "Report"}, period ended ${period}`;
|
|
118
|
+
return meaning || "Filing";
|
|
119
|
+
}
|
|
54
120
|
return meaning && normalizeComparableText(description) !== normalizeComparableText(meaning)
|
|
55
121
|
? `${description} | ${meaning}`
|
|
56
122
|
: description;
|
|
@@ -99,6 +165,22 @@ export function getFormDescription(form: string): string {
|
|
|
99
165
|
case "NPORT-P": return "Monthly Portfolio Holdings Report";
|
|
100
166
|
case "NPORT-EX": return "Portfolio Holdings Exhibit";
|
|
101
167
|
case "24F-2NT": return "Annual Notice of Securities Sold";
|
|
168
|
+
case "SD": return "Conflict Minerals Report";
|
|
169
|
+
case "S-3": return "Shelf Registration";
|
|
170
|
+
case "S-8": return "Employee Plan Registration";
|
|
171
|
+
case "FWP": return "Free Writing Prospectus";
|
|
172
|
+
case "DEFA14A": return "Additional Proxy Materials";
|
|
173
|
+
case "PX14A6G": return "Shareholder Exempt Solicitation";
|
|
174
|
+
case "11-K": return "Employee Plan Annual Report";
|
|
175
|
+
case "ARS": return "Annual Report to Shareholders";
|
|
176
|
+
case "NT 10-K": return "Late Annual Report Notice";
|
|
177
|
+
case "NT 10-Q": return "Late Quarterly Report Notice";
|
|
178
|
+
case "25-NSE": return "Delisting Notice";
|
|
179
|
+
case "6-K": return "Foreign Issuer Current Report";
|
|
180
|
+
case "40-F": return "Annual Report (Canadian)";
|
|
181
|
+
case "424B2":
|
|
182
|
+
case "424B3":
|
|
183
|
+
case "424B5": return "Prospectus";
|
|
102
184
|
default: {
|
|
103
185
|
const base = normalized.endsWith("/A") ? getFormDescription(normalized.slice(0, -2)) : "";
|
|
104
186
|
return base ? `${base} (Amended)` : "";
|
|
@@ -106,6 +188,70 @@ export function getFormDescription(form: string): string {
|
|
|
106
188
|
}
|
|
107
189
|
}
|
|
108
190
|
|
|
191
|
+
/** The groups of the Form filter: a reader looks for reports, events or insiders, not one form code. */
|
|
192
|
+
export interface FormGroup {
|
|
193
|
+
value: string;
|
|
194
|
+
label: string;
|
|
195
|
+
matches: (form: string) => boolean;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const upper = (form: string) => form.trim().toUpperCase().replace(/\/A$/, "");
|
|
199
|
+
const oneOf = (...forms: string[]) => (form: string) => forms.includes(upper(form));
|
|
200
|
+
|
|
201
|
+
export const ALL_FORMS = "all";
|
|
202
|
+
|
|
203
|
+
export const COMPANY_FORM_GROUPS: readonly FormGroup[] = [
|
|
204
|
+
{ value: "periodic", label: "Annual & quarterly", matches: oneOf("10-K", "10-Q", "10-KT", "10-QT", "20-F", "40-F", "NT 10-K", "NT 10-Q", "NT 20-F") },
|
|
205
|
+
{ value: "current", label: "Current reports", matches: (form) => isCurrentReport(form) || upper(form) === "8-K12B" },
|
|
206
|
+
{ value: "insider", label: "Insider", matches: oneOf("3", "4", "5", "144") },
|
|
207
|
+
{ value: "ownership", label: "13D / 13G", matches: (form) => /^(?:SC|SCHEDULE) 13[DG]$/.test(upper(form)) },
|
|
208
|
+
{ value: "proxy", label: "Proxy", matches: (form) => /^(?:DEF|DEFA|DEFM|PRE|PREM|DFAN)14[AC]$/.test(upper(form).replace(/\s+/g, "")) },
|
|
209
|
+
{ value: "offerings", label: "Registrations & offerings", matches: (form) => /^(?:S-\d|F-\d|424B\d|FWP|S-8 POS|POS AM|425)/.test(upper(form)) },
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
export const FUND_FORM_GROUPS: readonly FormGroup[] = [
|
|
213
|
+
{ value: "reports", label: "Shareholder reports", matches: oneOf("N-CSR", "N-CSRS", "N-30D") },
|
|
214
|
+
{ value: "holdings", label: "Holdings", matches: oneOf("NPORT-P", "NPORT-EX") },
|
|
215
|
+
{ value: "prospectus", label: "Prospectus & registration", matches: oneOf("N-1A", "N-2", "485APOS", "485BPOS", "485BXT", "497", "497J", "497K") },
|
|
216
|
+
{ value: "census", label: "Flows & census", matches: oneOf("N-CEN", "24F-2NT") },
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
/** Filings in a group; `all` and an unknown group keep every filing. */
|
|
220
|
+
export function filterFilingsByGroup<T extends { form: string }>(filings: readonly T[], groups: readonly FormGroup[], value: string): T[] {
|
|
221
|
+
const group = groups.find((entry) => entry.value === value);
|
|
222
|
+
return group ? filings.filter((filing) => group.matches(filing.form)) : [...filings];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export type FilingTone = "positive" | "negative" | "warning" | "accent" | "muted" | "text";
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* How much a filing matters, in the tone every research pane gives a state:
|
|
229
|
+
* events that can sink a company (bankruptcy, delisting, non-reliance, a late
|
|
230
|
+
* report) red, an insider sale red and a purchase green, a planned sale, an
|
|
231
|
+
* activist stake, a management or auditor change amber, the periodic reports,
|
|
232
|
+
* earnings and fund shareholder reports in the accent, and routine paperwork
|
|
233
|
+
* quiet. A fund's annual notice of securities sold reads green for a year of
|
|
234
|
+
* net creations and red for net redemptions. The form text never changes, so
|
|
235
|
+
* the colour is not the only signal.
|
|
236
|
+
*/
|
|
237
|
+
export function filingTone(filing: Pick<SecFilingItem, "form" | "items">, insiderDirection?: "buy" | "sell" | null): FilingTone {
|
|
238
|
+
const form = upper(filing.form);
|
|
239
|
+
if (/^NT /.test(form)) return "negative";
|
|
240
|
+
if (isCurrentReport(filing.form)) {
|
|
241
|
+
const codes = (secFilingItemCodes(filing.items) ?? "").split(",");
|
|
242
|
+
if (codes.some((code) => code === "1.03" || code === "3.01" || code === "4.02")) return "negative";
|
|
243
|
+
if (codes.some((code) => ["2.05", "2.06", "4.01", "5.01", "5.02", "1.05"].includes(code))) return "warning";
|
|
244
|
+
if (codes.includes("2.02")) return "accent";
|
|
245
|
+
return "text";
|
|
246
|
+
}
|
|
247
|
+
if (form === "4" || form === "5" || form === "24F-2NT") return insiderDirection === "sell" ? "negative" : insiderDirection === "buy" ? "positive" : form === "24F-2NT" ? "muted" : "text";
|
|
248
|
+
if (form === "144") return "warning";
|
|
249
|
+
if (/13D$/.test(form)) return "warning";
|
|
250
|
+
if (/^(?:10-K|10-Q|10-KT|10-QT|20-F|40-F|N-CSR|N-CSRS|N-30D|N-1A|N-2)$/.test(form)) return "accent";
|
|
251
|
+
if (/^(?:3|SC 13G|SCHEDULE 13G|SD|497|497J|497K|485BPOS|485BXT|NPORT-P|NPORT-EX)$/.test(form)) return "muted";
|
|
252
|
+
return "text";
|
|
253
|
+
}
|
|
254
|
+
|
|
109
255
|
export function buildSecFilingRows(filings: readonly SecFilingItem[]) {
|
|
110
256
|
return filings.map((filing) => {
|
|
111
257
|
return {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { SecFilingItem } from "../../../types/data-provider";
|
|
2
|
+
import { decodeHtmlEntities } from "../../../utils/html-entities";
|
|
3
|
+
import { formatCompact } from "../../../utils/format";
|
|
4
|
+
import { parseForm4Xml, transactionTypeLabel } from "../insider/insider-data";
|
|
5
|
+
import { buildInsiderTransactionTitle } from "./filing-display";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* What a small XML filing's own text says, for its row: who traded and which
|
|
9
|
+
* way on an insider form, how much money came in or went out on a fund's
|
|
10
|
+
* annual notice of securities sold.
|
|
11
|
+
*/
|
|
12
|
+
export interface RowSummary {
|
|
13
|
+
title: string;
|
|
14
|
+
/** An open-market purchase or sale, or a fund's net creations or redemptions. */
|
|
15
|
+
direction: "buy" | "sell" | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const OWNERSHIP_FORMS = /^(?:3|4|5|144)(?:\/A)?$/i;
|
|
19
|
+
const FUND_FLOW_FORM = /^24F-2NT(?:\/A)?$/i;
|
|
20
|
+
|
|
21
|
+
const isOwnershipForm = (form: string) => OWNERSHIP_FORMS.test(form.trim());
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The document a row is read from. Gloom Cloud returns a Form 3, 4 or 5 as
|
|
25
|
+
* its XML; a Form 144 and a 24F-2NT come as the SEC's rendered page unless
|
|
26
|
+
* asked for their XML, which a separate key keeps apart from the page the
|
|
27
|
+
* open filing shows. Other forms are not read for their rows.
|
|
28
|
+
*/
|
|
29
|
+
export function rowSummaryTarget(filing: SecFilingItem): SecFilingItem | null {
|
|
30
|
+
const form = filing.form.trim();
|
|
31
|
+
if (!isOwnershipForm(form) && !FUND_FLOW_FORM.test(form)) return null;
|
|
32
|
+
if (/^[345](?:\/A)?$/i.test(form)) return filing;
|
|
33
|
+
const url = filing.primaryDocumentUrl?.replace(/\/xsl[^/]+\//, "/");
|
|
34
|
+
if (!url || url === filing.primaryDocumentUrl) return null;
|
|
35
|
+
const document = url.split("/").pop() ?? "primary_doc.xml";
|
|
36
|
+
return { ...filing, accessionNumber: `${filing.accessionNumber}:${document}`, primaryDocument: document, primaryDocumentUrl: url, filingUrl: url };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function tagText(xml: string, tag: string): string {
|
|
40
|
+
const match = new RegExp(`<(?:[\\w.-]+:)?${tag}(?:\\s[^>]*)?>([\\s\\S]*?)<\\/(?:[\\w.-]+:)?${tag}\\s*>`, "i").exec(xml);
|
|
41
|
+
return decodeHtmlEntities((match?.[1] ?? "").replace(/<[^>]*>/g, "").trim());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const money = (value: number) => `$${formatCompact(value)}`;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A Form 144 is a notice that someone means to sell: who, how many shares
|
|
48
|
+
* and what they were worth when it was filed.
|
|
49
|
+
*/
|
|
50
|
+
function form144Summary(xml: string): RowSummary | null {
|
|
51
|
+
const name = tagText(xml, "nameOfPersonForWhoseAccountTheSecuritiesAreToBeSold");
|
|
52
|
+
if (!name) return null;
|
|
53
|
+
const shares = Number(tagText(xml, "noOfUnitsSold"));
|
|
54
|
+
const value = Number(tagText(xml, "aggregateMarketValue"));
|
|
55
|
+
const amount = [
|
|
56
|
+
Number.isFinite(shares) && shares > 0 ? `${formatCompact(shares)} shares` : "",
|
|
57
|
+
Number.isFinite(value) && value > 0 ? `(${money(value)})` : "",
|
|
58
|
+
].filter(Boolean).join(" ");
|
|
59
|
+
return { title: `${name}: proposed sale${amount ? ` of ${amount}` : ""}`, direction: null };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** The reporting owner of a Form 3, which lists holdings and no transactions. */
|
|
63
|
+
function initialOwnershipSummary(xml: string): RowSummary | null {
|
|
64
|
+
const name = tagText(xml, "rptOwnerName");
|
|
65
|
+
return name ? { title: `${name}: initial holdings`, direction: null } : null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const signedMoney = (value: number) => `${value < 0 ? "-" : "+"}${money(Math.abs(value))}`;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* A fund's 24F-2NT: what it sold (created) and redeemed over its fiscal year,
|
|
72
|
+
* and the difference, which is the year's net flow.
|
|
73
|
+
*/
|
|
74
|
+
function fundFlowSummary(xml: string): RowSummary | null {
|
|
75
|
+
const sold = Number(tagText(xml, "aggregateSalePriceOfSecuritiesSold"));
|
|
76
|
+
const redeemed = Number(tagText(xml, "aggregatePriceOfSecuritiesRedeemedOrRepurchasedInFiscalYear"));
|
|
77
|
+
if (!tagText(xml, "aggregateSalePriceOfSecuritiesSold") || !Number.isFinite(sold) || !Number.isFinite(redeemed)) return null;
|
|
78
|
+
const yearEnd = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(tagText(xml, "lastDayOfFiscalYear"));
|
|
79
|
+
const net = sold - redeemed;
|
|
80
|
+
return {
|
|
81
|
+
title: `${yearEnd ? `Year to ${yearEnd[3]}-${yearEnd[1]}-${yearEnd[2]}: ` : ""}net flow ${signedMoney(net)} (sold ${money(sold)}, redeemed ${money(redeemed)})`,
|
|
82
|
+
direction: net > 0 ? "buy" : net < 0 ? "sell" : null,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The row text of a filing whose content has been read, or null when it was
|
|
88
|
+
* not read or says nothing a row can use. An insider form with one
|
|
89
|
+
* transaction says it in full; one with several lists their kinds.
|
|
90
|
+
*/
|
|
91
|
+
export function rowSummary(form: string, content: string | null | undefined): RowSummary | null {
|
|
92
|
+
if (!content) return null;
|
|
93
|
+
if (FUND_FLOW_FORM.test(form.trim())) return fundFlowSummary(content);
|
|
94
|
+
if (!isOwnershipForm(form)) return null;
|
|
95
|
+
const base = form.trim().replace(/\/A$/i, "");
|
|
96
|
+
if (base === "144") return form144Summary(content);
|
|
97
|
+
const transactions = parseForm4Xml(content);
|
|
98
|
+
const first = transactions[0];
|
|
99
|
+
if (!first) return base === "3" ? initialOwnershipSummary(content) : null;
|
|
100
|
+
const codes = new Set(transactions.map((transaction) => transaction.transactionType));
|
|
101
|
+
const direction = codes.has("P") && !codes.has("S") ? "buy" : codes.has("S") && !codes.has("P") ? "sell" : null;
|
|
102
|
+
if (transactions.length === 1) return { title: `${first.reportedName}: ${buildInsiderTransactionTitle(first)}`, direction };
|
|
103
|
+
const kinds = [...new Set(transactions.map((transaction) => transactionTypeLabel(transaction.transactionType)))];
|
|
104
|
+
return { title: `${first.reportedName}: ${transactions.length} transactions, ${kinds.join(", ")}`, direction };
|
|
105
|
+
}
|
|
@@ -395,6 +395,8 @@ export const sectorsModule: PluginModule = {
|
|
|
395
395
|
name: "Sector Performance",
|
|
396
396
|
icon: "S",
|
|
397
397
|
component: SectorPerformancePane,
|
|
398
|
+
// BI and THEM open this pane: THEM sets the theme param; the ETF lists are preferences.
|
|
399
|
+
identitySettings: [],
|
|
398
400
|
defaultPosition: "right",
|
|
399
401
|
defaultMode: "floating",
|
|
400
402
|
defaultFloatingSize: { width: 82, height: 18 },
|
|
@@ -7,7 +7,7 @@ import { mergePriceHistoryIntegrity, pricePointIntegrity, type PriceHistoryInteg
|
|
|
7
7
|
import {
|
|
8
8
|
getExtendedSessionDisplay,
|
|
9
9
|
getRegularSessionDisplay,
|
|
10
|
-
|
|
10
|
+
extendedSessionStateLabel,
|
|
11
11
|
type ExtendedSession,
|
|
12
12
|
} from "../../../market-data/market/status";
|
|
13
13
|
import {
|
|
@@ -359,7 +359,7 @@ export function buildSectorColumns(width: number, extendedSessions: readonly Ext
|
|
|
359
359
|
const extendedColumns = extendedSessions.map((session): SectorColumn => ({
|
|
360
360
|
id: session === "PRE" ? "preMarket" : "afterHours",
|
|
361
361
|
// Named for the session it trades in, as the header labels it.
|
|
362
|
-
label:
|
|
362
|
+
label: extendedSessionStateLabel(session),
|
|
363
363
|
width: EXTENDED_COLUMN_WIDTH,
|
|
364
364
|
align: "right",
|
|
365
365
|
}));
|