gloomberb 0.15.7 → 0.16.0
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 +11 -2
- package/package.json +1 -1
- package/src/api-client/app-attention.ts +147 -0
- package/src/api-client/attention.ts +26 -0
- package/src/api-client/awards.ts +215 -0
- package/src/api-client/catalysts.ts +160 -0
- package/src/api-client/chat.ts +137 -14
- package/src/api-client/company-kpis.ts +153 -0
- package/src/api-client/cpi.ts +52 -0
- package/src/api-client/credit-documents.ts +107 -0
- package/src/api-client/data.ts +192 -3
- package/src/api-client/distress.ts +164 -0
- package/src/api-client/doe.ts +75 -0
- package/src/api-client/equity-screener.ts +11 -0
- package/src/api-client/exposure.ts +202 -0
- package/src/api-client/gpu.ts +93 -0
- package/src/api-client/hiring.ts +110 -0
- package/src/api-client/index.ts +78 -3
- package/src/api-client/normalizers.ts +31 -1
- package/src/api-client/paths.ts +3 -0
- package/src/api-client/perps.ts +127 -0
- package/src/api-client/power.ts +148 -0
- package/src/api-client/request.ts +58 -1
- package/src/api-client/research-activity.ts +172 -13
- package/src/api-client/socket.ts +75 -27
- package/src/api-client/supply-chain-graph.ts +40 -0
- package/src/api-client/supply-chain.ts +98 -0
- package/src/api-client/telemetry.ts +29 -0
- package/src/api-client/themes.ts +28 -0
- package/src/api-client/types.ts +33 -1
- package/src/api-client/wall-experiments.ts +100 -0
- package/src/api-client/wall-summary.ts +49 -0
- package/src/api-client/web-experiments.ts +136 -0
- package/src/app/pane-runtime/plugin-bindings.ts +5 -0
- package/src/app/pane-runtime/ticker-open-runtime.ts +8 -2
- package/src/app/runtime/update.ts +13 -1
- package/src/cli/commands/system.ts +2 -0
- package/src/cli/desktop-pane-shot.ts +3 -4
- package/src/cli/pane-functions/data.ts +78 -0
- package/src/cli/pane-functions/financial-ratio-shot.ts +160 -0
- package/src/cli/pane-functions/function-help.ts +220 -8
- package/src/cli/pane-functions/headless.ts +4 -0
- package/src/cli/pane-functions/screenshot-evidence.ts +21 -0
- package/src/cli/pane-functions/screenshot.ts +49 -5
- package/src/components/chart/composite/composite-chart.tsx +15 -2
- package/src/components/chart/composite/format.ts +24 -11
- package/src/components/chart/composite/rasterizer.ts +67 -16
- package/src/components/chart/composite/scene.ts +53 -2
- package/src/components/chart/composite/session-shading.ts +3 -2
- package/src/components/chart/composite/text-renderer.ts +52 -1
- package/src/components/chart/composite/time-axis.ts +100 -30
- package/src/components/chart/composite/tools.ts +2 -1
- package/src/components/chart/composite/types.ts +4 -0
- package/src/components/chart-table/index.ts +1 -0
- package/src/components/chart-table/layout.ts +15 -0
- package/src/components/command-bar/action-runtime.ts +1 -0
- package/src/components/command-bar/commands/direct/index.ts +32 -0
- package/src/components/command-bar/commands/registry.ts +7 -0
- package/src/components/command-bar/pane-templates/items.ts +11 -0
- package/src/components/command-bar/pane-templates/workflow.ts +19 -4
- package/src/components/command-bar/routes/root/command-items.ts +6 -0
- package/src/components/command-bar/routes/root/provider-search.ts +2 -0
- package/src/components/command-bar/routes/root/results.ts +64 -0
- package/src/components/command-bar/routes/root/runtime.ts +8 -0
- package/src/components/command-bar/routes/ticker-search/results.ts +89 -24
- package/src/components/command-bar/routes/ticker-search/route.ts +2 -0
- package/src/components/command-bar/surface/index.tsx +15 -0
- package/src/components/command-bar/view-model.ts +3 -0
- package/src/components/command-bar/workflow/collection-actions.ts +9 -7
- package/src/components/command-bar/workflow/ops.ts +19 -0
- package/src/components/command-bar/workflow/tickers.ts +5 -0
- package/src/components/company-logo.tsx +15 -2
- package/src/components/data-table/table-pane.ts +2 -1
- package/src/components/index.ts +1 -0
- package/src/components/layout/detached-pane-shell.tsx +12 -1
- package/src/components/layout/missing-pane.tsx +87 -23
- package/src/components/layout/pane/footer/index.tsx +64 -18
- package/src/components/layout/pane/footer/model.ts +85 -0
- package/src/components/layout/pane/sidebar.tsx +19 -1
- package/src/components/layout/shell/index.tsx +38 -28
- package/src/components/layout/shell/pane/management-shortcuts.ts +13 -1
- package/src/components/layout/status-bar.tsx +93 -28
- package/src/components/layout/status-widget-space.ts +72 -0
- package/src/components/onboarding/first-run-workspace.ts +8 -2
- package/src/components/onboarding/onboarding-wizard.tsx +35 -16
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +14 -5
- package/src/components/onboarding/wizard-model.ts +13 -5
- package/src/components/onboarding/wizard-positions.ts +56 -7
- package/src/components/price-performance.tsx +31 -34
- package/src/components/ui/data-table/index.tsx +11 -8
- package/src/components/ui/display.tsx +15 -6
- package/src/components/ui/fields.tsx +5 -0
- package/src/components/ui/figure.tsx +94 -0
- package/src/components/ui/header-case.ts +25 -3
- package/src/components/ui/icon.tsx +3 -1
- package/src/components/ui/message-composer.tsx +8 -1
- package/src/components/ui/query-bar.tsx +8 -1
- package/src/components/ui/ratio-bar.tsx +84 -0
- package/src/components/ui/toggle.tsx +3 -1
- package/src/components/use-scrollbox-layout.ts +17 -0
- package/src/core/state/app/layout.ts +35 -1
- package/src/core/state/app/state.ts +15 -0
- package/src/core/state/app/types.ts +1 -0
- package/src/data/config/store/normalize.ts +24 -0
- package/src/i18n/es.ts +2 -0
- package/src/i18n/ja.ts +2 -0
- package/src/i18n/ko.ts +2 -0
- package/src/i18n/zh-cn.ts +2 -0
- package/src/i18n/zh-tw.ts +2 -0
- package/src/layout-marketplace/pane-imagery.ts +1 -0
- package/src/market-data/coordinator/entries.ts +13 -0
- package/src/market-data/coordinator/financials.ts +33 -8
- package/src/market-data/market/freshness.ts +16 -5
- package/src/market-data/market/trading-sessions.ts +65 -1
- package/src/market-data/published-cn-sessions.ts +25 -0
- package/src/market-data/published-nse-sessions.ts +20 -0
- package/src/news/aggregator.ts +4 -0
- package/src/plugins/builtin/account-management/model.ts +6 -1
- package/src/plugins/builtin/account-management/pane.tsx +36 -25
- package/src/plugins/builtin/alerts/catalysts.ts +77 -0
- package/src/plugins/builtin/alerts/events-pane.tsx +3 -0
- package/src/plugins/builtin/alerts/events.ts +10 -4
- package/src/plugins/builtin/alerts/history-pane.tsx +1 -1
- package/src/plugins/builtin/alerts/history.ts +1 -0
- package/src/plugins/builtin/alerts/index.tsx +6 -0
- package/src/plugins/builtin/alerts/research-builder.ts +10 -3
- package/src/plugins/builtin/alerts/research-rules.ts +15 -1
- package/src/plugins/builtin/analytics/risk-client.ts +15 -3
- package/src/plugins/builtin/analytics/risk-headless.ts +123 -18
- package/src/plugins/builtin/attention/client.ts +80 -0
- package/src/plugins/builtin/attention/evidence.ts +30 -0
- package/src/plugins/builtin/attention/headless.ts +33 -0
- package/src/plugins/builtin/attention/index.tsx +18 -0
- package/src/plugins/builtin/attention/model.ts +59 -0
- package/src/plugins/builtin/attention/pane.tsx +211 -0
- package/src/plugins/builtin/attention/trending.tsx +59 -0
- package/src/plugins/builtin/awards/alerts.ts +32 -0
- package/src/plugins/builtin/awards/client.ts +82 -0
- package/src/plugins/builtin/awards/detail.tsx +107 -0
- package/src/plugins/builtin/awards/evidence.ts +36 -0
- package/src/plugins/builtin/awards/headless.ts +55 -0
- package/src/plugins/builtin/awards/index.tsx +20 -0
- package/src/plugins/builtin/awards/model.ts +42 -0
- package/src/plugins/builtin/awards/pane.tsx +263 -0
- package/src/plugins/builtin/catalysts/client.ts +28 -0
- package/src/plugins/builtin/catalysts/detail.tsx +88 -0
- package/src/plugins/builtin/catalysts/evidence.ts +47 -0
- package/src/plugins/builtin/catalysts/headless.ts +42 -0
- package/src/plugins/builtin/catalysts/history.ts +26 -0
- package/src/plugins/builtin/catalysts/index.tsx +29 -0
- package/src/plugins/builtin/catalysts/model.ts +135 -0
- package/src/plugins/builtin/catalysts/pane.tsx +159 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
- package/src/plugins/builtin/chart-composer/headless-schema.ts +6 -0
- package/src/plugins/builtin/chart-composer/index.tsx +8 -1
- package/src/plugins/builtin/chart-composer/pane.tsx +28 -1
- package/src/plugins/builtin/chat/attachments/desktop.tsx +610 -0
- package/src/plugins/builtin/chat/attachments/files.ts +45 -0
- package/src/plugins/builtin/chat/attachments/model.ts +164 -0
- package/src/plugins/builtin/chat/attachments/terminal.tsx +72 -0
- package/src/plugins/builtin/chat/attachments/transfer.ts +165 -0
- package/src/plugins/builtin/chat/content/channel-navigation.ts +13 -12
- package/src/plugins/builtin/chat/content/composer-runtime.ts +25 -4
- package/src/plugins/builtin/chat/content/composer.tsx +46 -4
- package/src/plugins/builtin/chat/content/footer.tsx +46 -8
- package/src/plugins/builtin/chat/content/index.tsx +263 -62
- package/src/plugins/builtin/chat/content/layout-metrics.ts +10 -2
- package/src/plugins/builtin/chat/content/open-targets.ts +17 -3
- package/src/plugins/builtin/chat/content/scroll.ts +7 -1
- package/src/plugins/builtin/chat/content/shortcuts.ts +36 -11
- package/src/plugins/builtin/chat/content/snapshot.ts +3 -0
- package/src/plugins/builtin/chat/content/transcript.tsx +17 -1
- package/src/plugins/builtin/chat/content/types.ts +7 -0
- package/src/plugins/builtin/chat/controller/attachments.ts +213 -0
- package/src/plugins/builtin/chat/controller/connections.ts +3 -1
- package/src/plugins/builtin/chat/controller/index.ts +114 -2
- package/src/plugins/builtin/chat/controller/lifecycle.ts +9 -0
- package/src/plugins/builtin/chat/controller/messages.ts +35 -2
- package/src/plugins/builtin/chat/controller/send.ts +128 -25
- package/src/plugins/builtin/chat/controller/state.ts +12 -1
- package/src/plugins/builtin/chat/controller/utils.ts +7 -5
- package/src/plugins/builtin/chat/controller/view.ts +2 -0
- package/src/plugins/builtin/chat/index.tsx +21 -0
- package/src/plugins/builtin/chat/layout.ts +9 -4
- package/src/plugins/builtin/chat/message/desktop.tsx +55 -20
- package/src/plugins/builtin/chat/message/header.tsx +30 -5
- package/src/plugins/builtin/chat/message/inline-tokens.tsx +9 -0
- package/src/plugins/builtin/chat/message/pending-send.ts +35 -0
- package/src/plugins/builtin/chat/message/profile-popover.tsx +5 -1
- package/src/plugins/builtin/chat/message/render-state.ts +12 -2
- package/src/plugins/builtin/chat/message/terminal.tsx +51 -4
- package/src/plugins/builtin/chat/message/types.ts +6 -1
- package/src/plugins/builtin/chat/pane-state.ts +59 -1
- package/src/plugins/builtin/chat/profile-popover.ts +50 -1
- package/src/plugins/builtin/chat/sidebar.tsx +8 -3
- package/src/plugins/builtin/chat/status-widget.tsx +29 -12
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +143 -0
- package/src/plugins/builtin/chat/unread-inbox.ts +91 -0
- package/src/plugins/builtin/clinical-trials/client.ts +237 -0
- package/src/plugins/builtin/clinical-trials/index.tsx +74 -0
- package/src/plugins/builtin/clinical-trials/pane.tsx +225 -0
- package/src/plugins/builtin/clinical-trials/types.ts +2 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +37 -6
- package/src/plugins/builtin/cloud/askg/executor.ts +59 -44
- package/src/plugins/builtin/cloud/askg/host.ts +8 -1
- package/src/plugins/builtin/cloud/askg/manifest.ts +29 -2
- package/src/plugins/builtin/cloud/askg/model.ts +47 -13
- package/src/plugins/builtin/cloud/askg/notes.ts +100 -0
- package/src/plugins/builtin/cloud/askg/pane.tsx +50 -14
- package/src/plugins/builtin/cloud/askg/protocol.ts +39 -1
- package/src/plugins/builtin/cloud/askg/result-budget.ts +156 -0
- package/src/plugins/builtin/cloud/askg/user-data.ts +89 -0
- package/src/plugins/builtin/cloud/auth-actions.tsx +55 -31
- package/src/plugins/builtin/cloud/plugin.tsx +4 -1
- package/src/plugins/builtin/cloud/team/pane-sections.tsx +10 -1
- package/src/plugins/builtin/cloud/team/pane.tsx +3 -2
- package/src/plugins/builtin/cloud/thesis/board-pane.tsx +2 -2
- package/src/plugins/builtin/cloud/thesis/detail.tsx +1 -1
- package/src/plugins/builtin/cloud/thesis/ticker-tab.tsx +2 -2
- package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +202 -0
- package/src/plugins/builtin/cloud/upgrade-command.ts +1 -1
- package/src/plugins/builtin/cloud/upgrade-dialog.tsx +29 -3
- package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +1 -1
- package/src/plugins/builtin/cloud-tweets/table.tsx +1 -1
- package/src/plugins/builtin/comment-letters/client.ts +342 -0
- package/src/plugins/builtin/comment-letters/index.tsx +74 -0
- package/src/plugins/builtin/comment-letters/pane.tsx +295 -0
- package/src/plugins/builtin/comment-letters/types.ts +43 -0
- package/src/plugins/builtin/company-attention/app-rank.tsx +79 -0
- package/src/plugins/builtin/company-attention/client.ts +69 -0
- package/src/plugins/builtin/company-attention/evidence.ts +45 -0
- package/src/plugins/builtin/company-attention/headless.ts +47 -0
- package/src/plugins/builtin/company-attention/index.tsx +30 -0
- package/src/plugins/builtin/company-attention/model.ts +129 -0
- package/src/plugins/builtin/company-attention/pane.tsx +183 -0
- package/src/plugins/builtin/company-kpis/client.ts +92 -0
- package/src/plugins/builtin/company-kpis/headless.ts +32 -0
- package/src/plugins/builtin/company-kpis/index.tsx +26 -0
- package/src/plugins/builtin/company-kpis/model.ts +124 -0
- package/src/plugins/builtin/company-kpis/pane.tsx +226 -0
- package/src/plugins/builtin/company-kpis/related.ts +11 -0
- package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +44 -0
- package/src/plugins/builtin/composite-plugins.ts +34 -0
- package/src/plugins/builtin/correlation/history.ts +4 -1
- package/src/plugins/builtin/correlation/index.tsx +139 -80
- package/src/plugins/builtin/correlation/matrix/pair-chart.tsx +48 -0
- package/src/plugins/builtin/correlation/matrix/selection.ts +77 -0
- package/src/plugins/builtin/correlation/relationship/model.ts +2 -2
- package/src/plugins/builtin/cpi/client.ts +66 -0
- package/src/plugins/builtin/cpi/headless.ts +78 -0
- package/src/plugins/builtin/cpi/index.tsx +31 -0
- package/src/plugins/builtin/cpi/model.ts +198 -0
- package/src/plugins/builtin/cpi/pane.tsx +157 -0
- package/src/plugins/builtin/credit-documents/client.ts +99 -0
- package/src/plugins/builtin/credit-documents/detail.tsx +109 -0
- package/src/plugins/builtin/credit-documents/evidence.ts +64 -0
- package/src/plugins/builtin/credit-documents/gauge.tsx +55 -0
- package/src/plugins/builtin/credit-documents/headless.ts +40 -0
- package/src/plugins/builtin/credit-documents/index.tsx +19 -0
- package/src/plugins/builtin/credit-documents/model.ts +336 -0
- package/src/plugins/builtin/credit-documents/pane.tsx +185 -0
- package/src/plugins/builtin/custom-view/view-spec.ts +14 -2
- package/src/plugins/builtin/debt-maturities/pane.tsx +1 -0
- package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
- package/src/plugins/builtin/doe/client.ts +73 -0
- package/src/plugins/builtin/doe/headless.ts +93 -0
- package/src/plugins/builtin/doe/index.tsx +41 -0
- package/src/plugins/builtin/doe/model.ts +215 -0
- package/src/plugins/builtin/doe/pane.tsx +148 -0
- package/src/plugins/builtin/doe/range-bar.tsx +45 -0
- package/src/plugins/builtin/earnings/board.tsx +3 -0
- package/src/plugins/builtin/earnings/index.tsx +2 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +7 -1
- package/src/plugins/builtin/earnings-calls/transcript-view.tsx +7 -7
- package/src/plugins/builtin/earnings-ripple/client.ts +60 -0
- package/src/plugins/builtin/earnings-ripple/headless.ts +35 -0
- package/src/plugins/builtin/earnings-ripple/index.tsx +35 -0
- package/src/plugins/builtin/earnings-ripple/model.ts +98 -0
- package/src/plugins/builtin/earnings-ripple/pane.tsx +103 -0
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/criterion-editor.tsx +1 -1
- package/src/plugins/builtin/equity-screener/model.ts +74 -9
- package/src/plugins/builtin/equity-screener/pane.tsx +2 -0
- package/src/plugins/builtin/estimate-revisions/pane.tsx +3 -0
- package/src/plugins/builtin/executives/pane.tsx +7 -5
- package/src/plugins/builtin/exposure/charts.tsx +66 -0
- package/src/plugins/builtin/exposure/client.ts +53 -0
- package/src/plugins/builtin/exposure/drivers.ts +29 -0
- package/src/plugins/builtin/exposure/editor.tsx +61 -0
- package/src/plugins/builtin/exposure/evidence.ts +39 -0
- package/src/plugins/builtin/exposure/headless.ts +51 -0
- package/src/plugins/builtin/exposure/holdings.ts +56 -0
- package/src/plugins/builtin/exposure/index.tsx +13 -0
- package/src/plugins/builtin/exposure/model.ts +104 -0
- package/src/plugins/builtin/exposure/pane.tsx +247 -0
- package/src/plugins/builtin/fear-greed/charts.tsx +1 -1
- package/src/plugins/builtin/fear-greed/pane.tsx +2 -9
- package/src/plugins/builtin/filing-events/pane.tsx +2 -1
- package/src/plugins/builtin/gpu/board.tsx +163 -0
- package/src/plugins/builtin/gpu/changes.tsx +94 -0
- package/src/plugins/builtin/gpu/client.ts +130 -0
- package/src/plugins/builtin/gpu/equities.tsx +112 -0
- package/src/plugins/builtin/gpu/headless.ts +67 -0
- package/src/plugins/builtin/gpu/history.tsx +140 -0
- package/src/plugins/builtin/gpu/index.tsx +18 -0
- package/src/plugins/builtin/gpu/ladder.tsx +127 -0
- package/src/plugins/builtin/gpu/model.ts +317 -0
- package/src/plugins/builtin/gpu/pane.tsx +63 -0
- package/src/plugins/builtin/help/index.tsx +19 -2
- package/src/plugins/builtin/holders/pane.tsx +5 -9
- package/src/plugins/builtin/insider/detail.tsx +119 -0
- package/src/plugins/builtin/insider/display.ts +221 -0
- package/src/plugins/builtin/insider/headless.ts +24 -3
- package/src/plugins/builtin/insider/index.tsx +206 -108
- package/src/plugins/builtin/insider/insider-data.ts +31 -3
- package/src/plugins/builtin/insider/model.ts +53 -4
- package/src/plugins/builtin/insider/table-model.ts +226 -0
- package/src/plugins/builtin/insider/table.tsx +79 -0
- package/src/plugins/builtin/iv-history/screen-pane.tsx +1 -1
- package/src/plugins/builtin/jobs/pane.tsx +14 -7
- package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
- package/src/plugins/builtin/macro-day/client.ts +50 -0
- package/src/plugins/builtin/macro-day/headless.ts +51 -0
- package/src/plugins/builtin/macro-day/index.tsx +21 -0
- package/src/plugins/builtin/macro-day/model.ts +137 -0
- package/src/plugins/builtin/macro-day/pane.tsx +118 -0
- package/src/plugins/builtin/macro-day/releases.ts +58 -0
- package/src/plugins/builtin/market-halts/model.ts +30 -17
- package/src/plugins/builtin/market-halts/pane.tsx +15 -21
- package/src/plugins/builtin/market-heatmap/data.ts +1 -1
- package/src/plugins/builtin/market-heatmap/index.tsx +198 -50
- package/src/plugins/builtin/market-heatmap/portfolio.ts +220 -0
- package/src/plugins/builtin/market-movers/index.tsx +1 -0
- package/src/plugins/builtin/market-movers/session-body.tsx +5 -2
- package/src/plugins/builtin/mna/distress/client.ts +326 -0
- package/src/plugins/builtin/mna/distress/detail.tsx +247 -0
- package/src/plugins/builtin/mna/distress/headless.ts +257 -0
- package/src/plugins/builtin/mna/distress/model.ts +427 -0
- package/src/plugins/builtin/mna/distress/pane.tsx +606 -0
- package/src/plugins/builtin/mna/index.tsx +15 -0
- package/src/plugins/builtin/mna/pane.tsx +42 -1
- package/src/plugins/builtin/news/index.tsx +62 -16
- package/src/plugins/builtin/news/wire/filter-articles.ts +54 -0
- package/src/plugins/builtin/news/wire/index.tsx +37 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +29 -13
- package/src/plugins/builtin/news/wire/mutes.ts +253 -0
- package/src/plugins/builtin/news/wire/news/command-bar-search.ts +117 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +4 -0
- package/src/plugins/builtin/news/wire/news/list-search.ts +34 -0
- package/src/plugins/builtin/news/wire/news/pop-out.tsx +158 -0
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +64 -19
- package/src/plugins/builtin/openfda/client.ts +300 -0
- package/src/plugins/builtin/openfda/index.tsx +71 -0
- package/src/plugins/builtin/openfda/pane.tsx +382 -0
- package/src/plugins/builtin/openfda/types.ts +76 -0
- package/src/plugins/builtin/options/footer.ts +1 -0
- package/src/plugins/builtin/options/view.tsx +1 -1
- package/src/plugins/builtin/options-calculator/pane.tsx +0 -1
- package/src/plugins/builtin/options-positioning/index.tsx +4 -2
- package/src/plugins/builtin/options-positioning/pane.tsx +1 -1
- package/src/plugins/builtin/pe-band/client.ts +61 -0
- package/src/plugins/builtin/pe-band/headless.ts +51 -0
- package/src/plugins/builtin/pe-band/index.tsx +20 -0
- package/src/plugins/builtin/pe-band/model.ts +267 -0
- package/src/plugins/builtin/pe-band/pane.tsx +159 -0
- package/src/plugins/builtin/perps/client.ts +66 -0
- package/src/plugins/builtin/perps/equity-row.tsx +28 -0
- package/src/plugins/builtin/perps/headless.ts +34 -0
- package/src/plugins/builtin/perps/index.tsx +14 -0
- package/src/plugins/builtin/perps/model.ts +58 -0
- package/src/plugins/builtin/perps/pane.tsx +185 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +0 -1
- package/src/plugins/builtin/portfolio-list/cli/render.ts +177 -87
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +6 -20
- package/src/plugins/builtin/portfolio-list/command-bar.ts +6 -2
- package/src/plugins/builtin/portfolio-list/headless.ts +237 -0
- package/src/plugins/builtin/portfolio-list/index.tsx +2 -0
- package/src/plugins/builtin/portfolio-list/mutations.ts +70 -1
- package/src/plugins/builtin/portfolio-list/pane/data.ts +17 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +88 -5
- package/src/plugins/builtin/portfolio-list/pane/remove-ticker.ts +85 -0
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +18 -12
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +6 -0
- package/src/plugins/builtin/power/client.ts +100 -0
- package/src/plugins/builtin/power/detail.tsx +82 -0
- package/src/plugins/builtin/power/evidence.ts +56 -0
- package/src/plugins/builtin/power/generation.tsx +37 -0
- package/src/plugins/builtin/power/headless.ts +43 -0
- package/src/plugins/builtin/power/index.tsx +16 -0
- package/src/plugins/builtin/power/model.ts +89 -0
- package/src/plugins/builtin/power/pane.tsx +209 -0
- package/src/plugins/builtin/power/query.ts +25 -0
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -3
- package/src/plugins/builtin/research/analyst-pane.tsx +1 -1
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +13 -4
- package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +11 -9
- package/src/plugins/builtin/research-search/pane.tsx +21 -7
- package/src/plugins/builtin/revenue-breakdown/pane.tsx +1 -1
- package/src/plugins/builtin/reverse-dcf/client.ts +35 -0
- package/src/plugins/builtin/reverse-dcf/headless.ts +44 -0
- package/src/plugins/builtin/reverse-dcf/index.tsx +20 -0
- package/src/plugins/builtin/reverse-dcf/model.ts +87 -0
- package/src/plugins/builtin/reverse-dcf/pane.tsx +107 -0
- package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
- package/src/plugins/builtin/scanner/denied.tsx +4 -2
- package/src/plugins/builtin/scanner/feed.ts +1 -0
- package/src/plugins/builtin/scanner/flow-pane.tsx +3 -2
- package/src/plugins/builtin/scanner/hilo-pane.tsx +1 -1
- package/src/plugins/builtin/seasonality/client.ts +4 -1
- package/src/plugins/builtin/sec/command-bar-search.ts +217 -0
- package/src/plugins/builtin/sec/forms.ts +54 -0
- package/src/plugins/builtin/sec/headless.ts +31 -5
- package/src/plugins/builtin/sec/index.tsx +67 -11
- package/src/plugins/builtin/sec/model.ts +35 -5
- package/src/plugins/builtin/sectors/headless.ts +1 -0
- package/src/plugins/builtin/sectors/index.tsx +72 -26
- package/src/plugins/builtin/shared/cloud-upgrade.ts +143 -39
- package/src/plugins/builtin/shared/pro-feature-wall.tsx +8 -4
- package/src/plugins/builtin/shared/research-cells.tsx +107 -0
- package/src/plugins/builtin/shared/series-list-detail.tsx +5 -1
- package/src/plugins/builtin/shared/ticker-request.ts +1 -1
- package/src/plugins/builtin/shared/wall-teaser-catalog.ts +80 -0
- package/src/plugins/builtin/shared/wall-teaser.tsx +192 -0
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +43 -0
- package/src/plugins/builtin/short-interest/index.tsx +31 -0
- package/src/plugins/builtin/short-interest/watch-client.ts +70 -0
- package/src/plugins/builtin/short-interest/watch-headless.ts +49 -0
- package/src/plugins/builtin/short-interest/watch-model.ts +185 -0
- package/src/plugins/builtin/short-interest/watch-pane.tsx +169 -0
- package/src/plugins/builtin/short-volume/pane.tsx +1 -1
- package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
- package/src/plugins/builtin/supply-chain/client.ts +104 -0
- package/src/plugins/builtin/supply-chain/disclosure-quote.tsx +17 -0
- package/src/plugins/builtin/supply-chain/evidence.ts +88 -0
- package/src/plugins/builtin/supply-chain/flow-layout.ts +133 -0
- package/src/plugins/builtin/supply-chain/flow.tsx +323 -0
- package/src/plugins/builtin/supply-chain/graph-client.ts +83 -0
- package/src/plugins/builtin/supply-chain/graph-evidence.ts +25 -0
- package/src/plugins/builtin/supply-chain/graph-model.ts +45 -0
- package/src/plugins/builtin/supply-chain/graph-pane.tsx +218 -0
- package/src/plugins/builtin/supply-chain/graph.tsx +163 -0
- package/src/plugins/builtin/supply-chain/headless.ts +105 -0
- package/src/plugins/builtin/supply-chain/index.tsx +17 -0
- package/src/plugins/builtin/supply-chain/model.ts +145 -0
- package/src/plugins/builtin/supply-chain/pane.tsx +276 -0
- package/src/plugins/builtin/supply-chain/row-evidence.tsx +96 -0
- package/src/plugins/builtin/supply-chain/trust.ts +34 -0
- package/src/plugins/builtin/themes/client.ts +39 -0
- package/src/plugins/builtin/themes/headless.ts +38 -0
- package/src/plugins/builtin/themes/model.ts +92 -0
- package/src/plugins/builtin/themes/pane.tsx +125 -0
- package/src/plugins/builtin/thirteenf/pane.tsx +6 -15
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +5 -6
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +201 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +327 -0
- package/src/plugins/builtin/ticker-detail/financials/period-end-history.ts +5 -2
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +60 -12
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/headless.ts +7 -2
- package/src/plugins/builtin/ticker-detail/index.tsx +22 -0
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +53 -30
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +2 -0
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +112 -47
- package/src/plugins/builtin/ticker-detail/pane.tsx +50 -6
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +21 -42
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +3 -0
- package/src/plugins/builtin/time-sales/pane.tsx +1 -1
- package/src/plugins/builtin/world-indices/indices.ts +1 -0
- package/src/plugins/catalog-browser.ts +40 -0
- package/src/plugins/catalog-manifest.ts +3 -0
- package/src/plugins/catalog-ui.ts +6 -0
- package/src/plugins/current-target.ts +0 -9
- package/src/public/__snapshots__/public-api.test.ts.snap +1 -0
- package/src/renderers/browser/cloud-transport.ts +66 -1
- package/src/renderers/browser/main.tsx +2 -0
- package/src/renderers/dom/data-table/row.tsx +2 -0
- package/src/renderers/dom/desktop/controls.tsx +38 -25
- package/src/renderers/dom/desktop/icons.tsx +8 -0
- package/src/renderers/dom/host/chart-surface.tsx +36 -2
- package/src/renderers/dom/host/focus-scope.ts +11 -1
- package/src/renderers/dom/styles.css +28 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +3 -2
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/http-fetch.ts +2 -0
- package/src/renderers/electrobun/view/main.tsx +3 -1
- package/src/renderers/electrobun/view/rpc-timeout.ts +59 -0
- package/src/renderers/opentui/start.tsx +3 -0
- package/src/shares/api.ts +3 -1
- package/src/sources/gloomberb-cloud/index.ts +47 -13
- package/src/sources/history-result.ts +1 -0
- package/src/sources/history-retention.ts +16 -0
- package/src/sources/provider-router/history.ts +4 -2
- package/src/sources/sec-edgar/identity.ts +36 -0
- package/src/sources/sec-edgar.ts +1 -34
- package/src/state/app/context/index.tsx +8 -0
- package/src/sync/core-contributors.ts +20 -2
- package/src/telemetry/attention-counts.ts +164 -0
- package/src/telemetry/live-config.ts +7 -0
- package/src/test-support/rpc-loopback.ts +7 -3
- package/src/test-support/shell-pane-keys.tsx +34 -0
- package/src/tickers/instrument-kind.ts +12 -6
- package/src/tickers/open-target.ts +1 -1
- package/src/tickers/search/asset-classes.ts +91 -0
- package/src/tickers/search/index.ts +62 -15
- package/src/tickers/search/ranking.ts +22 -3
- package/src/tickers/search/upsert.ts +4 -1
- package/src/time-series/live-bars.ts +8 -3
- package/src/time-series/resolve.ts +224 -17
- package/src/time-series/spec.ts +1 -0
- package/src/time-series/types.ts +9 -1
- package/src/types/config.ts +23 -1
- package/src/types/data-provider.ts +2 -0
- package/src/types/headless.ts +17 -0
- package/src/types/price-history.ts +2 -0
- package/src/ui/host.tsx +6 -0
- package/src/utils/double-escape-close.ts +20 -0
- package/src/utils/exchanges.ts +1 -0
- package/src/utils/html-entities.ts +15 -0
- package/src/utils/http-proxy-response.ts +44 -7
- package/src/utils/plugin-proxy-hosts.json +3 -0
- package/src/utils/price-history.ts +35 -1
- package/src/utils/sec.ts +21 -2
- package/src/utils/text-wrap.ts +13 -8
- package/src/utils/zoned-date-time.ts +67 -0
|
@@ -5,39 +5,61 @@ import {
|
|
|
5
5
|
useSecFilingsQuery,
|
|
6
6
|
} from "../../../market-data/hooks";
|
|
7
7
|
import { instrumentFromTicker } from "../../../market-data/request-types";
|
|
8
|
-
import type
|
|
9
|
-
import {
|
|
8
|
+
import { Box, type ScrollBoxRenderable } from "../../../ui";
|
|
9
|
+
import {
|
|
10
|
+
DataTableStackView,
|
|
11
|
+
EmptyState,
|
|
12
|
+
PaneLinkMenu,
|
|
13
|
+
QueryBar,
|
|
14
|
+
Spinner,
|
|
15
|
+
StatGrid,
|
|
16
|
+
sortStackItems,
|
|
17
|
+
useExternalLinkFooter,
|
|
18
|
+
usePaneFooter,
|
|
19
|
+
usePaneNoticeFooter,
|
|
20
|
+
useTableLoadMore,
|
|
21
|
+
type StackSortPreference,
|
|
22
|
+
type StatItem,
|
|
23
|
+
} from "../../../components";
|
|
10
24
|
import { useDebouncedPluginPaneState, usePluginPaneState } from "../../runtime";
|
|
11
25
|
import { isUsEquityTicker } from "../../../utils/sec";
|
|
12
26
|
import { formatCompact } from "../../../utils/format";
|
|
27
|
+
import { nextHeaderSort } from "../../../utils/sort-values";
|
|
28
|
+
import { formatShortDate } from "../../../utils/datetime-format";
|
|
13
29
|
import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
|
|
14
30
|
import { isCloudSessionRequired, useResearchCloudSession } from "../shared/research-cloud-session";
|
|
15
31
|
import { SignInWall } from "../cloud/auth-actions";
|
|
16
32
|
import { getSharedMarketDataCoordinator } from "../../../market-data/coordinator";
|
|
17
|
-
import {
|
|
18
|
-
buildInsiderTransactionDetailBody,
|
|
19
|
-
buildInsiderTransactionTitle,
|
|
20
|
-
formatFilingFormLabel,
|
|
21
|
-
formatFilingShortDate,
|
|
22
|
-
renderFilingNotice,
|
|
23
|
-
} from "../sec/filing-display";
|
|
33
|
+
import { formatFilingFormLabel, renderFilingNotice } from "../sec/filing-display";
|
|
24
34
|
import { useSecFilingContentCache } from "../sec/filing-content";
|
|
25
35
|
import {
|
|
36
|
+
DEFAULT_INSIDER_TYPE_FILTER,
|
|
26
37
|
buildInsiderSummaryFigures,
|
|
27
|
-
buildInsiderDisclosureText,
|
|
28
38
|
insiderSummaryCutoff,
|
|
39
|
+
isInsiderTypeFilter,
|
|
29
40
|
matchesInsiderOwner,
|
|
30
|
-
|
|
31
|
-
isInsiderDisclosureOnly,
|
|
41
|
+
matchesInsiderTypeFilter,
|
|
32
42
|
insiderReportedName,
|
|
33
43
|
parseInsiderFiling,
|
|
34
44
|
type InsiderSummaryFigures,
|
|
45
|
+
type InsiderTypeFilter,
|
|
35
46
|
type ParsedInsiderFiling as ParsedFiling,
|
|
36
47
|
} from "./model";
|
|
48
|
+
import { formatInsiderName } from "./display";
|
|
37
49
|
import { insiderHeadless } from "./headless";
|
|
38
50
|
import { isInsiderForm } from "./insider-data";
|
|
39
|
-
import {
|
|
51
|
+
import { relevantInsiderAmendments } from "./amendments";
|
|
40
52
|
import { usePaneTickerIdentity } from "../../../state/hooks/pane-ticker";
|
|
53
|
+
import {
|
|
54
|
+
buildInsiderColumns,
|
|
55
|
+
buildInsiderTableRows,
|
|
56
|
+
compareInsiderRows,
|
|
57
|
+
type InsiderColumn,
|
|
58
|
+
type InsiderColumnId,
|
|
59
|
+
type InsiderTableRow,
|
|
60
|
+
} from "./table-model";
|
|
61
|
+
import { renderInsiderCell } from "./table";
|
|
62
|
+
import { InsiderDetail, insiderDetailTitle } from "./detail";
|
|
41
63
|
|
|
42
64
|
|
|
43
65
|
const FORM4_PAGE_SIZE = 20;
|
|
@@ -47,20 +69,37 @@ const INSIDER_WINDOW_CAP = 120;
|
|
|
47
69
|
// Recent EDGAR dumps cap at 1,000 mixed forms. Older archives are fetched
|
|
48
70
|
// until this many filings or company history ends.
|
|
49
71
|
const SEC_FILING_SCAN_LIMIT = 20_000;
|
|
72
|
+
// A narrowed list too short to scroll cannot ask for more, so older filings
|
|
73
|
+
// load on their own until it fills a page, as far back as a year and no more
|
|
74
|
+
// filings than a busy 90-day window already loads.
|
|
75
|
+
const AUTO_FILL_DAYS = 365;
|
|
76
|
+
const AUTO_FILL_FILING_CAP = INSIDER_WINDOW_CAP;
|
|
77
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
78
|
+
/** Narrower panes pick the Type filter from a menu instead of showing every choice. */
|
|
79
|
+
const TYPE_FILTER_INLINE_WIDTH = 80;
|
|
50
80
|
/** The insider filter's value for every reporting owner. */
|
|
51
81
|
const ALL_INSIDERS = "*";
|
|
52
82
|
|
|
83
|
+
const TYPE_FILTER_LABELS: Record<InsiderTypeFilter, string> = {
|
|
84
|
+
all: "All",
|
|
85
|
+
trades: "Buys and sells",
|
|
86
|
+
other: "Other",
|
|
87
|
+
};
|
|
88
|
+
|
|
53
89
|
/**
|
|
54
90
|
* The 90-day buy and sale totals as figures: one cell per side, and per
|
|
55
|
-
* security when there are several. A side with no trades says so.
|
|
91
|
+
* security when there are several. A side with no trades says so. Buys read
|
|
92
|
+
* in the gain colour and sales in the loss colour, as in the table; when the
|
|
93
|
+
* Type filter hides open-market trades the figures dim and say so.
|
|
56
94
|
*/
|
|
57
|
-
function summaryItems(figures: InsiderSummaryFigures): StatItem[] {
|
|
95
|
+
function summaryItems(figures: InsiderSummaryFigures, typeFilter: InsiderTypeFilter): StatItem[] {
|
|
58
96
|
const securities = new Set(figures.totals.map((total) => total.security));
|
|
97
|
+
const hidden = typeFilter === "other";
|
|
59
98
|
const items: StatItem[] = [];
|
|
60
99
|
for (const side of ["P", "S"] as const) {
|
|
61
100
|
const label = side === "P" ? "90d buys" : "90d sales";
|
|
62
101
|
const totals = figures.totals.filter((total) => total.side === side);
|
|
63
|
-
if (totals.length === 0) items.push({ id: side, label, value: "None" });
|
|
102
|
+
if (totals.length === 0) items.push({ id: side, label, value: "None", tone: "muted" });
|
|
64
103
|
for (const total of totals) {
|
|
65
104
|
items.push({
|
|
66
105
|
id: `${total.security}:${side}`,
|
|
@@ -69,70 +108,28 @@ function summaryItems(figures: InsiderSummaryFigures): StatItem[] {
|
|
|
69
108
|
detail: [
|
|
70
109
|
total.unreconciled ? "amended" : total.knownValue ? `$${formatCompact(total.value)}` : "value unavailable",
|
|
71
110
|
securities.size > 1 ? total.security : null,
|
|
111
|
+
hidden ? "not in this view" : null,
|
|
72
112
|
].filter(Boolean).join(" · "),
|
|
73
|
-
tone: total.unreconciled ? "muted" :
|
|
113
|
+
tone: total.unreconciled || hidden ? "muted" : side === "P" ? "positive" : "negative",
|
|
74
114
|
});
|
|
75
115
|
}
|
|
76
116
|
}
|
|
77
117
|
return items;
|
|
78
118
|
}
|
|
79
119
|
|
|
80
|
-
function toFeedItems(parsed: ParsedFiling[]): FeedDataTableItem[] {
|
|
81
|
-
return parsed.map((entry) => {
|
|
82
|
-
const { filing, transaction, isLoading } = entry;
|
|
83
|
-
const id = insiderTransactionId(entry);
|
|
84
|
-
const amendment = isAmendedInsiderFiling(entry);
|
|
85
|
-
const disclosureText = buildInsiderDisclosureText(entry);
|
|
86
|
-
const filingMeta = [
|
|
87
|
-
`Filed ${formatFilingShortDate(filing.filingDate)}`,
|
|
88
|
-
`Accession ${filing.accessionNumber}`,
|
|
89
|
-
formatFilingFormLabel(filing.form),
|
|
90
|
-
...(amendment ? ["Unreconciled amendment"] : []),
|
|
91
|
-
...(entry.disclosure?.originalFilingDate ? [`Original filed ${entry.disclosure.originalFilingDate}`] : []),
|
|
92
|
-
];
|
|
93
|
-
|
|
94
|
-
if (!transaction) {
|
|
95
|
-
const disclosureOnly = isInsiderDisclosureOnly(entry);
|
|
96
|
-
const title = isLoading ? `Loading ${formatFilingFormLabel(filing.form)} filing...`
|
|
97
|
-
: disclosureOnly ? `${amendment ? "Form 4/A" : "Form 4"} disclosure` : "Form 4 transaction unavailable";
|
|
98
|
-
return {
|
|
99
|
-
id,
|
|
100
|
-
eyebrow: insiderReportedName(entry) ?? formatFilingFormLabel(filing.form),
|
|
101
|
-
title,
|
|
102
|
-
timestamp: filing.filingDate,
|
|
103
|
-
detailTitle: insiderReportedName(entry) ?? title,
|
|
104
|
-
detailMeta: filingMeta,
|
|
105
|
-
detailBody: isLoading
|
|
106
|
-
? "Loading filing content..."
|
|
107
|
-
: disclosureText || (disclosureOnly ? "No transaction lines reported." : "This Form 4 filing could not be parsed into a transaction summary."),
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return {
|
|
112
|
-
id,
|
|
113
|
-
eyebrow: transaction.reportedName,
|
|
114
|
-
title: `${amendment ? "4/A · " : ""}${buildInsiderTransactionTitle(transaction)}`,
|
|
115
|
-
timestamp: transaction.filingDate ?? filing.filingDate,
|
|
116
|
-
detailTitle: transaction.reportedName,
|
|
117
|
-
detailMeta: [
|
|
118
|
-
...(transaction.title ? [transaction.title] : []),
|
|
119
|
-
...filingMeta,
|
|
120
|
-
],
|
|
121
|
-
detailBody: [disclosureText, buildInsiderTransactionDetailBody(transaction)].filter(Boolean).join("\n\n"),
|
|
122
|
-
};
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
120
|
function InsiderView({ width, height, focused }: { width: number; height: number; focused: boolean }) {
|
|
127
121
|
const { ticker } = usePaneTickerIdentity();
|
|
128
122
|
const tickerKey = ticker?.metadata.ticker ?? "none";
|
|
129
|
-
const [
|
|
123
|
+
const [selectedId, setSelectedId] = usePluginPaneState<string | null>(`insider:selectedId:${tickerKey}`, null);
|
|
130
124
|
const [nameFilter, setNameFilter] = usePluginPaneState<string | null>(`insider:nameFilter:${tickerKey}`, null);
|
|
125
|
+
const [storedTypeFilter, setTypeFilter] = usePluginPaneState<InsiderTypeFilter>("insider:typeFilter", DEFAULT_INSIDER_TYPE_FILTER);
|
|
126
|
+
const typeFilter = isInsiderTypeFilter(storedTypeFilter) ? storedTypeFilter : DEFAULT_INSIDER_TYPE_FILTER;
|
|
131
127
|
const [openItemId, setOpenItemIdState] = useDebouncedPluginPaneState<string | null>(`insider:openItemId:${tickerKey}`, null);
|
|
132
128
|
const setOpenItemId = useCallback(
|
|
133
129
|
(itemId: string | null) => setOpenItemIdState(itemId, { immediate: true }),
|
|
134
130
|
[setOpenItemIdState],
|
|
135
131
|
);
|
|
132
|
+
const [sort, setSort] = useState<StackSortPreference<InsiderColumnId>>({ columnId: "date", direction: "desc" });
|
|
136
133
|
const eligibleTicker = isUsEquityTicker(ticker);
|
|
137
134
|
const instrument = instrumentFromTicker(ticker, ticker?.metadata.ticker ?? null);
|
|
138
135
|
|
|
@@ -156,10 +153,10 @@ function InsiderView({ width, height, focused }: { width: number; height: number
|
|
|
156
153
|
[form4Filings, shownCount],
|
|
157
154
|
);
|
|
158
155
|
const scrollRef = useRef<ScrollBoxRenderable | null>(null);
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
shownCount < form4Filings.length,
|
|
156
|
+
const detailScrollRef = useRef<ScrollBoxRenderable | null>(null);
|
|
157
|
+
const loadNextPage = useCallback(
|
|
162
158
|
() => setVisibleCount((current) => Math.min(form4Filings.length, Math.max(current, windowShown) + FORM4_PAGE_SIZE)),
|
|
159
|
+
[form4Filings.length, windowShown],
|
|
163
160
|
);
|
|
164
161
|
useEffect(() => {
|
|
165
162
|
setVisibleCount(FORM4_PAGE_SIZE);
|
|
@@ -195,13 +192,45 @@ function InsiderView({ width, height, focused }: { width: number; height: number
|
|
|
195
192
|
return parseInsiderFiling(filing, xml, !hasContent);
|
|
196
193
|
}), [contentMap, visibleForm4Filings]);
|
|
197
194
|
|
|
198
|
-
//
|
|
195
|
+
// The insider filter narrows the totals and the table; the Type filter only the table.
|
|
199
196
|
const parsed = useMemo(() => (
|
|
200
197
|
nameFilter
|
|
201
198
|
? allParsed.filter((entry) => matchesInsiderOwner(entry, nameFilter))
|
|
202
199
|
: allParsed
|
|
203
200
|
), [allParsed, nameFilter]);
|
|
204
|
-
const
|
|
201
|
+
const typeCounts = useMemo(() => {
|
|
202
|
+
const counts: Record<InsiderTypeFilter, number> = { all: 0, trades: 0, other: 0 };
|
|
203
|
+
for (const entry of parsed) {
|
|
204
|
+
if (entry.isLoading) continue;
|
|
205
|
+
counts.all += 1;
|
|
206
|
+
if (matchesInsiderTypeFilter(entry, "trades")) counts.trades += 1;
|
|
207
|
+
if (matchesInsiderTypeFilter(entry, "other")) counts.other += 1;
|
|
208
|
+
}
|
|
209
|
+
return counts;
|
|
210
|
+
}, [parsed]);
|
|
211
|
+
const shown = useMemo(
|
|
212
|
+
() => parsed.filter((entry) => matchesInsiderTypeFilter(entry, typeFilter)),
|
|
213
|
+
[parsed, typeFilter],
|
|
214
|
+
);
|
|
215
|
+
const rows = useMemo(() => buildInsiderTableRows(shown), [shown]);
|
|
216
|
+
const sortedRows = useMemo(() => {
|
|
217
|
+
const order = new Map(rows.map((row, index) => [row.id, index]));
|
|
218
|
+
return sortStackItems(rows, sort, compareInsiderRows, (a, b) => order.get(a.id)! - order.get(b.id)!);
|
|
219
|
+
}, [rows, sort]);
|
|
220
|
+
const columns = useMemo(() => buildInsiderColumns(width, rows), [rows, width]);
|
|
221
|
+
|
|
222
|
+
// A narrowed list that cannot scroll cannot ask for older filings, so they
|
|
223
|
+
// load here until it fills a page, back to a year at most.
|
|
224
|
+
const oldestShown = visibleForm4Filings[visibleForm4Filings.length - 1]?.filingDate;
|
|
225
|
+
const oldestShownTime = oldestShown ? new Date(oldestShown).getTime() : Number.NaN;
|
|
226
|
+
const wantsOlderFilings = (typeFilter !== "all" || nameFilter != null)
|
|
227
|
+
&& shown.length < Math.max(FORM4_PAGE_SIZE, height)
|
|
228
|
+
&& shownCount < Math.min(form4Filings.length, AUTO_FILL_FILING_CAP)
|
|
229
|
+
&& oldestShownTime >= Date.now() - AUTO_FILL_DAYS * DAY_MS;
|
|
230
|
+
useEffect(() => {
|
|
231
|
+
if (wantsOlderFilings && pendingCount === 0) loadNextPage();
|
|
232
|
+
}, [loadNextPage, pendingCount, shownCount, wantsOlderFilings]);
|
|
233
|
+
|
|
205
234
|
const summary = useMemo(() => buildInsiderSummaryFigures(parsed, Date.now(), allParsed), [parsed, allParsed]);
|
|
206
235
|
// A page loading in on scroll clears the totals until it is read; the last
|
|
207
236
|
// totals for the same ticker and owner stay up so the table does not jump.
|
|
@@ -209,39 +238,50 @@ function InsiderView({ width, height, focused }: { width: number; height: number
|
|
|
209
238
|
const lastSummaryRef = useRef<{ scope: string; figures: InsiderSummaryFigures } | null>(null);
|
|
210
239
|
if (summary) lastSummaryRef.current = { scope: summaryScope, figures: summary };
|
|
211
240
|
const shownSummary = summary ?? (lastSummaryRef.current?.scope === summaryScope ? lastSummaryRef.current.figures : null);
|
|
212
|
-
const statItems = useMemo(() => shownSummary ? summaryItems(shownSummary) : [], [shownSummary]);
|
|
241
|
+
const statItems = useMemo(() => shownSummary ? summaryItems(shownSummary, typeFilter) : [], [shownSummary, typeFilter]);
|
|
213
242
|
// The totals miss window filings only when the window passes the cap.
|
|
214
243
|
const windowPartial = shownCount < windowCount;
|
|
215
244
|
const insiderOptions = useMemo(() => {
|
|
216
|
-
const names = new
|
|
245
|
+
const names = new Map<string, string>();
|
|
217
246
|
for (const entry of allParsed) {
|
|
218
247
|
const name = insiderReportedName(entry);
|
|
219
|
-
if (name) names.
|
|
248
|
+
if (name) names.set(name, formatInsiderName(name));
|
|
220
249
|
}
|
|
221
|
-
if (nameFilter) names.
|
|
250
|
+
if (nameFilter && !names.has(nameFilter)) names.set(nameFilter, formatInsiderName(nameFilter));
|
|
222
251
|
return [
|
|
223
252
|
{ value: ALL_INSIDERS, label: "All" },
|
|
224
|
-
...[...names].sort((a, b) => a.localeCompare(b)).map((
|
|
253
|
+
...[...names].sort((a, b) => a[1].localeCompare(b[1])).map(([value, label]) => ({ value, label })),
|
|
225
254
|
];
|
|
226
255
|
}, [allParsed, nameFilter]);
|
|
256
|
+
const typeOptions = useMemo(() => (["all", "trades", "other"] as const).map((value) => ({
|
|
257
|
+
value,
|
|
258
|
+
label: `${TYPE_FILTER_LABELS[value]} (${typeCounts[value]})`,
|
|
259
|
+
})), [typeCounts]);
|
|
227
260
|
const amendments = useMemo(() => relevantInsiderAmendments(parsed, allParsed), [parsed, allParsed]);
|
|
228
|
-
const
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
261
|
+
const selectedRowIndex = Math.max(0, sortedRows.findIndex((row) => row.id === selectedId));
|
|
262
|
+
const selectedRow = sortedRows[selectedRowIndex] ?? null;
|
|
263
|
+
const selectedFilterName = selectedRow ? insiderReportedName(selectedRow.entry) : null;
|
|
264
|
+
const openRow = openItemId ? rows.find((row) => row.id === openItemId) ?? null : null;
|
|
265
|
+
const openFiling = openRow?.entry.filing ?? null;
|
|
266
|
+
const loadMore = useTableLoadMore(scrollRef, shownCount < form4Filings.length && !openRow, loadNextPage);
|
|
232
267
|
|
|
233
268
|
const toggleNameFilter = useCallback((reportedName: string) => {
|
|
234
269
|
setNameFilter((current) => current === reportedName ? null : reportedName);
|
|
235
|
-
|
|
236
|
-
}, [setNameFilter,
|
|
270
|
+
setSelectedId(null);
|
|
271
|
+
}, [setNameFilter, setSelectedId]);
|
|
237
272
|
const selectInsider = useCallback((value: string) => {
|
|
238
273
|
setNameFilter(value === ALL_INSIDERS ? null : value);
|
|
239
|
-
|
|
240
|
-
}, [setNameFilter,
|
|
274
|
+
setSelectedId(null);
|
|
275
|
+
}, [setNameFilter, setSelectedId]);
|
|
276
|
+
const selectType = useCallback((value: string) => {
|
|
277
|
+
if (!isInsiderTypeFilter(value)) return;
|
|
278
|
+
setTypeFilter(value);
|
|
279
|
+
setSelectedId(null);
|
|
280
|
+
}, [setSelectedId, setTypeFilter]);
|
|
241
281
|
const clearNameFilter = useCallback(() => {
|
|
242
282
|
setNameFilter(null);
|
|
243
|
-
|
|
244
|
-
}, [setNameFilter,
|
|
283
|
+
setSelectedId(null);
|
|
284
|
+
}, [setNameFilter, setSelectedId]);
|
|
245
285
|
// The [f]ilter hint is the binding, so the key works over an open
|
|
246
286
|
// transaction and an empty filtered list, and always does what a click does.
|
|
247
287
|
const filterActionRef = useRef<() => void>(() => {});
|
|
@@ -251,6 +291,24 @@ function InsiderView({ width, height, focused }: { width: number; height: number
|
|
|
251
291
|
};
|
|
252
292
|
const handleFilterPress = useCallback(() => filterActionRef.current(), []);
|
|
253
293
|
|
|
294
|
+
// Reading on: the next or previous row opens in place, and the cursor
|
|
295
|
+
// follows it so Back lands on the last one read.
|
|
296
|
+
const openIndex = openRow ? sortedRows.findIndex((row) => row.id === openRow.id) : -1;
|
|
297
|
+
const openAdjacent = useCallback((step: 1 | -1) => {
|
|
298
|
+
const row = openIndex >= 0 ? sortedRows[openIndex + step] : undefined;
|
|
299
|
+
if (!row) return;
|
|
300
|
+
setSelectedId(row.id);
|
|
301
|
+
setOpenItemId(row.id);
|
|
302
|
+
}, [openIndex, setOpenItemId, setSelectedId, sortedRows]);
|
|
303
|
+
usePaneFooter("insider:detail", () => (focused && openIndex >= 0 ? {
|
|
304
|
+
// After the pane's own hints.
|
|
305
|
+
order: 1,
|
|
306
|
+
hints: [
|
|
307
|
+
{ id: "insider-next", key: "n", label: "ext", title: "Next Transaction", disabled: openIndex >= sortedRows.length - 1, onPress: () => openAdjacent(1) },
|
|
308
|
+
{ id: "insider-previous", key: "p", label: "rev", title: "Previous Transaction", disabled: openIndex <= 0, onPress: () => openAdjacent(-1) },
|
|
309
|
+
],
|
|
310
|
+
} : null), [focused, openAdjacent, openIndex, sortedRows.length]);
|
|
311
|
+
|
|
254
312
|
const pendingLabel = pendingCount > 0 ? `loading ${pendingCount}...` : "";
|
|
255
313
|
const footerInfo = useMemo(() => [
|
|
256
314
|
...(pendingLabel ? [{ id: "pending", parts: [{ text: pendingLabel, tone: "muted" as const }] }] : []),
|
|
@@ -292,44 +350,84 @@ function InsiderView({ width, height, focused }: { width: number; height: number
|
|
|
292
350
|
return <EmptyState title="No ticker selected." message="Select a ticker to view insider activity." />;
|
|
293
351
|
}
|
|
294
352
|
if (!eligibleTicker) return renderFilingNotice("Insider transactions are only shown for US equities.", width);
|
|
295
|
-
if (authWall) return <SignInWall action="view insider transactions" needsVerification={cloudSession.needsVerification} />;
|
|
353
|
+
if (authWall) return <SignInWall placement="insider-signin" action="view insider transactions" needsVerification={cloudSession.needsVerification} />;
|
|
296
354
|
if (loading && allFilings.length === 0) return <Spinner label="Loading insider filings..." />;
|
|
297
355
|
if (error && allFilings.length === 0) return <EmptyState title="Insider filings unavailable." message={error} />;
|
|
298
356
|
if (!loading && form4Filings.length === 0) {
|
|
299
357
|
return renderFilingNotice(`No Form 4 filings found for ${ticker.metadata.ticker}.`, width);
|
|
300
358
|
}
|
|
301
359
|
|
|
360
|
+
// Until a filing is read there is nothing to say; after that the empty
|
|
361
|
+
// list says how far back the filings it has read go, while older ones load.
|
|
362
|
+
const oldestRead = allParsed.findLast((entry) => !entry.isLoading)?.filing.filingDate;
|
|
363
|
+
const since = oldestRead ? ` since ${formatShortDate(oldestRead, { utc: true })}` : "";
|
|
364
|
+
const emptyTitle = typeCounts.all === 0 && (pendingCount > 0 || wantsOlderFilings)
|
|
365
|
+
? "Loading Form 4 transactions..."
|
|
366
|
+
: typeFilter === "trades"
|
|
367
|
+
? `No open-market buys or sells${nameFilter ? " by this insider" : ""}${since}.`
|
|
368
|
+
: nameFilter ? "No insider transactions for this filter." : "No insider transactions.";
|
|
369
|
+
const emptyHint = typeFilter === "trades" && typeCounts.other > 0
|
|
370
|
+
? `Type Other has ${typeCounts.other} awards, exercises, tax and gift lines.`
|
|
371
|
+
: undefined;
|
|
372
|
+
|
|
302
373
|
return (
|
|
303
|
-
<
|
|
304
|
-
width={width}
|
|
305
|
-
height={height}
|
|
374
|
+
<DataTableStackView<InsiderTableRow, InsiderColumn>
|
|
306
375
|
focused={focused}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
376
|
+
detailOpen={!!openRow}
|
|
377
|
+
onBack={() => setOpenItemId(null)}
|
|
378
|
+
detailTitle={openRow ? insiderDetailTitle(openRow) : undefined}
|
|
379
|
+
detailScrollRef={detailScrollRef}
|
|
380
|
+
detailContent={openRow ? <PaneLinkMenu><InsiderDetail row={openRow} width={width} scrollRef={detailScrollRef} /></PaneLinkMenu> : <Box flexGrow={1} />}
|
|
381
|
+
selection={{
|
|
382
|
+
kind: "index",
|
|
383
|
+
selectedIndex: sortedRows.length > 0 ? selectedRowIndex : null,
|
|
384
|
+
onChange: (_index, row) => setSelectedId(row.id),
|
|
385
|
+
}}
|
|
386
|
+
onActivate={(row) => {
|
|
387
|
+
setSelectedId(row.id);
|
|
388
|
+
setOpenItemId(row.id);
|
|
389
|
+
}}
|
|
312
390
|
rootBefore={<>
|
|
313
391
|
<QueryBar
|
|
314
392
|
width={width}
|
|
315
|
-
filters={[
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
393
|
+
filters={[
|
|
394
|
+
{
|
|
395
|
+
id: "insider",
|
|
396
|
+
label: "Insider",
|
|
397
|
+
value: nameFilter ?? ALL_INSIDERS,
|
|
398
|
+
defaultValue: ALL_INSIDERS,
|
|
399
|
+
options: insiderOptions,
|
|
400
|
+
onChange: selectInsider,
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
id: "type",
|
|
404
|
+
label: "Type",
|
|
405
|
+
value: typeFilter,
|
|
406
|
+
defaultValue: DEFAULT_INSIDER_TYPE_FILTER,
|
|
407
|
+
options: typeOptions,
|
|
408
|
+
onChange: selectType,
|
|
409
|
+
// Every count in view where there is room, so a short list never looks like missing data.
|
|
410
|
+
inline: width >= TYPE_FILTER_INLINE_WIDTH,
|
|
411
|
+
},
|
|
412
|
+
]}
|
|
323
413
|
/>
|
|
324
414
|
<StatGrid items={statItems} width={width} />
|
|
325
415
|
</>}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
416
|
+
rootWidth={width}
|
|
417
|
+
rootHeight={height}
|
|
418
|
+
columns={columns}
|
|
419
|
+
items={sortedRows}
|
|
420
|
+
sortColumnId={sort.columnId}
|
|
421
|
+
sortDirection={sort.direction}
|
|
422
|
+
onHeaderClick={(columnId) => setSort((current) => nextHeaderSort(current, columnId as InsiderColumnId, {
|
|
423
|
+
firstDirection: columnId === "insider" || columnId === "role" || columnId === "type" || columnId === "security" ? "asc" : "desc",
|
|
424
|
+
}))}
|
|
425
|
+
getItemKey={(row) => row.id}
|
|
426
|
+
renderCell={renderInsiderCell}
|
|
427
|
+
selectedTextOverridesCellColor
|
|
428
|
+
emptyStateTitle={emptyTitle}
|
|
429
|
+
emptyStateHint={emptyHint}
|
|
430
|
+
showHorizontalScrollbar={false}
|
|
333
431
|
scrollRef={scrollRef}
|
|
334
432
|
onBodyScrollActivity={loadMore}
|
|
335
433
|
/>
|
|
@@ -17,14 +17,25 @@ export interface InsiderTransaction {
|
|
|
17
17
|
acquiredDisposed?: string;
|
|
18
18
|
ownershipType?: string;
|
|
19
19
|
ownershipNature?: string;
|
|
20
|
-
reportingOwners?:
|
|
20
|
+
reportingOwners?: InsiderReportingOwner[];
|
|
21
21
|
footnoteIds?: string[];
|
|
22
|
+
/** The filer checked the Rule 10b5-1 plan box; undefined on forms filed before the box existed. */
|
|
23
|
+
rule10b51?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface InsiderReportingOwner {
|
|
27
|
+
name: string;
|
|
28
|
+
cik: string;
|
|
29
|
+
title: string;
|
|
30
|
+
/** The relationship boxes, which `title` drops when there is an officer title. */
|
|
31
|
+
director?: boolean;
|
|
32
|
+
tenPercentOwner?: boolean;
|
|
22
33
|
}
|
|
23
34
|
|
|
24
35
|
export interface InsiderFilingDisclosure {
|
|
25
36
|
form: string;
|
|
26
37
|
originalFilingDate: string | null;
|
|
27
|
-
reportingOwners:
|
|
38
|
+
reportingOwners: InsiderReportingOwner[];
|
|
28
39
|
footnotes: Array<{ id: string; text: string }>;
|
|
29
40
|
remarks: string | null;
|
|
30
41
|
/** False for a filing with no transaction lines, e.g. one reporting only that the owner left Section 16. */
|
|
@@ -58,6 +69,14 @@ function isFlagSet(xml: string, tag: string): boolean {
|
|
|
58
69
|
return /^(?:1|true)$/i.test(tagText(xml, tag));
|
|
59
70
|
}
|
|
60
71
|
|
|
72
|
+
/** A checkbox the form may predate: true, false, or undefined when the tag is missing. */
|
|
73
|
+
function optionalFlag(xml: string, tag: string): boolean | undefined {
|
|
74
|
+
const value = tagText(xml, tag);
|
|
75
|
+
if (/^(?:1|true)$/i.test(value)) return true;
|
|
76
|
+
if (/^(?:0|false)$/i.test(value)) return false;
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
61
80
|
/** The officer title, else the relationship boxes checked on the form (directors have no officer title). */
|
|
62
81
|
function ownerTitle(owner: string): string {
|
|
63
82
|
const officerTitle = tagText(owner, "officerTitle");
|
|
@@ -72,7 +91,13 @@ function ownerTitle(owner: string): string {
|
|
|
72
91
|
|
|
73
92
|
function reportingOwners(xml: string): InsiderFilingDisclosure["reportingOwners"] {
|
|
74
93
|
return [...xml.matchAll(/<(?:[\w.-]+:)?reportingOwner(?:\s[^>]*)?>([\s\S]*?)<\/(?:[\w.-]+:)?reportingOwner\s*>/gi)]
|
|
75
|
-
.map((match) => ({
|
|
94
|
+
.map((match) => ({
|
|
95
|
+
name: tagText(match[1]!, "rptOwnerName"),
|
|
96
|
+
cik: tagText(match[1]!, "rptOwnerCik"),
|
|
97
|
+
title: ownerTitle(match[1]!),
|
|
98
|
+
director: isFlagSet(match[1]!, "isDirector"),
|
|
99
|
+
tenPercentOwner: isFlagSet(match[1]!, "isTenPercentOwner"),
|
|
100
|
+
}));
|
|
76
101
|
}
|
|
77
102
|
|
|
78
103
|
function disclosureText(xml: string): string {
|
|
@@ -98,6 +123,8 @@ export function parseForm4Xml(xml: string): InsiderTransaction[] {
|
|
|
98
123
|
const owners = reportingOwners(xml).filter((owner) => owner.name);
|
|
99
124
|
if (!owners.length) return [];
|
|
100
125
|
const blocks = [...xml.matchAll(/<(?:[\w.-]+:)?(nonDerivativeTransaction|derivativeTransaction)(?:\s[^>]*)?>([\s\S]*?)<\/(?:[\w.-]+:)?\1\s*>/gi)];
|
|
126
|
+
// One box for the whole form, so it covers every line on it.
|
|
127
|
+
const rule10b51 = optionalFlag(xml, "aff10b5One");
|
|
101
128
|
return blocks.map((match, transactionIndex) => {
|
|
102
129
|
const block = match[2]!;
|
|
103
130
|
const amounts = tagContent(block, "transactionAmounts") ?? "";
|
|
@@ -123,6 +150,7 @@ export function parseForm4Xml(xml: string): InsiderTransaction[] {
|
|
|
123
150
|
ownershipNature: tagText(tagContent(tagContent(block, "ownershipNature") ?? "", "natureOfOwnership") ?? "", "value"),
|
|
124
151
|
form: tagText(xml, "documentType") || "4",
|
|
125
152
|
footnoteIds: [...new Set([...block.matchAll(/<(?:[\w.-]+:)?footnoteId\s[^>]*\bid\s*=\s*["']([^"']+)["']/gi)].map((note) => note[1]!))],
|
|
153
|
+
...(rule10b51 === undefined ? {} : { rule10b51 }),
|
|
126
154
|
};
|
|
127
155
|
});
|
|
128
156
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { SecFilingItem } from "../../../types/data-provider";
|
|
2
2
|
import { formatCompact, formatCurrency } from "../../../utils/format";
|
|
3
|
-
import { parseForm4Xml, parseForm4Disclosure, transactionTypeLabel, type InsiderTransaction, type InsiderFilingDisclosure } from "./insider-data";
|
|
3
|
+
import { parseForm4Xml, parseForm4Disclosure, transactionTypeLabel, type InsiderTransaction, type InsiderFilingDisclosure, type InsiderReportingOwner } from "./insider-data";
|
|
4
4
|
import { affectingInsiderAmendments, buildInsiderAmendmentScopes, isAmendedInsiderFiling } from "./amendments";
|
|
5
|
+
import { formatInsiderName, isOpenMarketTrade, shortInsiderRole } from "./display";
|
|
5
6
|
|
|
6
7
|
const NINETY_DAYS_MS = 90 * 24 * 60 * 60 * 1000;
|
|
7
8
|
|
|
@@ -38,11 +39,56 @@ export function insiderReportedName(entry: ParsedInsiderFiling): string | null {
|
|
|
38
39
|
?? (entry.disclosure?.reportingOwners.map((owner) => owner.name).filter(Boolean).join("; ") || null);
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
/** The reported name as the table shows it, first name first. */
|
|
43
|
+
export function insiderDisplayName(entry: ParsedInsiderFiling): string | null {
|
|
44
|
+
const name = insiderReportedName(entry);
|
|
45
|
+
return name ? formatInsiderName(name) : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function insiderOwners(entry: ParsedInsiderFiling): InsiderReportingOwner[] {
|
|
49
|
+
return entry.disclosure?.reportingOwners ?? entry.transaction?.reportingOwners ?? [];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The owner's role in a word or two (CEO, Director, 10% owner); the full title stays in the detail. */
|
|
53
|
+
export function insiderRole(entry: ParsedInsiderFiling): string {
|
|
54
|
+
const owners = insiderOwners(entry);
|
|
55
|
+
if (owners.length) return shortInsiderRole(owners, entry.disclosure?.remarks);
|
|
56
|
+
return entry.transaction?.title ? shortInsiderRole([{ title: entry.transaction.title }]) : "";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Matches the name as filed ("COOK TIMOTHY D") or as shown ("Timothy D. Cook"). */
|
|
41
60
|
export function matchesInsiderOwner(entry: ParsedInsiderFiling, name: string): boolean {
|
|
42
61
|
const normalized = name.trim().toLocaleLowerCase();
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
62
|
+
const matches = (candidate: string | null | undefined) => !!candidate
|
|
63
|
+
&& (candidate.toLocaleLowerCase() === normalized || formatInsiderName(candidate).toLocaleLowerCase() === normalized);
|
|
64
|
+
return matches(insiderReportedName(entry)) || insiderOwners(entry).some((owner) => matches(owner.name));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The Type filter: open-market buys and sells (codes P and S, the default),
|
|
69
|
+
* everything else (awards, exercises, tax withholding, gifts), or all.
|
|
70
|
+
*/
|
|
71
|
+
export type InsiderTypeFilter = "trades" | "other" | "all";
|
|
72
|
+
|
|
73
|
+
export const DEFAULT_INSIDER_TYPE_FILTER: InsiderTypeFilter = "trades";
|
|
74
|
+
|
|
75
|
+
export function isInsiderTypeFilter(value: unknown): value is InsiderTypeFilter {
|
|
76
|
+
return value === "trades" || value === "other" || value === "all";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Whether a row belongs in a Type view. A filing still being read shows only
|
|
81
|
+
* under All, so the narrower views do not flicker as it resolves. A filing
|
|
82
|
+
* that could not be read, or an amendment without transaction lines, may
|
|
83
|
+
* hide a trade, so it shows under both narrower views.
|
|
84
|
+
*/
|
|
85
|
+
export function matchesInsiderTypeFilter(entry: ParsedInsiderFiling, filter: InsiderTypeFilter): boolean {
|
|
86
|
+
if (filter === "all") return true;
|
|
87
|
+
const { transaction } = entry;
|
|
88
|
+
if (transaction) return isOpenMarketTrade(transaction.transactionType) === (filter === "trades");
|
|
89
|
+
if (entry.isLoading) return false;
|
|
90
|
+
if (isInsiderDisclosureOnly(entry)) return filter === "other" || isAmendedInsiderFiling(entry);
|
|
91
|
+
return true;
|
|
46
92
|
}
|
|
47
93
|
|
|
48
94
|
export function buildInsiderDisclosureText(entry: ParsedInsiderFiling): string {
|
|
@@ -125,6 +171,8 @@ export function buildInsiderRows(parsed: readonly ParsedInsiderFiling[], context
|
|
|
125
171
|
filingDate: filing.filingDate instanceof Date ? filing.filingDate.toISOString() : String(filing.filingDate),
|
|
126
172
|
transactionDate: transaction?.filingDate?.toISOString() ?? null,
|
|
127
173
|
insider: insiderReportedName(entry),
|
|
174
|
+
insiderName: insiderDisplayName(entry),
|
|
175
|
+
role: insiderRole(entry) || null,
|
|
128
176
|
reportingOwners: entry.disclosure?.reportingOwners ?? transaction?.reportingOwners ?? [],
|
|
129
177
|
title: transaction?.title ?? (entry.disclosure?.reportingOwners.map((owner) => owner.title).filter(Boolean).join("; ") || null),
|
|
130
178
|
security: transaction?.securityTitle ?? null,
|
|
@@ -137,6 +185,7 @@ export function buildInsiderRows(parsed: readonly ParsedInsiderFiling[], context
|
|
|
137
185
|
shares: transaction?.shares ?? null,
|
|
138
186
|
pricePerShare: transaction?.pricePerShare ?? null,
|
|
139
187
|
totalValue: transaction?.totalValue ?? null,
|
|
188
|
+
rule10b51: transaction?.rule10b51 ?? null,
|
|
140
189
|
sharesOwnedAfter: transaction?.sharesOwned ?? null,
|
|
141
190
|
form: filing.form,
|
|
142
191
|
documentForm: entry.disclosure?.form ?? transaction?.form ?? null,
|