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.
Files changed (524) hide show
  1. package/README.md +11 -2
  2. package/package.json +1 -1
  3. package/src/api-client/app-attention.ts +147 -0
  4. package/src/api-client/attention.ts +26 -0
  5. package/src/api-client/awards.ts +215 -0
  6. package/src/api-client/catalysts.ts +160 -0
  7. package/src/api-client/chat.ts +137 -14
  8. package/src/api-client/company-kpis.ts +153 -0
  9. package/src/api-client/cpi.ts +52 -0
  10. package/src/api-client/credit-documents.ts +107 -0
  11. package/src/api-client/data.ts +192 -3
  12. package/src/api-client/distress.ts +164 -0
  13. package/src/api-client/doe.ts +75 -0
  14. package/src/api-client/equity-screener.ts +11 -0
  15. package/src/api-client/exposure.ts +202 -0
  16. package/src/api-client/gpu.ts +93 -0
  17. package/src/api-client/hiring.ts +110 -0
  18. package/src/api-client/index.ts +78 -3
  19. package/src/api-client/normalizers.ts +31 -1
  20. package/src/api-client/paths.ts +3 -0
  21. package/src/api-client/perps.ts +127 -0
  22. package/src/api-client/power.ts +148 -0
  23. package/src/api-client/request.ts +58 -1
  24. package/src/api-client/research-activity.ts +172 -13
  25. package/src/api-client/socket.ts +75 -27
  26. package/src/api-client/supply-chain-graph.ts +40 -0
  27. package/src/api-client/supply-chain.ts +98 -0
  28. package/src/api-client/telemetry.ts +29 -0
  29. package/src/api-client/themes.ts +28 -0
  30. package/src/api-client/types.ts +33 -1
  31. package/src/api-client/wall-experiments.ts +100 -0
  32. package/src/api-client/wall-summary.ts +49 -0
  33. package/src/api-client/web-experiments.ts +136 -0
  34. package/src/app/pane-runtime/plugin-bindings.ts +5 -0
  35. package/src/app/pane-runtime/ticker-open-runtime.ts +8 -2
  36. package/src/app/runtime/update.ts +13 -1
  37. package/src/cli/commands/system.ts +2 -0
  38. package/src/cli/desktop-pane-shot.ts +3 -4
  39. package/src/cli/pane-functions/data.ts +78 -0
  40. package/src/cli/pane-functions/financial-ratio-shot.ts +160 -0
  41. package/src/cli/pane-functions/function-help.ts +220 -8
  42. package/src/cli/pane-functions/headless.ts +4 -0
  43. package/src/cli/pane-functions/screenshot-evidence.ts +21 -0
  44. package/src/cli/pane-functions/screenshot.ts +49 -5
  45. package/src/components/chart/composite/composite-chart.tsx +15 -2
  46. package/src/components/chart/composite/format.ts +24 -11
  47. package/src/components/chart/composite/rasterizer.ts +67 -16
  48. package/src/components/chart/composite/scene.ts +53 -2
  49. package/src/components/chart/composite/session-shading.ts +3 -2
  50. package/src/components/chart/composite/text-renderer.ts +52 -1
  51. package/src/components/chart/composite/time-axis.ts +100 -30
  52. package/src/components/chart/composite/tools.ts +2 -1
  53. package/src/components/chart/composite/types.ts +4 -0
  54. package/src/components/chart-table/index.ts +1 -0
  55. package/src/components/chart-table/layout.ts +15 -0
  56. package/src/components/command-bar/action-runtime.ts +1 -0
  57. package/src/components/command-bar/commands/direct/index.ts +32 -0
  58. package/src/components/command-bar/commands/registry.ts +7 -0
  59. package/src/components/command-bar/pane-templates/items.ts +11 -0
  60. package/src/components/command-bar/pane-templates/workflow.ts +19 -4
  61. package/src/components/command-bar/routes/root/command-items.ts +6 -0
  62. package/src/components/command-bar/routes/root/provider-search.ts +2 -0
  63. package/src/components/command-bar/routes/root/results.ts +64 -0
  64. package/src/components/command-bar/routes/root/runtime.ts +8 -0
  65. package/src/components/command-bar/routes/ticker-search/results.ts +89 -24
  66. package/src/components/command-bar/routes/ticker-search/route.ts +2 -0
  67. package/src/components/command-bar/surface/index.tsx +15 -0
  68. package/src/components/command-bar/view-model.ts +3 -0
  69. package/src/components/command-bar/workflow/collection-actions.ts +9 -7
  70. package/src/components/command-bar/workflow/ops.ts +19 -0
  71. package/src/components/command-bar/workflow/tickers.ts +5 -0
  72. package/src/components/company-logo.tsx +15 -2
  73. package/src/components/data-table/table-pane.ts +2 -1
  74. package/src/components/index.ts +1 -0
  75. package/src/components/layout/detached-pane-shell.tsx +12 -1
  76. package/src/components/layout/missing-pane.tsx +87 -23
  77. package/src/components/layout/pane/footer/index.tsx +64 -18
  78. package/src/components/layout/pane/footer/model.ts +85 -0
  79. package/src/components/layout/pane/sidebar.tsx +19 -1
  80. package/src/components/layout/shell/index.tsx +38 -28
  81. package/src/components/layout/shell/pane/management-shortcuts.ts +13 -1
  82. package/src/components/layout/status-bar.tsx +93 -28
  83. package/src/components/layout/status-widget-space.ts +72 -0
  84. package/src/components/onboarding/first-run-workspace.ts +8 -2
  85. package/src/components/onboarding/onboarding-wizard.tsx +35 -16
  86. package/src/components/onboarding/portfolio-step/positions-panel.tsx +14 -5
  87. package/src/components/onboarding/wizard-model.ts +13 -5
  88. package/src/components/onboarding/wizard-positions.ts +56 -7
  89. package/src/components/price-performance.tsx +31 -34
  90. package/src/components/ui/data-table/index.tsx +11 -8
  91. package/src/components/ui/display.tsx +15 -6
  92. package/src/components/ui/fields.tsx +5 -0
  93. package/src/components/ui/figure.tsx +94 -0
  94. package/src/components/ui/header-case.ts +25 -3
  95. package/src/components/ui/icon.tsx +3 -1
  96. package/src/components/ui/message-composer.tsx +8 -1
  97. package/src/components/ui/query-bar.tsx +8 -1
  98. package/src/components/ui/ratio-bar.tsx +84 -0
  99. package/src/components/ui/toggle.tsx +3 -1
  100. package/src/components/use-scrollbox-layout.ts +17 -0
  101. package/src/core/state/app/layout.ts +35 -1
  102. package/src/core/state/app/state.ts +15 -0
  103. package/src/core/state/app/types.ts +1 -0
  104. package/src/data/config/store/normalize.ts +24 -0
  105. package/src/i18n/es.ts +2 -0
  106. package/src/i18n/ja.ts +2 -0
  107. package/src/i18n/ko.ts +2 -0
  108. package/src/i18n/zh-cn.ts +2 -0
  109. package/src/i18n/zh-tw.ts +2 -0
  110. package/src/layout-marketplace/pane-imagery.ts +1 -0
  111. package/src/market-data/coordinator/entries.ts +13 -0
  112. package/src/market-data/coordinator/financials.ts +33 -8
  113. package/src/market-data/market/freshness.ts +16 -5
  114. package/src/market-data/market/trading-sessions.ts +65 -1
  115. package/src/market-data/published-cn-sessions.ts +25 -0
  116. package/src/market-data/published-nse-sessions.ts +20 -0
  117. package/src/news/aggregator.ts +4 -0
  118. package/src/plugins/builtin/account-management/model.ts +6 -1
  119. package/src/plugins/builtin/account-management/pane.tsx +36 -25
  120. package/src/plugins/builtin/alerts/catalysts.ts +77 -0
  121. package/src/plugins/builtin/alerts/events-pane.tsx +3 -0
  122. package/src/plugins/builtin/alerts/events.ts +10 -4
  123. package/src/plugins/builtin/alerts/history-pane.tsx +1 -1
  124. package/src/plugins/builtin/alerts/history.ts +1 -0
  125. package/src/plugins/builtin/alerts/index.tsx +6 -0
  126. package/src/plugins/builtin/alerts/research-builder.ts +10 -3
  127. package/src/plugins/builtin/alerts/research-rules.ts +15 -1
  128. package/src/plugins/builtin/analytics/risk-client.ts +15 -3
  129. package/src/plugins/builtin/analytics/risk-headless.ts +123 -18
  130. package/src/plugins/builtin/attention/client.ts +80 -0
  131. package/src/plugins/builtin/attention/evidence.ts +30 -0
  132. package/src/plugins/builtin/attention/headless.ts +33 -0
  133. package/src/plugins/builtin/attention/index.tsx +18 -0
  134. package/src/plugins/builtin/attention/model.ts +59 -0
  135. package/src/plugins/builtin/attention/pane.tsx +211 -0
  136. package/src/plugins/builtin/attention/trending.tsx +59 -0
  137. package/src/plugins/builtin/awards/alerts.ts +32 -0
  138. package/src/plugins/builtin/awards/client.ts +82 -0
  139. package/src/plugins/builtin/awards/detail.tsx +107 -0
  140. package/src/plugins/builtin/awards/evidence.ts +36 -0
  141. package/src/plugins/builtin/awards/headless.ts +55 -0
  142. package/src/plugins/builtin/awards/index.tsx +20 -0
  143. package/src/plugins/builtin/awards/model.ts +42 -0
  144. package/src/plugins/builtin/awards/pane.tsx +263 -0
  145. package/src/plugins/builtin/catalysts/client.ts +28 -0
  146. package/src/plugins/builtin/catalysts/detail.tsx +88 -0
  147. package/src/plugins/builtin/catalysts/evidence.ts +47 -0
  148. package/src/plugins/builtin/catalysts/headless.ts +42 -0
  149. package/src/plugins/builtin/catalysts/history.ts +26 -0
  150. package/src/plugins/builtin/catalysts/index.tsx +29 -0
  151. package/src/plugins/builtin/catalysts/model.ts +135 -0
  152. package/src/plugins/builtin/catalysts/pane.tsx +159 -0
  153. package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
  154. package/src/plugins/builtin/chart-composer/headless-schema.ts +6 -0
  155. package/src/plugins/builtin/chart-composer/index.tsx +8 -1
  156. package/src/plugins/builtin/chart-composer/pane.tsx +28 -1
  157. package/src/plugins/builtin/chat/attachments/desktop.tsx +610 -0
  158. package/src/plugins/builtin/chat/attachments/files.ts +45 -0
  159. package/src/plugins/builtin/chat/attachments/model.ts +164 -0
  160. package/src/plugins/builtin/chat/attachments/terminal.tsx +72 -0
  161. package/src/plugins/builtin/chat/attachments/transfer.ts +165 -0
  162. package/src/plugins/builtin/chat/content/channel-navigation.ts +13 -12
  163. package/src/plugins/builtin/chat/content/composer-runtime.ts +25 -4
  164. package/src/plugins/builtin/chat/content/composer.tsx +46 -4
  165. package/src/plugins/builtin/chat/content/footer.tsx +46 -8
  166. package/src/plugins/builtin/chat/content/index.tsx +263 -62
  167. package/src/plugins/builtin/chat/content/layout-metrics.ts +10 -2
  168. package/src/plugins/builtin/chat/content/open-targets.ts +17 -3
  169. package/src/plugins/builtin/chat/content/scroll.ts +7 -1
  170. package/src/plugins/builtin/chat/content/shortcuts.ts +36 -11
  171. package/src/plugins/builtin/chat/content/snapshot.ts +3 -0
  172. package/src/plugins/builtin/chat/content/transcript.tsx +17 -1
  173. package/src/plugins/builtin/chat/content/types.ts +7 -0
  174. package/src/plugins/builtin/chat/controller/attachments.ts +213 -0
  175. package/src/plugins/builtin/chat/controller/connections.ts +3 -1
  176. package/src/plugins/builtin/chat/controller/index.ts +114 -2
  177. package/src/plugins/builtin/chat/controller/lifecycle.ts +9 -0
  178. package/src/plugins/builtin/chat/controller/messages.ts +35 -2
  179. package/src/plugins/builtin/chat/controller/send.ts +128 -25
  180. package/src/plugins/builtin/chat/controller/state.ts +12 -1
  181. package/src/plugins/builtin/chat/controller/utils.ts +7 -5
  182. package/src/plugins/builtin/chat/controller/view.ts +2 -0
  183. package/src/plugins/builtin/chat/index.tsx +21 -0
  184. package/src/plugins/builtin/chat/layout.ts +9 -4
  185. package/src/plugins/builtin/chat/message/desktop.tsx +55 -20
  186. package/src/plugins/builtin/chat/message/header.tsx +30 -5
  187. package/src/plugins/builtin/chat/message/inline-tokens.tsx +9 -0
  188. package/src/plugins/builtin/chat/message/pending-send.ts +35 -0
  189. package/src/plugins/builtin/chat/message/profile-popover.tsx +5 -1
  190. package/src/plugins/builtin/chat/message/render-state.ts +12 -2
  191. package/src/plugins/builtin/chat/message/terminal.tsx +51 -4
  192. package/src/plugins/builtin/chat/message/types.ts +6 -1
  193. package/src/plugins/builtin/chat/pane-state.ts +59 -1
  194. package/src/plugins/builtin/chat/profile-popover.ts +50 -1
  195. package/src/plugins/builtin/chat/sidebar.tsx +8 -3
  196. package/src/plugins/builtin/chat/status-widget.tsx +29 -12
  197. package/src/plugins/builtin/chat/unread-inbox-pane.tsx +143 -0
  198. package/src/plugins/builtin/chat/unread-inbox.ts +91 -0
  199. package/src/plugins/builtin/clinical-trials/client.ts +237 -0
  200. package/src/plugins/builtin/clinical-trials/index.tsx +74 -0
  201. package/src/plugins/builtin/clinical-trials/pane.tsx +225 -0
  202. package/src/plugins/builtin/clinical-trials/types.ts +2 -0
  203. package/src/plugins/builtin/cloud/askg/controller.ts +37 -6
  204. package/src/plugins/builtin/cloud/askg/executor.ts +59 -44
  205. package/src/plugins/builtin/cloud/askg/host.ts +8 -1
  206. package/src/plugins/builtin/cloud/askg/manifest.ts +29 -2
  207. package/src/plugins/builtin/cloud/askg/model.ts +47 -13
  208. package/src/plugins/builtin/cloud/askg/notes.ts +100 -0
  209. package/src/plugins/builtin/cloud/askg/pane.tsx +50 -14
  210. package/src/plugins/builtin/cloud/askg/protocol.ts +39 -1
  211. package/src/plugins/builtin/cloud/askg/result-budget.ts +156 -0
  212. package/src/plugins/builtin/cloud/askg/user-data.ts +89 -0
  213. package/src/plugins/builtin/cloud/auth-actions.tsx +55 -31
  214. package/src/plugins/builtin/cloud/plugin.tsx +4 -1
  215. package/src/plugins/builtin/cloud/team/pane-sections.tsx +10 -1
  216. package/src/plugins/builtin/cloud/team/pane.tsx +3 -2
  217. package/src/plugins/builtin/cloud/thesis/board-pane.tsx +2 -2
  218. package/src/plugins/builtin/cloud/thesis/detail.tsx +1 -1
  219. package/src/plugins/builtin/cloud/thesis/ticker-tab.tsx +2 -2
  220. package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +202 -0
  221. package/src/plugins/builtin/cloud/upgrade-command.ts +1 -1
  222. package/src/plugins/builtin/cloud/upgrade-dialog.tsx +29 -3
  223. package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +1 -1
  224. package/src/plugins/builtin/cloud-tweets/table.tsx +1 -1
  225. package/src/plugins/builtin/comment-letters/client.ts +342 -0
  226. package/src/plugins/builtin/comment-letters/index.tsx +74 -0
  227. package/src/plugins/builtin/comment-letters/pane.tsx +295 -0
  228. package/src/plugins/builtin/comment-letters/types.ts +43 -0
  229. package/src/plugins/builtin/company-attention/app-rank.tsx +79 -0
  230. package/src/plugins/builtin/company-attention/client.ts +69 -0
  231. package/src/plugins/builtin/company-attention/evidence.ts +45 -0
  232. package/src/plugins/builtin/company-attention/headless.ts +47 -0
  233. package/src/plugins/builtin/company-attention/index.tsx +30 -0
  234. package/src/plugins/builtin/company-attention/model.ts +129 -0
  235. package/src/plugins/builtin/company-attention/pane.tsx +183 -0
  236. package/src/plugins/builtin/company-kpis/client.ts +92 -0
  237. package/src/plugins/builtin/company-kpis/headless.ts +32 -0
  238. package/src/plugins/builtin/company-kpis/index.tsx +26 -0
  239. package/src/plugins/builtin/company-kpis/model.ts +124 -0
  240. package/src/plugins/builtin/company-kpis/pane.tsx +226 -0
  241. package/src/plugins/builtin/company-kpis/related.ts +11 -0
  242. package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +44 -0
  243. package/src/plugins/builtin/composite-plugins.ts +34 -0
  244. package/src/plugins/builtin/correlation/history.ts +4 -1
  245. package/src/plugins/builtin/correlation/index.tsx +139 -80
  246. package/src/plugins/builtin/correlation/matrix/pair-chart.tsx +48 -0
  247. package/src/plugins/builtin/correlation/matrix/selection.ts +77 -0
  248. package/src/plugins/builtin/correlation/relationship/model.ts +2 -2
  249. package/src/plugins/builtin/cpi/client.ts +66 -0
  250. package/src/plugins/builtin/cpi/headless.ts +78 -0
  251. package/src/plugins/builtin/cpi/index.tsx +31 -0
  252. package/src/plugins/builtin/cpi/model.ts +198 -0
  253. package/src/plugins/builtin/cpi/pane.tsx +157 -0
  254. package/src/plugins/builtin/credit-documents/client.ts +99 -0
  255. package/src/plugins/builtin/credit-documents/detail.tsx +109 -0
  256. package/src/plugins/builtin/credit-documents/evidence.ts +64 -0
  257. package/src/plugins/builtin/credit-documents/gauge.tsx +55 -0
  258. package/src/plugins/builtin/credit-documents/headless.ts +40 -0
  259. package/src/plugins/builtin/credit-documents/index.tsx +19 -0
  260. package/src/plugins/builtin/credit-documents/model.ts +336 -0
  261. package/src/plugins/builtin/credit-documents/pane.tsx +185 -0
  262. package/src/plugins/builtin/custom-view/view-spec.ts +14 -2
  263. package/src/plugins/builtin/debt-maturities/pane.tsx +1 -0
  264. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  265. package/src/plugins/builtin/doe/client.ts +73 -0
  266. package/src/plugins/builtin/doe/headless.ts +93 -0
  267. package/src/plugins/builtin/doe/index.tsx +41 -0
  268. package/src/plugins/builtin/doe/model.ts +215 -0
  269. package/src/plugins/builtin/doe/pane.tsx +148 -0
  270. package/src/plugins/builtin/doe/range-bar.tsx +45 -0
  271. package/src/plugins/builtin/earnings/board.tsx +3 -0
  272. package/src/plugins/builtin/earnings/index.tsx +2 -0
  273. package/src/plugins/builtin/earnings-calls/pane.tsx +7 -1
  274. package/src/plugins/builtin/earnings-calls/transcript-view.tsx +7 -7
  275. package/src/plugins/builtin/earnings-ripple/client.ts +60 -0
  276. package/src/plugins/builtin/earnings-ripple/headless.ts +35 -0
  277. package/src/plugins/builtin/earnings-ripple/index.tsx +35 -0
  278. package/src/plugins/builtin/earnings-ripple/model.ts +98 -0
  279. package/src/plugins/builtin/earnings-ripple/pane.tsx +103 -0
  280. package/src/plugins/builtin/equity-screener/client.ts +1 -1
  281. package/src/plugins/builtin/equity-screener/criterion-editor.tsx +1 -1
  282. package/src/plugins/builtin/equity-screener/model.ts +74 -9
  283. package/src/plugins/builtin/equity-screener/pane.tsx +2 -0
  284. package/src/plugins/builtin/estimate-revisions/pane.tsx +3 -0
  285. package/src/plugins/builtin/executives/pane.tsx +7 -5
  286. package/src/plugins/builtin/exposure/charts.tsx +66 -0
  287. package/src/plugins/builtin/exposure/client.ts +53 -0
  288. package/src/plugins/builtin/exposure/drivers.ts +29 -0
  289. package/src/plugins/builtin/exposure/editor.tsx +61 -0
  290. package/src/plugins/builtin/exposure/evidence.ts +39 -0
  291. package/src/plugins/builtin/exposure/headless.ts +51 -0
  292. package/src/plugins/builtin/exposure/holdings.ts +56 -0
  293. package/src/plugins/builtin/exposure/index.tsx +13 -0
  294. package/src/plugins/builtin/exposure/model.ts +104 -0
  295. package/src/plugins/builtin/exposure/pane.tsx +247 -0
  296. package/src/plugins/builtin/fear-greed/charts.tsx +1 -1
  297. package/src/plugins/builtin/fear-greed/pane.tsx +2 -9
  298. package/src/plugins/builtin/filing-events/pane.tsx +2 -1
  299. package/src/plugins/builtin/gpu/board.tsx +163 -0
  300. package/src/plugins/builtin/gpu/changes.tsx +94 -0
  301. package/src/plugins/builtin/gpu/client.ts +130 -0
  302. package/src/plugins/builtin/gpu/equities.tsx +112 -0
  303. package/src/plugins/builtin/gpu/headless.ts +67 -0
  304. package/src/plugins/builtin/gpu/history.tsx +140 -0
  305. package/src/plugins/builtin/gpu/index.tsx +18 -0
  306. package/src/plugins/builtin/gpu/ladder.tsx +127 -0
  307. package/src/plugins/builtin/gpu/model.ts +317 -0
  308. package/src/plugins/builtin/gpu/pane.tsx +63 -0
  309. package/src/plugins/builtin/help/index.tsx +19 -2
  310. package/src/plugins/builtin/holders/pane.tsx +5 -9
  311. package/src/plugins/builtin/insider/detail.tsx +119 -0
  312. package/src/plugins/builtin/insider/display.ts +221 -0
  313. package/src/plugins/builtin/insider/headless.ts +24 -3
  314. package/src/plugins/builtin/insider/index.tsx +206 -108
  315. package/src/plugins/builtin/insider/insider-data.ts +31 -3
  316. package/src/plugins/builtin/insider/model.ts +53 -4
  317. package/src/plugins/builtin/insider/table-model.ts +226 -0
  318. package/src/plugins/builtin/insider/table.tsx +79 -0
  319. package/src/plugins/builtin/iv-history/screen-pane.tsx +1 -1
  320. package/src/plugins/builtin/jobs/pane.tsx +14 -7
  321. package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
  322. package/src/plugins/builtin/macro-day/client.ts +50 -0
  323. package/src/plugins/builtin/macro-day/headless.ts +51 -0
  324. package/src/plugins/builtin/macro-day/index.tsx +21 -0
  325. package/src/plugins/builtin/macro-day/model.ts +137 -0
  326. package/src/plugins/builtin/macro-day/pane.tsx +118 -0
  327. package/src/plugins/builtin/macro-day/releases.ts +58 -0
  328. package/src/plugins/builtin/market-halts/model.ts +30 -17
  329. package/src/plugins/builtin/market-halts/pane.tsx +15 -21
  330. package/src/plugins/builtin/market-heatmap/data.ts +1 -1
  331. package/src/plugins/builtin/market-heatmap/index.tsx +198 -50
  332. package/src/plugins/builtin/market-heatmap/portfolio.ts +220 -0
  333. package/src/plugins/builtin/market-movers/index.tsx +1 -0
  334. package/src/plugins/builtin/market-movers/session-body.tsx +5 -2
  335. package/src/plugins/builtin/mna/distress/client.ts +326 -0
  336. package/src/plugins/builtin/mna/distress/detail.tsx +247 -0
  337. package/src/plugins/builtin/mna/distress/headless.ts +257 -0
  338. package/src/plugins/builtin/mna/distress/model.ts +427 -0
  339. package/src/plugins/builtin/mna/distress/pane.tsx +606 -0
  340. package/src/plugins/builtin/mna/index.tsx +15 -0
  341. package/src/plugins/builtin/mna/pane.tsx +42 -1
  342. package/src/plugins/builtin/news/index.tsx +62 -16
  343. package/src/plugins/builtin/news/wire/filter-articles.ts +54 -0
  344. package/src/plugins/builtin/news/wire/index.tsx +37 -3
  345. package/src/plugins/builtin/news/wire/industry-pane.tsx +29 -13
  346. package/src/plugins/builtin/news/wire/mutes.ts +253 -0
  347. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +117 -0
  348. package/src/plugins/builtin/news/wire/news/footer.ts +4 -0
  349. package/src/plugins/builtin/news/wire/news/list-search.ts +34 -0
  350. package/src/plugins/builtin/news/wire/news/pop-out.tsx +158 -0
  351. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +64 -19
  352. package/src/plugins/builtin/openfda/client.ts +300 -0
  353. package/src/plugins/builtin/openfda/index.tsx +71 -0
  354. package/src/plugins/builtin/openfda/pane.tsx +382 -0
  355. package/src/plugins/builtin/openfda/types.ts +76 -0
  356. package/src/plugins/builtin/options/footer.ts +1 -0
  357. package/src/plugins/builtin/options/view.tsx +1 -1
  358. package/src/plugins/builtin/options-calculator/pane.tsx +0 -1
  359. package/src/plugins/builtin/options-positioning/index.tsx +4 -2
  360. package/src/plugins/builtin/options-positioning/pane.tsx +1 -1
  361. package/src/plugins/builtin/pe-band/client.ts +61 -0
  362. package/src/plugins/builtin/pe-band/headless.ts +51 -0
  363. package/src/plugins/builtin/pe-band/index.tsx +20 -0
  364. package/src/plugins/builtin/pe-band/model.ts +267 -0
  365. package/src/plugins/builtin/pe-band/pane.tsx +159 -0
  366. package/src/plugins/builtin/perps/client.ts +66 -0
  367. package/src/plugins/builtin/perps/equity-row.tsx +28 -0
  368. package/src/plugins/builtin/perps/headless.ts +34 -0
  369. package/src/plugins/builtin/perps/index.tsx +14 -0
  370. package/src/plugins/builtin/perps/model.ts +58 -0
  371. package/src/plugins/builtin/perps/pane.tsx +185 -0
  372. package/src/plugins/builtin/plugin-marketplace/pane.tsx +0 -1
  373. package/src/plugins/builtin/portfolio-list/cli/render.ts +177 -87
  374. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +6 -20
  375. package/src/plugins/builtin/portfolio-list/command-bar.ts +6 -2
  376. package/src/plugins/builtin/portfolio-list/headless.ts +237 -0
  377. package/src/plugins/builtin/portfolio-list/index.tsx +2 -0
  378. package/src/plugins/builtin/portfolio-list/mutations.ts +70 -1
  379. package/src/plugins/builtin/portfolio-list/pane/data.ts +17 -0
  380. package/src/plugins/builtin/portfolio-list/pane/index.tsx +88 -5
  381. package/src/plugins/builtin/portfolio-list/pane/remove-ticker.ts +85 -0
  382. package/src/plugins/builtin/portfolio-list/pane/streaming.ts +18 -12
  383. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +6 -0
  384. package/src/plugins/builtin/power/client.ts +100 -0
  385. package/src/plugins/builtin/power/detail.tsx +82 -0
  386. package/src/plugins/builtin/power/evidence.ts +56 -0
  387. package/src/plugins/builtin/power/generation.tsx +37 -0
  388. package/src/plugins/builtin/power/headless.ts +43 -0
  389. package/src/plugins/builtin/power/index.tsx +16 -0
  390. package/src/plugins/builtin/power/model.ts +89 -0
  391. package/src/plugins/builtin/power/pane.tsx +209 -0
  392. package/src/plugins/builtin/power/query.ts +25 -0
  393. package/src/plugins/builtin/relative-rotation/pane.tsx +3 -3
  394. package/src/plugins/builtin/research/analyst-pane.tsx +1 -1
  395. package/src/plugins/builtin/research/corporate-actions-pane.tsx +13 -4
  396. package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +11 -9
  397. package/src/plugins/builtin/research-search/pane.tsx +21 -7
  398. package/src/plugins/builtin/revenue-breakdown/pane.tsx +1 -1
  399. package/src/plugins/builtin/reverse-dcf/client.ts +35 -0
  400. package/src/plugins/builtin/reverse-dcf/headless.ts +44 -0
  401. package/src/plugins/builtin/reverse-dcf/index.tsx +20 -0
  402. package/src/plugins/builtin/reverse-dcf/model.ts +87 -0
  403. package/src/plugins/builtin/reverse-dcf/pane.tsx +107 -0
  404. package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
  405. package/src/plugins/builtin/scanner/denied.tsx +4 -2
  406. package/src/plugins/builtin/scanner/feed.ts +1 -0
  407. package/src/plugins/builtin/scanner/flow-pane.tsx +3 -2
  408. package/src/plugins/builtin/scanner/hilo-pane.tsx +1 -1
  409. package/src/plugins/builtin/seasonality/client.ts +4 -1
  410. package/src/plugins/builtin/sec/command-bar-search.ts +217 -0
  411. package/src/plugins/builtin/sec/forms.ts +54 -0
  412. package/src/plugins/builtin/sec/headless.ts +31 -5
  413. package/src/plugins/builtin/sec/index.tsx +67 -11
  414. package/src/plugins/builtin/sec/model.ts +35 -5
  415. package/src/plugins/builtin/sectors/headless.ts +1 -0
  416. package/src/plugins/builtin/sectors/index.tsx +72 -26
  417. package/src/plugins/builtin/shared/cloud-upgrade.ts +143 -39
  418. package/src/plugins/builtin/shared/pro-feature-wall.tsx +8 -4
  419. package/src/plugins/builtin/shared/research-cells.tsx +107 -0
  420. package/src/plugins/builtin/shared/series-list-detail.tsx +5 -1
  421. package/src/plugins/builtin/shared/ticker-request.ts +1 -1
  422. package/src/plugins/builtin/shared/wall-teaser-catalog.ts +80 -0
  423. package/src/plugins/builtin/shared/wall-teaser.tsx +192 -0
  424. package/src/plugins/builtin/shared/wall-view-lifecycle.ts +43 -0
  425. package/src/plugins/builtin/short-interest/index.tsx +31 -0
  426. package/src/plugins/builtin/short-interest/watch-client.ts +70 -0
  427. package/src/plugins/builtin/short-interest/watch-headless.ts +49 -0
  428. package/src/plugins/builtin/short-interest/watch-model.ts +185 -0
  429. package/src/plugins/builtin/short-interest/watch-pane.tsx +169 -0
  430. package/src/plugins/builtin/short-volume/pane.tsx +1 -1
  431. package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
  432. package/src/plugins/builtin/supply-chain/client.ts +104 -0
  433. package/src/plugins/builtin/supply-chain/disclosure-quote.tsx +17 -0
  434. package/src/plugins/builtin/supply-chain/evidence.ts +88 -0
  435. package/src/plugins/builtin/supply-chain/flow-layout.ts +133 -0
  436. package/src/plugins/builtin/supply-chain/flow.tsx +323 -0
  437. package/src/plugins/builtin/supply-chain/graph-client.ts +83 -0
  438. package/src/plugins/builtin/supply-chain/graph-evidence.ts +25 -0
  439. package/src/plugins/builtin/supply-chain/graph-model.ts +45 -0
  440. package/src/plugins/builtin/supply-chain/graph-pane.tsx +218 -0
  441. package/src/plugins/builtin/supply-chain/graph.tsx +163 -0
  442. package/src/plugins/builtin/supply-chain/headless.ts +105 -0
  443. package/src/plugins/builtin/supply-chain/index.tsx +17 -0
  444. package/src/plugins/builtin/supply-chain/model.ts +145 -0
  445. package/src/plugins/builtin/supply-chain/pane.tsx +276 -0
  446. package/src/plugins/builtin/supply-chain/row-evidence.tsx +96 -0
  447. package/src/plugins/builtin/supply-chain/trust.ts +34 -0
  448. package/src/plugins/builtin/themes/client.ts +39 -0
  449. package/src/plugins/builtin/themes/headless.ts +38 -0
  450. package/src/plugins/builtin/themes/model.ts +92 -0
  451. package/src/plugins/builtin/themes/pane.tsx +125 -0
  452. package/src/plugins/builtin/thirteenf/pane.tsx +6 -15
  453. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +5 -6
  454. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +201 -0
  455. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +327 -0
  456. package/src/plugins/builtin/ticker-detail/financials/period-end-history.ts +5 -2
  457. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +60 -12
  458. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +2 -1
  459. package/src/plugins/builtin/ticker-detail/headless.ts +7 -2
  460. package/src/plugins/builtin/ticker-detail/index.tsx +22 -0
  461. package/src/plugins/builtin/ticker-detail/overview/components.tsx +53 -30
  462. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +2 -0
  463. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +112 -47
  464. package/src/plugins/builtin/ticker-detail/pane.tsx +50 -6
  465. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +21 -42
  466. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +3 -0
  467. package/src/plugins/builtin/time-sales/pane.tsx +1 -1
  468. package/src/plugins/builtin/world-indices/indices.ts +1 -0
  469. package/src/plugins/catalog-browser.ts +40 -0
  470. package/src/plugins/catalog-manifest.ts +3 -0
  471. package/src/plugins/catalog-ui.ts +6 -0
  472. package/src/plugins/current-target.ts +0 -9
  473. package/src/public/__snapshots__/public-api.test.ts.snap +1 -0
  474. package/src/renderers/browser/cloud-transport.ts +66 -1
  475. package/src/renderers/browser/main.tsx +2 -0
  476. package/src/renderers/dom/data-table/row.tsx +2 -0
  477. package/src/renderers/dom/desktop/controls.tsx +38 -25
  478. package/src/renderers/dom/desktop/icons.tsx +8 -0
  479. package/src/renderers/dom/host/chart-surface.tsx +36 -2
  480. package/src/renderers/dom/host/focus-scope.ts +11 -1
  481. package/src/renderers/dom/styles.css +28 -0
  482. package/src/renderers/electrobun/view/backend-rpc.ts +3 -2
  483. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  484. package/src/renderers/electrobun/view/http-fetch.ts +2 -0
  485. package/src/renderers/electrobun/view/main.tsx +3 -1
  486. package/src/renderers/electrobun/view/rpc-timeout.ts +59 -0
  487. package/src/renderers/opentui/start.tsx +3 -0
  488. package/src/shares/api.ts +3 -1
  489. package/src/sources/gloomberb-cloud/index.ts +47 -13
  490. package/src/sources/history-result.ts +1 -0
  491. package/src/sources/history-retention.ts +16 -0
  492. package/src/sources/provider-router/history.ts +4 -2
  493. package/src/sources/sec-edgar/identity.ts +36 -0
  494. package/src/sources/sec-edgar.ts +1 -34
  495. package/src/state/app/context/index.tsx +8 -0
  496. package/src/sync/core-contributors.ts +20 -2
  497. package/src/telemetry/attention-counts.ts +164 -0
  498. package/src/telemetry/live-config.ts +7 -0
  499. package/src/test-support/rpc-loopback.ts +7 -3
  500. package/src/test-support/shell-pane-keys.tsx +34 -0
  501. package/src/tickers/instrument-kind.ts +12 -6
  502. package/src/tickers/open-target.ts +1 -1
  503. package/src/tickers/search/asset-classes.ts +91 -0
  504. package/src/tickers/search/index.ts +62 -15
  505. package/src/tickers/search/ranking.ts +22 -3
  506. package/src/tickers/search/upsert.ts +4 -1
  507. package/src/time-series/live-bars.ts +8 -3
  508. package/src/time-series/resolve.ts +224 -17
  509. package/src/time-series/spec.ts +1 -0
  510. package/src/time-series/types.ts +9 -1
  511. package/src/types/config.ts +23 -1
  512. package/src/types/data-provider.ts +2 -0
  513. package/src/types/headless.ts +17 -0
  514. package/src/types/price-history.ts +2 -0
  515. package/src/ui/host.tsx +6 -0
  516. package/src/utils/double-escape-close.ts +20 -0
  517. package/src/utils/exchanges.ts +1 -0
  518. package/src/utils/html-entities.ts +15 -0
  519. package/src/utils/http-proxy-response.ts +44 -7
  520. package/src/utils/plugin-proxy-hosts.json +3 -0
  521. package/src/utils/price-history.ts +35 -1
  522. package/src/utils/sec.ts +21 -2
  523. package/src/utils/text-wrap.ts +13 -8
  524. 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 { ScrollBoxRenderable } from "../../../ui";
9
- import { EmptyState, FeedDataTableStackView, QueryBar, Spinner, StatGrid, useExternalLinkFooter, usePaneNoticeFooter, useTableLoadMore, type FeedDataTableItem, type StatItem } from "../../../components";
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
- insiderTransactionId,
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 { isAmendedInsiderFiling, relevantInsiderAmendments } from "./amendments";
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" : undefined,
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 [selectedIdx, setSelectedIdx] = usePluginPaneState<number>(`insider:selectedIdx:${tickerKey}`, 0);
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 loadMore = useTableLoadMore(
160
- scrollRef,
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
- // Apply name filter
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 feedItems = useMemo(() => toFeedItems(parsed), [parsed]);
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 Set<string>();
245
+ const names = new Map<string, string>();
217
246
  for (const entry of allParsed) {
218
247
  const name = insiderReportedName(entry);
219
- if (name) names.add(name);
248
+ if (name) names.set(name, formatInsiderName(name));
220
249
  }
221
- if (nameFilter) names.add(nameFilter);
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((name) => ({ value: name, label: name })),
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 selectedFilterName = parsed[selectedIdx] ? insiderReportedName(parsed[selectedIdx]!) : null;
229
- const openFiling = openItemId
230
- ? parsed.find((entry) => insiderTransactionId(entry) === openItemId)?.filing ?? null
231
- : null;
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
- setSelectedIdx(0);
236
- }, [setNameFilter, setSelectedIdx]);
270
+ setSelectedId(null);
271
+ }, [setNameFilter, setSelectedId]);
237
272
  const selectInsider = useCallback((value: string) => {
238
273
  setNameFilter(value === ALL_INSIDERS ? null : value);
239
- setSelectedIdx(0);
240
- }, [setNameFilter, setSelectedIdx]);
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
- setSelectedIdx(0);
244
- }, [setNameFilter, setSelectedIdx]);
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
- <FeedDataTableStackView
304
- width={width}
305
- height={height}
374
+ <DataTableStackView<InsiderTableRow, InsiderColumn>
306
375
  focused={focused}
307
- items={feedItems}
308
- selectedIdx={selectedIdx}
309
- onSelect={setSelectedIdx}
310
- openItemId={openItemId}
311
- onOpenItemIdChange={setOpenItemId}
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
- id: "insider",
317
- label: "Insider",
318
- value: nameFilter ?? ALL_INSIDERS,
319
- defaultValue: ALL_INSIDERS,
320
- options: insiderOptions,
321
- onChange: selectInsider,
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
- sourceLabel="Insider"
327
- titleLabel="Transaction"
328
- emptyStateTitle={nameFilter
329
- ? "No insider transactions for this filter."
330
- : pendingCount > 0
331
- ? "Loading Form 4 transactions..."
332
- : "No insider transactions."}
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?: Array<{ name: string; cik: string; title: string }>;
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: Array<{ name: string; cik: string; title: string }>;
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) => ({ name: tagText(match[1]!, "rptOwnerName"), cik: tagText(match[1]!, "rptOwnerCik"), title: ownerTitle(match[1]!) }));
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
- return insiderReportedName(entry)?.toLocaleLowerCase() === normalized
44
- || (entry.disclosure?.reportingOwners ?? entry.transaction?.reportingOwners ?? [])
45
- .some((owner) => owner.name.toLocaleLowerCase() === normalized);
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,