@gloom-sh/terminal 0.18.5 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (570) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/package.json +1 -1
  4. package/patches/electrobun@1.18.1.patch +163 -8
  5. package/src/api-client/app-attention.ts +3 -0
  6. package/src/api-client/beneficial-owners.ts +3 -0
  7. package/src/api-client/company-kpis.ts +3 -0
  8. package/src/api-client/cot.ts +6 -2
  9. package/src/api-client/credit-documents.ts +6 -0
  10. package/src/api-client/data.ts +19 -7
  11. package/src/api-client/equity-screener.ts +24 -0
  12. package/src/api-client/fund-facts.ts +45 -4
  13. package/src/api-client/hiring.ts +3 -0
  14. package/src/api-client/index.ts +15 -0
  15. package/src/api-client/ipo.ts +24 -1
  16. package/src/api-client/market-discovery.ts +10 -0
  17. package/src/api-client/market-movers.ts +7 -0
  18. package/src/api-client/members.ts +1 -1
  19. package/src/api-client/mna.ts +3 -0
  20. package/src/api-client/paths.ts +9 -1
  21. package/src/api-client/perps.ts +43 -0
  22. package/src/api-client/research-activity.ts +3 -2
  23. package/src/api-client/roadmap.ts +85 -0
  24. package/src/api-client/types.ts +82 -2
  25. package/src/app/pane-runtime/plugin-bindings.ts +9 -5
  26. package/src/app/runtime/broker-import.ts +0 -2
  27. package/src/app/runtime/plugin-bindings.ts +73 -56
  28. package/src/architecture/core-builtin-allowlist.json +2 -2
  29. package/src/architecture/pane-tabs-allowlist.json +3 -0
  30. package/src/architecture/ttl-caches-allowlist.json +19 -0
  31. package/src/brokers/sync-broker-instance.ts +2 -8
  32. package/src/capabilities/chart-series.ts +1 -1
  33. package/src/cli/commands/crypto-hints.ts +40 -24
  34. package/src/cli/commands/market.ts +107 -38
  35. package/src/cli/commands/news-search.ts +22 -0
  36. package/src/cli/commands/options-chain.ts +48 -7
  37. package/src/cli/commands/options-leaps.ts +9 -3
  38. package/src/cli/commands/overview.ts +147 -32
  39. package/src/cli/commands/search.ts +21 -1
  40. package/src/cli/commands/themes.ts +11 -2
  41. package/src/cli/commands/ticker.ts +87 -36
  42. package/src/cli/company-name-hint.ts +41 -0
  43. package/src/cli/earnings-calendar.ts +1 -1
  44. package/src/cli/errors.ts +25 -2
  45. package/src/cli/glossary.ts +31 -2
  46. package/src/cli/history-rows.ts +57 -9
  47. package/src/cli/index-roots.ts +0 -7
  48. package/src/cli/index.ts +2 -0
  49. package/src/cli/listing-arg.ts +235 -60
  50. package/src/cli/output-file.ts +68 -0
  51. package/src/cli/pane-functions/capabilities.ts +4 -0
  52. package/src/cli/pane-functions/catalog.ts +80 -7
  53. package/src/cli/pane-functions/discovery.ts +21 -1
  54. package/src/cli/pane-functions/dom.ts +13 -3
  55. package/src/cli/pane-functions/freshness.ts +93 -4
  56. package/src/cli/pane-functions/function-help.ts +20 -10
  57. package/src/cli/pane-functions/headless.ts +17 -7
  58. package/src/cli/pane-functions/index.ts +54 -16
  59. package/src/cli/pane-functions/market-session.ts +66 -4
  60. package/src/cli/pane-functions/options.ts +19 -1
  61. package/src/cli/pane-functions/resolver.ts +19 -8
  62. package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
  63. package/src/cli/pane-functions/screenshot-types.ts +5 -0
  64. package/src/cli/quote-answer.ts +22 -11
  65. package/src/cli/saved-copy-persistence.ts +88 -0
  66. package/src/cli/ticker-resolution.ts +3 -2
  67. package/src/components/chart/composite/composite-chart.tsx +4 -0
  68. package/src/components/chart/composite/interactions.ts +6 -0
  69. package/src/components/chart/composite/legend.tsx +45 -7
  70. package/src/components/chart/composite/panel-gestures.ts +33 -0
  71. package/src/components/chart/composite/panel-surface.tsx +52 -3
  72. package/src/components/chart/composite/rasterizer.ts +24 -13
  73. package/src/components/chart/composite/scene.ts +39 -4
  74. package/src/components/chart/composite/series-cues.ts +14 -0
  75. package/src/components/chart/composite/text-renderer.ts +9 -2
  76. package/src/components/chart/composite/tool-catalog.ts +36 -0
  77. package/src/components/chart/composite/tools.ts +203 -44
  78. package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
  79. package/src/components/command-bar/assist/inventory.ts +2 -1
  80. package/src/components/command-bar/commands/direct/index.ts +28 -13
  81. package/src/components/command-bar/list/model.ts +5 -0
  82. package/src/components/command-bar/pane-templates/items.ts +3 -2
  83. package/src/components/command-bar/routes/root/runtime.ts +6 -7
  84. package/src/components/command-bar/selection-runtime.ts +52 -3
  85. package/src/components/command-bar/surface/index.tsx +2 -0
  86. package/src/components/command-bar/workflow/collection-actions.ts +3 -3
  87. package/src/components/layout/pane/capture-stamp.ts +2 -2
  88. package/src/components/layout/pane/footer/index.tsx +22 -6
  89. package/src/components/layout/pane/footer/model.ts +83 -15
  90. package/src/components/layout/pane/status-footer.ts +35 -2
  91. package/src/components/layout/status-widget-space.ts +9 -0
  92. package/src/components/onboarding/onboarding-wizard.tsx +4 -1
  93. package/src/components/onboarding/portfolio-step/positions-panel.tsx +12 -7
  94. package/src/components/onboarding/wizard-broker-sync.ts +0 -1
  95. package/src/components/onboarding/wizard-positions.ts +3 -3
  96. package/src/components/ticker/list-table-view.tsx +8 -21
  97. package/src/components/ui/choice-dialog.tsx +23 -18
  98. package/src/components/ui/choice-groups.ts +18 -0
  99. package/src/components/ui/data-table/opentui/index.tsx +9 -7
  100. package/src/components/ui/data-table/opentui/model.ts +26 -1
  101. package/src/components/ui/data-table/types.ts +2 -0
  102. package/src/components/ui/display.tsx +18 -3
  103. package/src/components/ui/icon.tsx +4 -1
  104. package/src/components/ui/list-view.tsx +8 -4
  105. package/src/components/ui/query-bar-keys.tsx +2 -0
  106. package/src/components/ui/query-bar.tsx +1 -0
  107. package/src/components/ui/select-button.tsx +5 -1
  108. package/src/components/ui/select-field.tsx +1 -0
  109. package/src/components/ui/table-layout.ts +8 -5
  110. package/src/core/app-runtime.ts +2 -0
  111. package/src/core/app-services.ts +2 -2
  112. package/src/data/config/chart-settings.ts +1 -1
  113. package/src/data/json-ticker-repository.ts +17 -8
  114. package/src/data/plugin-cache.ts +5 -3
  115. package/src/data/saved-copies.ts +8 -0
  116. package/src/data/ticker-repository.ts +16 -9
  117. package/src/data/ticker-writes.ts +81 -0
  118. package/src/data/ttl-cache.ts +165 -0
  119. package/src/docs/snapshot.json +103 -142
  120. package/src/i18n/es.ts +20 -6
  121. package/src/i18n/ja.ts +20 -6
  122. package/src/i18n/ko.ts +20 -6
  123. package/src/i18n/zh-cn.ts +20 -6
  124. package/src/i18n/zh-tw.ts +20 -6
  125. package/src/market-data/depositary-pair.ts +27 -3
  126. package/src/market-data/fund-facts.ts +50 -0
  127. package/src/market-data/market/change-basis.ts +16 -3
  128. package/src/market-data/market/format.ts +25 -0
  129. package/src/market-data/market/freshness.ts +2 -1
  130. package/src/market-data/options-alternatives.ts +81 -4
  131. package/src/market-data/quotes/metadata.ts +7 -0
  132. package/src/news/aggregator.ts +119 -9
  133. package/src/news/cloud-session.ts +21 -0
  134. package/src/news/hooks.ts +1 -0
  135. package/src/news/news-model.ts +1 -0
  136. package/src/news/types.ts +17 -0
  137. package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
  138. package/src/plugins/builtin/alerts/alert-log.ts +33 -0
  139. package/src/plugins/builtin/alerts/calendar-events.ts +10 -4
  140. package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
  141. package/src/plugins/builtin/alerts/cli.ts +362 -80
  142. package/src/plugins/builtin/alerts/command.ts +25 -1
  143. package/src/plugins/builtin/alerts/event-rows.ts +24 -3
  144. package/src/plugins/builtin/alerts/events-pane.tsx +34 -10
  145. package/src/plugins/builtin/alerts/fired-log.ts +73 -9
  146. package/src/plugins/builtin/alerts/format.ts +75 -5
  147. package/src/plugins/builtin/alerts/headless.ts +34 -12
  148. package/src/plugins/builtin/alerts/history-rows.ts +3 -2
  149. package/src/plugins/builtin/alerts/history.ts +1 -1
  150. package/src/plugins/builtin/alerts/index.tsx +79 -16
  151. package/src/plugins/builtin/alerts/metric-command.ts +77 -4
  152. package/src/plugins/builtin/alerts/metric-data.ts +54 -13
  153. package/src/plugins/builtin/alerts/metric-model.ts +163 -14
  154. package/src/plugins/builtin/alerts/metric-parity-fixture.json +1 -1
  155. package/src/plugins/builtin/alerts/pane.tsx +61 -23
  156. package/src/plugins/builtin/alerts/quotes.ts +6 -0
  157. package/src/plugins/builtin/alerts/storage.ts +12 -8
  158. package/src/plugins/builtin/alerts/types.ts +4 -0
  159. package/src/plugins/builtin/analytics/index.tsx +2 -1
  160. package/src/plugins/builtin/analytics/risk-client.ts +16 -5
  161. package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
  162. package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
  163. package/src/plugins/builtin/analytics/risk-model.ts +55 -9
  164. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
  165. package/src/plugins/builtin/attention/index.tsx +2 -1
  166. package/src/plugins/builtin/attention/listing.ts +14 -0
  167. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  168. package/src/plugins/builtin/buildout/cells.tsx +12 -4
  169. package/src/plugins/builtin/buildout/format.ts +38 -0
  170. package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
  171. package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
  172. package/src/plugins/builtin/buildout/table-model.ts +23 -2
  173. package/src/plugins/builtin/builtin-plugin-meta.ts +6 -2
  174. package/src/plugins/builtin/cds/client.ts +142 -30
  175. package/src/plugins/builtin/cds/headless.ts +35 -8
  176. package/src/plugins/builtin/cds/model.ts +145 -18
  177. package/src/plugins/builtin/cds/pane.tsx +16 -5
  178. package/src/plugins/builtin/changelog/index.tsx +9 -6
  179. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +3 -0
  180. package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
  181. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -0
  182. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -2
  183. package/src/plugins/builtin/chart-composer/follow-binding.ts +14 -2
  184. package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -1
  185. package/src/plugins/builtin/chart-composer/headless.ts +6 -1
  186. package/src/plugins/builtin/chart-composer/index.tsx +5 -6
  187. package/src/plugins/builtin/chart-composer/pane.tsx +37 -8
  188. package/src/plugins/builtin/chart-composer/settings.ts +27 -0
  189. package/src/plugins/builtin/chat/content/command-suggestions.ts +1 -0
  190. package/src/plugins/builtin/chat/controller/index.ts +5 -1
  191. package/src/plugins/builtin/chat/controller/notifications.ts +33 -3
  192. package/src/plugins/builtin/chat/controller/state.ts +25 -2
  193. package/src/plugins/builtin/chat/controller/unread-people.ts +89 -23
  194. package/src/plugins/builtin/chat/controller/utils.ts +7 -2
  195. package/src/plugins/builtin/chat/pane-links/commands.ts +7 -1
  196. package/src/plugins/builtin/chat/status-preview.ts +79 -0
  197. package/src/plugins/builtin/chat/status-widget.tsx +147 -10
  198. package/src/plugins/builtin/chat/unread-badge.ts +51 -10
  199. package/src/plugins/builtin/clinical-trials/client.ts +49 -4
  200. package/src/plugins/builtin/clinical-trials/model.ts +86 -31
  201. package/src/plugins/builtin/clinical-trials/pane.tsx +61 -49
  202. package/src/plugins/builtin/clinical-trials/sponsor-listing.ts +90 -0
  203. package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +95 -15
  204. package/src/plugins/builtin/clinical-trials/trial-pages.ts +50 -0
  205. package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
  206. package/src/plugins/builtin/company-attention/building.tsx +5 -1
  207. package/src/plugins/builtin/company-attention/client.ts +8 -9
  208. package/src/plugins/builtin/company-attention/evidence.ts +4 -1
  209. package/src/plugins/builtin/company-attention/headless.ts +2 -1
  210. package/src/plugins/builtin/company-attention/moves.tsx +5 -1
  211. package/src/plugins/builtin/company-attention/pane.tsx +12 -2
  212. package/src/plugins/builtin/company-attention/signals.tsx +5 -1
  213. package/src/plugins/builtin/company-kpis/headless.ts +8 -3
  214. package/src/plugins/builtin/company-kpis/model.ts +73 -5
  215. package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
  216. package/src/plugins/builtin/composite-plugins.ts +2 -1
  217. package/src/plugins/builtin/correlation/headless.ts +13 -5
  218. package/src/plugins/builtin/correlation/history.ts +22 -0
  219. package/src/plugins/builtin/cot/client.ts +22 -18
  220. package/src/plugins/builtin/cot/headless.ts +15 -10
  221. package/src/plugins/builtin/cot/model.ts +14 -1
  222. package/src/plugins/builtin/cot/pane.tsx +22 -14
  223. package/src/plugins/builtin/credit-boards/client.ts +6 -10
  224. package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
  225. package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
  226. package/src/plugins/builtin/credit-conditions/model.ts +4 -0
  227. package/src/plugins/builtin/credit-conditions/series.ts +3 -1
  228. package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
  229. package/src/plugins/builtin/credit-documents/export.ts +173 -0
  230. package/src/plugins/builtin/credit-documents/headless.ts +10 -10
  231. package/src/plugins/builtin/credit-documents/model.ts +104 -14
  232. package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
  233. package/src/plugins/builtin/custom-view/sources.ts +2 -0
  234. package/src/plugins/builtin/dividend-yield/client.ts +131 -23
  235. package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
  236. package/src/plugins/builtin/dividend-yield/model.ts +8 -5
  237. package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
  238. package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
  239. package/src/plugins/builtin/dividend-yield/types.ts +20 -2
  240. package/src/plugins/builtin/dividend-yield/view.ts +30 -2
  241. package/src/plugins/builtin/docs/targets.ts +5 -1
  242. package/src/plugins/builtin/doe/headless.ts +3 -2
  243. package/src/plugins/builtin/doe/pane.tsx +13 -3
  244. package/src/plugins/builtin/earnings/board-model.ts +16 -15
  245. package/src/plugins/builtin/earnings/board.tsx +62 -71
  246. package/src/plugins/builtin/earnings/headless.ts +40 -4
  247. package/src/plugins/builtin/earnings/scope.ts +31 -43
  248. package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
  249. package/src/plugins/builtin/earnings-calls/data.ts +18 -6
  250. package/src/plugins/builtin/earnings-calls/format.ts +31 -2
  251. package/src/plugins/builtin/earnings-calls/headless.ts +20 -5
  252. package/src/plugins/builtin/earnings-calls/model.ts +6 -1
  253. package/src/plugins/builtin/earnings-calls/pane.tsx +12 -4
  254. package/src/plugins/builtin/econ/calendar-model.ts +48 -1
  255. package/src/plugins/builtin/econ/calendar-source.ts +4 -2
  256. package/src/plugins/builtin/econ/detail-view.tsx +2 -2
  257. package/src/plugins/builtin/econ/figures.ts +48 -0
  258. package/src/plugins/builtin/econ/index.tsx +14 -5
  259. package/src/plugins/builtin/econ/print-watch.ts +3 -1
  260. package/src/plugins/builtin/econ/types.ts +2 -0
  261. package/src/plugins/builtin/econ-statistics/client.ts +11 -0
  262. package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
  263. package/src/plugins/builtin/econ-statistics/headless.ts +16 -6
  264. package/src/plugins/builtin/econ-statistics/pane.tsx +8 -4
  265. package/src/plugins/builtin/econ-statistics/stats.ts +39 -9
  266. package/src/plugins/builtin/equity-screener/categories.ts +179 -0
  267. package/src/plugins/builtin/equity-screener/client.ts +4 -2
  268. package/src/plugins/builtin/equity-screener/headless.ts +144 -13
  269. package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
  270. package/src/plugins/builtin/equity-screener/model.ts +99 -10
  271. package/src/plugins/builtin/equity-screener/pane.tsx +68 -13
  272. package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
  273. package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
  274. package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
  275. package/src/plugins/builtin/fx-matrix/client.ts +6 -3
  276. package/src/plugins/builtin/fx-matrix/headless.ts +1 -1
  277. package/src/plugins/builtin/fx-matrix/index.tsx +4 -2
  278. package/src/plugins/builtin/fx-matrix/live-legs.ts +42 -3
  279. package/src/plugins/builtin/gpu/headless.ts +8 -2
  280. package/src/plugins/builtin/help/function-index.ts +4 -1
  281. package/src/plugins/builtin/help/index.tsx +62 -59
  282. package/src/plugins/builtin/help/shortcut-model.ts +1 -1
  283. package/src/plugins/builtin/help/shortcut-table.tsx +5 -6
  284. package/src/plugins/builtin/help/table-cells.tsx +13 -2
  285. package/src/plugins/builtin/holders/beneficial-client.ts +5 -1
  286. package/src/plugins/builtin/holders/beneficial-view.tsx +3 -0
  287. package/src/plugins/builtin/holders/fund-holders.ts +51 -0
  288. package/src/plugins/builtin/holders/headless.ts +22 -3
  289. package/src/plugins/builtin/holders/pane.tsx +3 -0
  290. package/src/plugins/builtin/holders/report-rows.ts +4 -1
  291. package/src/plugins/builtin/insider/headless.ts +2 -2
  292. package/src/plugins/builtin/ipo-calendar/client.ts +18 -2
  293. package/src/plugins/builtin/ipo-calendar/headless.ts +75 -11
  294. package/src/plugins/builtin/ipo-calendar/listing-ipo.ts +160 -0
  295. package/src/plugins/builtin/ipo-calendar/model.ts +98 -2
  296. package/src/plugins/builtin/iv-history/client.ts +52 -11
  297. package/src/plugins/builtin/iv-history/format.ts +36 -23
  298. package/src/plugins/builtin/iv-history/headless.ts +33 -18
  299. package/src/plugins/builtin/iv-history/index.tsx +2 -2
  300. package/src/plugins/builtin/iv-history/model.ts +163 -27
  301. package/src/plugins/builtin/iv-history/pane.tsx +10 -6
  302. package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
  303. package/src/plugins/builtin/iv-history/universe.ts +5 -1
  304. package/src/plugins/builtin/jobs/client.ts +25 -29
  305. package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
  306. package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
  307. package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
  308. package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
  309. package/src/plugins/builtin/macro-day/headless.ts +12 -5
  310. package/src/plugins/builtin/macro-day/index.tsx +1 -1
  311. package/src/plugins/builtin/macro-day/model.ts +39 -5
  312. package/src/plugins/builtin/macro-day/pane.tsx +27 -19
  313. package/src/plugins/builtin/macro-day/release-table.ts +2 -2
  314. package/src/plugins/builtin/market-heatmap/data.ts +8 -33
  315. package/src/plugins/builtin/market-movers/client.ts +27 -7
  316. package/src/plugins/builtin/market-movers/headless.ts +34 -12
  317. package/src/plugins/builtin/market-movers/index.tsx +26 -14
  318. package/src/plugins/builtin/market-movers/min-float.ts +74 -0
  319. package/src/plugins/builtin/market-movers/model.ts +4 -1
  320. package/src/plugins/builtin/market-movers/screener.ts +22 -4
  321. package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
  322. package/src/plugins/builtin/market-movers/session-table.ts +4 -3
  323. package/src/plugins/builtin/market-movers/table.ts +17 -5
  324. package/src/plugins/builtin/members/breadth-pane.tsx +2 -1
  325. package/src/plugins/builtin/mna/headless.ts +2 -0
  326. package/src/plugins/builtin/mna/pane.tsx +3 -1
  327. package/src/plugins/builtin/news/headless.ts +3 -1
  328. package/src/plugins/builtin/news/index.tsx +4 -0
  329. package/src/plugins/builtin/news/wire/categories.ts +19 -3
  330. package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
  331. package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
  332. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
  333. package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
  334. package/src/plugins/builtin/openfda/client.ts +14 -2
  335. package/src/plugins/builtin/openfda/index.tsx +3 -2
  336. package/src/plugins/builtin/openfda/model.ts +9 -0
  337. package/src/plugins/builtin/openfda/pane.tsx +42 -6
  338. package/src/plugins/builtin/options/analytics.ts +29 -5
  339. package/src/plugins/builtin/options/enrichment.ts +17 -5
  340. package/src/plugins/builtin/options/table.ts +5 -2
  341. package/src/plugins/builtin/options/types.ts +2 -0
  342. package/src/plugins/builtin/options/view.tsx +17 -4
  343. package/src/plugins/builtin/options-positioning/client.ts +11 -0
  344. package/src/plugins/builtin/options-positioning/headless.ts +21 -8
  345. package/src/plugins/builtin/options-positioning/index.tsx +1 -1
  346. package/src/plugins/builtin/options-positioning/model.ts +78 -9
  347. package/src/plugins/builtin/options-positioning/pane.tsx +21 -6
  348. package/src/plugins/builtin/options-scenario/client.ts +116 -47
  349. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
  350. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
  351. package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
  352. package/src/plugins/builtin/options-scenario/csp.ts +8 -2
  353. package/src/plugins/builtin/options-scenario/index.tsx +9 -7
  354. package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
  355. package/src/plugins/builtin/pe-band/model.ts +12 -49
  356. package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
  357. package/src/plugins/builtin/perps/cascade.ts +236 -0
  358. package/src/plugins/builtin/perps/client.ts +56 -2
  359. package/src/plugins/builtin/perps/headless.ts +65 -9
  360. package/src/plugins/builtin/perps/index.tsx +7 -5
  361. package/src/plugins/builtin/perps/model.ts +18 -4
  362. package/src/plugins/builtin/perps/pane.tsx +19 -22
  363. package/src/plugins/builtin/perps/status-line.ts +25 -0
  364. package/src/plugins/builtin/plugin-module.ts +5 -1
  365. package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
  366. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
  367. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
  368. package/src/plugins/builtin/portfolio-list/cli/render.ts +7 -5
  369. package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
  370. package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -7
  371. package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
  372. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
  373. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +0 -14
  374. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +5 -2
  375. package/src/plugins/builtin/portfolio-list/table.tsx +15 -9
  376. package/src/plugins/builtin/primary-dealers/client.ts +13 -38
  377. package/src/plugins/builtin/realized-vol/client.ts +13 -5
  378. package/src/plugins/builtin/realized-vol/evidence.ts +29 -4
  379. package/src/plugins/builtin/realized-vol/headless.ts +8 -4
  380. package/src/plugins/builtin/realized-vol/index.tsx +2 -2
  381. package/src/plugins/builtin/realized-vol/model.ts +37 -6
  382. package/src/plugins/builtin/realized-vol/pane.tsx +11 -9
  383. package/src/plugins/builtin/relative-rotation/client.ts +18 -4
  384. package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
  385. package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
  386. package/src/plugins/builtin/relative-rotation/model.ts +16 -40
  387. package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
  388. package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
  389. package/src/plugins/builtin/research/event-model.ts +40 -12
  390. package/src/plugins/builtin/research/headless.ts +1 -1
  391. package/src/plugins/builtin/research/index.tsx +7 -3
  392. package/src/plugins/builtin/research/relative-valuation-headless.ts +13 -26
  393. package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
  394. package/src/plugins/builtin/research/relative-valuation-pane.tsx +59 -20
  395. package/src/plugins/builtin/research/relative-valuation-peers.ts +217 -20
  396. package/src/plugins/builtin/research/relative-valuation-settings.ts +44 -0
  397. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
  398. package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
  399. package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
  400. package/src/plugins/builtin/risk-factors/data.ts +4 -2
  401. package/src/plugins/builtin/risk-factors/headless.ts +5 -2
  402. package/src/plugins/builtin/roadmap/index.tsx +28 -0
  403. package/src/plugins/builtin/roadmap/model.ts +67 -0
  404. package/src/plugins/builtin/roadmap/pane.tsx +244 -0
  405. package/src/plugins/builtin/scanner/flow-model.ts +50 -4
  406. package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
  407. package/src/plugins/builtin/scanner/flow-session.ts +146 -0
  408. package/src/plugins/builtin/scanner/headless.ts +204 -35
  409. package/src/plugins/builtin/scanner/index.ts +16 -5
  410. package/src/plugins/builtin/scanner/live-headless.ts +48 -5
  411. package/src/plugins/builtin/scanner/live-model.ts +11 -0
  412. package/src/plugins/builtin/scanner/live-pane.tsx +3 -3
  413. package/src/plugins/builtin/scanner/lock-note.ts +3 -2
  414. package/src/plugins/builtin/seasonality/headless.ts +10 -1
  415. package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
  416. package/src/plugins/builtin/sec/fund-facts.ts +116 -34
  417. package/src/plugins/builtin/sec/headless.ts +4 -2
  418. package/src/plugins/builtin/sec/index.tsx +3 -0
  419. package/src/plugins/builtin/series-inventory.ts +2 -0
  420. package/src/plugins/builtin/shared/cloud-resource.ts +16 -7
  421. package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
  422. package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
  423. package/src/plugins/builtin/shared/earnings-report.ts +2 -2
  424. package/src/plugins/builtin/shared/headless-market-data.ts +2 -2
  425. package/src/plugins/builtin/shared/job-posting.ts +1 -0
  426. package/src/plugins/builtin/shared/members-menu.tsx +3 -2
  427. package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
  428. package/src/plugins/builtin/shared/report-freshness.ts +11 -2
  429. package/src/plugins/builtin/shared/research-cells.tsx +1 -1
  430. package/src/plugins/builtin/shared/series-cache.ts +4 -4
  431. package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
  432. package/src/plugins/builtin/shared/ticker-company.ts +77 -0
  433. package/src/plugins/builtin/shared/use-quote-board.ts +2 -1
  434. package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
  435. package/src/plugins/builtin/shared/volatility/pricing.ts +19 -0
  436. package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
  437. package/src/plugins/builtin/themes/model.ts +16 -7
  438. package/src/plugins/builtin/themes/pane.tsx +5 -5
  439. package/src/plugins/builtin/thirteenf/api.ts +12 -1
  440. package/src/plugins/builtin/thirteenf/data.ts +7 -7
  441. package/src/plugins/builtin/thirteenf/headless.ts +31 -4
  442. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
  443. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
  444. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
  445. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
  446. package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
  447. package/src/plugins/builtin/ticker-detail/overview/model.ts +11 -3
  448. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +2 -1
  449. package/src/plugins/builtin/ticker-detail/pane.tsx +14 -8
  450. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
  451. package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
  452. package/src/plugins/builtin/time-sales/pane.tsx +31 -19
  453. package/src/plugins/builtin/treasury-auctions/headless.ts +21 -12
  454. package/src/plugins/builtin/treasury-auctions/pane.tsx +9 -3
  455. package/src/plugins/builtin/treasury-daily/client.ts +41 -5
  456. package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
  457. package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
  458. package/src/plugins/builtin/treasury-daily/model.ts +50 -3
  459. package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
  460. package/src/plugins/builtin/vol-surface/client.ts +3 -13
  461. package/src/plugins/builtin/vol-surface/headless.ts +3 -3
  462. package/src/plugins/builtin/vol-surface/index.tsx +1 -1
  463. package/src/plugins/builtin/vol-surface/pane.tsx +7 -5
  464. package/src/plugins/builtin/volatility/index.tsx +2 -2
  465. package/src/plugins/builtin/volatility/pane.tsx +7 -4
  466. package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
  467. package/src/plugins/builtin/yield-curve/client.ts +61 -1
  468. package/src/plugins/builtin/yield-curve/headless.ts +9 -6
  469. package/src/plugins/builtin/yield-curve/index.tsx +34 -10
  470. package/src/plugins/builtin/yield-curve/swaps.tsx +9 -5
  471. package/src/plugins/catalog-browser.ts +2 -1
  472. package/src/plugins/catalog-manifest.ts +2 -1
  473. package/src/plugins/host-link.ts +1 -1
  474. package/src/plugins/marketplace/model.ts +18 -5
  475. package/src/portfolio/column-values.ts +11 -7
  476. package/src/portfolio/futures-positions.ts +6 -3
  477. package/src/portfolio/position-quantity.ts +15 -9
  478. package/src/remote/resources.ts +2 -1
  479. package/src/renderers/dom/data-table/row.tsx +26 -0
  480. package/src/renderers/dom/desktop/icons.tsx +7 -0
  481. package/src/renderers/dom/desktop/list-view.tsx +13 -10
  482. package/src/renderers/dom/desktop/query-bar.tsx +4 -2
  483. package/src/renderers/dom/desktop/select-field.tsx +4 -2
  484. package/src/renderers/dom/host/chart-surface.tsx +21 -0
  485. package/src/renderers/electrobun/bun/desktop/app-name.ts +3 -3
  486. package/src/renderers/electrobun/bun/desktop/backend.ts +21 -10
  487. package/src/renderers/electrobun/bun/desktop/host-requests.ts +15 -0
  488. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
  489. package/src/renderers/electrobun/bun/desktop/installer-folder.ts +731 -0
  490. package/src/renderers/electrobun/bun/desktop/relaunch.ts +102 -10
  491. package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
  492. package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
  493. package/src/renderers/electrobun/bun/desktop/windows-script.ts +92 -0
  494. package/src/renderers/electrobun/bun/index.ts +26 -2
  495. package/src/renderers/electrobun/bun/window/create.ts +43 -4
  496. package/src/renderers/electrobun/bun/window/events.ts +20 -0
  497. package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +9 -2
  498. package/src/renderers/electrobun/bun/window/windows-native.ts +78 -0
  499. package/src/renderers/electrobun/bun/window/windows-quit.ts +57 -0
  500. package/src/renderers/electrobun/shared/app-name.ts +13 -0
  501. package/src/renderers/electrobun/shared/protocol.ts +12 -0
  502. package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
  503. package/src/renderers/electrobun/view/main.tsx +24 -1
  504. package/src/renderers/electrobun/view/news-source.ts +5 -1
  505. package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
  506. package/src/renderers/electrobun/view/ui-host.tsx +8 -0
  507. package/src/renderers/electrobun/view/view-ids.ts +85 -0
  508. package/src/renderers/opentui/start.tsx +5 -0
  509. package/src/series/registry.ts +1 -0
  510. package/src/series/types.ts +6 -0
  511. package/src/series/validate.ts +1 -0
  512. package/src/sources/gloomberb-cloud/fred-series.ts +3 -1
  513. package/src/sources/gloomberb-cloud/index.ts +43 -8
  514. package/src/sources/gloomberb-cloud/news.ts +47 -7
  515. package/src/sources/provider-router/financials.ts +15 -3
  516. package/src/sources/provider-router/index.ts +8 -5
  517. package/src/state/ticker-cleanup.ts +50 -0
  518. package/src/sync/controller.ts +4 -3
  519. package/src/sync/core-contributors.ts +90 -31
  520. package/src/telemetry/stall-reports-dom.ts +38 -0
  521. package/src/telemetry/stall-reports-node.ts +75 -0
  522. package/src/telemetry/stall-reports.ts +214 -0
  523. package/src/test-support/preload.ts +14 -0
  524. package/src/theme/themes.ts +10 -0
  525. package/src/tickers/company-name-listing.ts +53 -13
  526. package/src/tickers/company-name.ts +50 -4
  527. package/src/tickers/index-roots.ts +7 -0
  528. package/src/tickers/listing-fate.ts +2 -2
  529. package/src/tickers/listing-venues.ts +15 -3
  530. package/src/tickers/search/index.ts +6 -2
  531. package/src/tickers/search/ranking.ts +5 -1
  532. package/src/tickers/search/upsert.ts +13 -0
  533. package/src/tickers/terminal-codes.ts +19 -0
  534. package/src/tickers/ticker-changes.ts +8 -4
  535. package/src/time-series/alignment.ts +1 -1
  536. package/src/time-series/economic.ts +18 -4
  537. package/src/time-series/field-catalog.ts +1 -1
  538. package/src/time-series/filed-trailing-eps.ts +197 -0
  539. package/src/time-series/fundamentals.ts +44 -1
  540. package/src/time-series/ice-credit.ts +3 -0
  541. package/src/time-series/price-comparison.ts +30 -2
  542. package/src/time-series/resolve-context.ts +3 -1
  543. package/src/time-series/resolve.ts +56 -4
  544. package/src/time-series/session-bar.ts +11 -0
  545. package/src/time-series/spec.ts +3 -2
  546. package/src/time-series/studies.ts +8 -2
  547. package/src/time-series/total-return.ts +160 -0
  548. package/src/time-series/transforms.ts +2 -3
  549. package/src/time-series/types.ts +22 -1
  550. package/src/types/company-from.ts +40 -0
  551. package/src/types/data-provider.ts +7 -0
  552. package/src/types/financials.ts +59 -1
  553. package/src/types/headless.ts +27 -3
  554. package/src/types/news-source.ts +1 -0
  555. package/src/types/plugin.ts +9 -0
  556. package/src/ui/host.tsx +3 -0
  557. package/src/updater/failure.ts +4 -1
  558. package/src/utils/cli-output.ts +42 -1
  559. package/src/utils/crypto-pair.ts +165 -7
  560. package/src/utils/declared-dividends.ts +1 -1
  561. package/src/utils/forward-dividend.ts +2 -0
  562. package/src/utils/fundamentals.ts +60 -8
  563. package/src/utils/fx-market-hours.ts +26 -3
  564. package/src/utils/main-thread-stall.ts +35 -2
  565. package/src/utils/market-capitalization.ts +24 -1
  566. package/src/utils/price-history.ts +7 -1
  567. package/src/utils/shell-word.ts +8 -0
  568. package/src/utils/trailing-dividend.ts +53 -4
  569. package/src/version.ts +2 -2
  570. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
@@ -1,6 +1,6 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from "react";
2
2
  import { formatActionChords, hasKeybindingCaptureRequest, subscribeKeybindingCapture, useKeybindings } from "../../../app/keybindings";
3
- import { Notice, Section, SectionHeading, usePaneFooter, usePaneMenuItems, usePaneTabs, type PaneHint, type TableSection } from "../../../components";
3
+ import { Notice, usePaneFooter, usePaneMenuItems, usePaneTabs, type PaneHint, type TableSection } from "../../../components";
4
4
  import { t, tf } from "../../../i18n";
5
5
  import { colors } from "../../../theme/colors";
6
6
  import type { PaneProps } from "../../../types/plugin";
@@ -18,6 +18,7 @@ import { FunctionsTable } from "./functions-table";
18
18
  import { KeybindingsEditor } from "./keybindings-editor";
19
19
  import { ShortcutTable, type ShortcutTableEntry } from "./shortcut-table";
20
20
  import { resolveCommandShortcuts, resolveWindowTemplates } from "./shortcut-model";
21
+ import { badgeColumnWidth } from "./table-cells";
21
22
 
22
23
  const HELP_TABS = [
23
24
  { label: "Basics", value: "basics" },
@@ -47,6 +48,9 @@ const TRY_THESE = [
47
48
  { query: "EE TSLA", description: "What analysts expect from Tesla" },
48
49
  ] as const;
49
50
 
51
+ /** The most of a table its key column takes, however many chips a row carries. */
52
+ const KEY_COLUMN_MAX_SHARE = 0.4;
53
+
50
54
  function HelpPane({ focused, width, height }: PaneProps) {
51
55
  const registry = getSharedRegistry();
52
56
  const { openCommandBar, showPane, createPaneFromTemplate } = usePluginAppActions();
@@ -83,7 +87,7 @@ function HelpPane({ focused, width, height }: PaneProps) {
83
87
  // The UPGRADE line says how long the trial is; the length comes from /pricing.
84
88
  const [pricing, setPricing] = useState<CloudPricing | null>(null);
85
89
  useEffect(() => {
86
- if (activeTabId !== "basics" || pricing) return;
90
+ if (activeTabId !== "reference" || pricing) return;
87
91
  let live = true;
88
92
  void apiClient.getCloudPricing()
89
93
  .then((next) => {
@@ -131,10 +135,11 @@ function HelpPane({ focused, width, height }: PaneProps) {
131
135
  // Each tab's actions. `l` moves between tabs, so layout actions take `a`.
132
136
  const tabHints = useMemo<PaneHint[]>(() => {
133
137
  if (activeTabId === "basics") {
134
- return [
135
- { id: "docs", key: "d", label: "ocs", title: "Open Docs", onPress: openDocs },
136
- { id: "layout-actions", key: "a", label: " layout actions", title: "Layout Actions", onPress: openLayoutActions },
137
- ];
138
+ return [{ id: "docs", key: "d", label: "ocs", title: "Open Docs", onPress: openDocs }];
139
+ }
140
+ // The layout rows are here, so their action is.
141
+ if (activeTabId === "reference") {
142
+ return [{ id: "layout-actions", key: "a", label: " layout actions", title: "Layout Actions", onPress: openLayoutActions }];
138
143
  }
139
144
  if (activeTabId === "functions") {
140
145
  return [{ id: "plugins", key: "p", label: "lugins", title: "Manage Plugins", onPress: openPluginManager }];
@@ -192,32 +197,31 @@ function HelpPane({ focused, width, height }: PaneProps) {
192
197
  ].filter((item) => hasCode(item.badges[0]!)),
193
198
  }], [hasCode]);
194
199
 
195
- const commandBarSections = useMemo<Array<TableSection<ShortcutTableEntry>>>(() => [{
196
- label: "Command Bar",
197
- items: [
198
- ...(commandBarBadges.length > 0
199
- ? [entry("open", commandBarBadges, "Open command mode for actions, pane commands, and typed prefixes.")]
200
- : []),
201
- ...(tickerSearchBadges.length > 0
202
- ? [entry("ticker-search", tickerSearchBadges, "Open ticker search directly.")]
203
- : []),
204
- entry("des", ["DES", "<ticker>"], "Open security details for a specific ticker."),
205
- entry("help-card", ["HELP", "<function>"], "Open a function's help card: what it shows, its keys, how fresh its data is. HELP HELP reaches support."),
206
- entry("docs", ["DOCS", "<function>"], "Read the docs and Academy lessons in the app. The ? on a pane opens its entry."),
207
- entry("upgrade", ["UPGRADE"], tf("Go Pro for real-time data at gloom.sh/cloud, free for {days} days.", { days: trialDays })),
208
- entry("mcp", ["MCP"], "Connect Claude Code, Codex, Cursor or any MCP client to Gloom's research tools."),
209
- entry("move", ["Up/Down", "Ctrl+P/N"], "Move through command bar results."),
210
- entry("page", ["PageUp/PageDown", "Ctrl+Home/End"], "Jump a page, or to the first or last result."),
211
- entry("run", ["Enter", "Shift+Enter"], "Run the selected result or its secondary action."),
212
- entry("accept-arg", ["Tab"], "Accept a suggested command argument when one is available."),
213
- entry("close", ["Esc", ...tickerSearchBadges.filter((badge) => badge === "`" || badge.includes("+"))], "Close the command bar."),
214
- entry("clear", ["Ctrl+U"], "Clear command text."),
215
- entry("delete-word", ["Ctrl+W"], "Delete the previous word in command text."),
216
- entry("back", ["Backspace"], "Go back from a nested command screen when the query is empty."),
217
- ],
218
- }], [commandBarBadges, tickerSearchBadges, trialDays]);
219
-
220
200
  const referenceSections = useMemo<Array<TableSection<ShortcutTableEntry>>>(() => [
201
+ {
202
+ label: "Command Bar",
203
+ items: [
204
+ ...(commandBarBadges.length > 0
205
+ ? [entry("cmd-open", commandBarBadges, "Open command mode for actions, pane commands, and typed prefixes.")]
206
+ : []),
207
+ ...(tickerSearchBadges.length > 0
208
+ ? [entry("cmd-ticker-search", tickerSearchBadges, "Open ticker search directly.")]
209
+ : []),
210
+ entry("cmd-des", ["DES", "<ticker>"], "Open security details for a specific ticker."),
211
+ entry("cmd-help-card", ["HELP", "<function>"], "Open a function's help card: what it shows, its keys, how fresh its data is. HELP HELP reaches support."),
212
+ entry("cmd-docs", ["DOCS", "<function>"], "Read the docs and Academy lessons in the app. The ? on a pane opens its entry."),
213
+ entry("cmd-upgrade", ["UPGRADE"], tf("Go Pro for real-time data at gloom.sh/cloud, free for {days} days.", { days: trialDays })),
214
+ entry("cmd-mcp", ["MCP"], "Connect Claude Code, Codex, Cursor or any MCP client to Gloom's research tools."),
215
+ entry("cmd-move", ["Up/Down", "Ctrl+P/N"], "Move through command bar results."),
216
+ entry("cmd-page", ["PageUp/PageDown", "Ctrl+Home/End"], "Jump a page, or to the first or last result."),
217
+ entry("cmd-run", ["Enter", "Shift+Enter"], "Run the selected result or its secondary action."),
218
+ entry("cmd-accept-arg", ["Tab"], "Accept a suggested command argument when one is available."),
219
+ entry("cmd-close", ["Esc", ...tickerSearchBadges.filter((badge) => badge === "`" || badge.includes("+"))], "Close the command bar."),
220
+ entry("cmd-clear", ["Ctrl+U"], "Clear command text."),
221
+ entry("cmd-delete-word", ["Ctrl+W"], "Delete the previous word in command text."),
222
+ entry("cmd-back", ["Backspace"], "Go back from a nested command screen when the query is empty."),
223
+ ],
224
+ },
221
225
  {
222
226
  label: "Navigation",
223
227
  items: [
@@ -235,7 +239,8 @@ function HelpPane({ focused, width, height }: PaneProps) {
235
239
  entry("search", ["/"], "Search in the focused pane."),
236
240
  entry("sidebar", ["<", ">"], "Narrow or widen the list beside the content, as in MAP and ASKG."),
237
241
  entry("warnings", ["!"], "Open the focused pane's data warnings."),
238
- entry("notification", [...actionBadges("notification-action"), ...actionBadges("notification-dismiss")], "Run or dismiss the newest notification."),
242
+ entry("notification", actionBadges("notification-action"), "Run the newest notification."),
243
+ entry("notification-dismiss", actionBadges("notification-dismiss"), "Dismiss the newest notification."),
239
244
  ],
240
245
  },
241
246
  {
@@ -264,6 +269,7 @@ function HelpPane({ focused, width, height }: PaneProps) {
264
269
  entry("zoom-tool", ["Shift+Z", "Shift+Drag"], "Pick the zoom tool, then drag a time range."),
265
270
  entry("ruler", ["Shift+M", "Alt+Drag"], "Pick the ruler, then drag to measure a move."),
266
271
  entry("draw", ["Shift+D", "Shift+P"], "Draw a trend line or a freehand shape."),
272
+ entry("shapes", ["Shift+J", "Shift+B", "Shift+F"], "Draw a horizontal ray, a rectangle or a Fib retracement."),
267
273
  entry("place", ["Enter", "Arrows"], "With a tool picked, Enter starts it at the cursor, the arrows move its end, and Enter finishes it."),
268
274
  entry("drawings", ["[", "]"], "With a drawing tool picked, pick a drawing."),
269
275
  entry("colour", ["c", "Backspace"], "Cycle the drawing colour, or delete the picked drawing (the last one while a drawing tool is picked)."),
@@ -302,6 +308,14 @@ function HelpPane({ focused, width, height }: PaneProps) {
302
308
  entry("close-pane", ["Esc", "Esc"], "Close the focused pane when nothing is being dragged."),
303
309
  ],
304
310
  },
311
+ {
312
+ // Docked panes keep the saved layout and floating ones follow the pointer; the keyboard's way is Window Mode below.
313
+ label: "Layout",
314
+ items: [
315
+ entry("layout-drag", ["Drag"], "Floating panes can be dragged by the title bar and resized from the lower-right corner."),
316
+ { ...entry("layout-actions", ["LMA"], "Use Layout Actions for split, move, duplicate, close all floating panes, undo, redo, and layout presets."), run: openLayoutActions },
317
+ ],
318
+ },
305
319
  {
306
320
  label: "Window Mode",
307
321
  items: [
@@ -319,7 +333,14 @@ function HelpPane({ focused, width, height }: PaneProps) {
319
333
  entry("commit", ["Enter", "Esc"], "Commit pending changes or exit window mode."),
320
334
  ],
321
335
  },
322
- ], [askBadges, copyBadges, functionHelpBadges, pasteBadges, windowMoveBadges, windowResizeBadges]);
336
+ ], [askBadges, commandBarBadges, copyBadges, functionHelpBadges, openLayoutActions, pasteBadges, tickerSearchBadges, trialDays, windowMoveBadges, windowResizeBadges]);
337
+
338
+ // One key column for every key table in Help, wide enough for the longest group of chips, so the descriptions
339
+ // line up from table to table. A narrow pane caps it, and a row past the cap shows the chips that fit whole.
340
+ const keyColumnWidth = useMemo(() => badgeColumnWidth(
341
+ [...trySections, ...startSections, ...referenceSections].flatMap((section) => section.items.map((item) => item.badges)),
342
+ { min: 8, max: Math.max(8, Math.floor(width * KEY_COLUMN_MAX_SHARE)) },
343
+ ), [referenceSections, startSections, trySections, width]);
323
344
 
324
345
  const renderContent = () => {
325
346
  switch (activeTabId) {
@@ -338,38 +359,19 @@ function HelpPane({ focused, width, height }: PaneProps) {
338
359
  <>
339
360
  {trySections.length > 0 && (
340
361
  <Box marginBottom={1}>
341
- <ShortcutTable sections={trySections} width={bodyWidth} keyLabel="TYPE" descriptionLabel="OPENS" />
362
+ <ShortcutTable sections={trySections} width={bodyWidth} keyWidth={keyColumnWidth} keyLabel="TYPE" descriptionLabel="OPENS" />
342
363
  </Box>
343
364
  )}
344
365
 
345
- <Box flexDirection="column" gap={1}>
346
- <SectionHeading title="How To Use Gloom" />
347
- <Box flexDirection="column">
348
- <Text fg={colors.textDim} wrapText>{t("Click anything to use it; the keys shown beside actions are shortcuts.")}</Text>
349
- <Text fg={colors.textDim} wrapText>
350
- {commandBarBadges[0]
351
- ? tf("Not sure of a code? Click the search bar at the top, or press {key}, and type what you want in plain words, like \"top news\". The Functions tab lists everything.", { key: commandBarBadges[0] })
352
- : t("Not sure of a code? Click the search bar at the top and type what you want in plain words, like \"top news\". The Functions tab lists everything.")}
353
- </Text>
354
- </Box>
355
- </Box>
366
+ <Text fg={colors.textDim} wrapText>
367
+ {commandBarBadges[0]
368
+ ? tf("Not sure of a code? Click the search bar at the top, or press {key}, and type what you want in plain words, like \"top news\". The Functions tab lists everything.", { key: commandBarBadges[0] })
369
+ : t("Not sure of a code? Click the search bar at the top and type what you want in plain words, like \"top news\". The Functions tab lists everything.")}
370
+ </Text>
356
371
 
357
372
  <Box marginTop={1}>
358
- <ShortcutTable sections={startSections} width={bodyWidth} />
373
+ <ShortcutTable sections={startSections} width={bodyWidth} keyWidth={keyColumnWidth} />
359
374
  </Box>
360
-
361
- <Box marginTop={1}>
362
- <ShortcutTable sections={commandBarSections} width={bodyWidth} />
363
- </Box>
364
-
365
- <Section title="Layout Basics">
366
- <Text fg={colors.text}>{t("Docked panes stay in the saved layout.")}</Text>
367
- <Text fg={colors.text} wrapText>{t("Floating panes can be dragged by the title bar and resized from the lower-right corner.")}</Text>
368
- {windowMoveBadges[0] && windowResizeBadges[0] && (
369
- <Text fg={colors.text} wrapText>{tf("From the keyboard, {move} moves the focused window and {resize} resizes it.", { move: windowMoveBadges[0], resize: windowResizeBadges[0] })}</Text>
370
- )}
371
- <Text fg={colors.text} wrapText>{t("Use Layout Actions for split, move, duplicate, close all floating panes, undo, redo, and layout presets.")}</Text>
372
- </Section>
373
375
  </>
374
376
  );
375
377
  }
@@ -395,6 +397,7 @@ function HelpPane({ focused, width, height }: PaneProps) {
395
397
  <ShortcutTable
396
398
  sections={referenceSections}
397
399
  width={width}
400
+ keyWidth={keyColumnWidth}
398
401
  height={contentHeight}
399
402
  after={(
400
403
  <Box paddingX={1} flexShrink={0}>
@@ -18,7 +18,7 @@ export function resolveWindowTemplates(registry: SharedRegistry): HelpShortcutEn
18
18
  const allPlugins = registry.allPlugins ?? new Map<string, { name?: string }>();
19
19
 
20
20
  return [...registry.paneTemplates.values()]
21
- .filter((template) => template.shortcut)
21
+ .filter((template) => template.shortcut && template.listed !== false)
22
22
  .filter((template) => {
23
23
  const pluginId = registry.getPaneTemplatePluginId?.(template.id);
24
24
  return !pluginId || !disabledPlugins.has(pluginId);
@@ -22,7 +22,7 @@ import {
22
22
  import { t } from "../../../i18n";
23
23
  import { useThemeColors } from "../../../theme/theme-context";
24
24
  import { Box, useUiCapabilities } from "../../../ui";
25
- import { badgeCell, badgeColumnWidth } from "./table-cells";
25
+ import { badgeCell } from "./table-cells";
26
26
 
27
27
  export interface ShortcutTableEntry {
28
28
  id: string;
@@ -38,6 +38,7 @@ type ShortcutRow = SectionedRow<ShortcutTableEntry>;
38
38
  export function ShortcutTable({
39
39
  sections,
40
40
  width,
41
+ keyWidth,
41
42
  height: fillHeight,
42
43
  after,
43
44
  keyLabel = "KEY",
@@ -46,6 +47,8 @@ export function ShortcutTable({
46
47
  sections: ReadonlyArray<TableSection<ShortcutTableEntry>>;
47
48
  /** Content width available inside the tab body. */
48
49
  width: number;
50
+ /** The key column's width, one for every table in Help so their descriptions line up. */
51
+ keyWidth: number;
49
52
  /** Fill this many rows and scroll, instead of growing to fit every row. */
50
53
  height?: number;
51
54
  /** Sits under a full-height table inside the same frame. */
@@ -59,10 +62,6 @@ export function ShortcutTable({
59
62
  () => buildSectionedRows(sections, (entry) => entry.id),
60
63
  [sections],
61
64
  );
62
- const keyWidth = useMemo(
63
- () => badgeColumnWidth(rows.filter(isSectionedItemRow).map((row) => row.item.badges), { min: 8, max: 24 }),
64
- [rows],
65
- );
66
65
  const columns = useMemo<ShortcutColumn[]>(() => [
67
66
  { id: "key", label: keyLabel, width: keyWidth, align: "left" },
68
67
  { id: "description", label: descriptionLabel, width: 20, align: "left", flexGrow: 1 },
@@ -72,7 +71,7 @@ export function ShortcutTable({
72
71
  if (!isSectionedItemRow(row)) return EMPTY_TABLE_CELL;
73
72
  const run = row.item.run;
74
73
  const cell: DataTableCell = column.id === "key"
75
- ? badgeCell(row.item.badges, column.width)
74
+ ? badgeCell(row.item.badges, column.width, { wholeOnly: true })
76
75
  : { text: t(row.item.description), color: colors.text };
77
76
  if (!run) return cell;
78
77
  return {
@@ -31,10 +31,21 @@ export function badgeColumnWidth(
31
31
  return Math.max(min, Math.min(max, widest));
32
32
  }
33
33
 
34
+ /**
35
+ * The chips of a group that fit a column whole, in order. A chip that would be
36
+ * cut ("Ctrl+Hor") is left out instead, so what shows is always a real key; the
37
+ * first chip stays whatever the width.
38
+ */
39
+ export function wholeBadges(labels: readonly string[], width: number): readonly string[] {
40
+ let count = Math.min(1, labels.length);
41
+ while (count < labels.length && badgeGroupWidth(labels.slice(0, count + 1)) <= width) count += 1;
42
+ return labels.slice(0, count);
43
+ }
44
+
34
45
  export function badgeCell(
35
46
  labels: readonly string[],
36
47
  width: number,
37
- options: { tone?: "neutral" | "accent"; emptyLabel?: string; emptyColor?: string } = {},
48
+ options: { tone?: "neutral" | "accent"; emptyLabel?: string; emptyColor?: string; wholeOnly?: boolean } = {},
38
49
  ): DataTableCell {
39
50
  if (labels.length === 0) {
40
51
  return options.emptyLabel
@@ -45,7 +56,7 @@ export function badgeCell(
45
56
  text: labels.join(" "),
46
57
  content: (
47
58
  <Box flexDirection="row" gap={BADGE_GAP} width={width} height={1} overflow="hidden">
48
- {labels.map((label, index) => (
59
+ {(options.wholeOnly ? wholeBadges(labels, width) : labels).map((label, index) => (
49
60
  <Badge key={`${label}:${index}`} label={label} variant="solid" tone={options.tone ?? "neutral"} />
50
61
  ))}
51
62
  </Box>
@@ -11,6 +11,7 @@ import { listingAbroad, type IssuerListingParams } from "../../../api-client/pat
11
11
  import { createPluginCache } from "../../../data/plugin-cache";
12
12
  import { secListingKey } from "../../../utils/sec";
13
13
  import { finiteOrNull, recordOrNull } from "../../../utils/guards";
14
+ import { readCompanyFrom } from "../../../types/company-from";
14
15
  import { cachedCloudResource, loadCloudResource, type CloudResource } from "../shared/cloud-resource";
15
16
  import { CLOUD_SESSION_REQUIRED } from "../shared/research-cloud-session";
16
17
 
@@ -110,8 +111,11 @@ function normalizeBeneficialOwnersPayload(value: unknown, ticker: string): Benef
110
111
  if (!raw || !Array.isArray(raw.owners)) throw new Error("The server returned an invalid 13D/13G list");
111
112
  const coverage = recordOrNull(raw.coverage);
112
113
  const nextOffset = finiteOrNull(raw.nextOffset);
114
+ const companyFrom = readCompanyFrom(raw.companyFrom);
115
+ const { companyFrom: _unread, ...rest } = raw;
113
116
  return {
114
- ...raw,
117
+ ...rest,
118
+ ...(companyFrom ? { companyFrom } : {}),
115
119
  ticker: text(raw.ticker) ?? ticker,
116
120
  cik: cikOrNull(raw.cik) ?? "",
117
121
  companyName: text(raw.companyName) ?? "",
@@ -9,6 +9,7 @@ import {
9
9
  type DataTableCell,
10
10
  type DataTableKeyEvent,
11
11
  } from "../../../components";
12
+ import { useCompanyDataFooter } from "../../../components/layout/pane/status-footer";
12
13
  import { handleRefreshKey, loadingErrorFooterInfo } from "../../../components/data-table/table-pane";
13
14
  import type { IssuerListingParams } from "../../../api-client/paths";
14
15
  import { useAsyncResource } from "../../../react/async-resource";
@@ -233,6 +234,8 @@ export function BeneficialOwnersView({
233
234
  focused,
234
235
  title: "13D/13G coverage",
235
236
  });
237
+ // A foreign line's owners are its company's, read from the home line.
238
+ useCompanyDataFooter(payload?.companyFrom);
236
239
  usePaneFooter("holders-13dg", () => ({
237
240
  info: [
238
241
  ...(payload?.asOf ? [{ id: "as-of", parts: [{ text: `as of ${payload.asOf.slice(0, 10)}`, tone: "muted" as const }] }] : []),
@@ -0,0 +1,51 @@
1
+ import type { Quote } from "../../../types/financials";
2
+ import { quoteTradingExchange } from "../../../market-data/quotes/metadata";
3
+ import { isUsListingExchange } from "../../../utils/exchanges";
4
+ import { isFundType } from "../../../utils/sec";
5
+ import { isNoSourceAnswer } from "../../../sources/provider-errors";
6
+ import { logFailure } from "../../../utils/log-failure";
7
+ import { fetchFunds } from "../members/client";
8
+ import { coveredFund } from "../members/model";
9
+
10
+ type FundQuote = Pick<Quote, "instrumentType" | "tradingExchangeName" | "listingExchangeName" | "exchangeName">;
11
+
12
+ /** What a holder report says for a fund in place of its holders: the reason, and the commands that read the fund. */
13
+ export interface FundHoldersAnswer {
14
+ message: string;
15
+ /** One command a line, each for what it reads; empty for a fund abroad, which those do not cover. */
16
+ details: string[];
17
+ }
18
+
19
+ /**
20
+ * What `holders`, `13f` and `fn HDS` say for a fund no holder source has an
21
+ * ownership breakdown of (XLE, SPY, EWJ), in place of the source's bare
22
+ * NO_DATA: that it is a fund, and for a US-listed one the commands that read
23
+ * it, each only where it runs: the 13F filers that hold it (`fn 13F`), what it
24
+ * holds where `fn MEMB` covers it, and its fund facts (`fn ETF`). Null for a
25
+ * listing that is not a fund, or a failure that is no answer (a timeout).
26
+ */
27
+ export async function fundHoldersAnswer(
28
+ symbol: string,
29
+ error: unknown,
30
+ quote: FundQuote | null | undefined,
31
+ client: NonNullable<Parameters<typeof fetchFunds>[0]>,
32
+ ): Promise<FundHoldersAnswer | null> {
33
+ if (!isNoSourceAnswer(error) || !quote || !isFundType(quote.instrumentType)) return null;
34
+ const fund = symbol.split(":")[0]!.trim().toUpperCase();
35
+ const message = `${fund} is a fund: there is no ownership breakdown for funds.`;
36
+ if (!isUsListingExchange(quoteTradingExchange(quote))) return { message, details: [] };
37
+ // A covered-fund list that cannot be read names no fn MEMB, which may not run for this fund.
38
+ const funds = await fetchFunds(client).then((payload) => payload.funds)
39
+ .catch(logFailure("holders.fund-holders", "Read the funds fn MEMB covers failed", null));
40
+ const covered = !!funds && !!coveredFund(funds, fund);
41
+ // Shares of an exchange-traded fund are 13F securities; a mutual fund's are not.
42
+ const traded = !/MUTUAL/i.test(quote.instrumentType ?? "");
43
+ return {
44
+ message,
45
+ details: [
46
+ ...traded ? [`13F filers that hold it: gloom fn 13F ${fund}`] : [],
47
+ ...covered ? [`What it holds: gloom fn MEMB ${fund}`] : [],
48
+ `Fund facts: gloom fn ETF ${fund}`,
49
+ ],
50
+ };
51
+ }
@@ -7,7 +7,7 @@ import { loadHolderSnapshot } from "./client";
7
7
  import { holderValueCurrency } from "./format";
8
8
  import { logFailure } from "../../../utils/log-failure";
9
9
  import type { HolderData } from "../../../types/financials";
10
- import { SEC_FILINGS } from "../shared/report-freshness";
10
+ import { SEC_FILING_ROWS, SEC_FILINGS } from "../shared/report-freshness";
11
11
  import type { BeneficialOwnersPayload } from "../../../api-client/beneficial-owners";
12
12
  import { fetchBeneficialOwners, type BeneficialOwnersRequest } from "./beneficial-client";
13
13
  import { resolveHeadlessInstrument, resolveHeadlessIssuerListing } from "../shared/headless-market-data";
@@ -33,6 +33,8 @@ import {
33
33
  holderReportUnits,
34
34
  } from "./report-rows";
35
35
  import { REPORT_ROWS } from "../shared/headless-options";
36
+ import { fundHoldersAnswer } from "./fund-holders";
37
+ import { companyDataNotes } from "../../../types/company-from";
36
38
  import {
37
39
  HOLDER_ROWS_MAX,
38
40
  indexedHoldersPartialNote,
@@ -196,7 +198,7 @@ export function createHoldersHeadless(
196
198
  rows: limited,
197
199
  // Reports that could not be read this time are missing from the rows.
198
200
  complete: beneficialListComplete(payload),
199
- freshness: { ...SEC_FILINGS, basis: "13D/13G filings", observedKey: "filingDate" },
201
+ freshness: { ...SEC_FILING_ROWS, basis: "13D/13G filings" },
200
202
  metadata: {
201
203
  symbol: payload.ticker || symbol,
202
204
  name: payload.companyName || null,
@@ -211,6 +213,7 @@ export function createHoldersHeadless(
211
213
  [payload.companyName || symbol, ...beneficialListFacts(payload, form, rows.length, history)].join(" · "),
212
214
  ...(legend ? [legend] : []),
213
215
  ...beneficialCoverageNotices(payload.coverage),
216
+ ...companyDataNotes(payload.companyFrom),
214
217
  ],
215
218
  returned: limited.length,
216
219
  total: rows.length,
@@ -224,11 +227,27 @@ export function createHoldersHeadless(
224
227
  .catch(logFailure("holders.headless", "Resolve the listing's venue for the 13F holder index failed", null)))?.exchange ?? "";
225
228
  const indexTicker = indexHolderTicker(symbol, exchange);
226
229
  const [snapshot, index] = await Promise.all([
227
- dependencies.loadSnapshot(symbol, args, ctx),
230
+ dependencies.loadSnapshot(symbol, args, ctx).catch(async (error: unknown) => {
231
+ const quote = await ctx.marketData.getQuote(symbol)
232
+ .catch(logFailure("holders.headless", "Read the quote of a listing with no holders failed", null));
233
+ const fund = await fundHoldersAnswer(symbol, error, quote, ctx.apiClient);
234
+ if (fund) return fund;
235
+ throw error;
236
+ }),
228
237
  indexTicker
229
238
  ? dependencies.loadIndexedHolders(indexTicker, limit, ctx).catch(logFailure("holders.headless", "Load 13F holder index failed", null))
230
239
  : null,
231
240
  ]);
241
+ // A fund has no ownership breakdown: the report says so in place of rows, with what reads it, and fails as no data.
242
+ if ("message" in snapshot) {
243
+ return {
244
+ columns: HOLDER_REPORT_COLUMNS,
245
+ rows: [],
246
+ empty: [snapshot.message, ...snapshot.details].join("\n"),
247
+ unavailableSymbols: [symbol],
248
+ metadata: { symbol },
249
+ };
250
+ }
232
251
  const { marketCap } = snapshot;
233
252
  const indexed = !!index?.rows.length && indexHolderTicker(symbol, exchange, snapshot.data) != null;
234
253
  const partialNote = indexed ? indexedHoldersPartialNote(index, limit) : null;
@@ -12,6 +12,7 @@ import {
12
12
  type DataTableKeyEvent,
13
13
  type PageRequest,
14
14
  } from "../../../components";
15
+ import { useCompanyDataFooter } from "../../../components/layout/pane/status-footer";
15
16
  import { handleRefreshKey, loadingErrorFooterInfo } from "../../../components/data-table/table-pane";
16
17
  import { useAsyncResource } from "../../../react/async-resource";
17
18
  import { useShortcut } from "../../../react/input";
@@ -321,6 +322,8 @@ export function HoldersView({ focused, width, height }: { focused: boolean; widt
321
322
  }
322
323
  }, [currency, fundMatches, marketCap]);
323
324
 
325
+ // Holders read from the company's home line say whose; the SEC index's rows are the listing's own.
326
+ useCompanyDataFooter(indexed ? null : listData?.companyFrom, viewMode !== "13dg");
324
327
  // The 13D/G tab registers its own footer.
325
328
  usePaneFooter("holders", () => {
326
329
  if (viewMode === "13dg") return null;
@@ -25,6 +25,7 @@ import {
25
25
  import { HOLDER_ROWS_MAX } from "./index-holders";
26
26
  import { buildRows, DEFAULT_SORT, sortRows } from "./table-model";
27
27
  import type { HolderColumnId, SortPreference } from "./types";
28
+ import { companyDataNotes } from "../../../types/company-from";
28
29
 
29
30
  /**
30
31
  * A holder as `holders` and `fn HDS` report it; the keys are the JSON
@@ -231,7 +232,9 @@ export function holderReportNotices({
231
232
  ? `The source lists its ${reported} largest holders; fn 13F ${requested} lists all ${total.toLocaleString("en-US")}.`
232
233
  : null;
233
234
  return {
234
- notices: [header, basis, cut, positionsBasis, holderShareBasisNote(data, shown), unvaluedHoldersNote(shown, sort)]
235
+ notices: [header, basis, cut, positionsBasis, holderShareBasisNote(data, shown), unvaluedHoldersNote(shown, sort),
236
+ // The SEC index's rows are the listing's own; the source's, its company's home line's when it says so.
237
+ ...(indexed ? [] : companyDataNotes(data.companyFrom))]
235
238
  .filter((line): line is string => !!line),
236
239
  valueBasis,
237
240
  positionsBasis,
@@ -20,7 +20,7 @@ import {
20
20
  } from "./model";
21
21
  import { isAmendedInsiderFiling, relevantInsiderAmendments } from "./amendments";
22
22
  import { formatInsiderName } from "./display";
23
- import { SEC_FILINGS } from "../shared/report-freshness";
23
+ import { SEC_FILING_ROWS } from "../shared/report-freshness";
24
24
  import { resolveHeadlessInstrument, resolveHeadlessIssuerListing } from "../shared/headless-market-data";
25
25
  import { noSecReportsNotice } from "../../../utils/sec";
26
26
  import { parsePublicTickerKey } from "../../../utils/exchanges";
@@ -329,7 +329,7 @@ export function createInsiderHeadless(
329
329
  ): HeadlessPaneDefinition<"rows"> {
330
330
  return {
331
331
  shape: "rows",
332
- freshness: { ...SEC_FILINGS, basis: "Form 4 filings", observedKey: "filingDate" },
332
+ freshness: { ...SEC_FILING_ROWS, basis: "Form 4 filings" },
333
333
  argument: {
334
334
  kind: "ticker",
335
335
  optional: true,
@@ -1,5 +1,5 @@
1
1
  import { apiClient } from "../../../api-client";
2
- import type { IpoCalendarParams, IpoCalendarPayload, IpoDeal, IpoSourceHealth } from "../../../api-client/ipo";
2
+ import type { IpoCalendarParams, IpoCalendarPayload, IpoDeal, IpoLockup, IpoSourceHealth } from "../../../api-client/ipo";
3
3
  import type { HeadlessPaneApiClient } from "../../../types/plugin";
4
4
  import { createPluginCache } from "../../../data/plugin-cache";
5
5
  import { cachedCloudResource, loadCloudResource, unavailableOnServer, type CloudResource } from "../shared/cloud-resource";
@@ -45,6 +45,21 @@ function validDeal(deal: IpoDeal): boolean {
45
45
  && validFirstDay(deal.firstDay);
46
46
  }
47
47
 
48
+ function validLockup(value: unknown): value is IpoLockup {
49
+ const lockup = value as IpoLockup | null;
50
+ return !!lockup && typeof lockup === "object" && typeof lockup.endsOn === "string" && dayOrNull(lockup.endsOn)
51
+ && typeof lockup.days === "number" && Number.isFinite(lockup.days) && lockup.days > 0
52
+ && (lockup.basis === "prospectus" || lockup.basis === "typical")
53
+ && dayOrNull(lockup.prospectusDate ?? null) && textOrNull(lockup.prospectusUrl ?? null);
54
+ }
55
+
56
+ /** A lock-up estimate the client cannot read is left off its deal; the deal stays. */
57
+ function withReadableLockup(deal: IpoDeal): IpoDeal {
58
+ if (deal.lockup == null || validLockup(deal.lockup)) return deal;
59
+ const { lockup: _unreadable, ...rest } = deal;
60
+ return rest;
61
+ }
62
+
48
63
  function validSource(source: IpoSourceHealth): boolean {
49
64
  return !!source && text(source.id) && Array.isArray(source.mics) && source.mics.every(text)
50
65
  && typeof source.ok === "boolean" && textOrNull(source.asOf);
@@ -61,7 +76,8 @@ export function validateIpoCalendar(data: IpoCalendarPayload): IpoCalendarPayloa
61
76
  }
62
77
  return {
63
78
  asOf: data.asOf,
64
- deals: data.deals.filter(validDeal).map((deal) => (REGIONS.has(deal.region) ? deal : { ...deal, region: "other" })),
79
+ deals: data.deals.filter(validDeal).map(withReadableLockup)
80
+ .map((deal) => (REGIONS.has(deal.region) ? deal : { ...deal, region: "other" })),
65
81
  sources: data.sources.filter(validSource),
66
82
  };
67
83
  }