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
@@ -1,24 +0,0 @@
1
- import type { BrokerInstanceConfig } from "../../types/config";
2
- import type { CachePolicy } from "../../types/persistence";
3
- import { normalizeIbkrConfig } from "./config";
4
- import { fnv1aHashString } from "../../utils/hash";
5
-
6
- const FLEX_ACCOUNT_CACHE_POLICY = {
7
- staleMs: 6 * 60 * 60 * 1000,
8
- expireMs: 30 * 24 * 60 * 60 * 1000,
9
- } as const satisfies CachePolicy;
10
-
11
- const GATEWAY_ACCOUNT_CACHE_POLICY = {
12
- staleMs: 30 * 1000,
13
- expireMs: 7 * 24 * 60 * 60 * 1000,
14
- } as const satisfies CachePolicy;
15
-
16
- export function getIbkrAccountCacheSourceKey(instance: BrokerInstanceConfig): string {
17
- return fnv1aHashString(JSON.stringify(normalizeIbkrConfig(instance.config)));
18
- }
19
-
20
- export function getIbkrAccountCachePolicy(instance: BrokerInstanceConfig): CachePolicy {
21
- return normalizeIbkrConfig(instance.config).connectionMode === "gateway"
22
- ? GATEWAY_ACCOUNT_CACHE_POLICY
23
- : FLEX_ACCOUNT_CACHE_POLICY;
24
- }
@@ -1,286 +0,0 @@
1
- import type { BrokerAdapter, BrokerPosition } from "../../types/broker";
2
- import {
3
- buildPersistedIbkrGatewayConfig,
4
- buildIbkrConfigFromValues,
5
- IBKR_CONFIG_FIELDS,
6
- isFlexConfigured,
7
- isGatewayConfigured,
8
- normalizeIbkrConfig,
9
- type FlexQueryConfig,
10
- } from "./config";
11
- import { getIbkrAccountCachePolicy, getIbkrAccountCacheSourceKey } from "./account-cache";
12
- import { loadFlexStatement, parseFlexAccounts, parseFlexPositions } from "./flex";
13
- import { ibkrGatewayManager } from "./gateway/service";
14
- import { refreshGatewayData } from "./gateway/helpers";
15
- import { getIbkrPortfolioPerformance } from "./portfolio-performance";
16
-
17
- async function importFlexPositions(config: FlexQueryConfig): Promise<BrokerPosition[]> {
18
- const xml = await loadFlexStatement(config);
19
- return parseFlexPositions(xml);
20
- }
21
-
22
- export const ibkrBroker: BrokerAdapter = {
23
- id: "ibkr",
24
- name: "Interactive Brokers",
25
- configSchema: IBKR_CONFIG_FIELDS,
26
-
27
- async validate(instance) {
28
- const normalized = normalizeIbkrConfig(instance.config);
29
- return normalized.connectionMode === "gateway"
30
- ? isGatewayConfigured(instance.config)
31
- : isFlexConfigured(instance.config);
32
- },
33
-
34
- async importPositions(instance) {
35
- const normalized = normalizeIbkrConfig(instance.config);
36
- if (normalized.connectionMode === "gateway") {
37
- await refreshGatewayData(instance);
38
- return ibkrGatewayManager.getService(instance.id).getPositions(normalized.gateway);
39
- }
40
- return importFlexPositions(normalized.flex);
41
- },
42
-
43
- async importPortfolioSnapshot(instance) {
44
- const normalized = normalizeIbkrConfig(instance.config);
45
- if (normalized.connectionMode === "gateway") {
46
- const service = ibkrGatewayManager.getService(instance.id);
47
- await refreshGatewayData(instance);
48
- const [accounts, positions] = await Promise.all([
49
- service.getAccounts(normalized.gateway),
50
- service.getPositions(normalized.gateway),
51
- ]);
52
- return { accounts, positions };
53
- }
54
-
55
- const xml = await loadFlexStatement(normalized.flex);
56
- return {
57
- accounts: parseFlexAccounts(xml),
58
- positions: parseFlexPositions(xml),
59
- };
60
- },
61
-
62
- async connect(instance) {
63
- const normalized = normalizeIbkrConfig(instance.config);
64
- if (normalized.connectionMode !== "gateway") return;
65
- await ibkrGatewayManager.getService(instance.id).connect(normalized.gateway);
66
- },
67
-
68
- async disconnect(instance) {
69
- await ibkrGatewayManager.removeInstance(instance.id);
70
- },
71
-
72
- getStatus(instance) {
73
- const normalized = normalizeIbkrConfig(instance.config);
74
- if (normalized.connectionMode !== "gateway") {
75
- return {
76
- state: "disconnected",
77
- message: "Flex profiles sync on demand",
78
- mode: "flex",
79
- updatedAt: 0,
80
- };
81
- }
82
- return { ...ibkrGatewayManager.getSnapshot(instance.id).status, mode: "gateway" };
83
- },
84
-
85
- subscribeStatus(instance, listener) {
86
- return ibkrGatewayManager.subscribe(instance.id, listener);
87
- },
88
-
89
- getPersistedConfigUpdate(instance) {
90
- const normalized = normalizeIbkrConfig(instance.config);
91
- if (normalized.connectionMode !== "gateway") return null;
92
- const resolved = ibkrGatewayManager.getService(instance.id).getResolvedConnection();
93
- return resolved ? buildPersistedIbkrGatewayConfig(instance.config, resolved) : null;
94
- },
95
-
96
- getAccountCacheSourceKey: getIbkrAccountCacheSourceKey,
97
- getAccountCachePolicy: getIbkrAccountCachePolicy,
98
-
99
- getProfileActions(instance) {
100
- const normalized = normalizeIbkrConfig(instance.config);
101
- return [{
102
- id: "ibkr-console",
103
- label: "IBKR Console",
104
- paneId: "ibkr-trading",
105
- disabled: normalized.connectionMode !== "gateway",
106
- disabledReason: "IBKR Console is available for Gateway / TWS profiles.",
107
- }];
108
- },
109
-
110
- toConfigValues(instance) {
111
- const normalized = normalizeIbkrConfig(instance.config);
112
- return {
113
- connectionMode: normalized.connectionMode,
114
- token: normalized.flex.token,
115
- queryId: normalized.flex.queryId,
116
- endpoint: normalized.flex.endpoint,
117
- gatewaySetupMode: normalized.gatewaySetupMode,
118
- host: normalized.gateway.host,
119
- port: normalized.gateway.port,
120
- clientId: normalized.gateway.clientId,
121
- marketDataType: normalized.gateway.marketDataType,
122
- };
123
- },
124
-
125
- fromConfigValues(values, previous) {
126
- const next = buildIbkrConfigFromValues(values);
127
- const previousConfig = previous ? normalizeIbkrConfig(previous.config) : null;
128
- if (!previousConfig || next.connectionMode !== "gateway") return next as unknown as Record<string, unknown>;
129
-
130
- return {
131
- ...next,
132
- gateway: {
133
- ...next.gateway,
134
- marketDataType: next.gateway.marketDataType ?? previousConfig.gateway.marketDataType,
135
- lastSuccessfulPort: previousConfig.gateway.lastSuccessfulPort,
136
- lastSuccessfulClientId: previousConfig.gateway.lastSuccessfulClientId,
137
- },
138
- } as unknown as Record<string, unknown>;
139
- },
140
-
141
- async listAccounts(instance) {
142
- const normalized = normalizeIbkrConfig(instance.config);
143
- if (normalized.connectionMode === "gateway") {
144
- return ibkrGatewayManager.getService(instance.id).getAccounts(normalized.gateway);
145
- }
146
- const xml = await loadFlexStatement(normalized.flex);
147
- return parseFlexAccounts(xml);
148
- },
149
-
150
- async getPortfolioPerformance(instance, accountId) {
151
- return getIbkrPortfolioPerformance(instance, accountId);
152
- },
153
-
154
- async searchInstruments(query, instance) {
155
- const normalized = normalizeIbkrConfig(instance.config);
156
- if (normalized.connectionMode !== "gateway") return [];
157
- return (await ibkrGatewayManager.getService(instance.id).searchInstruments(query, normalized.gateway)).map((result) => ({
158
- ...result,
159
- brokerInstanceId: result.brokerInstanceId ?? instance.id,
160
- brokerLabel: result.brokerLabel ?? instance.label,
161
- brokerContract: result.brokerContract
162
- ? { ...result.brokerContract, brokerInstanceId: result.brokerContract.brokerInstanceId ?? instance.id }
163
- : undefined,
164
- }));
165
- },
166
-
167
- async getTickerFinancials(ticker, instance, exchange, instrument) {
168
- const normalized = normalizeIbkrConfig(instance.config);
169
- if (normalized.connectionMode !== "gateway") {
170
- throw new Error("Gateway mode is required for broker market data");
171
- }
172
- return ibkrGatewayManager.getService(instance.id).getTickerFinancials(ticker, normalized.gateway, exchange, instrument);
173
- },
174
-
175
- async getQuote(ticker, instance, exchange, instrument) {
176
- const normalized = normalizeIbkrConfig(instance.config);
177
- if (normalized.connectionMode !== "gateway") {
178
- throw new Error("Gateway mode is required for broker quotes");
179
- }
180
- return ibkrGatewayManager.getService(instance.id).getQuote(ticker, normalized.gateway, exchange, instrument);
181
- },
182
-
183
- async getPriceHistory(ticker, instance, exchange, range, instrument) {
184
- const normalized = normalizeIbkrConfig(instance.config);
185
- if (normalized.connectionMode !== "gateway") {
186
- throw new Error("Gateway mode is required for broker history");
187
- }
188
- return ibkrGatewayManager.getService(instance.id).getPriceHistory(ticker, normalized.gateway, exchange, range, instrument);
189
- },
190
-
191
- getChartResolutionSupport(ticker, instance, exchange, instrument) {
192
- const normalized = normalizeIbkrConfig(instance.config);
193
- if (normalized.connectionMode !== "gateway") {
194
- throw new Error("Gateway mode is required for broker history");
195
- }
196
- return ibkrGatewayManager.getService(instance.id).getChartResolutionSupport(
197
- ticker,
198
- normalized.gateway,
199
- exchange,
200
- instrument,
201
- );
202
- },
203
-
204
- async getPriceHistoryForResolution(ticker, instance, exchange, bufferRange, resolution, instrument) {
205
- const normalized = normalizeIbkrConfig(instance.config);
206
- if (normalized.connectionMode !== "gateway") {
207
- throw new Error("Gateway mode is required for broker history");
208
- }
209
- return ibkrGatewayManager.getService(instance.id).getPriceHistoryForResolution(
210
- ticker,
211
- normalized.gateway,
212
- exchange,
213
- bufferRange,
214
- resolution,
215
- instrument,
216
- );
217
- },
218
-
219
- async getDetailedPriceHistory(ticker, instance, exchange, startDate, endDate, barSize, instrument) {
220
- const normalized = normalizeIbkrConfig(instance.config);
221
- if (normalized.connectionMode !== "gateway") {
222
- throw new Error("Gateway mode is required for broker history");
223
- }
224
- return ibkrGatewayManager.getService(instance.id).getDetailedPriceHistory(
225
- ticker,
226
- normalized.gateway,
227
- exchange,
228
- startDate,
229
- endDate,
230
- barSize,
231
- instrument,
232
- );
233
- },
234
-
235
- subscribeQuotes(instance, targets, onQuote) {
236
- const normalized = normalizeIbkrConfig(instance.config);
237
- if (normalized.connectionMode !== "gateway") {
238
- return () => {};
239
- }
240
- return ibkrGatewayManager.getService(instance.id).subscribeQuotes(normalized.gateway, targets, onQuote);
241
- },
242
-
243
- async listOpenOrders(instance) {
244
- const normalized = normalizeIbkrConfig(instance.config);
245
- if (normalized.connectionMode !== "gateway") return [];
246
- return ibkrGatewayManager.getService(instance.id).listOpenOrders(normalized.gateway);
247
- },
248
-
249
- async listExecutions(instance) {
250
- const normalized = normalizeIbkrConfig(instance.config);
251
- if (normalized.connectionMode !== "gateway") return [];
252
- return ibkrGatewayManager.getService(instance.id).listExecutions(normalized.gateway);
253
- },
254
-
255
- async previewOrder(instance, request) {
256
- const normalized = normalizeIbkrConfig(instance.config);
257
- if (normalized.connectionMode !== "gateway") {
258
- throw new Error("Gateway mode is required for order preview");
259
- }
260
- return ibkrGatewayManager.getService(instance.id).previewOrder(normalized.gateway, request);
261
- },
262
-
263
- async placeOrder(instance, request) {
264
- const normalized = normalizeIbkrConfig(instance.config);
265
- if (normalized.connectionMode !== "gateway") {
266
- throw new Error("Gateway mode is required for trading");
267
- }
268
- return ibkrGatewayManager.getService(instance.id).placeOrder(normalized.gateway, request);
269
- },
270
-
271
- async modifyOrder(instance, orderId, request) {
272
- const normalized = normalizeIbkrConfig(instance.config);
273
- if (normalized.connectionMode !== "gateway") {
274
- throw new Error("Gateway mode is required for trading");
275
- }
276
- return ibkrGatewayManager.getService(instance.id).modifyOrder(normalized.gateway, orderId, request);
277
- },
278
-
279
- async cancelOrder(instance, orderId) {
280
- const normalized = normalizeIbkrConfig(instance.config);
281
- if (normalized.connectionMode !== "gateway") {
282
- throw new Error("Gateway mode is required for trading");
283
- }
284
- return ibkrGatewayManager.getService(instance.id).cancelOrder(normalized.gateway, orderId);
285
- },
286
- };
@@ -1,79 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import {
3
- buildIbkrConfigFromValues,
4
- IBKR_STATEMENT_URL,
5
- LEGACY_IBKR_STATEMENT_URL,
6
- normalizeIbkrConfig,
7
- } from "./config";
8
-
9
- describe("ibkr config helpers", () => {
10
- test("builds nested manual gateway config from flat wizard values", () => {
11
- const config = buildIbkrConfigFromValues({
12
- connectionMode: "gateway",
13
- gatewaySetupMode: "manual",
14
- host: "127.0.0.1",
15
- port: "4002",
16
- });
17
-
18
- expect(config.connectionMode).toBe("gateway");
19
- expect(config.gatewaySetupMode).toBe("manual");
20
- expect(config.gateway).toEqual({
21
- host: "127.0.0.1",
22
- port: 4002,
23
- clientId: undefined,
24
- lastSuccessfulPort: undefined,
25
- lastSuccessfulClientId: undefined,
26
- marketDataType: "auto",
27
- });
28
- });
29
-
30
- test("treats explicit port/client configs as manual at runtime", () => {
31
- const config = normalizeIbkrConfig({
32
- connectionMode: "gateway",
33
- host: "127.0.0.1",
34
- port: 4002,
35
- clientId: 1,
36
- });
37
-
38
- expect(config.gatewaySetupMode).toBe("manual");
39
- expect(config.gateway.port).toBe(4002);
40
- expect(config.gateway.clientId).toBe(1);
41
- });
42
-
43
- test("normalizes flat flex wizard values", () => {
44
- const config = normalizeIbkrConfig({
45
- token: "abc",
46
- queryId: "123",
47
- });
48
-
49
- expect(config.connectionMode).toBe("flex");
50
- expect(config.gatewaySetupMode).toBe("auto");
51
- expect(config.flex.token).toBe("abc");
52
- expect(config.flex.queryId).toBe("123");
53
- expect(config.flex.endpoint).toBe(IBKR_STATEMENT_URL);
54
- });
55
-
56
- test("migrates the legacy default Flex endpoint to the current IBKR endpoint", () => {
57
- const config = normalizeIbkrConfig({
58
- flex: {
59
- token: "abc",
60
- queryId: "123",
61
- endpoint: LEGACY_IBKR_STATEMENT_URL,
62
- },
63
- });
64
-
65
- expect(config.flex.endpoint).toBe(IBKR_STATEMENT_URL);
66
- });
67
-
68
- test("preserves custom non-default Flex endpoints", () => {
69
- const config = normalizeIbkrConfig({
70
- flex: {
71
- token: "abc",
72
- queryId: "123",
73
- endpoint: "https://example.com/custom/SendRequest",
74
- },
75
- });
76
-
77
- expect(config.flex.endpoint).toBe("https://example.com/custom/SendRequest");
78
- });
79
- });
@@ -1,250 +0,0 @@
1
- import type { BrokerConfigField } from "../../types/broker";
2
- import type { IbkrGatewayConfig, ResolvedIbkrGatewayConnection } from "./gateway/service";
3
-
4
- export const LEGACY_IBKR_STATEMENT_URL = "https://gdcdyn.interactivebrokers.com/Universal/servlet/FlexStatementService.SendRequest";
5
- export const IBKR_STATEMENT_URL = "https://ndcdyn.interactivebrokers.com/AccountManagement/FlexWebService/SendRequest";
6
-
7
- type IbkrConnectionMode = "flex" | "gateway";
8
- type IbkrGatewaySetupMode = "auto" | "manual";
9
-
10
- export interface FlexQueryConfig {
11
- token: string;
12
- queryId: string;
13
- endpoint?: string;
14
- }
15
-
16
- export interface IbkrConfig {
17
- connectionMode: IbkrConnectionMode;
18
- gatewaySetupMode: IbkrGatewaySetupMode;
19
- flex: FlexQueryConfig;
20
- gateway: IbkrGatewayConfig;
21
- }
22
-
23
- const DEFAULT_FLEX_CONFIG: FlexQueryConfig = {
24
- token: "",
25
- queryId: "",
26
- endpoint: IBKR_STATEMENT_URL,
27
- };
28
-
29
- const DEFAULT_GATEWAY_HOST = "127.0.0.1";
30
-
31
- const DEFAULT_GATEWAY_CONFIG: IbkrGatewayConfig = {
32
- host: DEFAULT_GATEWAY_HOST,
33
- marketDataType: "auto",
34
- };
35
-
36
- export const IBKR_CONFIG_FIELDS: BrokerConfigField[] = [
37
- {
38
- key: "connectionMode",
39
- label: "Connection Mode",
40
- type: "select",
41
- required: true,
42
- options: [
43
- { label: "Flex (read-only)", value: "flex", description: "Syncs via IBKR's Flex API. No software needed." },
44
- { label: "Gateway / TWS", value: "gateway", description: "Use IB Gateway or TWS for live data and trading." },
45
- ],
46
- },
47
- {
48
- key: "token",
49
- label: "Flex Token",
50
- type: "password",
51
- required: true,
52
- placeholder: "Your Flex Web Service token",
53
- dependsOn: { key: "connectionMode", value: "flex" },
54
- },
55
- {
56
- key: "queryId",
57
- label: "Query ID",
58
- type: "text",
59
- required: true,
60
- placeholder: "Numeric Flex Query ID",
61
- dependsOn: { key: "connectionMode", value: "flex" },
62
- },
63
- {
64
- key: "endpoint",
65
- label: "Endpoint",
66
- type: "text",
67
- required: false,
68
- placeholder: IBKR_STATEMENT_URL,
69
- dependsOn: { key: "connectionMode", value: "flex" },
70
- },
71
- {
72
- key: "gatewaySetupMode",
73
- label: "Connection Setup",
74
- type: "select",
75
- required: true,
76
- defaultValue: "auto",
77
- options: [
78
- {
79
- label: "Automatic",
80
- value: "auto",
81
- description: "Detect the local IBKR API port and choose a client ID automatically.",
82
- },
83
- {
84
- label: "Manual",
85
- value: "manual",
86
- description: "Specify the host and socket port yourself.",
87
- },
88
- ],
89
- dependsOn: { key: "connectionMode", value: "gateway" },
90
- },
91
- {
92
- key: "host",
93
- label: "Gateway Host",
94
- type: "text",
95
- required: true,
96
- placeholder: DEFAULT_GATEWAY_HOST,
97
- defaultValue: DEFAULT_GATEWAY_HOST,
98
- dependsOn: { key: "gatewaySetupMode", value: "manual" },
99
- },
100
- {
101
- key: "port",
102
- label: "Gateway Port",
103
- type: "number",
104
- required: true,
105
- placeholder: "4001, 4002, 7496, or 7497",
106
- dependsOn: { key: "gatewaySetupMode", value: "manual" },
107
- },
108
- ];
109
-
110
- function coerceOptionalNumber(value: unknown): number | undefined {
111
- if (typeof value === "number" && Number.isFinite(value)) return value;
112
- if (typeof value === "string" && value.trim()) {
113
- const parsed = Number(value);
114
- if (Number.isFinite(parsed)) return parsed;
115
- }
116
- return undefined;
117
- }
118
-
119
- function coerceString(value: unknown, fallback = ""): string {
120
- return typeof value === "string" ? value : fallback;
121
- }
122
-
123
- function normalizeFlexEndpoint(value: unknown): string {
124
- const endpoint = coerceString(value, DEFAULT_FLEX_CONFIG.endpoint).trim();
125
- if (!endpoint || endpoint === LEGACY_IBKR_STATEMENT_URL) return IBKR_STATEMENT_URL;
126
- return endpoint;
127
- }
128
-
129
- function getMode(value: unknown): IbkrConnectionMode {
130
- return value === "gateway" ? "gateway" : "flex";
131
- }
132
-
133
- function getGatewaySetupMode(value: unknown): IbkrGatewaySetupMode | undefined {
134
- return value === "manual" || value === "auto" ? value : undefined;
135
- }
136
-
137
- function inferGatewaySetupMode(
138
- connectionMode: IbkrConnectionMode,
139
- input: Record<string, unknown>,
140
- nestedGateway: Record<string, unknown>,
141
- ): IbkrGatewaySetupMode {
142
- if (connectionMode !== "gateway") return "auto";
143
-
144
- const explicit = getGatewaySetupMode(input.gatewaySetupMode ?? nestedGateway.setupMode);
145
- if (explicit) return explicit;
146
-
147
- const rawHost = coerceString(nestedGateway.host ?? input.host, DEFAULT_GATEWAY_HOST);
148
- const rawPort = coerceOptionalNumber(nestedGateway.port ?? input.port);
149
- const rawClientId = coerceOptionalNumber(nestedGateway.clientId ?? input.clientId);
150
-
151
- return rawHost !== DEFAULT_GATEWAY_HOST || rawPort != null || rawClientId != null
152
- ? "manual"
153
- : "auto";
154
- }
155
-
156
- export function normalizeIbkrConfig(raw?: Record<string, unknown>): IbkrConfig {
157
- const input = raw ?? {};
158
- const nestedFlex = typeof input.flex === "object" && input.flex ? input.flex as Record<string, unknown> : {};
159
- const nestedGateway = typeof input.gateway === "object" && input.gateway ? input.gateway as Record<string, unknown> : {};
160
- const inferredMode = input.connectionMode
161
- ?? (input.host || input.port || input.clientId || nestedGateway.host || nestedGateway.port || nestedGateway.clientId
162
- ? "gateway"
163
- : "flex");
164
- const connectionMode = getMode(inferredMode);
165
- const gatewaySetupMode = inferGatewaySetupMode(connectionMode, input, nestedGateway);
166
- const host = coerceString(nestedGateway.host ?? input.host, DEFAULT_GATEWAY_HOST);
167
- const port = coerceOptionalNumber(nestedGateway.port ?? input.port);
168
- const clientId = coerceOptionalNumber(nestedGateway.clientId ?? input.clientId);
169
-
170
- return {
171
- connectionMode,
172
- gatewaySetupMode,
173
- flex: {
174
- token: coerceString(nestedFlex.token ?? input.token),
175
- queryId: coerceString(nestedFlex.queryId ?? input.queryId),
176
- endpoint: normalizeFlexEndpoint(nestedFlex.endpoint ?? input.endpoint),
177
- },
178
- gateway: {
179
- host,
180
- port: gatewaySetupMode === "manual" ? port : undefined,
181
- clientId: gatewaySetupMode === "manual" ? clientId : undefined,
182
- lastSuccessfulPort: coerceOptionalNumber(nestedGateway.lastSuccessfulPort ?? input.lastSuccessfulPort),
183
- lastSuccessfulClientId: coerceOptionalNumber(nestedGateway.lastSuccessfulClientId ?? input.lastSuccessfulClientId),
184
- marketDataType: coerceString(
185
- nestedGateway.marketDataType ?? input.marketDataType,
186
- DEFAULT_GATEWAY_CONFIG.marketDataType,
187
- ) as IbkrGatewayConfig["marketDataType"],
188
- },
189
- };
190
- }
191
-
192
- export function buildIbkrConfigFromValues(values: Record<string, unknown>): IbkrConfig {
193
- const normalized = normalizeIbkrConfig(values);
194
- return {
195
- connectionMode: normalized.connectionMode,
196
- gatewaySetupMode: normalized.gatewaySetupMode,
197
- flex: normalized.flex,
198
- gateway: normalized.gateway,
199
- };
200
- }
201
-
202
- export function getGatewayConfig(raw?: Record<string, unknown>): IbkrGatewayConfig {
203
- return normalizeIbkrConfig(raw).gateway;
204
- }
205
-
206
- export function isGatewayConfigured(raw?: Record<string, unknown>): boolean {
207
- const config = normalizeIbkrConfig(raw);
208
- return config.connectionMode === "gateway"
209
- && !!config.gateway.host
210
- && (config.gatewaySetupMode !== "manual" || Number.isFinite(config.gateway.port));
211
- }
212
-
213
- export function isFlexConfigured(raw?: Record<string, unknown>): boolean {
214
- const config = normalizeIbkrConfig(raw);
215
- return config.connectionMode === "flex"
216
- && !!config.flex.token
217
- && !!config.flex.queryId;
218
- }
219
-
220
- export function buildPersistedIbkrGatewayConfig(
221
- raw: Record<string, unknown> | undefined,
222
- resolved: ResolvedIbkrGatewayConnection,
223
- ): Record<string, unknown> | null {
224
- const normalized = normalizeIbkrConfig(raw);
225
- if (normalized.connectionMode !== "gateway") return null;
226
-
227
- if (
228
- normalized.gateway.lastSuccessfulPort === resolved.port
229
- && normalized.gateway.lastSuccessfulClientId === resolved.clientId
230
- ) {
231
- return null;
232
- }
233
-
234
- const gateway: Record<string, unknown> = {
235
- host: normalized.gateway.host,
236
- marketDataType: normalized.gateway.marketDataType,
237
- lastSuccessfulPort: resolved.port,
238
- lastSuccessfulClientId: resolved.clientId,
239
- };
240
-
241
- if (normalized.gateway.port != null) gateway.port = normalized.gateway.port;
242
- if (normalized.gateway.clientId != null) gateway.clientId = normalized.gateway.clientId;
243
-
244
- return {
245
- connectionMode: normalized.connectionMode,
246
- gatewaySetupMode: normalized.gatewaySetupMode,
247
- flex: normalized.flex,
248
- gateway,
249
- };
250
- }
@@ -1,40 +0,0 @@
1
- import { Box, Input, Text } from "../../ui";
2
- import { TextAttributes } from "../../ui";
3
- import { type InputRenderable } from "../../ui";
4
- import { useEffect, useRef, useState } from "react";
5
- import { type PromptContext } from "../../ui/dialog";
6
- import type { WizardStep } from "../../types/plugin";
7
- import { colors } from "../../theme/colors";
8
-
9
- export { ChoiceDialog } from "../../components";
10
-
11
- export function InputDialog({ resolve, step }: PromptContext<string> & { step: WizardStep }) {
12
- const inputRef = useRef<InputRenderable>(null);
13
- const [value, setValue] = useState("");
14
-
15
- useEffect(() => {
16
- inputRef.current?.focus?.();
17
- }, []);
18
-
19
- return (
20
- <Box flexDirection="column">
21
- <Text attributes={TextAttributes.BOLD} fg={colors.text}>{step.label}</Text>
22
- <Box height={1} />
23
- {step.body?.map((line, index) => (
24
- <Text key={index} fg={colors.textDim}>{line || " "}</Text>
25
- ))}
26
- <Box height={1} />
27
- <Input
28
- ref={inputRef}
29
- focused
30
- placeholder={step.placeholder || ""}
31
- textColor={colors.text}
32
- placeholderColor={colors.textDim}
33
- backgroundColor={colors.bg}
34
- onInput={(nextValue: string) => setValue(nextValue)}
35
- onChange={(nextValue: string) => setValue(nextValue)}
36
- onSubmit={() => resolve(value.trim())}
37
- />
38
- </Box>
39
- );
40
- }