gloomberb 0.11.3 → 0.12.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 (489) hide show
  1. package/README.md +6 -4
  2. package/README.zh-CN.md +4 -2
  3. package/package.json +28 -1
  4. package/src/api-client/auth.ts +10 -0
  5. package/src/api-client/data.ts +103 -1
  6. package/src/api-client/index.test.ts +127 -0
  7. package/src/api-client/index.ts +129 -1
  8. package/src/api-client/normalizers.ts +39 -0
  9. package/src/api-client/paths.ts +85 -1
  10. package/src/api-client/types.ts +196 -0
  11. package/src/app/pane-runtime/index.tsx +15 -12
  12. package/src/app/pane-runtime/layout-placement.test.ts +27 -0
  13. package/src/app/pane-runtime/layout-placement.ts +13 -0
  14. package/src/app/pane-runtime/plugin-bindings.ts +62 -0
  15. package/src/app.tsx +60 -18
  16. package/src/architecture/browser-entry-graph.test.ts +96 -0
  17. package/src/brokers/profile-form.test.ts +25 -27
  18. package/src/brokers/test-broker.ts +68 -0
  19. package/src/cli/commands/plugins.ts +39 -11
  20. package/src/cli/desktop-pane-shot.ts +285 -16
  21. package/src/cli/index.ts +1 -1
  22. package/src/cli/pane-functions/capabilities.ts +58 -28
  23. package/src/cli/pane-functions/catalog.ts +16 -4
  24. package/src/cli/pane-functions/discovery.ts +1 -0
  25. package/src/cli/pane-functions/headless.test.ts +247 -0
  26. package/src/cli/pane-functions/headless.ts +423 -0
  27. package/src/cli/pane-functions/index.test.ts +9 -0
  28. package/src/cli/pane-functions/index.ts +6 -2
  29. package/src/cli/pane-functions/options.ts +27 -2
  30. package/src/cli/pane-functions/report.ts +4 -0
  31. package/src/cli/pane-functions/resolver.ts +26 -8
  32. package/src/cli/pane-functions/screenshot.test.ts +76 -0
  33. package/src/cli/pane-functions/screenshot.ts +236 -20
  34. package/src/cli/restore-plugins.ts +34 -0
  35. package/src/components/chart/{static/chart → composite}/axis-overlays.test.tsx +3 -3
  36. package/src/components/chart/{static/chart → composite}/axis-overlays.tsx +6 -11
  37. package/src/components/chart/{static/chart → composite}/bitmap.ts +17 -3
  38. package/src/components/chart/composite/composite-chart.test.tsx +68 -20
  39. package/src/components/chart/composite/composite-chart.tsx +301 -204
  40. package/src/components/chart/composite/format.ts +8 -2
  41. package/src/components/chart/composite/interactions.test.ts +68 -102
  42. package/src/components/chart/composite/interactions.ts +214 -264
  43. package/src/components/chart/composite/price-axis-labels.test.ts +21 -0
  44. package/src/components/chart/{price-axis-labels.tsx → composite/price-axis-labels.tsx} +9 -4
  45. package/src/components/chart/composite/scene.ts +57 -3
  46. package/src/components/chart/composite/text-renderer.ts +3 -2
  47. package/src/components/chart/composite/time-scale.ts +138 -57
  48. package/src/components/chart/composite/types.ts +25 -1
  49. package/src/components/chart/core/data.ts +9 -13
  50. package/src/components/chart/core/date-window.ts +7 -1
  51. package/src/components/chart/core/pointer.ts +3 -0
  52. package/src/components/chart/core/types.ts +0 -45
  53. package/src/components/chart/native/chart-rasterizer.ts +0 -1
  54. package/src/components/chart/native/kitty/index.test.ts +0 -188
  55. package/src/components/chart/native/raster/crosshair.ts +6 -5
  56. package/src/components/chart/native/raster/primitives.ts +0 -23
  57. package/src/components/chart/native/renderer-selection.ts +81 -31
  58. package/src/components/chart/static/{chart/surface.test.tsx → chart-surface.test.tsx} +28 -80
  59. package/src/components/chart/static/chart-surface.tsx +150 -0
  60. package/src/components/chart/static/index.ts +8 -0
  61. package/src/components/chart/static/multi-line-chart-surface.tsx +51 -166
  62. package/src/components/chart/static/scatter-chart-surface.test.ts +30 -0
  63. package/src/components/chart/static/scatter-chart-surface.tsx +135 -34
  64. package/src/components/chart/static/series.ts +49 -0
  65. package/src/components/command-bar/action-runtime.ts +1 -3
  66. package/src/components/command-bar/assist/model.test.ts +17 -2
  67. package/src/components/command-bar/assist/model.ts +40 -9
  68. package/src/components/command-bar/commands/direct/index.ts +1 -1
  69. package/src/components/command-bar/commands/direct/runtime.ts +1 -1
  70. package/src/components/command-bar/commands/registry.ts +0 -11
  71. package/src/components/command-bar/helpers.test.ts +2 -2
  72. package/src/components/command-bar/helpers.ts +2 -6
  73. package/src/components/command-bar/keyboard-shortcuts.ts +0 -11
  74. package/src/components/command-bar/list/badge.test.ts +54 -0
  75. package/src/components/command-bar/list/badge.ts +62 -0
  76. package/src/components/command-bar/list/model.test.ts +103 -0
  77. package/src/components/command-bar/list/model.ts +61 -4
  78. package/src/components/command-bar/list/view.test.tsx +113 -0
  79. package/src/components/command-bar/list/view.tsx +147 -131
  80. package/src/components/command-bar/pane-templates/workflow.ts +1 -1
  81. package/src/components/command-bar/panel/body.tsx +51 -65
  82. package/src/components/command-bar/panel/index.tsx +48 -13
  83. package/src/components/command-bar/panel/layout.test.ts +139 -0
  84. package/src/components/command-bar/panel/layout.ts +65 -28
  85. package/src/components/command-bar/panel/native-surface.ts +24 -0
  86. package/src/components/command-bar/panel/prompt-binding.ts +54 -0
  87. package/src/components/command-bar/panel/runtime.ts +6 -3
  88. package/src/components/command-bar/panel/state.ts +41 -7
  89. package/src/components/command-bar/panel/types.ts +7 -1
  90. package/src/components/command-bar/routes/root/provider-search.ts +34 -15
  91. package/src/components/command-bar/routes/root/results.test.ts +161 -0
  92. package/src/components/command-bar/routes/root/results.ts +30 -28
  93. package/src/components/command-bar/routes/root/runtime.ts +37 -20
  94. package/src/components/command-bar/routes/root/search-providers.test.tsx +224 -0
  95. package/src/components/command-bar/routes/root/search-providers.ts +205 -0
  96. package/src/components/command-bar/routes/root/selection.ts +1 -12
  97. package/src/components/command-bar/routes/root/shortcuts.test.ts +8 -8
  98. package/src/components/command-bar/routes/ticker-search/actions.ts +10 -9
  99. package/src/components/command-bar/routes/ticker-search/results.test.ts +54 -1
  100. package/src/components/command-bar/routes/ticker-search/results.ts +60 -19
  101. package/src/components/command-bar/routes/ticker-search/route.ts +1 -0
  102. package/src/components/command-bar/routing/actions.ts +1 -81
  103. package/src/components/command-bar/routing/launch-request.ts +1 -1
  104. package/src/components/command-bar/routing/list-state.ts +6 -21
  105. package/src/components/command-bar/selection-runtime.ts +1 -1
  106. package/src/components/command-bar/surface/assist.test.tsx +14 -10
  107. package/src/components/command-bar/surface/environment.ts +8 -1
  108. package/src/components/command-bar/surface/index.test.tsx +84 -68
  109. package/src/components/command-bar/surface/index.tsx +33 -36
  110. package/src/components/command-bar/surface/test-harness.tsx +15 -3
  111. package/src/components/command-bar/theme-picker.tsx +43 -15
  112. package/src/components/command-bar/view-model.test.ts +53 -8
  113. package/src/components/command-bar/view-model.ts +43 -14
  114. package/src/components/command-bar/workflow/tickers.ts +1 -0
  115. package/src/components/command-bar/workflow/types.ts +1 -1
  116. package/src/components/data-table/view.test.tsx +45 -6
  117. package/src/components/data-table/view.tsx +55 -9
  118. package/src/components/index.ts +10 -2
  119. package/src/components/input-search-bar.test.tsx +36 -2
  120. package/src/components/input-search-bar.tsx +18 -1
  121. package/src/components/layout/header.test.tsx +57 -10
  122. package/src/components/layout/header.tsx +292 -149
  123. package/src/components/layout/market-summary.test.ts +81 -0
  124. package/src/components/layout/market-summary.ts +109 -0
  125. package/src/components/layout/shell/chrome.ts +96 -1
  126. package/src/components/layout/shell/index.tsx +11 -3
  127. package/src/components/layout/shell/menu.ts +9 -0
  128. package/src/components/layout/shell/pane/actions.ts +29 -0
  129. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  130. package/src/components/layout/shell/shortcuts.ts +3 -0
  131. package/src/components/layout/status-bar.test.tsx +9 -8
  132. package/src/components/layout/status-bar.tsx +85 -54
  133. package/src/components/onboarding/onboarding-frame.tsx +2 -26
  134. package/src/components/price-sparkline/index.ts +61 -34
  135. package/src/components/price-sparkline/view.tsx +1 -1
  136. package/src/components/sign-in-gate.test.tsx +128 -0
  137. package/src/components/sign-in-gate.tsx +149 -0
  138. package/src/components/speedometer-gauge/desktop.tsx +2 -12
  139. package/src/components/ticker/badge/list.tsx +17 -3
  140. package/src/components/ticker/list-table-view.tsx +6 -3
  141. package/src/components/ui/data-table/index.tsx +3 -2
  142. package/src/components/ui/data-table/opentui/index.tsx +165 -120
  143. package/src/components/ui/frame.tsx +28 -0
  144. package/src/components/ui/index.ts +3 -0
  145. package/src/components/ui/native-select.tsx +20 -10
  146. package/src/components/ui/select-button.tsx +138 -0
  147. package/src/components/ui/status.tsx +2 -2
  148. package/src/components/ui/table-layout.ts +1 -1
  149. package/src/components/use-double-click-activation.ts +7 -3
  150. package/src/core/app-services.ts +3 -3
  151. package/src/data/config/layout.ts +12 -6
  152. package/src/data/config/store/index.test.ts +97 -0
  153. package/src/data/config/store/migrations.ts +41 -0
  154. package/src/data/config/store/normalize.ts +5 -3
  155. package/src/i18n/es.ts +6 -2
  156. package/src/i18n/ja.ts +6 -2
  157. package/src/i18n/ko.ts +6 -2
  158. package/src/i18n/zh-cn.ts +6 -2
  159. package/src/i18n/zh-tw.ts +6 -2
  160. package/src/layout-marketplace/pane-imagery.ts +1 -0
  161. package/src/layout-marketplace/payload.test.ts +17 -0
  162. package/src/layout-marketplace/payload.ts +40 -5
  163. package/src/market-data/coordinator/index.ts +3 -2
  164. package/src/market-data/coordinator/quotes.ts +173 -66
  165. package/src/market-data/coordinator/subscriptions.test.ts +215 -37
  166. package/src/pane-settings.ts +1 -1
  167. package/src/plugins/builtin/analytics/pane-model.ts +1 -1
  168. package/src/plugins/builtin/analytics/view.tsx +1 -1
  169. package/src/plugins/builtin/broker-manager/index.test.tsx +3 -2
  170. package/src/plugins/builtin/broker-manager/model.test.ts +2 -2
  171. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +16 -2
  172. package/src/plugins/builtin/chart-composer/pane.tsx +22 -8
  173. package/src/plugins/builtin/chat/content/composer.tsx +8 -4
  174. package/src/plugins/builtin/cloud/auth-dialog.tsx +14 -228
  175. package/src/plugins/builtin/cloud/auth-form.tsx +277 -0
  176. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  177. package/src/plugins/builtin/composite-plugins.ts +14 -1
  178. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  179. package/src/plugins/builtin/correlation/relationship/pane.tsx +2 -6
  180. package/src/plugins/builtin/correlation/relationship/view-model.ts +2 -2
  181. package/src/plugins/builtin/credit-conditions/index.test.tsx +12 -6
  182. package/src/plugins/builtin/credit-conditions/index.tsx +2 -21
  183. package/src/plugins/builtin/dividend-yield/index.tsx +1 -0
  184. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  185. package/src/plugins/builtin/earnings-calls/data.ts +223 -0
  186. package/src/plugins/builtin/earnings-calls/format.ts +49 -0
  187. package/src/plugins/builtin/earnings-calls/index.tsx +69 -0
  188. package/src/plugins/builtin/earnings-calls/pane.tsx +756 -0
  189. package/src/plugins/builtin/earnings-calls/prose.test.ts +36 -0
  190. package/src/plugins/builtin/earnings-calls/prose.ts +59 -0
  191. package/src/plugins/builtin/earnings-calls/transcript-view.tsx +334 -0
  192. package/src/plugins/builtin/econ/detail-view.tsx +1 -2
  193. package/src/plugins/builtin/econ/fred-series-map.ts +16 -1
  194. package/src/plugins/builtin/econ/index.tsx +2 -1
  195. package/src/plugins/builtin/econ-statistics/cache.ts +4 -0
  196. package/src/plugins/builtin/econ-statistics/client.ts +92 -0
  197. package/src/plugins/builtin/econ-statistics/defs.ts +66 -0
  198. package/src/plugins/builtin/econ-statistics/detail.tsx +154 -0
  199. package/src/plugins/builtin/econ-statistics/headless.test.ts +54 -0
  200. package/src/plugins/builtin/econ-statistics/headless.ts +171 -0
  201. package/src/plugins/builtin/econ-statistics/index.tsx +54 -0
  202. package/src/plugins/builtin/econ-statistics/pane.tsx +356 -0
  203. package/src/plugins/builtin/econ-statistics/stats.ts +522 -0
  204. package/src/plugins/builtin/econ-statistics/transform.test.ts +86 -0
  205. package/src/plugins/builtin/econ-statistics/transform.ts +85 -0
  206. package/src/plugins/builtin/econ-statistics/trend.ts +57 -0
  207. package/src/plugins/builtin/econ-statistics/view.ts +90 -0
  208. package/src/plugins/builtin/fear-greed/charts.tsx +7 -18
  209. package/src/plugins/builtin/fear-greed/data.ts +6 -1
  210. package/src/plugins/builtin/help/index.tsx +4 -0
  211. package/src/plugins/builtin/holders/index.tsx +1 -0
  212. package/src/plugins/builtin/insider/index.tsx +1 -0
  213. package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
  214. package/src/plugins/builtin/kelly-sizer/sections.tsx +2 -3
  215. package/src/plugins/builtin/market-valuation/align.ts +122 -0
  216. package/src/plugins/builtin/market-valuation/cache.ts +32 -0
  217. package/src/plugins/builtin/market-valuation/chart-projection.ts +217 -0
  218. package/src/plugins/builtin/market-valuation/chart-series.ts +89 -0
  219. package/src/plugins/builtin/market-valuation/client.test.ts +141 -0
  220. package/src/plugins/builtin/market-valuation/client.ts +116 -0
  221. package/src/plugins/builtin/market-valuation/defs.ts +251 -0
  222. package/src/plugins/builtin/market-valuation/detail.tsx +159 -0
  223. package/src/plugins/builtin/market-valuation/headless.test.ts +58 -0
  224. package/src/plugins/builtin/market-valuation/headless.ts +170 -0
  225. package/src/plugins/builtin/market-valuation/index.tsx +64 -0
  226. package/src/plugins/builtin/market-valuation/indicators.ts +422 -0
  227. package/src/plugins/builtin/market-valuation/model.test.ts +350 -0
  228. package/src/plugins/builtin/market-valuation/model.ts +8 -0
  229. package/src/plugins/builtin/market-valuation/pane.test.tsx +247 -0
  230. package/src/plugins/builtin/market-valuation/pane.tsx +344 -0
  231. package/src/plugins/builtin/market-valuation/series.ts +12 -0
  232. package/src/plugins/builtin/market-valuation/settings.ts +38 -0
  233. package/src/plugins/builtin/market-valuation/sources.ts +130 -0
  234. package/src/plugins/builtin/market-valuation/trend.ts +89 -0
  235. package/src/plugins/builtin/market-valuation/view.ts +137 -0
  236. package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +123 -0
  237. package/src/plugins/builtin/market-valuation/zone-scale/index.tsx +12 -0
  238. package/src/plugins/builtin/market-valuation/zone-scale/model.ts +60 -0
  239. package/src/plugins/builtin/market-valuation/zone-scale/terminal.tsx +94 -0
  240. package/src/plugins/builtin/news/wire/breaking/filters.test.ts +99 -0
  241. package/src/plugins/builtin/news/wire/breaking/filters.ts +110 -0
  242. package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +157 -53
  243. package/src/plugins/builtin/news/wire/breaking/notifications.ts +91 -25
  244. package/src/plugins/builtin/news/wire/index.tsx +31 -44
  245. package/src/plugins/builtin/options/index.tsx +1 -0
  246. package/src/plugins/builtin/options/view.test.tsx +29 -1
  247. package/src/plugins/builtin/options/view.tsx +1 -22
  248. package/src/plugins/builtin/plugin-marketplace/feed.ts +80 -0
  249. package/src/plugins/builtin/plugin-marketplace/index.tsx +31 -0
  250. package/src/plugins/builtin/plugin-marketplace/model.test.ts +200 -0
  251. package/src/plugins/builtin/plugin-marketplace/model.ts +204 -0
  252. package/src/plugins/builtin/plugin-marketplace/pane.tsx +385 -0
  253. package/src/plugins/builtin/plugin-marketplace/store.ts +46 -0
  254. package/src/plugins/builtin/plugin-module.ts +9 -1
  255. package/src/plugins/builtin/research/index.tsx +4 -0
  256. package/src/plugins/builtin/research-search/command-bar-search.test.ts +81 -0
  257. package/src/plugins/builtin/research-search/command-bar-search.ts +175 -0
  258. package/src/plugins/builtin/research-search/data.ts +109 -0
  259. package/src/plugins/builtin/research-search/document-view.test.tsx +75 -0
  260. package/src/plugins/builtin/research-search/document-view.tsx +254 -0
  261. package/src/plugins/builtin/research-search/filter-bar.tsx +62 -0
  262. package/src/plugins/builtin/research-search/focus-handoff.ts +47 -0
  263. package/src/plugins/builtin/research-search/index.tsx +85 -0
  264. package/src/plugins/builtin/research-search/model.test.ts +40 -0
  265. package/src/plugins/builtin/research-search/model.ts +291 -0
  266. package/src/plugins/builtin/research-search/pane.test.tsx +241 -0
  267. package/src/plugins/builtin/research-search/pane.tsx +678 -0
  268. package/src/plugins/builtin/research-search/saved-view.tsx +171 -0
  269. package/src/plugins/builtin/research-search/snippet-text.tsx +35 -0
  270. package/src/plugins/builtin/research-search/snippet.test.ts +78 -0
  271. package/src/plugins/builtin/research-search/snippet.ts +134 -0
  272. package/src/plugins/builtin/scanner/flow-model.test.ts +11 -1
  273. package/src/plugins/builtin/scanner/flow-model.ts +29 -1
  274. package/src/plugins/builtin/scanner/flow-pane.tsx +78 -22
  275. package/src/plugins/builtin/sec/index.tsx +1 -0
  276. package/src/plugins/builtin/shared/series-cache.ts +93 -0
  277. package/src/plugins/builtin/short-interest/index.tsx +1 -0
  278. package/src/plugins/builtin/short-interest/pane.tsx +1 -1
  279. package/src/plugins/builtin/thirteenf/index.tsx +1 -0
  280. package/src/plugins/builtin/ticker-detail/index.tsx +0 -14
  281. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
  282. package/src/plugins/builtin/volatility/index.tsx +1 -1
  283. package/src/plugins/builtin/world-venue-map/map.desktop.test.tsx +106 -0
  284. package/src/plugins/builtin/world-venue-map/map.tsx +213 -69
  285. package/src/plugins/builtin/world-venue-map/model.test.ts +32 -0
  286. package/src/plugins/builtin/world-venue-map/model.ts +134 -13
  287. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  288. package/src/plugins/bundle.test.ts +109 -0
  289. package/src/plugins/bundle.ts +139 -0
  290. package/src/plugins/catalog-browser.test.ts +1 -0
  291. package/src/plugins/catalog-browser.ts +10 -0
  292. package/src/plugins/catalog-manifest.test.ts +43 -0
  293. package/src/plugins/catalog-manifest.ts +92 -0
  294. package/src/plugins/catalog-ui.ts +20 -4
  295. package/src/plugins/catalog.ts +9 -2
  296. package/src/plugins/current-target.ts +28 -0
  297. package/src/plugins/host-contract.ts +37 -0
  298. package/src/plugins/host-link.ts +158 -0
  299. package/src/plugins/host-modules.ts +61 -0
  300. package/src/plugins/loader.ts +41 -22
  301. package/src/plugins/ownership.ts +1 -0
  302. package/src/plugins/prediction-markets/index.tsx +1 -0
  303. package/src/plugins/registry/context.ts +3 -0
  304. package/src/plugins/registry/contributions.ts +27 -0
  305. package/src/plugins/registry/index.test.ts +25 -0
  306. package/src/plugins/registry/index.ts +8 -0
  307. package/src/plugins/registry/pane-settings.ts +6 -13
  308. package/src/plugins/seed.test.ts +83 -0
  309. package/src/plugins/seed.ts +80 -0
  310. package/src/public/broker.ts +31 -0
  311. package/src/public/market-data.ts +9 -0
  312. package/src/public/pane-hooks.ts +57 -0
  313. package/src/public/public-api.test.ts +285 -0
  314. package/src/public/react.ts +62 -0
  315. package/src/public/test-support.ts +23 -0
  316. package/src/public/time-series.ts +8 -0
  317. package/src/public/utils.ts +77 -0
  318. package/src/remote/layout-helpers.ts +6 -8
  319. package/src/remote/resources.ts +2 -0
  320. package/src/remote/schema.ts +1 -0
  321. package/src/remote/semantic-tree.test.tsx +105 -0
  322. package/src/remote/semantic-tree.tsx +53 -35
  323. package/src/renderers/browser/main.tsx +6 -0
  324. package/src/renderers/cloudflare/worker.test.ts +19 -0
  325. package/src/renderers/cloudflare/worker.ts +1 -1
  326. package/src/renderers/electrobun/bun/external-plugins.ts +140 -0
  327. package/src/renderers/electrobun/bun/index.ts +5 -2
  328. package/src/renderers/electrobun/shared/protocol.ts +21 -0
  329. package/src/renderers/electrobun/view/build-assets.ts +0 -1
  330. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +51 -1
  331. package/src/renderers/electrobun/view/external-plugins.test.ts +45 -0
  332. package/src/renderers/electrobun/view/external-plugins.ts +63 -0
  333. package/src/renderers/electrobun/view/host/ascii-text.test.ts +48 -0
  334. package/src/renderers/electrobun/view/host/ascii-text.ts +11 -1
  335. package/src/renderers/electrobun/view/host/chart-surface.tsx +38 -32
  336. package/src/renderers/electrobun/view/host/mouse.ts +16 -2
  337. package/src/renderers/electrobun/view/main.tsx +27 -2
  338. package/src/renderers/electrobun/view/styles.css +5 -0
  339. package/src/renderers/electrobun/view/toast-host.tsx +18 -0
  340. package/src/renderers/opentui/chart-surface.tsx +5 -1
  341. package/src/renderers/opentui/host.tsx +16 -2
  342. package/src/renderers/opentui/interaction-performance.test.ts +79 -0
  343. package/src/renderers/opentui/interaction-performance.ts +188 -0
  344. package/src/renderers/opentui/start.tsx +26 -1
  345. package/src/renderers/opentui/test-utils.tsx +13 -0
  346. package/src/renderers/opentui/toast-host.tsx +26 -2
  347. package/src/renderers/opentui/ui-host.tsx +1 -0
  348. package/src/sources/provider-router/search.test.ts +191 -0
  349. package/src/sources/provider-router/search.ts +92 -39
  350. package/src/state/app/context/index.tsx +2 -0
  351. package/src/state/app/context/selector.test.tsx +33 -1
  352. package/src/state/hooks/quote-streaming.test.tsx +93 -0
  353. package/src/state/hooks/quote-streaming.ts +106 -30
  354. package/src/state/pane-table-export-registry.ts +19 -0
  355. package/src/theme/color-utils.ts +1 -1
  356. package/src/theme/colors.ts +50 -0
  357. package/src/theme/themes.test.ts +15 -2
  358. package/src/theme/themes.ts +19 -1
  359. package/src/tickers/search/index.test.ts +26 -0
  360. package/src/tickers/search/index.ts +42 -17
  361. package/src/tickers/search/ranking.ts +14 -0
  362. package/src/time-series/chart-data.test.ts +216 -0
  363. package/src/time-series/resolution.test.ts +25 -26
  364. package/src/time-series/resolution.ts +41 -10
  365. package/src/time-series/resolve.ts +7 -0
  366. package/src/time-series/types.ts +3 -1
  367. package/src/time-series/use-chart-resolution.ts +5 -0
  368. package/src/types/config.ts +123 -3
  369. package/src/types/data-provider.ts +11 -0
  370. package/src/types/headless.ts +161 -0
  371. package/src/types/plugin.ts +102 -0
  372. package/src/ui/host.tsx +12 -1
  373. package/src/ui/index.tsx +1 -0
  374. package/src/ui/toast.tsx +8 -4
  375. package/src/utils/format.ts +35 -0
  376. package/src/utils/fuzzy-search.test.ts +94 -0
  377. package/src/utils/fuzzy-search.ts +81 -22
  378. package/src/utils/shortcut-labels.ts +14 -0
  379. package/src/components/chart/core/axis-format.ts +0 -157
  380. package/src/components/chart/core/index.test.ts +0 -1075
  381. package/src/components/chart/core/projection.ts +0 -357
  382. package/src/components/chart/core/render-utils.ts +0 -4
  383. package/src/components/chart/core/renderer.ts +0 -244
  384. package/src/components/chart/core/scene.ts +0 -180
  385. package/src/components/chart/core/time-axis.ts +0 -330
  386. package/src/components/chart/cursor-motion.ts +0 -47
  387. package/src/components/chart/indicators/types.ts +0 -26
  388. package/src/components/chart/market-session.ts +0 -176
  389. package/src/components/chart/multi-line/renderer.test.ts +0 -83
  390. package/src/components/chart/multi-line/renderer.ts +0 -161
  391. package/src/components/chart/multi-line/scene.ts +0 -188
  392. package/src/components/chart/native/raster/layout.ts +0 -129
  393. package/src/components/chart/native/raster/price-chart.ts +0 -244
  394. package/src/components/chart/rendering/chart-draw.ts +0 -270
  395. package/src/components/chart/rendering/geometry.ts +0 -95
  396. package/src/components/chart/rendering/pixel-buffer.ts +0 -267
  397. package/src/components/chart/scatter-chart-renderer.test.ts +0 -31
  398. package/src/components/chart/scatter-chart-renderer.ts +0 -223
  399. package/src/components/chart/static/chart/surface.tsx +0 -358
  400. package/src/components/command-bar/routes/root/article-results.test.ts +0 -83
  401. package/src/components/command-bar/routes/root/article-results.ts +0 -55
  402. package/src/plugins/builtin/news/wire/article-search.test.ts +0 -72
  403. package/src/plugins/builtin/news/wire/article-search.ts +0 -140
  404. package/src/plugins/builtin/substack/api/auth.ts +0 -185
  405. package/src/plugins/builtin/substack/api/cache.ts +0 -82
  406. package/src/plugins/builtin/substack/api/loaders.ts +0 -170
  407. package/src/plugins/builtin/substack/api/store.ts +0 -186
  408. package/src/plugins/builtin/substack/api/types.ts +0 -42
  409. package/src/plugins/builtin/substack/api.test.ts +0 -298
  410. package/src/plugins/builtin/substack/article-detail.tsx +0 -397
  411. package/src/plugins/builtin/substack/article-stack.tsx +0 -133
  412. package/src/plugins/builtin/substack/content.ts +0 -478
  413. package/src/plugins/builtin/substack/feed-tabs.tsx +0 -44
  414. package/src/plugins/builtin/substack/index.tsx +0 -49
  415. package/src/plugins/builtin/substack/login-view.tsx +0 -126
  416. package/src/plugins/builtin/substack/model.test.ts +0 -155
  417. package/src/plugins/builtin/substack/normalize.ts +0 -343
  418. package/src/plugins/builtin/substack/pane-footer.ts +0 -52
  419. package/src/plugins/builtin/substack/pane-state.ts +0 -170
  420. package/src/plugins/builtin/substack/pane.tsx +0 -547
  421. package/src/plugins/builtin/substack/read-state.ts +0 -30
  422. package/src/plugins/builtin/substack/table.ts +0 -111
  423. package/src/plugins/builtin/substack/types.ts +0 -71
  424. package/src/plugins/builtin/substack/utils.ts +0 -131
  425. package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +0 -229
  426. package/src/plugins/ibkr/account-cache.test.ts +0 -102
  427. package/src/plugins/ibkr/account-cache.ts +0 -24
  428. package/src/plugins/ibkr/broker-adapter.ts +0 -286
  429. package/src/plugins/ibkr/config.test.ts +0 -79
  430. package/src/plugins/ibkr/config.ts +0 -250
  431. package/src/plugins/ibkr/dialogs.tsx +0 -40
  432. package/src/plugins/ibkr/flex/index.test.ts +0 -264
  433. package/src/plugins/ibkr/flex/index.ts +0 -367
  434. package/src/plugins/ibkr/flex/statement-client.ts +0 -235
  435. package/src/plugins/ibkr/fundamental-parser.ts +0 -149
  436. package/src/plugins/ibkr/gateway/account-loaders.ts +0 -266
  437. package/src/plugins/ibkr/gateway/account-summary.ts +0 -164
  438. package/src/plugins/ibkr/gateway/client-lock.ts +0 -154
  439. package/src/plugins/ibkr/gateway/connection-events.ts +0 -114
  440. package/src/plugins/ibkr/gateway/connection.ts +0 -152
  441. package/src/plugins/ibkr/gateway/contracts.ts +0 -132
  442. package/src/plugins/ibkr/gateway/fundamentals.ts +0 -83
  443. package/src/plugins/ibkr/gateway/helpers.ts +0 -33
  444. package/src/plugins/ibkr/gateway/history-requests.ts +0 -158
  445. package/src/plugins/ibkr/gateway/history.ts +0 -120
  446. package/src/plugins/ibkr/gateway/instrument-lookup.ts +0 -117
  447. package/src/plugins/ibkr/gateway/market-data.ts +0 -345
  448. package/src/plugins/ibkr/gateway/order-execution.ts +0 -218
  449. package/src/plugins/ibkr/gateway/orders.ts +0 -63
  450. package/src/plugins/ibkr/gateway/price-normalization.test.ts +0 -44
  451. package/src/plugins/ibkr/gateway/price-normalization.ts +0 -50
  452. package/src/plugins/ibkr/gateway/quote-stream-controller.ts +0 -203
  453. package/src/plugins/ibkr/gateway/quote-stream.ts +0 -171
  454. package/src/plugins/ibkr/gateway/selection.ts +0 -51
  455. package/src/plugins/ibkr/gateway/service/index.test.ts +0 -675
  456. package/src/plugins/ibkr/gateway/service/index.ts +0 -429
  457. package/src/plugins/ibkr/gateway/service/lifecycle.ts +0 -191
  458. package/src/plugins/ibkr/gateway/service/manager.ts +0 -52
  459. package/src/plugins/ibkr/gateway/service/market-data.ts +0 -48
  460. package/src/plugins/ibkr/gateway/service/native-loader.ts +0 -12
  461. package/src/plugins/ibkr/gateway/service/native.ts +0 -543
  462. package/src/plugins/ibkr/gateway/timeouts.ts +0 -13
  463. package/src/plugins/ibkr/gateway/types.ts +0 -27
  464. package/src/plugins/ibkr/index.test.tsx +0 -162
  465. package/src/plugins/ibkr/index.tsx +0 -159
  466. package/src/plugins/ibkr/instance-selection.test.ts +0 -120
  467. package/src/plugins/ibkr/instance-selection.ts +0 -36
  468. package/src/plugins/ibkr/portfolio-performance.test.ts +0 -32
  469. package/src/plugins/ibkr/portfolio-performance.ts +0 -129
  470. package/src/plugins/ibkr/trade/badge.tsx +0 -28
  471. package/src/plugins/ibkr/trade/dialogs.tsx +0 -44
  472. package/src/plugins/ibkr/trade/preview-panel.tsx +0 -130
  473. package/src/plugins/ibkr/trade/tab/actions.tsx +0 -443
  474. package/src/plugins/ibkr/trade/tab/field-editors.tsx +0 -79
  475. package/src/plugins/ibkr/trade/tab/footer.ts +0 -53
  476. package/src/plugins/ibkr/trade/tab/header.tsx +0 -167
  477. package/src/plugins/ibkr/trade/tab/index.test.tsx +0 -178
  478. package/src/plugins/ibkr/trade/tab/index.tsx +0 -298
  479. package/src/plugins/ibkr/trade/tab/model.ts +0 -159
  480. package/src/plugins/ibkr/trade/tab/order-request.ts +0 -64
  481. package/src/plugins/ibkr/trade/tab/shortcuts.ts +0 -89
  482. package/src/plugins/ibkr/trade/tab/state-sync.ts +0 -86
  483. package/src/plugins/ibkr/trade/ticket-panel.tsx +0 -301
  484. package/src/plugins/ibkr/trade/utils.test.ts +0 -175
  485. package/src/plugins/ibkr/trade/utils.ts +0 -144
  486. package/src/plugins/ibkr/trading/pane-view.tsx +0 -237
  487. package/src/plugins/ibkr/trading/pane.tsx +0 -332
  488. package/src/plugins/ibkr/trading/state.test.ts +0 -109
  489. package/src/plugins/ibkr/trading/state.ts +0 -335
@@ -3,11 +3,50 @@ import type {
3
3
  ChatMessage,
4
4
  ChatNotification,
5
5
  ChatStateResponse,
6
+ CloudSavedSearch,
7
+ CloudSearchHit,
8
+ CloudSearchResponse,
6
9
  CloudTweetPayload,
7
10
  CloudTweetSearchResponse,
8
11
  } from "./types";
9
12
  import { normalizeTimestamp } from "../utils/timestamp";
10
13
 
14
+ /**
15
+ * Saved-search writes answer with either the record itself or `{ search }`.
16
+ * Both shapes are accepted so a server-side envelope change cannot silently
17
+ * hand the pane an object with no `id`.
18
+ */
19
+ export function normalizeSavedSearchResponse(response: unknown): CloudSavedSearch {
20
+ const envelope = response as { search?: CloudSavedSearch } | CloudSavedSearch | null;
21
+ const search = envelope && "search" in envelope && envelope.search
22
+ ? envelope.search
23
+ : envelope as CloudSavedSearch | null;
24
+ if (!search?.id) throw new Error("The saved search response was missing a record.");
25
+ return search;
26
+ }
27
+
28
+ export function normalizeSavedSearchHits(response: unknown): CloudSearchHit[] {
29
+ const hits = (response as { hits?: unknown } | null)?.hits;
30
+ return Array.isArray(hits) ? (hits as CloudSearchHit[]).map(normalizeSearchHit) : [];
31
+ }
32
+
33
+ /**
34
+ * A wire story or filing need not name an issuer, and the server sends `ticker:
35
+ * null` when it does not. Coerced to an empty string at the boundary so the
36
+ * declared `string` is true of every hit: consumers test it for truthiness,
37
+ * join it into search text, and hand it to a badge, and a null reaches each of
38
+ * those as an empty chip, the literal "null", or a crash.
39
+ */
40
+ export function normalizeSearchHit(hit: CloudSearchHit): CloudSearchHit {
41
+ return hit.ticker ? hit : { ...hit, ticker: "" };
42
+ }
43
+
44
+ export function normalizeSearchResponse(response: CloudSearchResponse): CloudSearchResponse {
45
+ const hits = response.hits;
46
+ if (!Array.isArray(hits)) return { ...response, hits: [] };
47
+ return { ...response, hits: hits.map(normalizeSearchHit) };
48
+ }
49
+
11
50
  export function normalizeChatMessage(message: ChatMessage): ChatMessage {
12
51
  return {
13
52
  ...message,
@@ -1,4 +1,4 @@
1
- import type { CloudTweetQueryType } from "./types";
1
+ import type { CloudSearchDocType, CloudSearchSort, CloudTweetQueryType } from "./types";
2
2
  import { normalizeSymbol, publicTickerKey } from "../utils/exchanges";
3
3
 
4
4
  export type CloudHistoryParams = {
@@ -34,6 +34,14 @@ export type CloudCongressHouseParams = {
34
34
  refresh?: boolean;
35
35
  };
36
36
 
37
+ export type CloudEarningsCallsParams = {
38
+ ticker?: string;
39
+ limit?: number;
40
+ offset?: number;
41
+ /** Include calls still working through the transcription pipeline. */
42
+ includePending?: boolean;
43
+ };
44
+
37
45
  export type CloudNewsParams = {
38
46
  feed?: "latest" | "top" | "breaking" | "ticker" | "sector" | "topic";
39
47
  ticker?: string;
@@ -131,6 +139,10 @@ export function cloudHistoryPath(symbol: string, exchange: string, params: Cloud
131
139
  return appendQuery("/market/history", search);
132
140
  }
133
141
 
142
+ export function cloudShillerPath(): string {
143
+ return "/cloud/econ/shiller";
144
+ }
145
+
134
146
  export function cloudExchangeRatePath(fromCurrency: string): string {
135
147
  return appendQuery("/market/exchange-rate", new URLSearchParams({ fromCurrency }));
136
148
  }
@@ -181,6 +193,19 @@ export function cloudCongressHousePath(params: CloudCongressHouseParams = {}): s
181
193
  return appendQuery("/cloud/congress/house", search);
182
194
  }
183
195
 
196
+ export function cloudEarningsCallsPath(params: CloudEarningsCallsParams = {}): string {
197
+ const search = new URLSearchParams();
198
+ if (params.ticker) search.set("ticker", params.ticker);
199
+ if (params.limit != null) search.set("limit", String(params.limit));
200
+ if (params.offset != null) search.set("offset", String(params.offset));
201
+ if (params.includePending) search.set("includePending", "true");
202
+ return appendQuery("/cloud/transcripts", search);
203
+ }
204
+
205
+ export function cloudEarningsTranscriptPath(id: string): string {
206
+ return `/cloud/transcripts/${encodeURIComponent(id)}`;
207
+ }
208
+
184
209
  export function cloudSecFilingsPath(params: CloudSecFilingsParams): string {
185
210
  const search = new URLSearchParams({ ticker: params.ticker });
186
211
  if (params.limit != null) search.set("limit", String(params.limit));
@@ -218,6 +243,65 @@ export function cloudSec13FPath(path: string, params: Record<string, string | nu
218
243
  return appendQuery(`/cloud/sec/13f/${normalized}`, search);
219
244
  }
220
245
 
246
+ export type CloudSearchParams = {
247
+ query: string;
248
+ tickers?: readonly string[];
249
+ docTypes?: readonly CloudSearchDocType[];
250
+ sources?: readonly string[];
251
+ /** Inclusive ISO date or timestamp bounds on `publishedAt`. */
252
+ from?: string;
253
+ to?: string;
254
+ sort?: CloudSearchSort;
255
+ limit?: number;
256
+ offset?: number;
257
+ /** Set false to skip the total-count pass when only the top hits are shown. */
258
+ count?: boolean;
259
+ /** One row per source document instead of one per matching chunk. */
260
+ distinct?: boolean;
261
+ };
262
+
263
+ function csvParam(values: readonly string[] | undefined): string | null {
264
+ if (!values) return null;
265
+ const cleaned = values.map((value) => value.trim()).filter((value) => value.length > 0);
266
+ return cleaned.length > 0 ? cleaned.join(",") : null;
267
+ }
268
+
269
+ export function cloudSearchPath(params: CloudSearchParams): string {
270
+ const search = new URLSearchParams({ q: params.query.trim() });
271
+ const tickers = csvParam(params.tickers?.map(normalizeSymbol));
272
+ if (tickers) search.set("tickers", tickers);
273
+ const docTypes = csvParam(params.docTypes);
274
+ if (docTypes) search.set("docTypes", docTypes);
275
+ const sources = csvParam(params.sources);
276
+ if (sources) search.set("sources", sources);
277
+ if (params.from) search.set("from", params.from);
278
+ if (params.to) search.set("to", params.to);
279
+ if (params.sort) search.set("sort", params.sort);
280
+ if (params.limit != null) search.set("limit", String(params.limit));
281
+ // Offset 0 is the default page, so sending it only lengthens the cache key.
282
+ if (params.offset) search.set("offset", String(params.offset));
283
+ // Counting is the server default, so only opting out is worth a parameter.
284
+ if (params.count === false) search.set("count", "false");
285
+ if (params.distinct) search.set("distinct", "true");
286
+ return appendQuery("/cloud/search", search);
287
+ }
288
+
289
+ export function cloudSearchDocumentPath(docType: CloudSearchDocType, sourceId: string): string {
290
+ return `/cloud/search/documents/${encodeURIComponent(docType)}/${encodeURIComponent(sourceId)}`;
291
+ }
292
+
293
+ export function cloudSavedSearchesPath(): string {
294
+ return "/cloud/search/saved";
295
+ }
296
+
297
+ export function cloudSavedSearchPath(id: string): string {
298
+ return `/cloud/search/saved/${encodeURIComponent(id)}`;
299
+ }
300
+
301
+ export function cloudSavedSearchHitsPath(id: string): string {
302
+ return `${cloudSavedSearchPath(id)}/hits`;
303
+ }
304
+
221
305
  export function cloudNewsPath(params: CloudNewsParams = {}): string {
222
306
  const search = new URLSearchParams();
223
307
  if (params.feed) search.set("feed", params.feed);
@@ -346,6 +346,28 @@ export interface CloudFredSeriesPayload {
346
346
  info: CloudFredSeriesInfoPayload | null;
347
347
  }
348
348
 
349
+ /**
350
+ * One month of Robert Shiller's dataset. FRED carries no long-run S&P earnings,
351
+ * so this is what every earnings-based valuation ratio is built from.
352
+ */
353
+ export interface CloudShillerObservationPayload {
354
+ date: string;
355
+ price: number | null;
356
+ dividend: number | null;
357
+ earnings: number | null;
358
+ cpi: number | null;
359
+ longRate: number | null;
360
+ cape: number | null;
361
+ /** CAPE earnings yield over the real 10-year rate; Shiller's equity risk premium. */
362
+ excessCapeYield: number | null;
363
+ }
364
+
365
+ export interface CloudShillerPayload {
366
+ observations: CloudShillerObservationPayload[];
367
+ sourceUrl: string;
368
+ fetchedAt: string;
369
+ }
370
+
349
371
  /** One publicly disseminated single-name CDS transaction report. */
350
372
  export interface CloudCdsTradePayload {
351
373
  disseminationId: string;
@@ -466,6 +488,180 @@ export interface CloudCongressHousePayload {
466
488
  members: CloudCongressMemberPayload[];
467
489
  }
468
490
 
491
+ export interface CloudEarningsCallPayload {
492
+ id: string;
493
+ ticker: string;
494
+ companyName: string | null;
495
+ fiscalYear: number | null;
496
+ fiscalQuarter: number | null;
497
+ callAt: string | null;
498
+ status: string;
499
+ durationSeconds: number | null;
500
+ wordCount: number | null;
501
+ hasTranscript: boolean;
502
+ sentiment: number | null;
503
+ /** The replay or event page the transcript came from. */
504
+ webcastUrl?: string | null;
505
+ }
506
+
507
+ export interface CloudEarningsCallListPayload {
508
+ calls: CloudEarningsCallPayload[];
509
+ /** Set when asking about a company started a search that is still running. */
510
+ pending?: boolean;
511
+ /** Set when the requested symbol is not one the SEC knows. */
512
+ unknownTicker?: boolean;
513
+ }
514
+
515
+ export interface CloudTranscriptTurnPayload {
516
+ speaker: string;
517
+ role?: string;
518
+ company?: string;
519
+ text: string;
520
+ isQa: boolean;
521
+ startSeconds: number;
522
+ }
523
+
524
+ export interface CloudTranscriptParticipantPayload {
525
+ name: string;
526
+ role?: string;
527
+ company?: string;
528
+ }
529
+
530
+ export interface CloudEarningsTranscriptPayload {
531
+ id: string;
532
+ ticker: string;
533
+ companyName: string | null;
534
+ fiscalYear: number | null;
535
+ fiscalQuarter: number | null;
536
+ callAt: string | null;
537
+ timing: string | null;
538
+ webcastUrl: string | null;
539
+ durationSeconds: number | null;
540
+ status: string;
541
+ fullText: string;
542
+ turns: CloudTranscriptTurnPayload[];
543
+ participants: CloudTranscriptParticipantPayload[];
544
+ summary: string | null;
545
+ guidance: string | null;
546
+ riskFactors: string | null;
547
+ analystFocus: string | null;
548
+ notable: string | null;
549
+ sentiment: number | null;
550
+ sentimentRationale: string | null;
551
+ qaStartTurn: number | null;
552
+ wordCount: number | null;
553
+ asrModel: string | null;
554
+ updatedAt: string | null;
555
+ }
556
+
557
+ export type CloudSearchDocType = "transcript" | "news" | "filing";
558
+
559
+ export type CloudSearchSort = "relevance" | "newest" | "oldest";
560
+
561
+ /**
562
+ * Per-chunk provenance. Which keys are present depends on `docType`: transcripts
563
+ * carry speaker attribution, news carries the wire source, filings carry the
564
+ * form and section. Kept as one optional-key record so an unknown docType from a
565
+ * newer server still renders instead of failing to parse.
566
+ */
567
+ export interface CloudSearchChunkMetadata {
568
+ speaker?: string;
569
+ role?: string;
570
+ company?: string;
571
+ isQa?: boolean;
572
+ startSeconds?: number;
573
+ turnIndex?: number;
574
+ source?: string;
575
+ summary?: string;
576
+ form?: string;
577
+ accession?: string;
578
+ section?: string;
579
+ }
580
+
581
+ export interface CloudSearchHit {
582
+ id: string;
583
+ docType: CloudSearchDocType;
584
+ sourceId: string;
585
+ chunkIndex: number;
586
+ ticker: string;
587
+ publishedAt: string;
588
+ title: string;
589
+ url: string;
590
+ /** Matched terms wrapped in `<mark>`, fragments joined by an ellipsis. */
591
+ snippet: string;
592
+ score: number;
593
+ metadata: CloudSearchChunkMetadata;
594
+ /**
595
+ * Chunks of this document that matched. Only sent for a distinct search,
596
+ * where the row stands for the document rather than for one chunk.
597
+ */
598
+ matchCount?: number;
599
+ }
600
+
601
+ export interface CloudSearchResponse {
602
+ hits: CloudSearchHit[];
603
+ total: number;
604
+ /** The server stopped counting past its cap, so `total` is a lower bound. */
605
+ countCapped: boolean;
606
+ hasMore: boolean;
607
+ nextOffset: number;
608
+ tookMs: number;
609
+ }
610
+
611
+ export interface CloudSearchDocumentChunk {
612
+ id: string;
613
+ chunkIndex: number;
614
+ body: string;
615
+ metadata: CloudSearchChunkMetadata;
616
+ }
617
+
618
+ export interface CloudSearchDocument {
619
+ docType: CloudSearchDocType;
620
+ sourceId: string;
621
+ ticker: string;
622
+ title: string;
623
+ url: string;
624
+ publishedAt: string;
625
+ chunks: CloudSearchDocumentChunk[];
626
+ }
627
+
628
+ export interface CloudSearchDocumentResponse {
629
+ document: CloudSearchDocument;
630
+ }
631
+
632
+ export interface CloudSavedSearchFilters {
633
+ tickers?: string[];
634
+ docTypes?: CloudSearchDocType[];
635
+ sources?: string[];
636
+ from?: string;
637
+ to?: string;
638
+ }
639
+
640
+ export interface CloudSavedSearch {
641
+ id: string;
642
+ name: string;
643
+ query: string;
644
+ filters: CloudSavedSearchFilters;
645
+ alertEnabled: boolean;
646
+ alertChannels: string[];
647
+ lastRunAt: string | null;
648
+ lastMatchAt: string | null;
649
+ matchCount: number;
650
+ createdAt: string;
651
+ }
652
+
653
+ export interface CloudSavedSearchListResponse {
654
+ searches: CloudSavedSearch[];
655
+ }
656
+
657
+ export interface CloudSavedSearchInput {
658
+ name: string;
659
+ query: string;
660
+ filters?: CloudSavedSearchFilters;
661
+ alertEnabled?: boolean;
662
+ alertChannels?: string[];
663
+ }
664
+
469
665
  export interface CloudSecFilingPayload {
470
666
  accessionNumber: string;
471
667
  form: string;
@@ -9,6 +9,7 @@ import {
9
9
  isPaneInLayout,
10
10
  } from "../../plugins/pane-manager";
11
11
  import type { PluginRegistry } from "../../plugins/registry";
12
+ import type { LoadedExternalPlugin } from "../../plugins/loader";
12
13
  import {
13
14
  getFocusedCollectionId,
14
15
  resolveTickerForPane,
@@ -19,7 +20,6 @@ import {
19
20
  import { scheduleConfigSave } from "../../state/config-save-scheduler";
20
21
  import {
21
22
  createPaneInstance,
22
- findPaneInstance,
23
23
  isTickerPaneId,
24
24
  normalizePaneId,
25
25
  normalizePaneLayout,
@@ -36,6 +36,7 @@ import type {
36
36
  import type { DialogApi } from "../../ui/dialog";
37
37
  import {
38
38
  resolvePanelForPane,
39
+ resolvePaneShowTarget,
39
40
  resolvePaneTarget as resolvePaneTargetInLayout,
40
41
  selectEdgeAnchor,
41
42
  } from "./layout-placement";
@@ -50,8 +51,10 @@ interface AppPaneRuntimeArgs {
50
51
  detachedPaneId: string | null;
51
52
  dialog: DialogApi;
52
53
  dispatch: Dispatch<AppAction>;
54
+ externalPlugins: readonly LoadedExternalPlugin[];
53
55
  isDetachedWindow: boolean;
54
56
  notify: (body: string, options?: { type?: "info" | "success" | "error" }) => void;
57
+ persistConfig: (nextConfig: AppState["config"]) => void;
55
58
  pluginRegistry: PluginRegistry;
56
59
  state: AppState;
57
60
  stateRef: { current: AppState };
@@ -63,8 +66,10 @@ export function useAppPaneRuntime({
63
66
  detachedPaneId,
64
67
  dialog,
65
68
  dispatch,
69
+ externalPlugins,
66
70
  isDetachedWindow,
67
71
  notify,
72
+ persistConfig,
68
73
  pluginRegistry,
69
74
  state,
70
75
  stateRef,
@@ -204,26 +209,23 @@ export function useAppPaneRuntime({
204
209
  ]);
205
210
 
206
211
  const showPane = useCallback((paneId: string) => {
207
- const normalizedPaneId = normalizePaneId(paneId);
208
- const paneDef = pluginRegistry.panes.get(normalizedPaneId);
212
+ const target = resolvePaneShowTarget(state.config.layout, paneId);
213
+ const paneDef = pluginRegistry.panes.get(target.paneType);
209
214
  if (!paneDef) return;
210
215
 
211
- if (normalizedPaneId === TICKER_RESEARCH_PANE_ID) {
216
+ if (target.paneType === TICKER_RESEARCH_PANE_ID) {
212
217
  showTickerResearchPane();
213
218
  return;
214
219
  }
215
220
 
216
- const existingInstanceId = resolvePaneTarget(normalizedPaneId);
217
- if (existingInstanceId && isPaneInLayout(state.config.layout, existingInstanceId)) {
218
- pluginRegistry.focusPaneFn(existingInstanceId);
221
+ if (target.instance && isPaneInLayout(state.config.layout, target.instance.instanceId)) {
222
+ pluginRegistry.focusPaneFn(target.instance.instanceId);
219
223
  return;
220
224
  }
221
225
 
222
- const instance = existingInstanceId
223
- ? findPaneInstance(state.config.layout, existingInstanceId)
224
- : buildPaneInstance(normalizedPaneId);
226
+ const instance = target.instance ?? buildPaneInstance(target.paneType);
225
227
  if (!instance) {
226
- if (isTickerPaneId(paneId)) {
228
+ if (isTickerPaneId(target.paneType)) {
227
229
  notify("Open a ticker or collection context first.");
228
230
  }
229
231
  return;
@@ -234,7 +236,6 @@ export function useAppPaneRuntime({
234
236
  notify,
235
237
  placePaneInstance,
236
238
  pluginRegistry,
237
- resolvePaneTarget,
238
239
  showTickerResearchPane,
239
240
  state.config.layout,
240
241
  ]);
@@ -286,10 +287,12 @@ export function useAppPaneRuntime({
286
287
  dataProvider,
287
288
  detachedPaneId,
288
289
  dispatch,
290
+ externalPlugins,
289
291
  focusVisiblePane,
290
292
  isDetachedWindow,
291
293
  openPaneSettings,
292
294
  openPinnedTicker,
295
+ persistConfig,
293
296
  persistLayout,
294
297
  placePaneInstance,
295
298
  placePinnedTickerTarget,
@@ -0,0 +1,27 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import type { LayoutConfig } from "../../types/config";
3
+ import { resolvePaneShowTarget } from "./layout-placement";
4
+
5
+ describe("resolvePaneShowTarget", () => {
6
+ test("resolves a hidden pane by its exact instance id", () => {
7
+ const hiddenPane = {
8
+ instanceId: "news-top:stored",
9
+ paneId: "news-top",
10
+ binding: { kind: "none" as const },
11
+ placementMemory: {
12
+ floating: { x: 12, y: 4, width: 90, height: 30 },
13
+ },
14
+ };
15
+ const layout: LayoutConfig = {
16
+ dockRoot: null,
17
+ instances: [hiddenPane],
18
+ floating: [],
19
+ detached: [],
20
+ };
21
+
22
+ expect(resolvePaneShowTarget(layout, hiddenPane.instanceId)).toEqual({
23
+ paneType: "news-top",
24
+ instance: hiddenPane,
25
+ });
26
+ });
27
+ });
@@ -28,6 +28,19 @@ export function resolvePaneTarget(layout: LayoutConfig, paneId: string): string
28
28
  ?? null;
29
29
  }
30
30
 
31
+ /** Resolve an instance id before looking up its registered pane type. */
32
+ export function resolvePaneShowTarget(
33
+ layout: LayoutConfig,
34
+ paneId: string,
35
+ ): { paneType: string; instance: PaneInstanceConfig | null } {
36
+ const instanceId = resolvePaneTarget(layout, paneId);
37
+ const instance = instanceId ? findPaneInstance(layout, instanceId) ?? null : null;
38
+ return {
39
+ paneType: normalizePaneId(instance?.paneId ?? paneId),
40
+ instance,
41
+ };
42
+ }
43
+
31
44
  export function resolvePanelForPane({
32
45
  layout,
33
46
  paneId,
@@ -5,6 +5,9 @@ import {
5
5
  } from "../../components/command-bar/workflow/ops";
6
6
  import type { AppTickerRepositoryPort } from "../../core/app-service-ports";
7
7
  import { setLayoutManagerDispatch } from "../../plugins/builtin/layout-manager";
8
+ import { setMarketplaceHost } from "../../plugins/builtin/plugin-marketplace/store";
9
+ import type { InstalledPlugin } from "../../plugins/builtin/plugin-marketplace/model";
10
+ import type { LoadedExternalPlugin } from "../../plugins/loader";
8
11
  import { materializeMarketplaceLayout } from "../../layout-marketplace/payload";
9
12
  import {
10
13
  isPaneInLayout,
@@ -46,10 +49,12 @@ interface BindAppPanePluginRegistryOptions {
46
49
  dataProvider: DataProvider;
47
50
  detachedPaneId: string | null;
48
51
  dispatch: Dispatch<AppAction>;
52
+ externalPlugins: readonly LoadedExternalPlugin[];
49
53
  focusVisiblePane: (paneId: string, layout?: LayoutConfig) => void;
50
54
  isDetachedWindow: boolean;
51
55
  openPaneSettings: (paneId?: string) => Promise<void>;
52
56
  openPinnedTicker: (rawSymbol: string, options?: PinTickerOptions) => Promise<void>;
57
+ persistConfig: (nextConfig: AppState["config"]) => void;
53
58
  persistLayout: (layout: LayoutConfig, options?: { pushHistory?: boolean }) => void;
54
59
  placePaneInstance: (
55
60
  instance: PaneInstanceConfig,
@@ -76,10 +81,12 @@ export function bindAppPanePluginRegistry({
76
81
  dataProvider,
77
82
  detachedPaneId,
78
83
  dispatch,
84
+ externalPlugins,
79
85
  focusVisiblePane,
80
86
  isDetachedWindow,
81
87
  openPaneSettings,
82
88
  openPinnedTicker,
89
+ persistConfig,
83
90
  persistLayout,
84
91
  placePaneInstance,
85
92
  placePinnedTickerTarget,
@@ -249,4 +256,59 @@ export function bindAppPanePluginRegistry({
249
256
  termHeight: pluginRegistry.getTermSizeFn().height,
250
257
  focusedPaneId: state.focusedPaneId,
251
258
  }));
259
+
260
+ setMarketplaceHost({
261
+ listInstalled: () => {
262
+ const disabled = new Set(stateRef.current.config.disabledPlugins ?? []);
263
+ const externalById = new Map(externalPlugins.map((entry) => [entry.plugin.id, entry]));
264
+ const installed: InstalledPlugin[] = [];
265
+
266
+ for (const plugin of pluginRegistry.allPlugins.values()) {
267
+ const external = externalById.get(plugin.id);
268
+ externalById.delete(plugin.id);
269
+ installed.push({
270
+ id: plugin.id,
271
+ name: plugin.name,
272
+ version: plugin.version,
273
+ ...(plugin.description ? { description: plugin.description } : {}),
274
+ toggleable: plugin.toggleable === true,
275
+ enabled: !disabled.has(plugin.id),
276
+ source: external ? "external" : "builtin",
277
+ });
278
+ }
279
+
280
+ // Entries that never registered — a broken import, or a plugin this
281
+ // renderer cannot run. They are invisible everywhere else, which is
282
+ // exactly why the marketplace has to show them.
283
+ for (const entry of externalById.values()) {
284
+ installed.push({
285
+ id: entry.plugin.id,
286
+ name: entry.plugin.name,
287
+ version: entry.plugin.version ?? "0.0.0",
288
+ ...(entry.plugin.description ? { description: entry.plugin.description } : {}),
289
+ toggleable: true,
290
+ enabled: !disabled.has(entry.plugin.id),
291
+ source: "external",
292
+ ...(entry.unsupportedTarget ? { unsupportedTarget: entry.unsupportedTarget } : {}),
293
+ ...(entry.error ? { loadError: entry.error } : {}),
294
+ });
295
+ }
296
+
297
+ return installed;
298
+ },
299
+ setPluginEnabled: (pluginId, enabled) => {
300
+ const current = stateRef.current.config;
301
+ if (!enabled) {
302
+ for (const paneId of pluginRegistry.getPluginPaneIds(pluginId)) pluginRegistry.hidePane(paneId);
303
+ }
304
+ dispatch({ type: "TOGGLE_PLUGIN", pluginId });
305
+ const disabled = current.disabledPlugins ?? [];
306
+ persistConfig({
307
+ ...current,
308
+ disabledPlugins: enabled
309
+ ? disabled.filter((entry) => entry !== pluginId)
310
+ : [...disabled, pluginId],
311
+ });
312
+ },
313
+ });
252
314
  }