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
@@ -14,6 +14,8 @@ const STAT_LABEL_WIDTH = 12;
14
14
  const FUNDAMENTALS_MAX_COLUMN_WIDTH = 40;
15
15
  const BOOK_LABEL_WIDTH = 4;
16
16
  const RANGE_ENDPOINT_WIDTH = 11;
17
+ /** Shortest track worth drawing; below it the label shortens. */
18
+ const RANGE_MIN_TRACK_WIDTH = 6;
17
19
  const POSITION_COLUMN_GAP = 1;
18
20
 
19
21
  interface PositionColumn {
@@ -77,62 +79,82 @@ function RangeTrack({
77
79
  );
78
80
  }
79
81
 
82
+ /** Cells a range row needs around its track: label, endpoints and the gaps between them. */
83
+ export function rangeRowChrome(labelWidth: number, endpointWidth: number): number {
84
+ return labelWidth + 1 + endpointWidth * 2 + 2;
85
+ }
86
+
87
+ /** The longest endpoint among ranges drawn together, so stacked tracks start and end in one column. */
88
+ export function rangeEndpointWidth(
89
+ values: readonly number[],
90
+ currency: string,
91
+ priceOptions: MarketFormatOptions,
92
+ ): number {
93
+ const widths = values.map((value) => displayWidth(formatMarketPriceWithCurrency(value, currency, priceOptions)));
94
+ return Math.min(RANGE_ENDPOINT_WIDTH, Math.max(1, ...widths));
95
+ }
96
+
97
+ /**
98
+ * Where the price sits in a range, on one row: the label, the low, a track
99
+ * with a neutral marker and the high. The marker is the position, so no
100
+ * percentage repeats it.
101
+ */
80
102
  export function CompactRangeBar({
81
103
  current,
82
104
  low,
83
105
  high,
84
106
  label,
107
+ shortLabel,
108
+ labelWidth,
109
+ endpointWidth,
85
110
  width,
86
111
  currency,
87
112
  priceOptions,
88
- markerColor,
89
113
  }: {
90
114
  current: number;
91
115
  low: number;
92
116
  high: number;
117
+ /** Translated, like `shortLabel`; `labelWidth` is measured from them. */
93
118
  label: string;
119
+ /** Used when the full label would leave the track too short. */
120
+ shortLabel?: string;
121
+ /** Shared by ranges drawn together, so their values line up. */
122
+ labelWidth: number;
123
+ endpointWidth: number;
94
124
  width: number;
95
125
  currency: string;
96
126
  /** The quote's live price options, so the endpoints keep the price's decimals. */
97
127
  priceOptions: MarketFormatOptions;
98
- markerColor: string;
99
128
  }) {
100
129
  const range = high - low;
101
130
  if (range <= 0) return null;
102
131
  const position = Math.max(0, Math.min(1, (current - low) / range));
103
- const pctLabel = `${Math.round(position * 100)}%`;
104
- const endpointWidth = Math.min(
105
- RANGE_ENDPOINT_WIDTH,
106
- Math.max(7, Math.floor((width - 8) / 3)),
107
- );
132
+ const useShortLabel = shortLabel != null && width - rangeRowChrome(labelWidth, endpointWidth) < RANGE_MIN_TRACK_WIDTH;
133
+ const shownLabel = useShortLabel ? shortLabel : label;
134
+ const shownLabelWidth = useShortLabel ? displayWidth(shortLabel) : labelWidth;
108
135
  const endpointOptions = { ...priceOptions, maxWidth: endpointWidth };
109
136
  const lowText = formatMarketPriceWithCurrency(low, currency, endpointOptions);
110
137
  const highText = formatMarketPriceWithCurrency(high, currency, endpointOptions);
111
- const barWidth = Math.max(5, width - endpointWidth * 2 - 2);
138
+ const barWidth = Math.max(1, width - rangeRowChrome(shownLabelWidth, endpointWidth));
112
139
  const markerIndex = Math.max(0, Math.min(barWidth - 1, Math.round(position * (barWidth - 1))));
113
- const labelWidth = Math.max(0, width - displayWidth(pctLabel));
114
140
 
115
141
  return (
116
- <Box flexDirection="column" width={width} flexShrink={0}>
117
- <Box flexDirection="row" height={1}>
118
- <Box width={labelWidth} overflow="hidden">
119
- <Text fg={colors.textDim}>{label}</Text>
120
- </Box>
121
- <Text fg={markerColor}>{pctLabel}</Text>
142
+ <Box flexDirection="row" height={1} width={width} flexShrink={0} overflow="hidden">
143
+ <Box width={shownLabelWidth + 1} flexShrink={0} overflow="hidden">
144
+ <Text fg={colors.textDim}>{shownLabel}</Text>
122
145
  </Box>
123
- <Box flexDirection="row" height={1}>
124
- <Box width={endpointWidth} overflow="hidden">
125
- <Text fg={colors.textDim}>{lowText}</Text>
126
- </Box>
127
- <RangeTrack
128
- barWidth={barWidth}
129
- markerIndex={markerIndex}
130
- position={position}
131
- markerColor={markerColor}
132
- />
133
- <Box flexDirection="row" width={endpointWidth} justifyContent="flex-end" overflow="hidden">
134
- <Text fg={colors.textDim}>{highText}</Text>
135
- </Box>
146
+ {/* The low sits against the track and the high starts after it, so stacked tracks align. */}
147
+ <Box flexDirection="row" width={endpointWidth} flexShrink={0} justifyContent="flex-end" overflow="hidden">
148
+ <Text fg={colors.text}>{lowText}</Text>
149
+ </Box>
150
+ <RangeTrack
151
+ barWidth={barWidth}
152
+ markerIndex={markerIndex}
153
+ position={position}
154
+ markerColor={colors.textBright}
155
+ />
156
+ <Box width={endpointWidth} flexShrink={0} overflow="hidden">
157
+ <Text fg={colors.text}>{highText}</Text>
136
158
  </Box>
137
159
  </Box>
138
160
  );
@@ -175,8 +197,9 @@ export function QuoteBook({ quote, assetCategory, width }: { quote: Quote; asset
175
197
 
176
198
  return (
177
199
  <Box flexDirection="column" width={width} flexShrink={0}>
178
- <BookRow label={t("Bid")} value={bidText} width={width} valueColor={colors.borderFocused} />
179
- <BookRow label={t("Ask")} value={askText} width={width} valueColor={colors.negative} />
200
+ {/* Neutral: a red ask would read as a loss. */}
201
+ <BookRow label={t("Bid")} value={bidText} width={width} valueColor={colors.textBright} />
202
+ <BookRow label={t("Ask")} value={askText} width={width} valueColor={colors.textBright} />
180
203
  <BookRow label={t("Spr")} value={spreadText} width={width} valueColor={colors.textDim} />
181
204
  </Box>
182
205
  );
@@ -1,3 +1,4 @@
1
+ import { PerpEquityRow } from "../../perps/equity-row";
1
2
  import { useCallback } from "react";
2
3
  import type { TickerResearchTabProps } from "../../../../types/plugin";
3
4
  import {
@@ -41,6 +42,7 @@ export function OverviewResearchTab({ width, focused }: TickerResearchTabProps)
41
42
  financials={financials}
42
43
  onOpenChart={paneId && !lockedToOneTab ? openChart : undefined}
43
44
  onOpenFunction={openFunction}
45
+ perpetuals={ticker ? <PerpEquityRow symbol={ticker.metadata.ticker} instrumentType={financials?.quote?.instrumentType ?? ticker.metadata.assetCategory} /> : undefined}
44
46
  />
45
47
  );
46
48
  }
@@ -1,6 +1,9 @@
1
+ import { TrendingSummary } from "../attention/trending";
2
+ import type { ReactNode } from "react";
1
3
  import { EmptyState, PaneLinkMenu, SectionHeading, usePaneNoticeFooter } from "../../../components";
2
4
  import { CompositeChart, pricePointsToResolvedSeries } from "../../../components/chart/composite";
3
- import { CompanyLogo } from "../../../components/company-logo";
5
+ import { CompanyLogo, resolveCompanyLogoSrc } from "../../../components/company-logo";
6
+ import { bodyLineInk, FigureText, figureLineInk, useFigureCells } from "../../../components/ui/figure";
4
7
  import { PriceReturnStrip } from "../../../components/price-performance";
5
8
  import { t } from "../../../i18n";
6
9
  import { useFxRatesMap } from "../../../market-data/hooks";
@@ -13,17 +16,33 @@ import { colors, priceColor } from "../../../theme/colors";
13
16
  import { appendLiveQuotePoint, hasUnknownBondHistoryBasis } from "../../../time-series/chart-data";
14
17
  import type { TickerFinancials } from "../../../types/financials";
15
18
  import type { TickerRecord } from "../../../types/ticker";
16
- import { Box, ScrollBox, Text, TextAttributes, useUiCapabilities } from "../../../ui";
17
- import { resolveExchangeTimeZone } from "../../../utils/exchanges";
19
+ import { Box, ScrollBox, Text, useUiCapabilities } from "../../../ui";
20
+ import { publicTickerKey, resolveExchangeTimeZone } from "../../../utils/exchanges";
18
21
  import { convertCurrency, displayWidth, formatPercentRaw, truncateToDisplayWidth } from "../../../utils/format";
19
22
  import { zonedDateKey } from "../../../utils/zoned-date-time";
20
- import { CompactRangeBar, FundamentalsGrid, PositionTable, QuoteBook, textGridColumns } from "./overview/components";
23
+ import {
24
+ CompactRangeBar,
25
+ FundamentalsGrid,
26
+ PositionTable,
27
+ QuoteBook,
28
+ rangeEndpointWidth,
29
+ rangeRowChrome,
30
+ textGridColumns,
31
+ } from "./overview/components";
21
32
  import { buildOverviewStats, buildPositionRows, buildProfileFields } from "./overview/model";
22
33
  import type { OverviewFunctionLink } from "./overview/types";
23
34
  import { describeFundamentalMarketCap } from "../../../utils/market-capitalization";
24
35
  import { liveFiftyTwoWeekRange, liveMarketCapitalization } from "../portfolio-list/live-valuation";
25
36
 
37
+ /** Cells between the Day and 52W ranges when they share a row. */
38
+ const RANGE_PAIR_GAP = 4;
39
+ /** Shortest track that still reads as a range; below it the two ranges stack. */
40
+ const RANGE_INLINE_MIN_TRACK = 10;
41
+ /** Desktop px between the logo and the name and price beside it. */
42
+ const LOGO_GAP_PX = 6;
43
+
26
44
  interface OverviewTabProps {
45
+ perpetuals?: ReactNode;
27
46
  width?: number;
28
47
  focused?: boolean;
29
48
  ticker: TickerRecord | null;
@@ -39,10 +58,11 @@ export function OverviewTab(props: OverviewTabProps) {
39
58
  return <ResolvedOverviewTab {...props} ticker={props.ticker} />;
40
59
  }
41
60
 
42
- function ResolvedOverviewTab({ width, focused = false, ticker, financials, onOpenChart, onOpenFunction }: OverviewTabProps & { ticker: TickerRecord }) {
61
+ function ResolvedOverviewTab({ width, focused = false, ticker, financials, onOpenChart, onOpenFunction, perpetuals }: OverviewTabProps & { ticker: TickerRecord }) {
43
62
  const baseCurrency = useAppSelector((state) => state.config.baseCurrency);
44
63
  const { width: termWidth } = useViewport();
45
- const { fractionalViewport = false, nativePaneChrome } = useUiCapabilities();
64
+ const { fractionalViewport = false, nativePaneChrome, cellWidthPx = 8, cellHeightPx = 18, pixelRatio = 1 } = useUiCapabilities();
65
+ const figureCells = useFigureCells();
46
66
 
47
67
  const quote = financials?.quote;
48
68
  const fundamentals = financials?.fundamentals;
@@ -58,7 +78,7 @@ function ResolvedOverviewTab({ width, focused = false, ticker, financials, onOpe
58
78
  capitalization?.currency,
59
79
  ...ticker.metadata.positions.map((position) => position.currency),
60
80
  ]);
61
- const quoteCurrency = quote?.currency ?? ticker.metadata.currency ?? baseCurrency;
81
+ const quoteCurrency = quote?.currency || ticker.metadata.currency || baseCurrency;
62
82
  const toBase = (value: number, fromCurrency: string) =>
63
83
  convertCurrency(value, fromCurrency, baseCurrency, exchangeRates);
64
84
  const sector = ticker.metadata.sector ?? profile?.sector;
@@ -114,23 +134,48 @@ function ResolvedOverviewTab({ width, focused = false, ticker, financials, onOpe
114
134
  const quotePriceText = quote ? formatMarketPriceWithCurrency(quote.price, quote.currency, moneyOptions) : "";
115
135
  const quoteChangeText = quote ? formatSignedMarketPrice(quote.change, moneyOptions) : "";
116
136
  const quotePercentText = quote ? `(${formatPercentRaw(quote.changePercent)})` : "";
117
- const quoteTextWidth = Math.max(1, quoteSummaryWidth - (nativePaneChrome ? 6 : 0));
118
- const stackQuoteChange = displayWidth(quoteChangeText) + 1 + displayWidth(quotePercentText) > quoteTextWidth;
119
- const stackQuoteSummary = displayWidth(quotePriceText) + 3 + displayWidth(quoteChangeText) + displayWidth(quotePercentText) > quoteTextWidth;
120
- // The pane title already names the ticker, so the line leads with the company.
121
- const companyName = ticker.metadata.name || quote?.name || ticker.metadata.ticker;
137
+ // The pane title already names the ticker, so the line leads with the company and drops a name that only repeats it.
138
+ const companyName = [ticker.metadata.name, quote?.name].find((name) => name && name !== ticker.metadata.ticker) ?? "";
139
+ const venueText = listingVenue ? (companyName ? ` (${listingVenue})` : listingVenue) : "";
122
140
  const marketStateText = quote?.marketState ? t(marketStateLabel(quote.marketState)) : "";
123
- const companyNameWidth = Math.max(8, quoteSummaryWidth - (nativePaneChrome ? 6 : 0)
124
- - (listingVenue ? listingVenue.length + 3 : 0)
125
- - (marketStateText ? marketStateText.length + 1 : 0));
141
+ const hasIdentityLine = Boolean(companyName || venueText || marketStateText);
142
+ // The desktop logo is a square from the top of the company name's capitals down to the
143
+ // price's baseline, so its edges meet the text's and the three read as one block. The
144
+ // extended-hours line hangs below, as it comes and goes with the session. Without a
145
+ // price the logo keeps two rows.
146
+ const hasLogo = nativePaneChrome === true
147
+ && resolveCompanyLogoSrc({ symbol: ticker.metadata.ticker, assetCategory: ticker.metadata.assetCategory }) != null;
148
+ const priceInk = figureLineInk();
149
+ // The top snaps up to a whole px so it never starts below the capitals; the bottom lands
150
+ // on the device pixel nearest the baseline.
151
+ const logoTopPx = quote ? Math.floor(hasIdentityLine ? bodyLineInk(cellHeightPx).capTop : priceInk.capTop) : 0;
152
+ const logoBottomPx = Math.round(((hasIdentityLine ? cellHeightPx : 0) + priceInk.baseline) * pixelRatio) / pixelRatio;
153
+ const logoPx = quote ? logoBottomPx - logoTopPx : cellHeightPx * 2;
154
+ const logoCells = hasLogo ? Math.ceil((logoPx + LOGO_GAP_PX) / cellWidthPx) : 0;
155
+ const quoteTextWidth = Math.max(1, quoteSummaryWidth - logoCells);
156
+ const quoteChangeCells = figureCells(quoteChangeText, "sub") + 1 + figureCells(quotePercentText, "sub");
157
+ const stackQuoteChange = quoteChangeCells > quoteTextWidth;
158
+ const stackQuoteSummary = figureCells(quotePriceText) + 2 + quoteChangeCells > quoteTextWidth;
159
+ const companyNameWidth = Math.max(8, quoteTextWidth
160
+ - displayWidth(venueText)
161
+ - (marketStateText ? displayWidth(marketStateText) + 1 : 0));
126
162
  const hasDayRange = quote?.low != null && quote?.high != null && quote.high > quote.low;
127
163
  const yearRange = liveFiftyTwoWeekRange(quote);
128
- const hasYearRange = yearRange != null;
129
- const rangeInline = contentWidth >= 70 && hasDayRange && hasYearRange;
130
- const rangeWidth = rangeInline
131
- ? Math.floor((contentWidth - 2) / 2)
132
- : contentWidth;
133
- const rangeMarkerColor = quote ? priceColor(quote.change) : colors.textDim;
164
+ const dayRangeLabel = t("Day Range");
165
+ const yearRangeLabel = t("52W Range");
166
+ const rangeLabelWidth = Math.max(
167
+ hasDayRange ? displayWidth(dayRangeLabel) : 0,
168
+ yearRange ? displayWidth(yearRangeLabel) : 0,
169
+ );
170
+ // Both ranges share one endpoint width, so stacked tracks start and end in the same column.
171
+ const rangeEndpointCells = rangeEndpointWidth([
172
+ ...(hasDayRange ? [quote.low!, quote.high!] : []),
173
+ ...(yearRange ? [yearRange.low, yearRange.high] : []),
174
+ ], quoteCurrency, moneyOptions);
175
+ const rangeHalfWidth = Math.floor((contentWidth - RANGE_PAIR_GAP) / 2);
176
+ const rangeInline = hasDayRange && yearRange != null
177
+ && rangeHalfWidth - rangeRowChrome(rangeLabelWidth, rangeEndpointCells) >= RANGE_INLINE_MIN_TRACK;
178
+ const rangeWidth = rangeInline ? rangeHalfWidth : contentWidth;
134
179
  const stats = buildOverviewStats({
135
180
  quote,
136
181
  fundamentals,
@@ -163,37 +208,49 @@ function ResolvedOverviewTab({ width, focused = false, ticker, financials, onOpe
163
208
 
164
209
  return (
165
210
  <ScrollBox flexGrow={1} flexBasis={0} scrollY focusable={false}>
166
- <Box flexDirection="column" paddingX={1} paddingBottom={1} gap={1}>
211
+ <Box
212
+ flexDirection="column"
213
+ paddingX={1}
214
+ paddingBottom={1}
215
+ gap={1}
216
+ // On the desktop the header keeps half a row clear of the tab bar's edge.
217
+ style={nativePaneChrome ? { paddingTop: Math.round(cellHeightPx / 2) } : undefined}
218
+ >
167
219
  <Box flexDirection={quoteBookInline ? "row" : "column"} gap={quoteBookInline ? 2 : 0} width={contentWidth}>
168
220
  <Box flexDirection="row" width={quoteSummaryWidth} flexShrink={0} minWidth={0} overflow="hidden">
169
221
  <CompanyLogo
170
222
  symbol={ticker.metadata.ticker}
171
223
  assetCategory={ticker.metadata.assetCategory}
172
224
  name={ticker.metadata.name || quote?.name}
225
+ width={logoPx / cellWidthPx}
226
+ height={logoPx / cellHeightPx}
227
+ style={{ marginTop: logoTopPx, marginRight: LOGO_GAP_PX }}
173
228
  />
174
229
  <Box flexDirection="column" flexGrow={1} flexShrink={1} minWidth={0}>
175
- <Box flexDirection="row" minWidth={0} overflow="hidden">
176
- <Text attributes={TextAttributes.BOLD} fg={colors.textBright}>
177
- {truncateToDisplayWidth(companyName, companyNameWidth)}
178
- </Text>
179
- {listingVenue && (
180
- <Text flexShrink={0} fg={colors.textDim}>{" "}({listingVenue})</Text>
181
- )}
182
- {quote?.marketState && (
183
- <Text flexShrink={0} fg={marketStateColor(quote.marketState)}>
184
- {" "}{t(marketStateLabel(quote.marketState))}
185
- </Text>
186
- )}
187
- </Box>
230
+ {hasIdentityLine && (
231
+ <Box flexDirection="row" minWidth={0} overflow="hidden">
232
+ {companyName && (
233
+ <Text fg={colors.textDim}>{truncateToDisplayWidth(companyName, companyNameWidth)}</Text>
234
+ )}
235
+ {venueText && <Text flexShrink={0} fg={colors.textDim}>{venueText}</Text>}
236
+ {quote?.marketState && (
237
+ <Text flexShrink={0} fg={marketStateColor(quote.marketState)}>
238
+ {companyName || venueText ? " " : ""}{marketStateText}
239
+ </Text>
240
+ )}
241
+ </Box>
242
+ )}
188
243
 
189
244
  {quote && (
190
- <Box flexDirection={stackQuoteSummary ? "column" : "row"} gap={stackQuoteSummary ? 0 : 2}>
191
- <Text attributes={TextAttributes.BOLD} fg={colors.textBright}>
192
- {quotePriceText}
193
- </Text>
245
+ <Box
246
+ flexDirection={stackQuoteSummary ? "column" : "row"}
247
+ gap={stackQuoteSummary ? 0 : 2}
248
+ alignItems={stackQuoteSummary ? undefined : "baseline"}
249
+ >
250
+ <FigureText>{quotePriceText}</FigureText>
194
251
  <Box flexDirection={stackQuoteChange ? "column" : "row"} gap={stackQuoteChange ? 0 : 1}>
195
- <Text fg={priceColor(quote.change)}>{quoteChangeText}</Text>
196
- <Text fg={priceColor(quote.change)}>{quotePercentText}</Text>
252
+ <FigureText part="sub" change={quote.change}>{quoteChangeText}</FigureText>
253
+ <FigureText part="sub" change={quote.change}>{quotePercentText}</FigureText>
197
254
  </Box>
198
255
  </Box>
199
256
  )}
@@ -230,18 +287,22 @@ function ResolvedOverviewTab({ width, focused = false, ticker, financials, onOpe
230
287
  )}
231
288
  </Box>
232
289
 
233
- {(hasDayRange || hasYearRange) && quote && (
234
- <Box flexDirection={rangeInline ? "row" : "column"} gap={rangeInline ? 2 : 0} width={contentWidth}>
290
+ {perpetuals}
291
+
292
+ {(hasDayRange || yearRange) && quote && (
293
+ <Box flexDirection={rangeInline ? "row" : "column"} gap={rangeInline ? RANGE_PAIR_GAP : 0} width={contentWidth}>
235
294
  {hasDayRange && (
236
295
  <CompactRangeBar
237
296
  current={quote.price}
238
297
  low={quote.low!}
239
298
  high={quote.high!}
240
- label="Day Range"
299
+ label={dayRangeLabel}
300
+ shortLabel={t("Day")}
301
+ labelWidth={rangeLabelWidth}
302
+ endpointWidth={rangeEndpointCells}
241
303
  width={rangeWidth}
242
304
  currency={quoteCurrency}
243
305
  priceOptions={moneyOptions}
244
- markerColor={rangeMarkerColor}
245
306
  />
246
307
  )}
247
308
  {yearRange && (
@@ -249,11 +310,13 @@ function ResolvedOverviewTab({ width, focused = false, ticker, financials, onOpe
249
310
  current={quote.price}
250
311
  low={yearRange.low}
251
312
  high={yearRange.high}
252
- label="52W Range"
313
+ label={yearRangeLabel}
314
+ shortLabel={t("52W")}
315
+ labelWidth={rangeLabelWidth}
316
+ endpointWidth={rangeEndpointCells}
253
317
  width={rangeWidth}
254
318
  currency={quoteCurrency}
255
319
  priceOptions={moneyOptions}
256
- markerColor={rangeMarkerColor}
257
320
  />
258
321
  )}
259
322
  </Box>
@@ -316,6 +379,8 @@ function ResolvedOverviewTab({ width, focused = false, ticker, financials, onOpe
316
379
  </Box>
317
380
  )}
318
381
 
382
+ <TrendingSummary symbol={publicTickerKey(ticker.metadata.ticker, ticker.metadata.exchange)} onOpen={onOpenFunction ? () => onOpenFunction({ name: "Research attention", templateId: "attention-pane" }) : undefined} />
383
+
319
384
  {/* Description — last, collapsed */}
320
385
  {description && (
321
386
  <Box flexDirection="column" width={contentWidth}>
@@ -31,16 +31,30 @@ import { TICKER_RESEARCH_BUILTIN_TABS } from "./research-tabs";
31
31
  import { useLiveStreamingSetting } from "../../../state/hooks/live-streaming";
32
32
  import { useCloudAccessFooter } from "../shared/cloud-upgrade";
33
33
  import { CLOUD_QUOTE_DELAY_MINUTES } from "../../../api-client/plan-access";
34
- import { parsePublicTickerKey } from "../../../utils/exchanges";
34
+ import { parsePublicTickerKey, publicTickerKey } from "../../../utils/exchanges";
35
+ import { captureAttentionAction } from "../../../telemetry/attention-counts";
35
36
  import { tickerHasListingSuffix } from "../../../sources/listing-symbols";
36
37
  import { tickerQuoteFooterInfo } from "./quote-footer";
37
38
  import { ResearchTabKeysProvider, useResearchTabKeysHost } from "./research-tab-keys";
38
39
  import { ResearchTabNavigationProvider } from "./research-tab-navigation";
40
+ import { usePluginAppActions } from "../../../public/react";
41
+ import { resolveTickerInstrumentKind } from "../../../tickers/instrument-kind";
39
42
 
40
43
  const TICKER_RESEARCH_TAB_COMMIT_DELAY_MS = 120;
41
44
  /** A tab counts as viewed once it stays open this long, not when h/l passes over it. */
42
45
  const TICKER_RESEARCH_TAB_VIEW_DWELL_MS = 2_000;
43
46
 
47
+ const COMPANY_RESEARCH = [
48
+ { id: "supply-chain-pane", label: "Supply Chain (SPLC)" },
49
+ { id: "company-kpis-pane", label: "Company KPIs (KPIS)" },
50
+ { id: "company-guidance-pane", label: "Company Guidance (GUIDE)" },
51
+ { id: "credit-documents-pane", label: "Credit Documents (CRDOC)" },
52
+ { id: "awards-pane", label: "Government Awards (AWARDS)" },
53
+ { id: "catalysts-pane", label: "Catalysts (CATL)" },
54
+ { id: "hiring-pane", label: "Hiring Momentum (HIRE)" },
55
+ { id: "apps-pane", label: "App Attention (APPS)" },
56
+ ];
57
+
44
58
  function sameStringSet(left: Set<string>, right: Set<string>): boolean {
45
59
  if (left.size !== right.size) return false;
46
60
  for (const value of left) {
@@ -100,6 +114,7 @@ function getCollectionName(state: AppState, collectionId: string | null): string
100
114
 
101
115
  export function TickerResearchPane({ focused, width, height }: PaneProps) {
102
116
  const dispatch = useAppDispatch();
117
+ const { createPaneFromTemplate } = usePluginAppActions();
103
118
  const config = usePaneAppConfig();
104
119
  const paneInstance = usePaneInstance();
105
120
  const { ticker, financials, error: instrumentError } = usePaneTicker();
@@ -220,15 +235,44 @@ export function TickerResearchPane({ focused, width, height }: PaneProps) {
220
235
  // those keys while it is open; Esc gives them back to the strip.
221
236
  const researchTabKeys = useResearchTabKeysHost();
222
237
  const stripFocused = focused && !pluginCaptured && !researchTabKeys.claimed;
238
+ const selectResearchTab = useCallback((tabId: string) => {
239
+ if (ticker && tabId !== resolvedTabId) {
240
+ const action = tabId === "chart" ? "chart" : tabId === "options" ? "option_chain" : null;
241
+ if (action) captureAttentionAction()(publicTickerKey(ticker.metadata.ticker, ticker.metadata.exchange), action);
242
+ }
243
+ setActiveTabId(tabId);
244
+ }, [resolvedTabId, setActiveTabId, ticker]);
223
245
  const { strip: tabStrip, rows: tabBarHeight } = usePaneTabs(!paneSettings.hideTabs && ticker ? {
224
246
  tabs: tabItems,
225
247
  activeValue: resolvedTabId,
226
- onSelect: setActiveTabId,
248
+ onSelect: selectResearchTab,
227
249
  focused: stripFocused,
228
250
  } : null);
229
251
  // h/l step one tab at a time; with twenty tabs the pane menu jumps straight to one.
230
252
  const dialog = useOptionalDialog();
231
253
  const showTabs = !paneSettings.hideTabs && !!ticker;
254
+ const companySymbol = ticker && resolveTickerInstrumentKind(ticker, financials) === "equity"
255
+ ? tickerHasListingSuffix(ticker.metadata.ticker) ? ticker.metadata.ticker : publicTickerKey(ticker.metadata.ticker, ticker.metadata.exchange)
256
+ : null;
257
+ usePaneMenuItems("ticker-research:company-research", () => {
258
+ if (!companySymbol || !dialog) return null;
259
+ const choices = COMPANY_RESEARCH.filter((item) => {
260
+ if (!registry) return true;
261
+ const owner = registry.getPaneTemplatePluginId?.(item.id);
262
+ return registry.paneTemplates?.has(item.id) && (!owner || !disabledPlugins.includes(owner));
263
+ });
264
+ if (!choices.length) return null;
265
+ return [{ id: "company-research", label: t("Company Research…"), onSelect: () => {
266
+ void dialog.prompt<string>({
267
+ closeOnClickOutside: true,
268
+ content: (context: PromptContext<string>) => <ChoiceDialog {...context}
269
+ title={tf("Research {symbol}", { symbol: companySymbol })}
270
+ choices={choices.map((item) => ({ ...item, label: t(item.label) }))} />,
271
+ }).then((templateId) => {
272
+ if (templateId && choices.some((item) => item.id === templateId)) createPaneFromTemplate(templateId, { symbol: companySymbol });
273
+ }).catch(() => {});
274
+ } }];
275
+ }, [companySymbol, createPaneFromTemplate, dialog, disabledPlugins, registry, tickerResearchTabsSnapshot]);
232
276
  usePaneMenuItems("ticker-research:go-to-tab", () => {
233
277
  if (!showTabs || !dialog || tabItems.length < 2) return null;
234
278
  return [{
@@ -246,11 +290,11 @@ export function TickerResearchPane({ focused, width, height }: PaneProps) {
246
290
  />
247
291
  ),
248
292
  }).then((tabId) => {
249
- if (tabId) setActiveTabId(tabId);
293
+ if (tabId) selectResearchTab(tabId);
250
294
  }).catch(() => {});
251
295
  },
252
296
  }];
253
- }, [dialog, resolvedTabId, setActiveTabId, showTabs, tabItems]);
297
+ }, [dialog, resolvedTabId, selectResearchTab, showTabs, tabItems]);
254
298
  // Which tabs people stay on. A pane pinned to one tab has no strip; opening
255
299
  // it is a function open, counted with those.
256
300
  useEffect(() => {
@@ -265,9 +309,9 @@ export function TickerResearchPane({ focused, width, height }: PaneProps) {
265
309
  // A figure on one tab can open the tab that explains it (holders, short interest).
266
310
  const openResearchTab = useCallback((tabId: string) => {
267
311
  if (!visibleTabIds.has(tabId)) return false;
268
- setActiveTabId(tabId);
312
+ selectResearchTab(tabId);
269
313
  return true;
270
- }, [setActiveTabId, visibleTabIds]);
314
+ }, [selectResearchTab, visibleTabIds]);
271
315
  const renderedTabIds = useMemo(() => {
272
316
  const next = new Set<string>();
273
317
  for (const tabId of mountedTabIds) {