gloomberb 0.10.4 → 0.11.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 (555) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +30 -3
  3. package/README.zh-CN.md +1 -1
  4. package/package.json +5 -5
  5. package/src/api-client/auth.ts +34 -0
  6. package/src/api-client/data.ts +25 -0
  7. package/src/api-client/index.test.ts +81 -0
  8. package/src/api-client/index.ts +37 -0
  9. package/src/api-client/request.test.ts +30 -0
  10. package/src/api-client/request.ts +44 -20
  11. package/src/api-client/socket-health.test.ts +79 -0
  12. package/src/api-client/socket.ts +108 -5
  13. package/src/api-client/types.ts +139 -0
  14. package/src/app/global-shortcuts.test.tsx +144 -9
  15. package/src/app/global-shortcuts.ts +33 -11
  16. package/src/app/runtime/broker-import.ts +39 -5
  17. package/src/app/runtime/startup.ts +7 -1
  18. package/src/app/runtime/update.ts +26 -1
  19. package/src/app.tsx +36 -19
  20. package/src/brokers/broker-ticker-sync.ts +3 -8
  21. package/src/brokers/sync-broker-instance.test.ts +59 -0
  22. package/src/brokers/sync-broker-instance.ts +37 -13
  23. package/src/capabilities/chart-series.ts +318 -0
  24. package/src/capabilities/factories.ts +46 -0
  25. package/src/capabilities/index.ts +1 -0
  26. package/src/capabilities/registry.test.ts +135 -0
  27. package/src/capabilities/registry.ts +31 -4
  28. package/src/capabilities/types.ts +52 -0
  29. package/src/cli/commands/overview.ts +2 -2
  30. package/src/cli/desktop-pane-shot.ts +2 -0
  31. package/src/cli/entry.ts +11 -1
  32. package/src/cli/native-smoke.ts +5 -0
  33. package/src/cli/pane-functions/catalog.ts +4 -0
  34. package/src/cli/pane-functions/data.ts +6 -1
  35. package/src/cli/pane-functions/discovery.ts +76 -10
  36. package/src/cli/pane-functions/report.test.ts +76 -0
  37. package/src/cli/pane-functions/report.ts +7 -1
  38. package/src/cli/pane-functions/resolver.ts +4 -1
  39. package/src/cli/pane-functions/screenshot.test.ts +56 -3
  40. package/src/cli/pane-functions/screenshot.ts +68 -5
  41. package/src/components/chart/composite/composite-chart.test.tsx +54 -0
  42. package/src/components/chart/composite/composite-chart.tsx +92 -15
  43. package/src/components/chart/composite/downsample.test.ts +136 -0
  44. package/src/components/chart/composite/downsample.ts +208 -0
  45. package/src/components/chart/composite/panel-series.test.ts +66 -0
  46. package/src/components/chart/composite/panel-series.ts +101 -0
  47. package/src/components/chart/multi-line/renderer.ts +1 -0
  48. package/src/components/chart/native/kitty/index.test.ts +25 -3
  49. package/src/components/chart/native/kitty/support.ts +37 -3
  50. package/src/components/chart/native/use-chart-text-fallback.ts +19 -0
  51. package/src/components/chart/static/chart/surface.tsx +43 -12
  52. package/src/components/chart/static/multi-line-chart-surface.tsx +41 -5
  53. package/src/components/command-bar/commands/direct/index.ts +18 -0
  54. package/src/components/command-bar/commands/registry.ts +14 -0
  55. package/src/components/command-bar/routes/root/article-results.test.ts +83 -0
  56. package/src/components/command-bar/routes/root/article-results.ts +55 -0
  57. package/src/components/command-bar/routes/root/command-items.ts +3 -0
  58. package/src/components/command-bar/routes/root/provider-search.ts +7 -3
  59. package/src/components/command-bar/routes/root/results.ts +38 -11
  60. package/src/components/command-bar/routes/root/runtime.ts +6 -2
  61. package/src/components/command-bar/routes/ticker-search/actions.ts +1 -1
  62. package/src/components/command-bar/routes/ticker-search/results.test.ts +34 -0
  63. package/src/components/command-bar/routes/ticker-search/results.ts +4 -6
  64. package/src/components/command-bar/routes/ticker-search/route.ts +2 -2
  65. package/src/components/command-bar/routing/list-state.ts +2 -1
  66. package/src/components/command-bar/surface/assist.test.tsx +13 -6
  67. package/src/components/command-bar/surface/index.test.tsx +65 -0
  68. package/src/components/command-bar/surface/index.tsx +35 -0
  69. package/src/components/command-bar/view-model.test.ts +43 -0
  70. package/src/components/command-bar/view-model.ts +41 -4
  71. package/src/components/command-bar/workflow/collection-actions.test.ts +94 -0
  72. package/src/components/command-bar/workflow/collection-actions.ts +14 -3
  73. package/src/components/command-bar/workflow/ops.test.ts +136 -0
  74. package/src/components/command-bar/workflow/ops.ts +71 -1
  75. package/src/components/index.ts +1 -1
  76. package/src/components/layout/header.test.tsx +35 -0
  77. package/src/components/layout/header.tsx +101 -9
  78. package/src/components/layout/pane/cycle-order.test.ts +0 -1
  79. package/src/components/layout/pane/footer/index.test.tsx +19 -19
  80. package/src/components/layout/pane/header.tsx +2 -13
  81. package/src/components/layout/shell/index.tsx +3 -0
  82. package/src/components/layout/status-bar.test.tsx +36 -1
  83. package/src/components/layout/status-bar.tsx +48 -2
  84. package/src/components/onboarding/account-step/chooser-panel.tsx +27 -50
  85. package/src/components/onboarding/account-step/form-panel.tsx +85 -29
  86. package/src/components/onboarding/account-step/index.tsx +10 -1
  87. package/src/components/onboarding/account-step/model.ts +3 -3
  88. package/src/components/onboarding/account-step/qr-panel.tsx +68 -0
  89. package/src/components/onboarding/account-step/signed-in-panel.tsx +4 -7
  90. package/src/components/onboarding/onboarding-frame.tsx +517 -0
  91. package/src/components/onboarding/onboarding-steps.tsx +0 -264
  92. package/src/components/onboarding/onboarding-wizard.test.tsx +626 -200
  93. package/src/components/onboarding/onboarding-wizard.tsx +848 -266
  94. package/src/components/onboarding/portfolio-step/broker-fields-panel.tsx +81 -51
  95. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +14 -20
  96. package/src/components/onboarding/portfolio-step/broker-sync-panel.tsx +8 -11
  97. package/src/components/onboarding/portfolio-step/choose-panel.tsx +18 -22
  98. package/src/components/onboarding/portfolio-step/index.tsx +3 -0
  99. package/src/components/onboarding/portfolio-step/types.ts +3 -0
  100. package/src/components/onboarding/wizard-account.ts +38 -3
  101. package/src/components/onboarding/wizard-broker-sync.ts +45 -67
  102. package/src/components/onboarding/wizard-model.ts +18 -2
  103. package/src/components/ui/data-table/opentui/index.tsx +29 -9
  104. package/src/components/ui/data-table/types.ts +2 -0
  105. package/src/components/ui/external-link.tsx +4 -2
  106. package/src/components/ui/loading.tsx +1 -1
  107. package/src/components/ui/status.tsx +76 -4
  108. package/src/components/ui/table-layout.test.ts +24 -0
  109. package/src/components/ui/table-layout.ts +90 -23
  110. package/src/components/ui/tabs.tsx +86 -4
  111. package/src/core/app-services.ts +14 -3
  112. package/src/core/connection-health.test.ts +143 -0
  113. package/src/core/connection-health.ts +295 -0
  114. package/src/core/state/app/layout-reducer.ts +34 -0
  115. package/src/core/state/app/layout.ts +21 -0
  116. package/src/core/state/app/state.ts +10 -0
  117. package/src/core/state/app/types.ts +7 -1
  118. package/src/data/config/chart-settings.ts +11 -2
  119. package/src/data/config/store/index.test.ts +83 -9
  120. package/src/data/config/store/migrations.ts +16 -0
  121. package/src/data/config/store/normalize.ts +59 -1
  122. package/src/data/fred-series.ts +8 -2
  123. package/src/i18n/es.ts +126 -0
  124. package/src/i18n/ja.ts +126 -0
  125. package/src/i18n/ko.ts +126 -0
  126. package/src/i18n/zh-cn.ts +126 -0
  127. package/src/i18n/zh-tw.ts +126 -0
  128. package/src/market-data/coordinator/chart.ts +3 -2
  129. package/src/market-data/hooks.tsx +3 -2
  130. package/src/market-data/market/status.test.ts +12 -0
  131. package/src/market-data/market/status.ts +34 -17
  132. package/src/news/aggregator.test.ts +39 -0
  133. package/src/news/aggregator.ts +78 -22
  134. package/src/news/hooks.ts +4 -0
  135. package/src/notifications/app-notifier.test.ts +28 -6
  136. package/src/notifications/app-notifier.ts +24 -5
  137. package/src/plugins/broker-sync/broker-sync.test.ts +130 -0
  138. package/src/plugins/broker-sync/native-loader.ts +10 -0
  139. package/src/plugins/broker-sync/normalize.ts +271 -0
  140. package/src/plugins/broker-sync/public.ts +122 -0
  141. package/src/plugins/broker-sync/robinhood-native.ts +367 -0
  142. package/src/plugins/broker-sync/robinhood.ts +114 -0
  143. package/src/plugins/broker-sync/simplefin-native.ts +201 -0
  144. package/src/plugins/broker-sync/simplefin.ts +70 -0
  145. package/src/plugins/builtin/account-management/footer.ts +8 -19
  146. package/src/plugins/builtin/account-management/pane.tsx +13 -8
  147. package/src/plugins/builtin/ai/pi/host.ts +6 -5
  148. package/src/plugins/builtin/ai/providers.ts +1 -1
  149. package/src/plugins/builtin/ai/screener/footer.ts +0 -7
  150. package/src/plugins/builtin/ai/screener/pane.test.tsx +2 -1
  151. package/src/plugins/builtin/ai/screener/pane.tsx +3 -1
  152. package/src/plugins/builtin/ai/screener/results-view.tsx +8 -0
  153. package/src/plugins/builtin/ai/screener/runner.ts +7 -3
  154. package/src/plugins/builtin/ai/workspace/pane.tsx +13 -2
  155. package/src/plugins/builtin/alerts/alert-engine.ts +13 -0
  156. package/src/plugins/builtin/alerts/constants.ts +2 -1
  157. package/src/plugins/builtin/alerts/index.tsx +67 -36
  158. package/src/plugins/builtin/alerts/pane.tsx +17 -65
  159. package/src/plugins/builtin/analytics/index.tsx +24 -11
  160. package/src/plugins/builtin/analytics/pane-model.ts +48 -14
  161. package/src/plugins/builtin/analytics/portfolio-selection.ts +19 -0
  162. package/src/plugins/builtin/analytics/view.tsx +5 -3
  163. package/src/plugins/builtin/broker-manager/detail.tsx +7 -2
  164. package/src/plugins/builtin/broker-manager/footer.ts +4 -9
  165. package/src/plugins/builtin/broker-manager/index.tsx +1 -6
  166. package/src/plugins/builtin/buildout/detail/ui.tsx +15 -4
  167. package/src/plugins/builtin/buildout/pane/header.tsx +1 -0
  168. package/src/plugins/builtin/changelog/index.tsx +60 -45
  169. package/src/plugins/builtin/chart-composer/catalog-inventory.test.ts +97 -0
  170. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +366 -0
  171. package/src/plugins/builtin/chart-composer/chart-spec.ts +21 -1
  172. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +307 -0
  173. package/src/plugins/builtin/chart-composer/editor/controller.ts +1 -1
  174. package/src/plugins/builtin/chart-composer/index.tsx +45 -5
  175. package/src/plugins/builtin/chart-composer/pane.tsx +7 -88
  176. package/src/plugins/builtin/chart-composer/presets.test.ts +48 -0
  177. package/src/plugins/builtin/chart-composer/presets.ts +69 -3
  178. package/src/plugins/builtin/chart-composer/quick-add.tsx +8 -5
  179. package/src/plugins/builtin/chart-composer/semantic.ts +18 -14
  180. package/src/plugins/builtin/chart-composer/series-catalog.test.ts +39 -0
  181. package/src/plugins/builtin/chart-composer/series-catalog.ts +104 -3
  182. package/src/plugins/builtin/chart-composer/shortcuts.test.ts +10 -10
  183. package/src/plugins/builtin/chart-composer/shortcuts.ts +4 -12
  184. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +230 -23
  185. package/src/plugins/builtin/chat/chat.test.tsx +22 -0
  186. package/src/plugins/builtin/chat/content/composer.tsx +6 -4
  187. package/src/plugins/builtin/chat/content/index.tsx +7 -0
  188. package/src/plugins/builtin/chat/content/snapshot.ts +9 -2
  189. package/src/plugins/builtin/chat/content/transcript.tsx +10 -0
  190. package/src/plugins/builtin/chat/controller/channel-actions.ts +11 -1
  191. package/src/plugins/builtin/chat/controller/channels.ts +13 -3
  192. package/src/plugins/builtin/chat/controller/index.ts +61 -7
  193. package/src/plugins/builtin/chat/controller/lifecycle.ts +3 -0
  194. package/src/plugins/builtin/chat/controller/message-loading.ts +5 -1
  195. package/src/plugins/builtin/chat/controller/messages.ts +3 -1
  196. package/src/plugins/builtin/chat/controller/notifications.ts +32 -11
  197. package/src/plugins/builtin/chat/controller/state.ts +7 -0
  198. package/src/plugins/builtin/chat/controller/view.ts +1 -0
  199. package/src/plugins/builtin/chat/controller.test.ts +117 -28
  200. package/src/plugins/builtin/cloud/auth-commands.ts +18 -0
  201. package/src/plugins/builtin/cloud/device-signin-dialog.tsx +243 -0
  202. package/src/plugins/builtin/cloud/device-signin.test.ts +217 -0
  203. package/src/plugins/builtin/cloud/device-signin.ts +240 -0
  204. package/src/plugins/builtin/cloud/plugin.tsx +7 -1
  205. package/src/plugins/builtin/cloud-tweets/table.tsx +57 -10
  206. package/src/plugins/builtin/composite-plugins.ts +39 -5
  207. package/src/plugins/builtin/congress-trades/detail.tsx +2 -4
  208. package/src/plugins/builtin/congress-trades/footer.ts +7 -16
  209. package/src/plugins/builtin/congress-trades/pane.tsx +12 -20
  210. package/src/plugins/builtin/connections/index.ts +53 -0
  211. package/src/plugins/builtin/connections/pane.test.tsx +79 -0
  212. package/src/plugins/builtin/connections/pane.tsx +258 -0
  213. package/src/plugins/builtin/correlation/index.test.tsx +2 -1
  214. package/src/plugins/builtin/correlation/index.tsx +58 -19
  215. package/src/plugins/builtin/correlation/matrix/model.ts +18 -7
  216. package/src/plugins/builtin/correlation/relationship/pane.tsx +43 -19
  217. package/src/plugins/builtin/correlation/settings.ts +1 -1
  218. package/src/plugins/builtin/credit-conditions/client.test.ts +80 -0
  219. package/src/plugins/builtin/credit-conditions/client.ts +105 -0
  220. package/src/plugins/builtin/credit-conditions/index.test.tsx +111 -0
  221. package/src/plugins/builtin/credit-conditions/index.tsx +227 -0
  222. package/src/plugins/builtin/credit-conditions/model.test.ts +43 -0
  223. package/src/plugins/builtin/credit-conditions/model.ts +64 -0
  224. package/src/plugins/builtin/debug/index.tsx +11 -6
  225. package/src/plugins/builtin/dividend-yield/client.test.ts +58 -0
  226. package/src/plugins/builtin/dividend-yield/client.ts +252 -0
  227. package/src/plugins/builtin/dividend-yield/index.tsx +61 -0
  228. package/src/plugins/builtin/dividend-yield/model.ts +83 -0
  229. package/src/plugins/builtin/dividend-yield/pane.tsx +305 -0
  230. package/src/plugins/builtin/dividend-yield/types.ts +22 -0
  231. package/src/plugins/builtin/earnings/data/cache.test.ts +10 -7
  232. package/src/plugins/builtin/earnings/data/cache.ts +23 -9
  233. package/src/plugins/builtin/earnings/index.tsx +57 -6
  234. package/src/plugins/builtin/econ/calendar-model.test.ts +37 -3
  235. package/src/plugins/builtin/econ/calendar-model.ts +32 -26
  236. package/src/plugins/builtin/econ/fred-cache.test.ts +8 -1
  237. package/src/plugins/builtin/econ/fred-series-map.ts +18 -0
  238. package/src/plugins/builtin/econ/index.test.tsx +105 -0
  239. package/src/plugins/builtin/econ/index.tsx +95 -52
  240. package/src/plugins/builtin/fear-greed/cache.test.ts +18 -3
  241. package/src/plugins/builtin/fear-greed/cache.ts +18 -6
  242. package/src/plugins/builtin/fear-greed/charts.tsx +9 -2
  243. package/src/plugins/builtin/fear-greed/format.ts +0 -9
  244. package/src/plugins/builtin/fear-greed/pane.tsx +21 -21
  245. package/src/plugins/builtin/futures/contracts.ts +100 -0
  246. package/src/plugins/builtin/futures/index.tsx +298 -0
  247. package/src/plugins/builtin/futures/model.test.ts +129 -0
  248. package/src/plugins/builtin/futures/model.ts +136 -0
  249. package/src/plugins/builtin/futures/pane.test.tsx +239 -0
  250. package/src/plugins/builtin/futures/table.test.ts +155 -0
  251. package/src/plugins/builtin/futures/table.tsx +241 -0
  252. package/src/plugins/builtin/fx-matrix/index.tsx +160 -134
  253. package/src/plugins/builtin/fx-matrix/pairs.ts +12 -1
  254. package/src/plugins/builtin/fx-matrix/pane.test.tsx +109 -0
  255. package/src/plugins/builtin/help/components.tsx +1 -30
  256. package/src/plugins/builtin/help/index.tsx +5 -25
  257. package/src/plugins/builtin/holders/pane.tsx +17 -12
  258. package/src/plugins/builtin/holders/treemap.tsx +4 -2
  259. package/src/plugins/builtin/insider/index.tsx +20 -10
  260. package/src/plugins/builtin/ipo-calendar/cache.ts +101 -0
  261. package/src/plugins/builtin/ipo-calendar/client.test.ts +31 -0
  262. package/src/plugins/builtin/ipo-calendar/client.ts +296 -0
  263. package/src/plugins/builtin/ipo-calendar/index.tsx +65 -0
  264. package/src/plugins/builtin/ipo-calendar/model.ts +145 -0
  265. package/src/plugins/builtin/ipo-calendar/pane.tsx +328 -0
  266. package/src/plugins/builtin/ipo-calendar/types.ts +19 -0
  267. package/src/plugins/builtin/kelly-sizer/index.test.tsx +3 -3
  268. package/src/plugins/builtin/kelly-sizer/model.ts +8 -1
  269. package/src/plugins/builtin/kelly-sizer/pane.tsx +33 -15
  270. package/src/plugins/builtin/kelly-sizer/view.tsx +12 -5
  271. package/src/plugins/builtin/market-halts/client.ts +114 -0
  272. package/src/plugins/builtin/market-halts/index.tsx +29 -0
  273. package/src/plugins/builtin/market-halts/model.test.ts +187 -0
  274. package/src/plugins/builtin/market-halts/model.ts +291 -0
  275. package/src/plugins/builtin/market-halts/pane.tsx +256 -0
  276. package/src/plugins/builtin/market-heatmap/data.test.ts +1 -0
  277. package/src/plugins/builtin/market-heatmap/data.ts +12 -3
  278. package/src/plugins/builtin/market-heatmap/index.tsx +131 -46
  279. package/src/plugins/builtin/market-movers/index.tsx +145 -63
  280. package/src/plugins/builtin/market-movers/model.ts +19 -3
  281. package/src/plugins/builtin/market-movers/screener.test.ts +108 -0
  282. package/src/plugins/builtin/market-movers/screener.ts +168 -10
  283. package/src/plugins/builtin/news/index.tsx +78 -99
  284. package/src/plugins/builtin/news/wire/article-search.test.ts +72 -0
  285. package/src/plugins/builtin/news/wire/article-search.ts +140 -0
  286. package/src/plugins/builtin/news/wire/breaking/pane.tsx +18 -8
  287. package/src/plugins/builtin/news/wire/categories.ts +35 -0
  288. package/src/plugins/builtin/news/wire/default-feeds.ts +47 -1
  289. package/src/plugins/builtin/news/wire/feed-config.test.ts +10 -5
  290. package/src/plugins/builtin/news/wire/index.tsx +41 -4
  291. package/src/plugins/builtin/news/wire/industry-pane.tsx +38 -19
  292. package/src/plugins/builtin/news/wire/news/detail-view.tsx +10 -5
  293. package/src/plugins/builtin/news/wire/news/footer.ts +1 -0
  294. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +15 -6
  295. package/src/plugins/builtin/news/wire/news/query-presets.ts +16 -2
  296. package/src/plugins/builtin/news/wire/news/table.test.tsx +54 -0
  297. package/src/plugins/builtin/news/wire/news/table.tsx +111 -17
  298. package/src/plugins/builtin/notes/files.test.ts +18 -0
  299. package/src/plugins/builtin/notes/files.ts +10 -4
  300. package/src/plugins/builtin/notes/index.tsx +4 -1
  301. package/src/plugins/builtin/notes/quick-notes-pane.test.tsx +182 -0
  302. package/src/plugins/builtin/notes/quick-notes-pane.tsx +74 -31
  303. package/src/plugins/builtin/notes/ticker-notes-tab.test.tsx +380 -0
  304. package/src/plugins/builtin/notes/ticker-notes-tab.tsx +97 -40
  305. package/src/plugins/builtin/options/calc-seed.test.ts +118 -0
  306. package/src/plugins/builtin/options/calc-seed.ts +52 -0
  307. package/src/plugins/builtin/options/footer.ts +4 -1
  308. package/src/plugins/builtin/options/index.tsx +22 -1
  309. package/src/plugins/builtin/options/live-quotes.ts +8 -0
  310. package/src/plugins/builtin/options/table.ts +3 -1
  311. package/src/plugins/builtin/options/view.test.tsx +5 -1
  312. package/src/plugins/builtin/options/view.tsx +112 -17
  313. package/src/plugins/builtin/options-calculator/index.tsx +35 -0
  314. package/src/plugins/builtin/options-calculator/model.test.ts +172 -0
  315. package/src/plugins/builtin/options-calculator/model.ts +273 -0
  316. package/src/plugins/builtin/options-calculator/pane.test.tsx +138 -0
  317. package/src/plugins/builtin/options-calculator/pane.tsx +218 -0
  318. package/src/plugins/builtin/plugin-module.test.ts +16 -2
  319. package/src/plugins/builtin/plugin-module.ts +5 -1
  320. package/src/plugins/builtin/polls/client.ts +57 -0
  321. package/src/plugins/builtin/polls/hooks.ts +20 -0
  322. package/src/plugins/builtin/polls/index.tsx +35 -0
  323. package/src/plugins/builtin/polls/normalize.test.ts +230 -0
  324. package/src/plugins/builtin/polls/normalize.ts +319 -0
  325. package/src/plugins/builtin/polls/pane.tsx +722 -0
  326. package/src/plugins/builtin/polls/types.ts +77 -0
  327. package/src/plugins/builtin/portfolio-list/header.tsx +15 -4
  328. package/src/plugins/builtin/portfolio-list/pane/index.tsx +7 -3
  329. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +1 -1
  330. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -0
  331. package/src/plugins/builtin/portfolio-list/summary/index.tsx +2 -1
  332. package/src/plugins/builtin/research/analyst-pane.tsx +40 -67
  333. package/src/plugins/builtin/research/corporate-actions-pane.tsx +25 -19
  334. package/src/plugins/builtin/research/index.test.ts +6 -25
  335. package/src/plugins/builtin/research/index.tsx +11 -5
  336. package/src/plugins/builtin/research/relative-valuation-pane.tsx +86 -26
  337. package/src/plugins/builtin/scanner/denied.tsx +31 -0
  338. package/src/plugins/builtin/scanner/feed.ts +94 -0
  339. package/src/plugins/builtin/scanner/flow-model.test.ts +85 -0
  340. package/src/plugins/builtin/scanner/flow-model.ts +133 -0
  341. package/src/plugins/builtin/scanner/flow-pane.tsx +197 -0
  342. package/src/plugins/builtin/scanner/hilo-bars.tsx +121 -0
  343. package/src/plugins/builtin/scanner/hilo-model.test.ts +44 -0
  344. package/src/plugins/builtin/scanner/hilo-model.ts +75 -0
  345. package/src/plugins/builtin/scanner/hilo-pane.tsx +165 -0
  346. package/src/plugins/builtin/scanner/index.ts +109 -0
  347. package/src/plugins/builtin/scanner/waiting.tsx +11 -0
  348. package/src/plugins/builtin/sec/index.tsx +11 -7
  349. package/src/plugins/builtin/sectors/index.tsx +105 -81
  350. package/src/plugins/builtin/sectors/move-bar.tsx +43 -0
  351. package/src/plugins/builtin/sectors/sector-data.ts +18 -0
  352. package/src/plugins/builtin/sectors/sector-model.ts +13 -15
  353. package/src/plugins/builtin/shared/auto-refresh.ts +51 -0
  354. package/src/plugins/builtin/shared/cloud-upgrade.ts +29 -5
  355. package/src/plugins/builtin/shared/pane-footer.ts +16 -3
  356. package/src/plugins/builtin/shared/screener-live-quotes.test.ts +91 -0
  357. package/src/plugins/builtin/shared/screener-live-quotes.ts +134 -0
  358. package/src/plugins/builtin/shared/table-pane.ts +1 -5
  359. package/src/plugins/builtin/shared/use-quote-board.test.tsx +211 -0
  360. package/src/plugins/builtin/shared/use-quote-board.ts +195 -0
  361. package/src/plugins/builtin/short-interest/client.ts +124 -0
  362. package/src/plugins/builtin/short-interest/index.tsx +62 -0
  363. package/src/plugins/builtin/short-interest/model.ts +113 -0
  364. package/src/plugins/builtin/short-interest/pane.tsx +252 -0
  365. package/src/plugins/builtin/short-interest/types.ts +9 -0
  366. package/src/plugins/builtin/substack/article-detail.tsx +15 -1
  367. package/src/plugins/builtin/substack/pane-footer.ts +3 -22
  368. package/src/plugins/builtin/substack/pane.tsx +1 -2
  369. package/src/plugins/builtin/thirteenf/api.ts +4 -1
  370. package/src/plugins/builtin/thirteenf/model.ts +2 -0
  371. package/src/plugins/builtin/thirteenf/pane.tsx +6 -2
  372. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +10 -4
  373. package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +67 -11
  374. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +15 -8
  375. package/src/plugins/builtin/ticker-detail/index.tsx +5 -21
  376. package/src/plugins/builtin/ticker-detail/overview/components.tsx +72 -32
  377. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +0 -10
  378. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -1
  379. package/src/plugins/builtin/ticker-detail/quote-monitor/card.test.tsx +70 -0
  380. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +34 -26
  381. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +96 -49
  382. package/src/plugins/builtin/ticker-detail/research-tabs.ts +32 -0
  383. package/src/plugins/builtin/ticker-research-backend-plugin.ts +2 -9
  384. package/src/plugins/builtin/ticker-research-plugin.ts +6 -0
  385. package/src/plugins/builtin/treasury-auctions/cache.test.ts +173 -0
  386. package/src/plugins/builtin/treasury-auctions/cache.ts +104 -0
  387. package/src/plugins/builtin/treasury-auctions/client.test.ts +181 -0
  388. package/src/plugins/builtin/treasury-auctions/client.ts +151 -0
  389. package/src/plugins/builtin/treasury-auctions/index.tsx +90 -0
  390. package/src/plugins/builtin/treasury-auctions/model.test.ts +147 -0
  391. package/src/plugins/builtin/treasury-auctions/model.ts +188 -0
  392. package/src/plugins/builtin/treasury-auctions/pane.test.tsx +170 -0
  393. package/src/plugins/builtin/treasury-auctions/pane.tsx +417 -0
  394. package/src/plugins/builtin/treasury-auctions/types.ts +59 -0
  395. package/src/plugins/builtin/tv/pane.tsx +67 -7
  396. package/src/plugins/builtin/tv/youtube-embed.test.ts +84 -0
  397. package/src/plugins/builtin/tv/youtube-embed.ts +173 -0
  398. package/src/plugins/builtin/tv/youtube-stream.ts +27 -2
  399. package/src/plugins/builtin/volatility/client.ts +87 -0
  400. package/src/plugins/builtin/volatility/index.test.tsx +107 -0
  401. package/src/plugins/builtin/volatility/index.tsx +193 -0
  402. package/src/plugins/builtin/volatility/model.test.ts +66 -0
  403. package/src/plugins/builtin/volatility/model.ts +113 -0
  404. package/src/plugins/builtin/world-indices/footer.ts +43 -23
  405. package/src/plugins/builtin/world-indices/index.tsx +44 -93
  406. package/src/plugins/builtin/world-indices/indices.ts +11 -2
  407. package/src/plugins/builtin/world-indices/model.ts +17 -24
  408. package/src/plugins/builtin/world-indices/pane.test.tsx +116 -0
  409. package/src/plugins/builtin/world-indices/table.tsx +82 -37
  410. package/src/plugins/builtin/yield-curve/index.tsx +16 -7
  411. package/src/plugins/builtin/yield-curve/treasury-data.test.ts +25 -0
  412. package/src/plugins/builtin/yield-curve/treasury-data.ts +16 -0
  413. package/src/plugins/catalog-backend.ts +8 -0
  414. package/src/plugins/catalog-ui.ts +8 -0
  415. package/src/plugins/event-bus.ts +1 -0
  416. package/src/plugins/ibkr/gateway/account-loaders.ts +1 -1
  417. package/src/plugins/ibkr/trade/tab/footer.ts +9 -23
  418. package/src/plugins/ibkr/trade/tab/index.tsx +1 -5
  419. package/src/plugins/ibkr/trading/pane-view.tsx +149 -87
  420. package/src/plugins/ibkr/trading/pane.tsx +2 -2
  421. package/src/plugins/ownership.ts +3 -0
  422. package/src/plugins/pane-manager/floating.ts +8 -2
  423. package/src/plugins/pane-manager.test.ts +13 -0
  424. package/src/plugins/prediction-markets/backend-plugin.ts +2 -0
  425. package/src/plugins/prediction-markets/capability.test.ts +281 -0
  426. package/src/plugins/prediction-markets/capability.ts +171 -0
  427. package/src/plugins/prediction-markets/chart.tsx +18 -26
  428. package/src/plugins/prediction-markets/columns.ts +3 -2
  429. package/src/plugins/prediction-markets/controller/catalog.ts +15 -12
  430. package/src/plugins/prediction-markets/controller/detail.ts +4 -0
  431. package/src/plugins/prediction-markets/controller/index.ts +0 -15
  432. package/src/plugins/prediction-markets/detail/book.tsx +3 -23
  433. package/src/plugins/prediction-markets/detail/overview.tsx +0 -18
  434. package/src/plugins/prediction-markets/detail/pane.tsx +36 -12
  435. package/src/plugins/prediction-markets/detail.test.tsx +1 -1
  436. package/src/plugins/prediction-markets/index.tsx +2 -0
  437. package/src/plugins/prediction-markets/metrics.ts +28 -18
  438. package/src/plugins/prediction-markets/navigation.ts +1 -0
  439. package/src/plugins/prediction-markets/pane.test.tsx +1 -1
  440. package/src/plugins/prediction-markets/pane.tsx +0 -1
  441. package/src/plugins/prediction-markets/services/fetch.ts +3 -2
  442. package/src/plugins/prediction-markets/services/kalshi/adapter.ts +82 -23
  443. package/src/plugins/prediction-markets/services/kalshi/normalize.ts +3 -1
  444. package/src/plugins/prediction-markets/services/kalshi/types.ts +14 -0
  445. package/src/plugins/prediction-markets/services/polymarket/adapter.ts +26 -14
  446. package/src/plugins/prediction-markets/services/polymarket/detail.ts +67 -18
  447. package/src/plugins/prediction-markets/test-helpers.tsx +0 -1
  448. package/src/plugins/prediction-markets/types.ts +3 -9
  449. package/src/plugins/registry/context.ts +6 -1
  450. package/src/plugins/registry/index.test.ts +2 -0
  451. package/src/plugins/registry/index.ts +53 -3
  452. package/src/plugins/runtime/context.tsx +4 -2
  453. package/src/plugins/runtime/index.tsx +9 -0
  454. package/src/remote/controller.test.ts +17 -4
  455. package/src/renderers/electrobun/bun/application-menu/index.ts +1 -1
  456. package/src/renderers/electrobun/bun/core-capabilities.ts +2 -2
  457. package/src/renderers/electrobun/bun/desktop/capability-bridge.test.ts +64 -0
  458. package/src/renderers/electrobun/bun/desktop/capability-bridge.ts +41 -7
  459. package/src/renderers/electrobun/bun/desktop/host-requests.ts +5 -2
  460. package/src/renderers/electrobun/bun/desktop/http-fetch.test.ts +33 -0
  461. package/src/renderers/electrobun/bun/desktop/http-fetch.ts +53 -6
  462. package/src/renderers/electrobun/bun/desktop/initialization.test.ts +27 -0
  463. package/src/renderers/electrobun/bun/desktop/initialization.ts +6 -1
  464. package/src/renderers/electrobun/bun/index.ts +1 -0
  465. package/src/renderers/electrobun/shared/protocol.ts +2 -0
  466. package/src/renderers/electrobun/view/app-services.ts +25 -2
  467. package/src/renderers/electrobun/view/build-assets.ts +5 -0
  468. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +186 -65
  469. package/src/renderers/electrobun/view/cli-pane-shot-health.test.ts +25 -0
  470. package/src/renderers/electrobun/view/cli-pane-shot-health.ts +31 -0
  471. package/src/renderers/electrobun/view/data-table/index.tsx +10 -11
  472. package/src/renderers/electrobun/view/data-table/row.tsx +2 -0
  473. package/src/renderers/electrobun/view/desktop/controls.test.tsx +68 -0
  474. package/src/renderers/electrobun/view/desktop/controls.tsx +25 -1
  475. package/src/renderers/electrobun/view/host/box.test.tsx +80 -0
  476. package/src/renderers/electrobun/view/host/media-surface.tsx +70 -3
  477. package/src/renderers/electrobun/view/host/tabs.tsx +97 -5
  478. package/src/renderers/electrobun/view/host/text.tsx +5 -2
  479. package/src/renderers/electrobun/view/input-host.test.tsx +140 -0
  480. package/src/renderers/electrobun/view/input-host.tsx +4 -2
  481. package/src/renderers/electrobun/view/native-stubs/backend-rpc.ts +19 -1
  482. package/src/renderers/electrobun/view/native-stubs/broker-sync-native-loader.ts +7 -0
  483. package/src/renderers/electrobun/view/native-stubs/chart/kitty-support.ts +10 -0
  484. package/src/renderers/electrobun/view/remote/capability-invoker.test.ts +46 -0
  485. package/src/renderers/electrobun/view/remote/capability-invoker.ts +34 -13
  486. package/src/renderers/electrobun/view/remote/connection-health-backend.ts +19 -0
  487. package/src/renderers/electrobun/view/remote/connection-health.test.ts +44 -0
  488. package/src/renderers/electrobun/view/remote/connection-health.ts +49 -0
  489. package/src/renderers/electrobun/view/styles.css +5 -0
  490. package/src/renderers/opentui/dialog-host.tsx +279 -80
  491. package/src/renderers/opentui/host.tsx +15 -1
  492. package/src/renderers/opentui/terminal-media.test.ts +102 -0
  493. package/src/renderers/opentui/terminal-media.ts +142 -0
  494. package/src/renderers/opentui/test-utils.tsx +9 -6
  495. package/src/renderers/opentui/toast-host.tsx +136 -7
  496. package/src/renderers/opentui/ui-host.tsx +19 -3
  497. package/src/sources/provider-router/connection-health.test.ts +28 -0
  498. package/src/sources/provider-router/connection-health.ts +47 -0
  499. package/src/sources/provider-router/history.test.ts +53 -4
  500. package/src/sources/provider-router/history.ts +119 -14
  501. package/src/sources/provider-router/index.ts +12 -1
  502. package/src/sources/yahoo-finance/history.ts +2 -9
  503. package/src/sources/yahoo-finance/http.ts +1 -1
  504. package/src/sources/yahoo-finance/symbols.ts +1 -1
  505. package/src/sources/yahoo-finance/types.ts +19 -0
  506. package/src/state/app/context/index.test.tsx +101 -0
  507. package/src/state/app/context/index.tsx +10 -0
  508. package/src/state/config-save-scheduler.ts +1 -6
  509. package/src/state/persist-scheduler.test.ts +31 -0
  510. package/src/state/persist-scheduler.ts +21 -8
  511. package/src/sync/auth-transition.test.ts +36 -0
  512. package/src/sync/auth-transition.ts +22 -0
  513. package/src/sync/core-contributors.test.ts +58 -1
  514. package/src/sync/core-contributors.ts +39 -2
  515. package/src/sync/react.ts +9 -0
  516. package/src/test-support/plugin-runtime.ts +3 -0
  517. package/src/theme/font-scale.test.ts +14 -0
  518. package/src/theme/font-scale.ts +69 -0
  519. package/src/tickers/search/index.test.ts +284 -2
  520. package/src/tickers/search/index.ts +55 -18
  521. package/src/tickers/search/ranking.ts +143 -18
  522. package/src/tickers/search/types.ts +1 -0
  523. package/src/time-series/alignment.ts +15 -11
  524. package/src/time-series/field-catalog.ts +10 -0
  525. package/src/time-series/fundamentals.ts +19 -11
  526. package/src/time-series/hooks.ts +8 -2
  527. package/src/time-series/parsed-history-cache.ts +35 -0
  528. package/src/time-series/resolution.ts +10 -0
  529. package/src/time-series/resolve.test.ts +332 -3
  530. package/src/time-series/resolve.ts +225 -25
  531. package/src/time-series/spec-studies.test.ts +23 -0
  532. package/src/time-series/spec.ts +21 -2
  533. package/src/time-series/transforms.test.ts +76 -1
  534. package/src/time-series/transforms.ts +39 -8
  535. package/src/time-series/types.ts +9 -2
  536. package/src/time-series/use-chart-resolution.test.tsx +105 -1
  537. package/src/time-series/use-chart-resolution.ts +67 -13
  538. package/src/types/config.ts +30 -27
  539. package/src/types/media.ts +2 -0
  540. package/src/types/plugin.ts +8 -1
  541. package/src/ui/host.tsx +4 -0
  542. package/src/ui/pointer-drag.ts +12 -0
  543. package/src/ui/qr.test.ts +79 -0
  544. package/src/ui/qr.ts +82 -0
  545. package/src/updater/github-releases.test.ts +3 -3
  546. package/src/updater/github-releases.ts +1 -1
  547. package/src/updater.test.ts +117 -16
  548. package/src/updater.ts +32 -5
  549. package/src/utils/exchanges.ts +1 -0
  550. package/src/utils/external-url.test.ts +32 -0
  551. package/src/utils/external-url.ts +23 -0
  552. package/src/utils/sort-values.ts +31 -0
  553. package/src/utils/zoned-date-time.ts +67 -0
  554. package/src/components/onboarding/wizard-keyboard.ts +0 -306
  555. package/src/plugins/builtin/chart-composer/date-window-dialog.tsx +0 -120
@@ -30,6 +30,7 @@ import type {
30
30
  KalshiEventResponse,
31
31
  KalshiEventsResponse,
32
32
  KalshiOrderbookResponse,
33
+ KalshiSeriesResponse,
33
34
  KalshiTradesResponse,
34
35
  } from "./types";
35
36
 
@@ -39,9 +40,16 @@ const KALSHI_EVENT_PAGE_LIMIT = 200;
39
40
  const DEFAULT_KALSHI_EVENT_MAX_PAGES = 3;
40
41
  const SEARCH_KALSHI_EVENT_MAX_PAGES = 3;
41
42
 
42
- function buildKalshiCatalogUrl(cursor?: string, category?: string): string {
43
+ function kalshiSeriesTickerFromEvent(eventTicker: string | undefined): string | undefined {
44
+ const trimmed = eventTicker?.trim().toUpperCase();
45
+ if (!trimmed) return undefined;
46
+ const withoutDateSuffix = trimmed.replace(/-[0-9].*$/, "");
47
+ return withoutDateSuffix || trimmed;
48
+ }
49
+
50
+ function buildKalshiCatalogUrl(cursor?: string, category?: string, limit = KALSHI_EVENT_PAGE_LIMIT): string {
43
51
  const url = new URL("https://api.elections.kalshi.com/trade-api/v2/events");
44
- url.searchParams.set("limit", String(KALSHI_EVENT_PAGE_LIMIT));
52
+ url.searchParams.set("limit", String(limit));
45
53
  url.searchParams.set("status", "open");
46
54
  url.searchParams.set("with_nested_markets", "true");
47
55
  if (category) url.searchParams.set("category", category);
@@ -51,13 +59,16 @@ function buildKalshiCatalogUrl(cursor?: string, category?: string): string {
51
59
 
52
60
  async function fetchKalshiCatalogEvents(
53
61
  maxPages = DEFAULT_KALSHI_EVENT_MAX_PAGES,
62
+ limit = KALSHI_EVENT_PAGE_LIMIT,
63
+ signal?: AbortSignal,
54
64
  ): Promise<KalshiEventRecord[]> {
55
65
  const events: KalshiEventRecord[] = [];
56
66
  let cursor: string | undefined;
57
67
 
58
68
  for (let page = 0; page < maxPages; page += 1) {
59
69
  const response = await fetchJson<KalshiEventsResponse>(
60
- buildKalshiCatalogUrl(cursor),
70
+ buildKalshiCatalogUrl(cursor, undefined, limit),
71
+ signal,
61
72
  );
62
73
  events.push(...(response.events ?? []));
63
74
  cursor = response.cursor?.trim() || undefined;
@@ -70,16 +81,19 @@ async function fetchKalshiCatalogEvents(
70
81
  async function fetchKalshiCatalogEventsForCategory(
71
82
  categoryId: PredictionCategoryId,
72
83
  maxPages = DEFAULT_KALSHI_EVENT_MAX_PAGES,
84
+ limit = KALSHI_EVENT_PAGE_LIMIT,
85
+ signal?: AbortSignal,
73
86
  ): Promise<KalshiEventRecord[]> {
74
87
  const categories = getKalshiCategoryNames(categoryId);
75
- if (categories.length === 0) return await fetchKalshiCatalogEvents(maxPages);
88
+ if (categories.length === 0) return await fetchKalshiCatalogEvents(maxPages, limit, signal);
76
89
 
77
90
  const deduped = new Map<string, KalshiEventRecord>();
78
91
  for (const category of categories) {
79
92
  let cursor: string | undefined;
80
93
  for (let page = 0; page < maxPages; page += 1) {
81
94
  const response = await fetchJson<KalshiEventsResponse>(
82
- buildKalshiCatalogUrl(cursor, category),
95
+ buildKalshiCatalogUrl(cursor, category, limit),
96
+ signal,
83
97
  );
84
98
  for (const event of response.events ?? []) {
85
99
  const key = event.event_ticker ?? event.title;
@@ -96,20 +110,20 @@ async function fetchKalshiCatalogEventsForCategory(
96
110
  export async function loadKalshiCatalog(
97
111
  searchQuery = "",
98
112
  categoryId: PredictionCategoryId = "all",
113
+ options: { limit?: number; signal?: AbortSignal } = {},
99
114
  ): Promise<PredictionMarketSummary[]> {
100
115
  const normalizedQuery = searchQuery.trim().toLowerCase();
116
+ const requestedLimit = Math.max(1, Math.min(KALSHI_EVENT_PAGE_LIMIT, options.limit ?? KALSHI_EVENT_PAGE_LIMIT));
117
+ const pageLimit = Math.max(20, requestedLimit);
118
+ const maxPages = options.limit ? 1 : normalizedQuery ? SEARCH_KALSHI_EVENT_MAX_PAGES : DEFAULT_KALSHI_EVENT_MAX_PAGES;
101
119
  return await loadCachedPredictionResource(
102
120
  "catalog",
103
- buildPredictionCatalogResourceKey("kalshi", categoryId, normalizedQuery),
121
+ `${buildPredictionCatalogResourceKey("kalshi", categoryId, normalizedQuery)}:${requestedLimit}`,
104
122
  async () => {
105
- const maxPages = normalizedQuery
106
- ? SEARCH_KALSHI_EVENT_MAX_PAGES
107
- : DEFAULT_KALSHI_EVENT_MAX_PAGES;
108
- const events =
109
- categoryId === "all"
110
- ? await fetchKalshiCatalogEvents(maxPages)
111
- : await fetchKalshiCatalogEventsForCategory(categoryId, maxPages);
112
- return normalizeKalshiCatalog(events, normalizedQuery, categoryId);
123
+ const events = categoryId === "all"
124
+ ? await fetchKalshiCatalogEvents(maxPages, pageLimit, options.signal)
125
+ : await fetchKalshiCatalogEventsForCategory(categoryId, maxPages, pageLimit, options.signal);
126
+ return normalizeKalshiCatalog(events, normalizedQuery, categoryId).slice(0, requestedLimit);
113
127
  },
114
128
  PREDICTION_CACHE_POLICIES.catalog,
115
129
  );
@@ -134,6 +148,23 @@ async function loadKalshiEvent(
134
148
  }
135
149
  }
136
150
 
151
+ export async function resolveKalshiChartSummary(
152
+ eventTicker: string,
153
+ marketTicker: string,
154
+ signal?: AbortSignal,
155
+ ): Promise<PredictionMarketSummary> {
156
+ const response = await fetchJson<KalshiEventResponse>(
157
+ `https://api.elections.kalshi.com/trade-api/v2/events/${eventTicker}`,
158
+ signal,
159
+ );
160
+ const market = response.markets?.find((candidate) => candidate.ticker === marketTicker);
161
+ const summary = market ? normalizeKalshiMarket(market, response.event) : null;
162
+ if (!summary) {
163
+ throw new Error(`Kalshi market ${marketTicker} in event ${eventTicker} is no longer resolvable. Remove or replace this chart series.`);
164
+ }
165
+ return summary;
166
+ }
167
+
137
168
  async function loadKalshiTrades(
138
169
  summary: PredictionMarketSummary,
139
170
  ): Promise<PredictionTrade[]> {
@@ -194,11 +225,15 @@ async function loadKalshiBook(
194
225
  export async function loadKalshiHistory(
195
226
  summary: PredictionMarketSummary,
196
227
  range: "1D" | "1W" | "1M" | "ALL",
228
+ options: { start?: Date; end?: Date; signal?: AbortSignal; strict?: boolean } = {},
197
229
  ): Promise<PredictionHistoryPoint[]> {
198
- const event = await loadKalshiEvent(summary.eventTicker);
199
- if (!event?.event?.series_ticker) return [];
230
+ const seriesTicker = summary.seriesTicker ?? (await loadKalshiEvent(summary.eventTicker))?.event?.series_ticker;
231
+ if (!seriesTicker) {
232
+ if (options.strict) throw new Error(`Kalshi event ${summary.eventTicker ?? "unknown"} no longer exposes chart history.`);
233
+ return [];
234
+ }
200
235
 
201
- const now = Math.floor(Date.now() / 1000);
236
+ const now = Math.floor((options.end?.getTime() ?? Date.now()) / 1000);
202
237
  const rangeSeconds =
203
238
  range === "1D"
204
239
  ? 24 * 60 * 60
@@ -208,15 +243,16 @@ export async function loadKalshiHistory(
208
243
  ? 30 * 24 * 60 * 60
209
244
  : 365 * 24 * 60 * 60;
210
245
  const periodInterval = range === "1D" ? 60 : range === "1W" ? 60 : 1440;
211
- const start = now - rangeSeconds;
246
+ const start = Math.floor((options.start?.getTime() ?? (now * 1000 - rangeSeconds * 1000)) / 1000);
212
247
 
213
248
  try {
214
249
  return await loadCachedPredictionResource(
215
250
  "history",
216
- `${summary.key}:${range}`,
251
+ `${summary.key}:${range}:${start}:${now}`,
217
252
  async () => {
218
253
  const response = await fetchJson<KalshiCandlestickResponse>(
219
- `https://api.elections.kalshi.com/trade-api/v2/series/${event.event.series_ticker}/markets/${summary.marketId}/candlesticks?start_ts=${start}&end_ts=${now}&period_interval=${periodInterval}`,
254
+ `https://api.elections.kalshi.com/trade-api/v2/series/${seriesTicker}/markets/${summary.marketId}/candlesticks?start_ts=${start}&end_ts=${now}&period_interval=${periodInterval}`,
255
+ options.signal,
220
256
  );
221
257
  return (response.candlesticks ?? [])
222
258
  .map((candle) => ({
@@ -234,11 +270,30 @@ export async function loadKalshiHistory(
234
270
  },
235
271
  PREDICTION_CACHE_POLICIES.history,
236
272
  );
237
- } catch {
273
+ } catch (error) {
274
+ if (options.strict) throw error;
238
275
  return [];
239
276
  }
240
277
  }
241
278
 
279
+ async function loadKalshiSeriesSettlement(
280
+ seriesTicker: string | undefined,
281
+ ): Promise<string | undefined> {
282
+ const ticker = seriesTicker?.trim();
283
+ if (!ticker) return undefined;
284
+ try {
285
+ const response = await fetchJson<KalshiSeriesResponse>(
286
+ `https://api.elections.kalshi.com/trade-api/v2/series/${encodeURIComponent(ticker)}`,
287
+ );
288
+ const names = (response.series?.settlement_sources ?? [])
289
+ .map((source) => source.name?.trim())
290
+ .filter((name): name is string => !!name);
291
+ return names.length > 0 ? names.join(", ") : undefined;
292
+ } catch {
293
+ return undefined;
294
+ }
295
+ }
296
+
242
297
  export async function loadKalshiDetail(
243
298
  summary: PredictionMarketSummary,
244
299
  range: "1D" | "1W" | "1M" | "ALL",
@@ -247,11 +302,14 @@ export async function loadKalshiDetail(
247
302
  "detail",
248
303
  buildPredictionDetailResourceKey(summary.key, range),
249
304
  async () => {
250
- const [event, history, book, trades] = await Promise.all([
305
+ const seriesTicker = summary.seriesTicker
306
+ || kalshiSeriesTickerFromEvent(summary.eventTicker);
307
+ const [event, history, book, trades, resolutionSource] = await Promise.all([
251
308
  loadKalshiEvent(summary.eventTicker),
252
309
  loadKalshiHistory(summary, range),
253
310
  loadKalshiBook(summary),
254
311
  loadKalshiTrades(summary),
312
+ loadKalshiSeriesSettlement(seriesTicker),
255
313
  ]);
256
314
  const eventMeta = event?.event;
257
315
  const siblings: PredictionSiblingMarket[] = (event?.markets ?? [])
@@ -277,7 +335,8 @@ export async function loadKalshiDetail(
277
335
  ...summary,
278
336
  eventLabel: event?.event?.title ?? summary.eventLabel,
279
337
  category: event?.event?.category ?? summary.category,
280
- seriesTicker: event?.event?.series_ticker ?? summary.seriesTicker,
338
+ seriesTicker: event?.event?.series_ticker ?? seriesTicker,
339
+ resolutionSource: resolutionSource ?? summary.resolutionSource,
281
340
  tags: summary.tags?.length
282
341
  ? summary.tags
283
342
  : event?.event?.category
@@ -66,6 +66,7 @@ export function normalizeKalshiMarket(
66
66
  title?: string;
67
67
  category?: string;
68
68
  series_ticker?: string;
69
+ event_ticker?: string;
69
70
  sub_title?: string;
70
71
  },
71
72
  ): PredictionMarketSummary | null {
@@ -103,7 +104,7 @@ export function normalizeKalshiMarket(
103
104
  title: record.title,
104
105
  marketLabel,
105
106
  eventLabel: eventLabel || eventMeta?.title || record.title,
106
- eventTicker: record.event_ticker,
107
+ eventTicker: record.event_ticker ?? eventMeta?.event_ticker,
107
108
  seriesTicker: eventMeta?.series_ticker,
108
109
  category,
109
110
  tags: category ? [category] : [],
@@ -161,6 +162,7 @@ function flattenKalshiEvents(
161
162
  title: event.title,
162
163
  category: event.category,
163
164
  series_ticker: event.series_ticker,
165
+ event_ticker: event.event_ticker,
164
166
  sub_title: event.sub_title,
165
167
  });
166
168
  if (!normalized) continue;
@@ -74,6 +74,20 @@ export interface KalshiOrderbookResponse {
74
74
  };
75
75
  }
76
76
 
77
+ export interface KalshiSettlementSource {
78
+ name?: string;
79
+ url?: string;
80
+ }
81
+
82
+ export interface KalshiSeriesResponse {
83
+ series?: {
84
+ ticker?: string;
85
+ title?: string;
86
+ category?: string;
87
+ settlement_sources?: KalshiSettlementSource[];
88
+ };
89
+ }
90
+
77
91
  export interface KalshiCandlestickResponse {
78
92
  candlesticks?: Array<{
79
93
  end_period_ts: number;
@@ -31,9 +31,9 @@ export { loadPolymarketDetail } from "./detail";
31
31
  const POLYMARKET_CATALOG_OFFSETS = [0, 200, 400];
32
32
  const POLYMARKET_CATEGORY_OFFSETS = [0, 200];
33
33
 
34
- function buildPolymarketCatalogUrl(offset: number, tagSlug?: string): string {
34
+ function buildPolymarketCatalogUrl(offset: number, tagSlug?: string, limit = 200): string {
35
35
  const url = new URL("https://gamma-api.polymarket.com/events");
36
- url.searchParams.set("limit", "200");
36
+ url.searchParams.set("limit", String(limit));
37
37
  url.searchParams.set("offset", String(offset));
38
38
  url.searchParams.set("active", "true");
39
39
  url.searchParams.set("closed", "false");
@@ -43,10 +43,10 @@ function buildPolymarketCatalogUrl(offset: number, tagSlug?: string): string {
43
43
  return url.toString();
44
44
  }
45
45
 
46
- function buildPolymarketSearchUrl(query: string): string {
46
+ function buildPolymarketSearchUrl(query: string, limit = 40): string {
47
47
  const url = new URL("https://gamma-api.polymarket.com/public-search");
48
48
  url.searchParams.set("q", query);
49
- url.searchParams.set("limit_per_type", "40");
49
+ url.searchParams.set("limit_per_type", String(limit));
50
50
  url.searchParams.set("search_profiles", "false");
51
51
  url.searchParams.set("search_tags", "false");
52
52
  url.searchParams.set("events_status", "open");
@@ -57,11 +57,14 @@ function buildPolymarketSearchUrl(query: string): string {
57
57
  async function loadPolymarketCatalogPages(
58
58
  offsets: number[],
59
59
  tagSlug?: string,
60
+ limit = 200,
61
+ signal?: AbortSignal,
60
62
  ): Promise<PolymarketEventRecord[]> {
61
63
  const results = await Promise.allSettled(
62
64
  offsets.map((offset) =>
63
65
  fetchJson<PolymarketEventRecord[]>(
64
- buildPolymarketCatalogUrl(offset, tagSlug),
66
+ buildPolymarketCatalogUrl(offset, tagSlug, limit),
67
+ signal,
65
68
  ),
66
69
  ),
67
70
  );
@@ -78,21 +81,25 @@ async function loadPolymarketCatalogPages(
78
81
  export async function loadPolymarketCatalog(
79
82
  searchQuery = "",
80
83
  categoryId: PredictionCategoryId = "all",
84
+ options: { limit?: number; signal?: AbortSignal } = {},
81
85
  ): Promise<PredictionMarketSummary[]> {
82
86
  const normalizedQuery = searchQuery.trim().toLowerCase();
87
+ const requestedLimit = Math.max(1, Math.min(200, options.limit ?? 200));
88
+ const pageLimit = Math.max(20, requestedLimit);
83
89
  return await loadCachedPredictionResource(
84
90
  "catalog",
85
- buildPredictionCatalogResourceKey("polymarket", categoryId, normalizedQuery),
91
+ `${buildPredictionCatalogResourceKey("polymarket", categoryId, normalizedQuery)}:${requestedLimit}`,
86
92
  async () => {
87
93
  if (normalizedQuery.length > 0) {
88
94
  const response = await fetchJson<PolymarketSearchResponse>(
89
- buildPolymarketSearchUrl(normalizedQuery),
95
+ buildPolymarketSearchUrl(normalizedQuery, Math.min(40, pageLimit)),
96
+ options.signal,
90
97
  );
91
- const searchEvents = response.events ?? [];
98
+ const searchEvents = (response.events ?? []).slice(0, requestedLimit);
92
99
  const hydratedEvents = (
93
100
  await Promise.all(
94
101
  [...new Set(searchEvents.map((event) => event.id).filter(Boolean))]
95
- .map((eventId) => loadPolymarketEvent(eventId)),
102
+ .map((eventId) => loadPolymarketEvent(eventId, options.signal)),
96
103
  )
97
104
  ).filter((event): event is PolymarketEventRecord => event != null);
98
105
  const resolvedEvents = reconcilePolymarketSearchEvents(
@@ -103,7 +110,7 @@ export async function loadPolymarketCatalog(
103
110
  resolvedEvents,
104
111
  normalizedQuery,
105
112
  categoryId,
106
- );
113
+ ).slice(0, requestedLimit);
107
114
  }
108
115
 
109
116
  if (categoryId !== "all") {
@@ -111,8 +118,10 @@ export async function loadPolymarketCatalog(
111
118
  const categoryPages = await Promise.all(
112
119
  tagSlugs.map((tagSlug) =>
113
120
  loadPolymarketCatalogPages(
114
- POLYMARKET_CATEGORY_OFFSETS,
121
+ options.limit ? [0] : POLYMARKET_CATEGORY_OFFSETS,
115
122
  tagSlug,
123
+ pageLimit,
124
+ options.signal,
116
125
  ).catch(() => []),
117
126
  ),
118
127
  );
@@ -121,13 +130,16 @@ export async function loadPolymarketCatalog(
121
130
  "",
122
131
  categoryId,
123
132
  );
124
- if (categorized.length > 0) return categorized;
133
+ if (categorized.length > 0) return categorized.slice(0, requestedLimit);
125
134
  }
126
135
 
127
136
  const pages = await loadPolymarketCatalogPages(
128
- POLYMARKET_CATALOG_OFFSETS,
137
+ options.limit ? [0] : POLYMARKET_CATALOG_OFFSETS,
138
+ undefined,
139
+ pageLimit,
140
+ options.signal,
129
141
  );
130
- return normalizePolymarketCatalog(pages, "", categoryId);
142
+ return normalizePolymarketCatalog(pages, "", categoryId).slice(0, requestedLimit);
131
143
  },
132
144
  PREDICTION_CACHE_POLICIES.catalog,
133
145
  );
@@ -36,8 +36,10 @@ import type {
36
36
 
37
37
  export async function loadPolymarketEvent(
38
38
  eventId: string | undefined,
39
+ signal?: AbortSignal,
39
40
  ): Promise<PolymarketEventRecord | null> {
40
41
  if (!eventId) return null;
42
+ signal?.throwIfAborted();
41
43
  try {
42
44
  return await loadCachedPredictionResource(
43
45
  "rules",
@@ -45,10 +47,12 @@ export async function loadPolymarketEvent(
45
47
  async () =>
46
48
  await fetchJson<PolymarketEventRecord>(
47
49
  `https://gamma-api.polymarket.com/events/${eventId}`,
50
+ signal,
48
51
  ),
49
52
  PREDICTION_CACHE_POLICIES.rules,
50
53
  );
51
- } catch {
54
+ } catch (error) {
55
+ if (signal?.aborted) throw signal.reason ?? error;
52
56
  return null;
53
57
  }
54
58
  }
@@ -81,6 +85,25 @@ function findCanonicalPolymarketMarket(
81
85
  );
82
86
  }
83
87
 
88
+ export async function resolvePolymarketChartSummary(
89
+ eventId: string,
90
+ marketId: string,
91
+ signal?: AbortSignal,
92
+ ): Promise<PredictionMarketSummary> {
93
+ const event = await fetchJson<PolymarketEventRecord>(
94
+ `https://gamma-api.polymarket.com/events/${eventId}`,
95
+ signal,
96
+ );
97
+ const market = event.markets?.find((candidate) => candidate.id === marketId);
98
+ const summary = market
99
+ ? normalizePolymarketMarket(hydratePolymarketMarket(market, event))
100
+ : null;
101
+ if (!summary) {
102
+ throw new Error(`Polymarket market ${marketId} in event ${eventId} is no longer resolvable. Remove or replace this chart series.`);
103
+ }
104
+ return summary;
105
+ }
106
+
84
107
  async function resolvePolymarketSummary(
85
108
  summary: PredictionMarketSummary,
86
109
  ): Promise<{
@@ -121,12 +144,16 @@ async function resolvePolymarketSummary(
121
144
  };
122
145
  }
123
146
 
124
- async function loadPolymarketHistory(
147
+ export async function loadPolymarketHistory(
125
148
  summary: PredictionMarketSummary,
126
149
  range: "1D" | "1W" | "1M" | "ALL",
150
+ options: { start?: Date; end?: Date; signal?: AbortSignal; strict?: boolean } = {},
127
151
  ): Promise<PredictionHistoryPoint[]> {
128
152
  const tokenId = summary.yesTokenId;
129
- if (!tokenId) return [];
153
+ if (!tokenId) {
154
+ if (options.strict) throw new Error(`Polymarket market ${summary.marketId} no longer exposes a YES token for chart history.`);
155
+ return [];
156
+ }
130
157
  const interval =
131
158
  range === "1D"
132
159
  ? "1d"
@@ -137,14 +164,24 @@ async function loadPolymarketHistory(
137
164
  : "max";
138
165
  const fidelity =
139
166
  range === "1D" ? 15 : range === "1W" ? 60 : range === "1M" ? 240 : 1440;
167
+ const start = options.start ? Math.floor(options.start.getTime() / 1000) : null;
168
+ const end = options.end ? Math.floor(options.end.getTime() / 1000) : null;
169
+ const bounded = start !== null && end !== null && Number.isFinite(start) && Number.isFinite(end) && start <= end;
140
170
 
141
171
  return await loadCachedPredictionResource(
142
172
  "history",
143
- `${summary.key}:${range}`,
173
+ `${summary.key}:${range}${bounded ? `:${start}:${end}` : ""}`,
144
174
  async () => {
145
- const response = await fetchJson<PolymarketHistoryResponse>(
146
- `https://clob.polymarket.com/prices-history?market=${tokenId}&interval=${interval}&fidelity=${fidelity}`,
147
- );
175
+ const url = new URL("https://clob.polymarket.com/prices-history");
176
+ url.searchParams.set("market", tokenId);
177
+ url.searchParams.set("fidelity", String(fidelity));
178
+ if (bounded) {
179
+ url.searchParams.set("startTs", String(start));
180
+ url.searchParams.set("endTs", String(end));
181
+ } else {
182
+ url.searchParams.set("interval", interval);
183
+ }
184
+ const response = await fetchJson<PolymarketHistoryResponse>(url.toString(), options.signal);
148
185
  return (response.history ?? [])
149
186
  .map((point) => ({
150
187
  date: new Date(point.t * 1000),
@@ -204,19 +241,31 @@ async function loadPolymarketBook(
204
241
  "book",
205
242
  summary.key,
206
243
  async () => {
207
- const [yesBook, noBook] = await Promise.all([
208
- yesTokenId
209
- ? fetchJson<PolymarketBookResponse>(
210
- `https://clob.polymarket.com/book?token_id=${yesTokenId}`,
211
- ).catch(() => null)
212
- : Promise.resolve(null),
213
- noTokenId
214
- ? fetchJson<PolymarketBookResponse>(
215
- `https://clob.polymarket.com/book?token_id=${noTokenId}`,
216
- ).catch(() => null)
217
- : Promise.resolve(null),
244
+ const loadSide = async (tokenId: string | null | undefined) => {
245
+ if (!tokenId) return { book: null, error: null as string | null };
246
+ try {
247
+ return {
248
+ book: await fetchJson<PolymarketBookResponse>(
249
+ `https://clob.polymarket.com/book?token_id=${tokenId}`,
250
+ ),
251
+ error: null as string | null,
252
+ };
253
+ } catch (error) {
254
+ // Keep the failure: an empty book and a failed book look identical downstream.
255
+ return {
256
+ book: null,
257
+ error: error instanceof Error ? error.message : String(error),
258
+ };
259
+ }
260
+ };
261
+ const [yesResult, noResult] = await Promise.all([
262
+ loadSide(yesTokenId),
263
+ loadSide(noTokenId),
218
264
  ]);
265
+ const yesBook = yesResult.book;
266
+ const noBook = noResult.book;
219
267
  return {
268
+ error: yesResult.error ?? noResult.error,
220
269
  yesBids: (yesBook?.bids ?? [])
221
270
  .map(normalizePolymarketBookLevel)
222
271
  .filter((level): level is PredictionBookLevel => level != null),
@@ -522,7 +522,6 @@ export function GroupedDetailHarness({
522
522
  historyRange="1M"
523
523
  onDetailTabChange={() => {}}
524
524
  onHistoryRangeChange={() => {}}
525
- onPreviewOrder={() => {}}
526
525
  onSelectMarket={() => {}}
527
526
  scrollRef={scrollRef}
528
527
  selectedRow={selectedRow!}
@@ -4,7 +4,7 @@ import type { PredictionCategoryId } from "./categories";
4
4
  export type PredictionVenue = "polymarket" | "kalshi";
5
5
  export type PredictionVenueScope = "all" | PredictionVenue;
6
6
  export type PredictionBrowseTab = "top" | "ending" | "new" | "watchlist";
7
- export type PredictionDetailTab = "overview" | "book" | "trades" | "rules";
7
+ export type PredictionDetailTab = "overview" | "chart" | "book" | "trades" | "rules";
8
8
  export type PredictionHistoryRange = "1D" | "1W" | "1M" | "ALL";
9
9
  export type PredictionVolumeUnit = "usd" | "contracts";
10
10
  type PredictionSortDirection = "asc" | "desc";
@@ -99,6 +99,8 @@ export interface PredictionBookSnapshot {
99
99
  noBids: PredictionBookLevel[];
100
100
  noAsks: PredictionBookLevel[];
101
101
  lastTradePrice: number | null;
102
+ /** Set when the venue book request failed, so empty depth is never reported as "no depth". */
103
+ error?: string | null;
102
104
  }
103
105
 
104
106
  export interface PredictionTrade {
@@ -119,14 +121,6 @@ export interface PredictionMarketDetail {
119
121
  trades: PredictionTrade[];
120
122
  }
121
123
 
122
- export interface PredictionOrderPreviewIntent {
123
- marketKey: string;
124
- outcome: "yes" | "no";
125
- side: "buy" | "sell";
126
- price: number;
127
- size: number;
128
- }
129
-
130
124
  export interface PredictionColumnDef extends ColumnConfig {
131
125
  description: string;
132
126
  }
@@ -1,10 +1,12 @@
1
1
  import type { AppPersistencePort, AppTickerRepositoryPort } from "../../core/app-service-ports";
2
+ import type { ConnectionHealthRegistry } from "../../core/connection-health";
2
3
  import type { BrokerInstanceConfig, LayoutConfig } from "../../types/config";
3
4
  import type { DataProvider } from "../../types/data-provider";
4
5
  import type { TickerFinancials } from "../../types/financials";
5
6
  import type { TickerRecord } from "../../types/ticker";
6
7
  import type { PluginCapability } from "../../capabilities";
7
8
  import type {
9
+ AppNotificationDelivery,
8
10
  AppNotificationRequest,
9
11
  BrokerInstanceUpdateOptions,
10
12
  GloomPluginContext,
@@ -29,6 +31,7 @@ export interface RegistryPluginContextOptions {
29
31
  contributions: RegistryContributions;
30
32
  enableCapabilityHandlers: boolean;
31
33
  marketData: DataProvider;
34
+ connectionHealth: ConnectionHealthRegistry;
32
35
  tickerRepository: AppTickerRepositoryPort;
33
36
  persistence: AppPersistencePort;
34
37
  getLayout: () => LayoutConfig;
@@ -69,7 +72,7 @@ export interface RegistryPluginContextOptions {
69
72
  on: <K extends keyof PluginEvents>(event: K, handler: (payload: PluginEvents[K]) => void) => () => void;
70
73
  emit: <K extends keyof PluginEvents>(event: K, payload: PluginEvents[K]) => void;
71
74
  };
72
- notify: (notification: AppNotificationRequest) => void;
75
+ notify: (notification: AppNotificationRequest) => AppNotificationDelivery | void;
73
76
  }
74
77
 
75
78
  export function createRegistryPluginContext({
@@ -78,6 +81,7 @@ export function createRegistryPluginContext({
78
81
  contributions,
79
82
  enableCapabilityHandlers,
80
83
  marketData,
84
+ connectionHealth,
81
85
  tickerRepository,
82
86
  persistence,
83
87
  getLayout,
@@ -169,6 +173,7 @@ export function createRegistryPluginContext({
169
173
  getPaneDef: (paneId) => contributions.panesMap.get(paneId),
170
174
 
171
175
  marketData,
176
+ connectionHealth,
172
177
  tickerRepository,
173
178
  persistence: pluginPersistence,
174
179
  log,
@@ -145,6 +145,7 @@ describe("built-in composite plugin ownership", () => {
145
145
  expect(registry.getPluginPaneIds("application")).toEqual(expect.arrayContaining([
146
146
  "help",
147
147
  "changelog",
148
+ "connections",
148
149
  ]));
149
150
  expect(registry.getPluginPaneIds("macro")).toEqual(expect.arrayContaining([
150
151
  "econ-calendar",
@@ -155,6 +156,7 @@ describe("built-in composite plugin ownership", () => {
155
156
  expect(registry.getPaneTemplatePluginId("macro-tv-pane")).toBe("macro");
156
157
  expect(registry.getPanePluginId("analytics")).toBe("portfolio");
157
158
  expect(registry.getPanePluginId("help")).toBe("application");
159
+ expect(registry.getPanePluginId("connections")).toBe("application");
158
160
  expect(registry.getPanePluginId("macro-tv")).toBe("macro");
159
161
  expect(registry.getCommandPluginId("earnings-monitor-shortcut")).toBe("macro");
160
162
  expect(registry.getCommandPluginId("gridlock-all")).toBe("application");