gloomberb 0.5.1 → 0.6.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 (500) hide show
  1. package/README.md +91 -11
  2. package/package.json +12 -5
  3. package/src/app.tsx +414 -191
  4. package/src/architecture/import-boundaries.test.ts +118 -0
  5. package/src/assets/gloomberb-logo.png +0 -0
  6. package/src/assets/gloomberb-logo.svg +73 -0
  7. package/src/brokers/account-cache.ts +157 -0
  8. package/src/brokers/profile-form.test.ts +73 -0
  9. package/src/brokers/profile-form.ts +109 -0
  10. package/src/brokers/remote-broker-adapter.ts +91 -0
  11. package/src/brokers/sync-broker-instance.test.ts +124 -1
  12. package/src/brokers/sync-broker-instance.ts +113 -17
  13. package/src/capabilities/factories.ts +100 -0
  14. package/src/capabilities/index.ts +4 -0
  15. package/src/capabilities/registry.test.ts +137 -0
  16. package/src/capabilities/registry.ts +131 -0
  17. package/src/capabilities/service-capabilities.ts +27 -0
  18. package/src/capabilities/types.ts +84 -0
  19. package/src/cli/commands/ticker.ts +11 -3
  20. package/src/cli/context.ts +19 -13
  21. package/src/cli/helpers.ts +1 -0
  22. package/src/cli/types.ts +2 -2
  23. package/src/components/chart/chart-controller.ts +4 -24
  24. package/src/components/chart/chart-data.ts +3 -14
  25. package/src/components/chart/chart-indicator-selector.tsx +28 -6
  26. package/src/components/chart/chart-pane-settings.ts +15 -28
  27. package/src/components/chart/chart-renderer.ts +74 -99
  28. package/src/components/chart/chart-resolution.ts +10 -10
  29. package/src/components/chart/chart-scroll.test.ts +6 -5
  30. package/src/components/chart/chart-scroll.ts +2 -2
  31. package/src/components/chart/chart-types.ts +0 -1
  32. package/src/components/chart/chart-viewport.ts +2 -1
  33. package/src/components/chart/chart.test.ts +37 -0
  34. package/src/components/chart/comparison-chart-data.ts +3 -17
  35. package/src/components/chart/comparison-chart-renderer.test.ts +27 -4
  36. package/src/components/chart/comparison-chart-renderer.ts +1 -1
  37. package/src/components/chart/comparison-stock-chart.tsx +304 -108
  38. package/src/components/chart/cursor-motion.ts +3 -2
  39. package/src/components/chart/indicators/bands.test.ts +0 -14
  40. package/src/components/chart/indicators/moving-averages.test.ts +0 -15
  41. package/src/components/chart/indicators/options.ts +3 -3
  42. package/src/components/chart/indicators/oscillators.test.ts +0 -21
  43. package/src/components/chart/indicators/types.ts +1 -2
  44. package/src/components/chart/native/chart-rasterizer.ts +37 -44
  45. package/src/components/chart/native/kitty-adapter.ts +2 -5
  46. package/src/components/chart/native/kitty-manager.ts +1 -1
  47. package/src/components/chart/native/kitty-protocol.ts +0 -1
  48. package/src/components/chart/native/kitty-support.ts +1 -1
  49. package/src/components/chart/native/kitty.test.ts +30 -13
  50. package/src/components/chart/native/renderer-selection.ts +1 -1
  51. package/src/components/chart/native/surface-manager.ts +1 -1
  52. package/src/components/chart/static-chart-surface.test.tsx +51 -0
  53. package/src/components/chart/static-chart-surface.tsx +191 -0
  54. package/src/components/chart/stock-chart-renderer-switch.test.tsx +242 -35
  55. package/src/components/chart/stock-chart.tsx +438 -141
  56. package/src/components/command-bar/__snapshots__/command-bar.test.tsx.snap +4 -4
  57. package/src/components/command-bar/command-bar.test.tsx +269 -20
  58. package/src/components/command-bar/command-bar.tsx +830 -353
  59. package/src/components/command-bar/command-registry.ts +10 -20
  60. package/src/components/command-bar/helpers.test.ts +1 -1
  61. package/src/components/command-bar/helpers.ts +9 -11
  62. package/src/components/command-bar/pane-template-display.ts +4 -8
  63. package/src/components/command-bar/root-shortcuts.ts +12 -11
  64. package/src/components/command-bar/view-model.test.ts +8 -42
  65. package/src/components/command-bar/view-model.ts +8 -36
  66. package/src/components/command-bar/workflow-ops.test.ts +76 -2
  67. package/src/components/command-bar/workflow-ops.ts +40 -9
  68. package/src/components/command-bar/workflow-types.ts +2 -1
  69. package/src/components/data-table-stack-view.test.tsx +206 -0
  70. package/src/components/data-table-stack-view.tsx +152 -0
  71. package/src/components/data-table-view.test.tsx +188 -0
  72. package/src/components/data-table-view.tsx +279 -0
  73. package/src/components/feed-data-table-stack-view.test.tsx +225 -0
  74. package/src/components/feed-data-table-stack-view.tsx +365 -0
  75. package/src/components/index.ts +26 -31
  76. package/src/components/layout/detached-pane-shell.tsx +185 -0
  77. package/src/components/layout/floating-pane.tsx +87 -14
  78. package/src/components/layout/header.tsx +139 -49
  79. package/src/components/layout/pane-content.tsx +40 -0
  80. package/src/components/layout/pane-footer.test.tsx +195 -0
  81. package/src/components/layout/pane-footer.tsx +410 -0
  82. package/src/components/layout/pane-header.test.tsx +39 -0
  83. package/src/components/layout/pane-header.tsx +192 -14
  84. package/src/components/layout/pane-sizing.ts +9 -10
  85. package/src/components/layout/pane-title.ts +35 -0
  86. package/src/components/layout/pane.tsx +52 -11
  87. package/src/components/layout/shell.test.tsx +315 -107
  88. package/src/components/layout/shell.tsx +1010 -368
  89. package/src/components/layout/status-bar.test.tsx +17 -10
  90. package/src/components/layout/status-bar.tsx +367 -100
  91. package/src/components/layout/titlebar-overlay.ts +2 -0
  92. package/src/components/markdown-editor.tsx +29 -14
  93. package/src/components/markdown-text.tsx +25 -24
  94. package/src/components/onboarding/onboarding-wizard.test.tsx +8 -3
  95. package/src/components/onboarding/onboarding-wizard.tsx +347 -473
  96. package/src/components/pane-settings-dialog.tsx +521 -89
  97. package/src/components/pane-template-wizard.test.tsx +1 -1
  98. package/src/components/pane-template-wizard.tsx +38 -37
  99. package/src/components/price-selector-dialog.tsx +22 -21
  100. package/src/components/ticker-badge-list.tsx +53 -0
  101. package/src/components/ticker-badge-text.test.tsx +44 -1
  102. package/src/components/ticker-badge-text.tsx +78 -38
  103. package/src/components/ticker-badge.tsx +28 -7
  104. package/src/components/ticker-list-table-view.tsx +248 -0
  105. package/src/components/ticker-list-table.test.tsx +47 -1
  106. package/src/components/ticker-list-table.tsx +212 -41
  107. package/src/components/toggle-list.tsx +114 -8
  108. package/src/components/ui/button.tsx +24 -16
  109. package/src/components/ui/choice-dialog.tsx +89 -0
  110. package/src/components/ui/data-table.tsx +313 -114
  111. package/src/components/ui/external-link.tsx +52 -13
  112. package/src/components/ui/fields.tsx +44 -30
  113. package/src/components/ui/frame.tsx +24 -57
  114. package/src/components/ui/index.ts +15 -50
  115. package/src/components/ui/list-view.tsx +65 -25
  116. package/src/components/ui/loading.tsx +5 -60
  117. package/src/components/ui/message-composer.test.tsx +81 -0
  118. package/src/components/ui/message-composer.tsx +139 -0
  119. package/src/components/ui/multi-select-dialog.tsx +100 -32
  120. package/src/components/ui/multi-select.ts +112 -0
  121. package/src/components/ui/native-select.tsx +120 -0
  122. package/src/components/ui/page-stack-view.tsx +49 -19
  123. package/src/components/ui/remote-image.tsx +75 -0
  124. package/src/components/ui/row-value-cache.test.ts +17 -0
  125. package/src/components/ui/row-value-cache.ts +41 -0
  126. package/src/components/ui/shortcut-hint.tsx +55 -0
  127. package/src/components/ui/status.tsx +12 -67
  128. package/src/components/ui/table-layout.ts +59 -0
  129. package/src/components/ui/tabs.tsx +249 -27
  130. package/src/components/ui/toggle.tsx +19 -127
  131. package/src/components/ui/ui.test.tsx +266 -53
  132. package/src/components/use-double-click-activation.ts +7 -2
  133. package/src/components/use-external-link-footer.ts +79 -0
  134. package/src/core/app-services.ts +95 -0
  135. package/src/core/state/app-state.ts +886 -0
  136. package/src/{state → core/state}/session-persistence.ts +7 -6
  137. package/src/data/config-layout.ts +225 -0
  138. package/src/data/config-store-node.ts +390 -0
  139. package/src/data/config-store.test.ts +72 -0
  140. package/src/data/config-store.ts +35 -478
  141. package/src/data/sqlite-json.ts +2 -5
  142. package/src/data/sqlite-retry.ts +1 -0
  143. package/src/index.tsx +2 -68
  144. package/src/market-data/coordinator-subscriptions.test.ts +125 -0
  145. package/src/market-data/coordinator.test.ts +73 -4
  146. package/src/market-data/coordinator.ts +190 -24
  147. package/src/market-data/hooks.test.tsx +1 -1
  148. package/src/market-data/hooks.tsx +109 -87
  149. package/src/market-data/query-store.ts +3 -3
  150. package/src/market-data/request-types.test.ts +0 -14
  151. package/src/market-data/result-types.ts +1 -0
  152. package/src/news/aggregator.test.ts +267 -0
  153. package/src/news/aggregator.ts +452 -0
  154. package/src/news/hooks.ts +39 -0
  155. package/src/news/ticker-symbols.ts +21 -0
  156. package/src/news/types.ts +77 -0
  157. package/src/notifications/app-notifier.ts +21 -10
  158. package/src/pane-settings.test.ts +0 -2
  159. package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +124 -134
  160. package/src/plugins/builtin/ai/gloomberb-cli.ts +14 -13
  161. package/src/plugins/builtin/ai/providers.ts +1 -9
  162. package/src/plugins/builtin/ai/runner.ts +41 -5
  163. package/src/plugins/builtin/ai/screener-pane.test.tsx +37 -55
  164. package/src/plugins/builtin/ai/screener-pane.tsx +231 -276
  165. package/src/plugins/builtin/ai/settings.ts +0 -1
  166. package/src/plugins/builtin/ai/utils.ts +1 -71
  167. package/src/plugins/builtin/alerts/alert-engine.test.ts +1 -38
  168. package/src/plugins/builtin/alerts/index.test.tsx +61 -82
  169. package/src/plugins/builtin/alerts/index.tsx +263 -155
  170. package/src/plugins/builtin/alerts/types.ts +7 -0
  171. package/src/plugins/builtin/analytics/index.test.tsx +13 -8
  172. package/src/plugins/builtin/analytics/index.tsx +78 -120
  173. package/src/plugins/builtin/analytics/metrics.test.ts +0 -4
  174. package/src/plugins/builtin/ask-ai.test.tsx +55 -54
  175. package/src/plugins/builtin/broker-manager/index.test.tsx +168 -0
  176. package/src/plugins/builtin/broker-manager/index.tsx +759 -0
  177. package/src/plugins/builtin/broker-manager/model.test.ts +107 -0
  178. package/src/plugins/builtin/broker-manager/model.ts +142 -0
  179. package/src/plugins/builtin/chat-controller.test.ts +129 -0
  180. package/src/plugins/builtin/chat-controller.ts +85 -14
  181. package/src/plugins/builtin/chat.test.tsx +283 -36
  182. package/src/plugins/builtin/chat.tsx +363 -170
  183. package/src/plugins/builtin/cloud-auth-actions.tsx +72 -0
  184. package/src/plugins/builtin/cloud-tweets.tsx +896 -0
  185. package/src/plugins/builtin/comparison-chart.test.tsx +46 -57
  186. package/src/plugins/builtin/comparison-chart.tsx +11 -11
  187. package/src/plugins/builtin/correlation/compute.test.ts +0 -17
  188. package/src/plugins/builtin/correlation/index.tsx +51 -46
  189. package/src/plugins/builtin/correlation/settings.ts +1 -2
  190. package/src/plugins/builtin/debug.tsx +99 -70
  191. package/src/plugins/builtin/earnings/earnings-cache.test.ts +2 -2
  192. package/src/plugins/builtin/earnings/earnings-cache.ts +12 -3
  193. package/src/plugins/builtin/earnings/earnings-data.test.ts +42 -11
  194. package/src/plugins/builtin/earnings/earnings-data.ts +70 -5
  195. package/src/plugins/builtin/earnings/index.tsx +187 -94
  196. package/src/plugins/builtin/econ/calendar-source.ts +9 -116
  197. package/src/plugins/builtin/econ/fred-client.test.ts +1 -14
  198. package/src/plugins/builtin/econ/fred-series-map.ts +1 -0
  199. package/src/plugins/builtin/econ/index.tsx +236 -421
  200. package/src/plugins/builtin/econ/types.ts +1 -0
  201. package/src/plugins/builtin/fx-matrix/index.tsx +43 -45
  202. package/src/plugins/builtin/fx-matrix/pairs.ts +1 -0
  203. package/src/plugins/builtin/help.test.tsx +120 -60
  204. package/src/plugins/builtin/help.tsx +266 -82
  205. package/src/plugins/builtin/holders/index.test.ts +45 -0
  206. package/src/plugins/builtin/holders/index.tsx +949 -0
  207. package/src/plugins/builtin/insider/index.tsx +207 -253
  208. package/src/plugins/builtin/insider/insider-data.test.ts +1 -11
  209. package/src/plugins/builtin/insider/insider-data.ts +1 -0
  210. package/src/plugins/builtin/layout-manager.tsx +12 -40
  211. package/src/plugins/builtin/market-movers/index.tsx +70 -144
  212. package/src/plugins/builtin/market-movers/screener.test.ts +0 -18
  213. package/src/plugins/builtin/news-wire/breaking-notifications.test.ts +98 -0
  214. package/src/plugins/builtin/news-wire/breaking-notifications.ts +110 -0
  215. package/src/plugins/builtin/news-wire/breaking-pane.tsx +157 -0
  216. package/src/plugins/builtin/news-wire/categories.ts +114 -0
  217. package/src/plugins/builtin/news-wire/default-feeds.ts +3 -0
  218. package/src/plugins/builtin/news-wire/digest-store.ts +44 -0
  219. package/src/plugins/builtin/news-wire/feed-config.test.ts +90 -0
  220. package/src/plugins/builtin/news-wire/feed-config.ts +216 -0
  221. package/src/plugins/builtin/news-wire/feed-pane.tsx +21 -0
  222. package/src/plugins/builtin/news-wire/hash.ts +9 -0
  223. package/src/plugins/builtin/news-wire/index.tsx +92 -0
  224. package/src/plugins/builtin/news-wire/industry-pane.tsx +140 -0
  225. package/src/plugins/builtin/news-wire/news-detail-view.tsx +256 -0
  226. package/src/plugins/builtin/news-wire/news-footer.ts +28 -0
  227. package/src/plugins/builtin/news-wire/news-preset-pane.tsx +94 -0
  228. package/src/plugins/builtin/news-wire/news-query-presets.ts +40 -0
  229. package/src/plugins/builtin/news-wire/news-table.test.tsx +206 -0
  230. package/src/plugins/builtin/news-wire/news-table.tsx +296 -0
  231. package/src/plugins/builtin/news-wire/persisted-articles.ts +92 -0
  232. package/src/plugins/builtin/news-wire/read-state.test.ts +30 -0
  233. package/src/plugins/builtin/news-wire/read-state.ts +76 -0
  234. package/src/plugins/builtin/news-wire/rss-parser.test.ts +184 -0
  235. package/src/plugins/builtin/news-wire/rss-parser.ts +213 -0
  236. package/src/plugins/builtin/news-wire/rss-source.test.ts +159 -0
  237. package/src/plugins/builtin/news-wire/rss-source.ts +181 -0
  238. package/src/plugins/builtin/news-wire/top-pane.tsx +21 -0
  239. package/src/plugins/builtin/news.tsx +98 -25
  240. package/src/plugins/builtin/notes-files.ts +51 -10
  241. package/src/plugins/builtin/notes.tsx +332 -131
  242. package/src/plugins/builtin/options.test.tsx +185 -0
  243. package/src/plugins/builtin/options.tsx +346 -206
  244. package/src/plugins/builtin/plugin-groups.ts +92 -0
  245. package/src/plugins/builtin/portfolio-list/header.tsx +72 -37
  246. package/src/plugins/builtin/portfolio-list/index.tsx +25 -42
  247. package/src/plugins/builtin/portfolio-list/metrics.ts +1 -1
  248. package/src/plugins/builtin/portfolio-list/pane.tsx +246 -164
  249. package/src/plugins/builtin/portfolio-list/settings.ts +19 -51
  250. package/src/plugins/builtin/portfolio-list/summary.tsx +21 -16
  251. package/src/plugins/builtin/portfolio-list/table.tsx +66 -25
  252. package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.test.tsx +1 -1
  253. package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.ts +1 -1
  254. package/src/plugins/builtin/portfolio-list.test.ts +60 -1
  255. package/src/plugins/builtin/portfolio-list.test.tsx +122 -55
  256. package/src/plugins/builtin/quote-monitor.test.tsx +2 -27
  257. package/src/plugins/builtin/research/index.tsx +724 -0
  258. package/src/plugins/builtin/sec.tsx +67 -19
  259. package/src/plugins/builtin/sectors/index.tsx +42 -80
  260. package/src/plugins/builtin/sectors/sector-data.ts +1 -0
  261. package/src/plugins/builtin/ticker-detail/chart-tab.tsx +25 -39
  262. package/src/plugins/builtin/ticker-detail/financials-tab.tsx +108 -58
  263. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +383 -271
  264. package/src/plugins/builtin/ticker-detail/pane.tsx +135 -49
  265. package/src/plugins/builtin/ticker-detail/quote-monitor.tsx +43 -27
  266. package/src/plugins/builtin/ticker-detail/settings.ts +3 -6
  267. package/src/plugins/builtin/ticker-detail-chart-tab-switch.test.tsx +16 -10
  268. package/src/plugins/builtin/ticker-detail.test.tsx +156 -33
  269. package/src/plugins/builtin/ticker-surface.ts +68 -0
  270. package/src/plugins/builtin/world-indices/index.tsx +44 -82
  271. package/src/plugins/builtin/world-indices/indices.ts +1 -0
  272. package/src/plugins/builtin/yahoo.ts +55 -1
  273. package/src/plugins/builtin/yield-curve/index.tsx +87 -145
  274. package/src/plugins/builtin/yield-curve/treasury-data.ts +6 -77
  275. package/src/plugins/catalog-ui.test.ts +24 -0
  276. package/src/plugins/catalog-ui.ts +44 -0
  277. package/src/plugins/catalog.ts +2 -50
  278. package/src/plugins/ibkr/__snapshots__/trade-tab.test.tsx.snap +5 -5
  279. package/src/plugins/ibkr/account-cache.test.ts +15 -14
  280. package/src/plugins/ibkr/account-cache.ts +15 -120
  281. package/src/plugins/ibkr/broker-adapter.ts +67 -0
  282. package/src/plugins/ibkr/config.test.ts +0 -18
  283. package/src/plugins/ibkr/dialogs.tsx +14 -56
  284. package/src/plugins/ibkr/flex.test.ts +57 -2
  285. package/src/plugins/ibkr/flex.ts +116 -12
  286. package/src/plugins/ibkr/gateway-helpers.ts +8 -3
  287. package/src/plugins/ibkr/gateway-service-native.ts +2381 -0
  288. package/src/plugins/ibkr/gateway-service.test.ts +47 -9
  289. package/src/plugins/ibkr/gateway-service.ts +264 -2235
  290. package/src/plugins/ibkr/gateway-types.ts +27 -0
  291. package/src/plugins/ibkr/index.test.tsx +3 -11
  292. package/src/plugins/ibkr/index.tsx +1 -0
  293. package/src/plugins/ibkr/price-normalization.test.ts +1 -6
  294. package/src/plugins/ibkr/price-normalization.ts +1 -0
  295. package/src/plugins/ibkr/trade-badge.tsx +6 -5
  296. package/src/plugins/ibkr/trade-tab.test.tsx +2 -2
  297. package/src/plugins/ibkr/trade-tab.tsx +164 -117
  298. package/src/plugins/ibkr/trading-pane.tsx +117 -83
  299. package/src/plugins/pane-manager.test.ts +3 -1
  300. package/src/plugins/pane-manager.ts +93 -19
  301. package/src/plugins/plugin-runtime.test.tsx +176 -74
  302. package/src/plugins/plugin-runtime.tsx +228 -10
  303. package/src/plugins/prediction-markets/cache.ts +49 -0
  304. package/src/plugins/prediction-markets/categories.ts +0 -10
  305. package/src/plugins/prediction-markets/chart.test.tsx +1 -1
  306. package/src/plugins/prediction-markets/chart.tsx +21 -20
  307. package/src/plugins/prediction-markets/cli.ts +1 -0
  308. package/src/plugins/prediction-markets/controller-data.ts +135 -89
  309. package/src/plugins/prediction-markets/controller.ts +37 -108
  310. package/src/plugins/prediction-markets/detail/book.tsx +23 -22
  311. package/src/plugins/prediction-markets/detail/outcomes.tsx +33 -32
  312. package/src/plugins/prediction-markets/detail/overview.tsx +17 -12
  313. package/src/plugins/prediction-markets/detail/pane.tsx +59 -45
  314. package/src/plugins/prediction-markets/detail/rules.tsx +22 -7
  315. package/src/plugins/prediction-markets/detail/shared.tsx +3 -2
  316. package/src/plugins/prediction-markets/detail/trades.tsx +12 -11
  317. package/src/plugins/prediction-markets/detail.test.tsx +1 -1
  318. package/src/plugins/prediction-markets/keyboard.ts +1 -0
  319. package/src/plugins/prediction-markets/launch.test.ts +1 -1
  320. package/src/plugins/prediction-markets/launch.ts +1 -1
  321. package/src/plugins/prediction-markets/metrics.ts +25 -12
  322. package/src/plugins/prediction-markets/pane.test.tsx +16 -7
  323. package/src/plugins/prediction-markets/pane.tsx +180 -82
  324. package/src/plugins/prediction-markets/plugin.test.tsx +37 -45
  325. package/src/plugins/prediction-markets/services/fetch.ts +27 -10
  326. package/src/plugins/prediction-markets/services/kalshi-adapter.ts +20 -3
  327. package/src/plugins/prediction-markets/services/polymarket-adapter.ts +29 -11
  328. package/src/plugins/prediction-markets/settings.ts +0 -5
  329. package/src/plugins/prediction-markets/test-helpers.tsx +19 -44
  330. package/src/plugins/registry.test.ts +307 -0
  331. package/src/plugins/registry.ts +293 -82
  332. package/src/react/input.ts +53 -0
  333. package/src/react/plugins/plugin-slot.tsx +21 -0
  334. package/src/react/use-raf-callback.ts +44 -0
  335. package/src/renderers/electrobun/bun/application-menu-click.test.ts +41 -0
  336. package/src/renderers/electrobun/bun/application-menu-click.ts +55 -0
  337. package/src/renderers/electrobun/bun/application-menu.ts +118 -0
  338. package/src/renderers/electrobun/bun/context-menu-click.test.ts +67 -0
  339. package/src/renderers/electrobun/bun/context-menu-click.ts +61 -0
  340. package/src/renderers/electrobun/bun/context-menu-normalize.test.ts +52 -0
  341. package/src/renderers/electrobun/bun/context-menu-normalize.ts +67 -0
  342. package/src/renderers/electrobun/bun/core-capabilities.ts +328 -0
  343. package/src/renderers/electrobun/bun/desktop-workspace.test.ts +63 -0
  344. package/src/renderers/electrobun/bun/desktop-workspace.ts +162 -0
  345. package/src/renderers/electrobun/bun/index.ts +1149 -0
  346. package/src/renderers/electrobun/bun/window-frame.test.ts +40 -0
  347. package/src/renderers/electrobun/bun/window-frame.ts +46 -0
  348. package/src/renderers/electrobun/shared/protocol.ts +72 -0
  349. package/src/renderers/electrobun/view/ai-host.ts +93 -0
  350. package/src/renderers/electrobun/view/app-services.ts +348 -0
  351. package/src/renderers/electrobun/view/application-menu-bridge.ts +12 -0
  352. package/src/renderers/electrobun/view/backend-rpc.ts +173 -0
  353. package/src/renderers/electrobun/view/broker-remote-client.ts +154 -0
  354. package/src/renderers/electrobun/view/config-host.ts +35 -0
  355. package/src/renderers/electrobun/view/context-menu.test.ts +140 -0
  356. package/src/renderers/electrobun/view/context-menu.ts +164 -0
  357. package/src/renderers/electrobun/view/data-table.tsx +773 -0
  358. package/src/renderers/electrobun/view/desktop-controls.tsx +651 -0
  359. package/src/renderers/electrobun/view/desktop-window-bridge.ts +59 -0
  360. package/src/renderers/electrobun/view/desktop-window-snapshot.test.ts +57 -0
  361. package/src/renderers/electrobun/view/desktop-window-snapshot.ts +41 -0
  362. package/src/renderers/electrobun/view/dialog-host.tsx +62 -0
  363. package/src/renderers/electrobun/view/http-fetch.ts +113 -0
  364. package/src/renderers/electrobun/view/input-host.tsx +65 -0
  365. package/src/renderers/electrobun/view/key-event.test.ts +34 -0
  366. package/src/renderers/electrobun/view/key-event.ts +134 -0
  367. package/src/renderers/electrobun/view/main.tsx +104 -0
  368. package/src/renderers/electrobun/view/native-renderer.ts +110 -0
  369. package/src/renderers/electrobun/view/native-stubs/chart-kitty-support.ts +9 -0
  370. package/src/renderers/electrobun/view/native-stubs/chart-surface-manager.ts +25 -0
  371. package/src/renderers/electrobun/view/native-stubs/chart-surface-sync.ts +1 -0
  372. package/src/renderers/electrobun/view/notes-files.ts +56 -0
  373. package/src/renderers/electrobun/view/resource-store.test.ts +44 -0
  374. package/src/renderers/electrobun/view/resource-store.ts +106 -0
  375. package/src/renderers/electrobun/view/rpc-codec.ts +42 -0
  376. package/src/renderers/electrobun/view/scrollbar-activity.ts +23 -0
  377. package/src/renderers/electrobun/view/toast-host.tsx +65 -0
  378. package/src/renderers/electrobun/view/ui-host.tsx +1490 -0
  379. package/src/renderers/electrobun/view/update-host.ts +38 -0
  380. package/src/renderers/opentui/dialog-host.test.tsx +44 -0
  381. package/src/renderers/opentui/dialog-host.tsx +56 -0
  382. package/src/renderers/opentui/host.tsx +134 -0
  383. package/src/renderers/opentui/input-host.tsx +24 -0
  384. package/src/renderers/opentui/start.tsx +71 -0
  385. package/src/renderers/opentui/test-utils.tsx +76 -0
  386. package/src/renderers/opentui/toast-host.tsx +10 -0
  387. package/src/renderers/opentui/ui-host.tsx +97 -0
  388. package/src/sources/gloomberb-cloud.test.ts +288 -17
  389. package/src/sources/gloomberb-cloud.ts +176 -9
  390. package/src/sources/provider-errors.ts +1 -0
  391. package/src/sources/provider-router.test.ts +220 -81
  392. package/src/sources/provider-router.ts +294 -111
  393. package/src/sources/sec-edgar.ts +19 -6
  394. package/src/sources/yahoo-finance.test.ts +0 -5
  395. package/src/sources/yahoo-finance.ts +420 -11
  396. package/src/state/app-activity.ts +7 -7
  397. package/src/state/app-bootstrap.ts +114 -40
  398. package/src/state/app-context-selector.test.tsx +70 -1
  399. package/src/state/app-context.test.ts +71 -3
  400. package/src/state/app-context.test.tsx +14 -7
  401. package/src/state/app-context.tsx +205 -869
  402. package/src/state/config-save-scheduler.ts +35 -0
  403. package/src/state/persist-scheduler.test.ts +67 -0
  404. package/src/state/persist-scheduler.ts +69 -0
  405. package/src/state/selectors-ui.ts +55 -0
  406. package/src/state/selectors.ts +15 -71
  407. package/src/state/session-persistence.test.ts +1 -1
  408. package/src/state/ticker-refresh-queue.ts +52 -3
  409. package/src/state/use-inline-tickers.ts +18 -16
  410. package/src/state/use-quote-streaming.test.ts +0 -6
  411. package/src/state/use-quote-streaming.test.tsx +1 -1
  412. package/src/state/use-quote-streaming.ts +3 -2
  413. package/src/state/use-session-snapshot.ts +32 -17
  414. package/src/test-support/plugin-runtime.ts +149 -0
  415. package/src/theme/color-utils.ts +1 -0
  416. package/src/theme/colors.ts +16 -0
  417. package/src/types/broker.ts +16 -1
  418. package/src/types/capability-route-source.ts +23 -0
  419. package/src/types/config.test.ts +23 -0
  420. package/src/types/config.ts +145 -10
  421. package/src/types/context-menu.ts +95 -0
  422. package/src/types/data-provider.ts +34 -3
  423. package/src/types/desktop-menu.ts +13 -0
  424. package/src/types/desktop-window.ts +29 -0
  425. package/src/types/financials.ts +123 -2
  426. package/src/types/instrument.ts +1 -0
  427. package/src/types/news-source.ts +9 -0
  428. package/src/types/persistence.ts +1 -0
  429. package/src/types/plugin.ts +32 -8
  430. package/src/ui/ascii-font.ts +85 -0
  431. package/src/ui/context-menu.test.ts +94 -0
  432. package/src/ui/context-menu.tsx +433 -0
  433. package/src/ui/dialog.tsx +55 -0
  434. package/src/ui/host.tsx +361 -0
  435. package/src/ui/index.tsx +126 -0
  436. package/src/ui/toast.tsx +40 -0
  437. package/src/updater.test.ts +69 -0
  438. package/src/updater.ts +61 -18
  439. package/src/utils/api-client.test.ts +99 -9
  440. package/src/utils/api-client.ts +378 -18
  441. package/src/utils/back-navigation.ts +6 -0
  442. package/src/utils/cli-output.ts +1 -0
  443. package/src/utils/currency-units.test.ts +1 -6
  444. package/src/utils/currency-units.ts +1 -0
  445. package/src/utils/debug-log.ts +68 -0
  446. package/src/utils/dom-screenshot.ts +326 -0
  447. package/src/utils/exchange-rate-map.ts +1 -0
  448. package/src/utils/exchanges.ts +214 -0
  449. package/src/utils/format.test.ts +7 -0
  450. package/src/utils/format.ts +1 -0
  451. package/src/utils/fuzzy-search.ts +1 -0
  452. package/src/utils/html-entities.test.ts +8 -0
  453. package/src/utils/html-entities.ts +32 -0
  454. package/src/utils/http-transport.ts +11 -0
  455. package/src/utils/main-thread-monitor.ts +127 -0
  456. package/src/utils/perf-marks.ts +54 -0
  457. package/src/utils/quote-freshness.ts +27 -50
  458. package/src/utils/quote-resolution.test.ts +5 -5
  459. package/src/utils/quote-resolution.ts +0 -1
  460. package/src/utils/quote-time.ts +1 -5
  461. package/src/utils/sec.test.ts +0 -12
  462. package/src/utils/selection-clipboard.ts +14 -6
  463. package/src/utils/shortcut-labels.test.ts +20 -0
  464. package/src/utils/shortcut-labels.ts +67 -0
  465. package/src/utils/slugify.ts +1 -0
  466. package/src/utils/terminal-input-reset.ts +1 -0
  467. package/src/utils/text-wrap.ts +82 -0
  468. package/src/utils/throttled-fetch.test.ts +19 -0
  469. package/src/utils/throttled-fetch.ts +11 -9
  470. package/src/utils/ticker-list.test.ts +1 -7
  471. package/src/utils/ticker-list.ts +1 -0
  472. package/src/utils/timestamp.ts +29 -0
  473. package/src/utils/tweet-text.test.ts +13 -0
  474. package/src/utils/tweet-text.ts +18 -0
  475. package/src/utils/url.ts +11 -0
  476. package/src/version.ts +1 -1
  477. package/src/components/chart/chart-control-hint.tsx +0 -39
  478. package/src/components/detail-feed-view.test.tsx +0 -180
  479. package/src/components/detail-feed-view.tsx +0 -531
  480. package/src/components/layout/pixel-mouse-mode.test.ts +0 -122
  481. package/src/components/layout/pixel-mouse.test.ts +0 -40
  482. package/src/components/spinner.tsx +0 -2
  483. package/src/components/tab-bar.tsx +0 -17
  484. package/src/components/ui/multi-select-chips.tsx +0 -69
  485. package/src/plugins/builtin/ai/index.test.tsx +0 -139
  486. package/src/plugins/builtin/ask-ai.tsx +0 -9
  487. package/src/plugins/builtin/correlation/plugin.test.ts +0 -44
  488. package/src/plugins/builtin/correlation/settings.test.ts +0 -34
  489. package/src/plugins/builtin/econ/calendar-source.test.ts +0 -219
  490. package/src/plugins/builtin/econ/fred-client.ts +0 -79
  491. package/src/plugins/builtin/fred-settings.ts +0 -10
  492. package/src/plugins/builtin/options-availability.test.tsx +0 -179
  493. package/src/plugins/builtin/options-availability.ts +0 -158
  494. package/src/plugins/builtin/portfolio-list/metrics.test.ts +0 -61
  495. package/src/plugins/builtin/yield-curve/treasury-data.test.ts +0 -212
  496. package/src/plugins/prediction-markets/status.ts +0 -24
  497. package/src/plugins/prediction-markets/table.tsx +0 -87
  498. package/src/utils/quote-time.test.ts +0 -22
  499. package/src/utils/selection-clipboard.test.ts +0 -43
  500. package/src/utils/terminal-input-reset.test.ts +0 -50
@@ -1,51 +1,58 @@
1
- import { TextAttributes } from "@opentui/core";
1
+ import { Box, ScrollBox, Text, useUiHost } from "../../ui";
2
+ import { TextAttributes } from "../../ui";
2
3
  import { useState } from "react";
3
4
  import type { ReactNode } from "react";
4
- import type { GloomPlugin, PaneProps } from "../../types/plugin";
5
+ import type { CommandDef, GloomPlugin, KeyboardShortcut, PaneProps } from "../../types/plugin";
5
6
  import { colors, hoverBg } from "../../theme/colors";
6
7
  import { getSharedRegistry } from "../registry";
8
+ import { usePluginAppActions } from "../plugin-runtime";
9
+ import { commands as coreCommands } from "../../components/command-bar/command-registry";
10
+ import { detectShortcutPlatform, formatPrimaryShortcut, getShortcutDisplayMode } from "../../utils/shortcut-labels";
7
11
 
8
12
  function ShortcutBadge({ label }: { label: string }) {
9
13
  return (
10
- <box backgroundColor={colors.selected}>
11
- <text fg={colors.selectedText} attributes={TextAttributes.BOLD}>
14
+ <Box backgroundColor={colors.selected}>
15
+ <Text fg={colors.selectedText} attributes={TextAttributes.BOLD}>
12
16
  {` ${label} `}
13
- </text>
14
- </box>
17
+ </Text>
18
+ </Box>
15
19
  );
16
20
  }
17
21
 
18
22
  function ShortcutRow({
19
23
  badges,
20
24
  description,
21
- compact,
22
25
  }: {
23
26
  badges: string[];
24
27
  description: string;
25
- compact: boolean;
26
28
  }) {
27
29
  return (
28
- <box flexDirection={compact ? "column" : "row"} gap={1}>
29
- <box flexDirection="row" gap={1} flexShrink={0}>
30
+ <Box flexDirection="row" gap={1}>
31
+ <Box flexDirection="row" gap={1} flexShrink={0}>
30
32
  {badges.map((badge) => <ShortcutBadge key={badge} label={badge} />)}
31
- </box>
32
- <box flexGrow={1}>
33
- <text fg={colors.text}>{description}</text>
34
- </box>
35
- </box>
33
+ </Box>
34
+ <Box flexGrow={1}>
35
+ <Text fg={colors.text} wrapText>{description}</Text>
36
+ </Box>
37
+ </Box>
36
38
  );
37
39
  }
38
40
 
39
41
  function HelpSection({ title, children }: { title: string; children: ReactNode }) {
40
42
  return (
41
- <box flexDirection="column">
42
- <text fg={colors.textBright} attributes={TextAttributes.BOLD}>{title}</text>
43
- <box height={1} />
43
+ <Box flexDirection="column" marginTop={1}>
44
+ <Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{title}</Text>
44
45
  {children}
45
- </box>
46
+ </Box>
46
47
  );
47
48
  }
48
49
 
50
+ interface HelpShortcutEntry {
51
+ id: string;
52
+ badges: string[];
53
+ description: string;
54
+ }
55
+
49
56
  function ActionButton({
50
57
  id,
51
58
  label,
@@ -60,7 +67,7 @@ function ActionButton({
60
67
  onPress: () => void;
61
68
  }) {
62
69
  return (
63
- <box
70
+ <Box
64
71
  backgroundColor={hovered ? hoverBg() : colors.panel}
65
72
  onMouseMove={() => onHover(id)}
66
73
  onMouseOut={() => onHover(null)}
@@ -70,15 +77,15 @@ function ActionButton({
70
77
  onPress();
71
78
  }}
72
79
  >
73
- <text fg={hovered ? colors.textBright : colors.text}>{` ${label} `}</text>
74
- </box>
80
+ <Text fg={hovered ? colors.textBright : colors.text}>{` ${label} `}</Text>
81
+ </Box>
75
82
  );
76
83
  }
77
84
 
78
85
  function resolveWindowTemplates(registry: ReturnType<typeof getSharedRegistry>) {
79
86
  if (!registry || !registry.paneTemplates) return [];
80
87
 
81
- const disabledPlugins = new Set(registry.getConfigFn?.().disabledPlugins ?? []);
88
+ const disabledPlugins = resolveDisabledPlugins(registry);
82
89
  const allPlugins = registry.allPlugins ?? new Map<string, { name?: string }>();
83
90
 
84
91
  return [...registry.paneTemplates.values()]
@@ -106,37 +113,149 @@ function resolveWindowTemplates(registry: ReturnType<typeof getSharedRegistry>)
106
113
  ));
107
114
  }
108
115
 
116
+ function resolveDisabledPlugins(registry: ReturnType<typeof getSharedRegistry>): Set<string> {
117
+ try {
118
+ return new Set(registry?.getConfigFn?.().disabledPlugins ?? []);
119
+ } catch {
120
+ return new Set();
121
+ }
122
+ }
123
+
124
+ function formatPlaceholder(value: string | undefined): string | null {
125
+ return value ? `<${value}>` : null;
126
+ }
127
+
128
+ function withPluginName(description: string | undefined, pluginName: string | null | undefined): string {
129
+ const base = description?.trim() || "Run command";
130
+ return pluginName ? `${base} (${pluginName})` : base;
131
+ }
132
+
133
+ function resolveCommandShortcuts(registry: ReturnType<typeof getSharedRegistry>): HelpShortcutEntry[] {
134
+ const coreRows: HelpShortcutEntry[] = coreCommands
135
+ .filter((command) => command.prefix.trim().length > 0)
136
+ .map((command) => ({
137
+ id: `core:${command.id}`,
138
+ badges: [
139
+ command.prefix.toUpperCase(),
140
+ formatPlaceholder(command.argPlaceholder),
141
+ ].filter((value): value is string => !!value),
142
+ description: command.description,
143
+ }));
144
+
145
+ if (!registry || !registry.commands) return coreRows;
146
+
147
+ const disabledPlugins = resolveDisabledPlugins(registry);
148
+ const allPlugins = registry.allPlugins ?? new Map<string, { name?: string }>();
149
+ const pluginRows = [...registry.commands.values()]
150
+ .filter((command: CommandDef) => command.shortcut?.trim().length)
151
+ .filter((command: CommandDef) => {
152
+ const pluginId = registry.getCommandPluginId?.(command.id);
153
+ if (pluginId && disabledPlugins.has(pluginId)) return false;
154
+ return !(command.hidden?.() ?? false);
155
+ })
156
+ .map((command: CommandDef) => {
157
+ const pluginId = registry.getCommandPluginId?.(command.id);
158
+ const pluginName = pluginId ? allPlugins.get(pluginId)?.name : null;
159
+ return {
160
+ id: `plugin-command:${command.id}`,
161
+ badges: [
162
+ command.shortcut!.toUpperCase(),
163
+ formatPlaceholder(command.shortcutArg?.placeholder),
164
+ ].filter((value): value is string => !!value),
165
+ description: withPluginName(command.label, pluginName),
166
+ };
167
+ })
168
+ .sort((left, right) => (
169
+ left.badges.join(" ").localeCompare(right.badges.join(" "))
170
+ || left.description.localeCompare(right.description)
171
+ ));
172
+
173
+ return [...coreRows, ...pluginRows];
174
+ }
175
+
176
+ function formatShortcutKey(shortcut: KeyboardShortcut): string {
177
+ const key = shortcut.key.length === 1
178
+ ? shortcut.key.toUpperCase()
179
+ : shortcut.key[0]!.toUpperCase() + shortcut.key.slice(1);
180
+ return [
181
+ shortcut.ctrl ? "Ctrl" : null,
182
+ shortcut.shift ? "Shift" : null,
183
+ key,
184
+ ].filter((value): value is string => !!value).join("+");
185
+ }
186
+
187
+ function resolvePluginShortcuts(registry: ReturnType<typeof getSharedRegistry>): HelpShortcutEntry[] {
188
+ if (!registry || !registry.shortcuts) return [];
189
+
190
+ const disabledPlugins = resolveDisabledPlugins(registry);
191
+ const allPlugins = registry.allPlugins ?? new Map<string, { name?: string }>();
192
+ return [...registry.shortcuts.values()]
193
+ .filter((shortcut: KeyboardShortcut) => {
194
+ const pluginId = registry.getShortcutPluginId?.(shortcut.id);
195
+ return !pluginId || !disabledPlugins.has(pluginId);
196
+ })
197
+ .map((shortcut: KeyboardShortcut) => {
198
+ const pluginId = registry.getShortcutPluginId?.(shortcut.id);
199
+ const pluginName = pluginId ? allPlugins.get(pluginId)?.name : null;
200
+ return {
201
+ id: `plugin-shortcut:${shortcut.id}`,
202
+ badges: [formatShortcutKey(shortcut)],
203
+ description: withPluginName(shortcut.description, pluginName),
204
+ };
205
+ })
206
+ .sort((left, right) => (
207
+ left.badges.join(" ").localeCompare(right.badges.join(" "))
208
+ || left.description.localeCompare(right.description)
209
+ ));
210
+ }
211
+
109
212
  export function HelpPane({ width, height }: PaneProps) {
110
213
  const registry = getSharedRegistry();
111
- const compact = width < 78;
214
+ const { openCommandBar, showWidget } = usePluginAppActions();
112
215
  const [hoveredAction, setHoveredAction] = useState<string | null>(null);
216
+ const commandShortcuts = resolveCommandShortcuts(registry);
217
+ const pluginShortcuts = resolvePluginShortcuts(registry);
113
218
  const windowTemplates = resolveWindowTemplates(registry);
219
+ const uiHost = useUiHost();
220
+ const isDesktopWeb = uiHost.kind === "desktop-web";
221
+ const shortcutPlatform = detectShortcutPlatform();
222
+ const shortcutDisplayMode = getShortcutDisplayMode(uiHost.kind);
223
+ const platformShortcut = (keys: string | readonly string[]) => formatPrimaryShortcut(keys, shortcutPlatform, shortcutDisplayMode);
224
+ const commandBarBadges = shortcutDisplayMode === "terminal"
225
+ ? ["Ctrl+P", "`"]
226
+ : ["Ctrl+P", platformShortcut("K"), "`"];
227
+ const copyBadges = shortcutDisplayMode === "terminal"
228
+ ? ["Ctrl+Shift+C"]
229
+ : [platformShortcut("C")];
230
+ const pasteBadges = shortcutDisplayMode === "terminal"
231
+ ? ["Ctrl+Shift+V"]
232
+ : [platformShortcut("V")];
114
233
 
115
234
  const openDebugLog = () => {
116
- registry?.showWidget("debug");
235
+ showWidget("debug");
117
236
  };
118
237
 
119
238
  const openLayoutActions = () => {
120
- registry?.openCommandBarFn("LAY ");
239
+ openCommandBar("LAY ");
121
240
  };
122
241
 
123
242
  const openPluginManager = () => {
124
- registry?.openCommandBarFn("PL ");
243
+ openCommandBar("PL ");
125
244
  };
126
245
 
127
246
  return (
128
- <box flexDirection="column" width={width} height={height}>
129
- <scrollbox flexGrow={1} scrollY>
130
- <box flexDirection="column" padding={1}>
131
- <box flexDirection="column" gap={1}>
132
- <text fg={colors.textBright} attributes={TextAttributes.BOLD}>How To Use Gloomberb</text>
133
- <box flexDirection="column">
134
- <text fg={colors.textDim}>Gloomberb is command-bar first.</text>
135
- <text fg={colors.textDim}>Use the keyboard for speed, and the mouse for windows.</text>
136
- </box>
137
- </box>
138
-
139
- <box flexDirection={compact ? "column" : "row"} gap={1}>
247
+ <Box flexDirection="column" width={width} height={height}>
248
+ <ScrollBox flexGrow={1} scrollY>
249
+ <Box flexDirection="column" padding={1}>
250
+ <Box flexDirection="column" gap={1}>
251
+ <Text fg={colors.textBright} attributes={TextAttributes.BOLD}>How To Use Gloomberb</Text>
252
+ <Box flexDirection="column">
253
+ <Text fg={colors.textDim}>Gloomberb is command-bar first.</Text>
254
+ <Text fg={colors.textDim}>Use the keyboard for speed, and the mouse for windows.</Text>
255
+ </Box>
256
+ </Box>
257
+
258
+ <Box flexDirection="row" gap={1}>
140
259
  <ActionButton
141
260
  id="debug"
142
261
  label="Open Debug Log"
@@ -158,91 +277,156 @@ export function HelpPane({ width, height }: PaneProps) {
158
277
  onHover={setHoveredAction}
159
278
  onPress={openPluginManager}
160
279
  />
161
- </box>
280
+ </Box>
162
281
 
163
282
  <HelpSection title="Command Bar">
164
283
  <ShortcutRow
165
- badges={["Ctrl+P", "`"]}
166
- description="Open the command bar from anywhere."
167
- compact={compact}
284
+ badges={commandBarBadges}
285
+ description="Open or toggle the command bar from anywhere."
286
+ />
287
+ <ShortcutRow
288
+ badges={["<ticker>"]}
289
+ description="Search for a ticker or open the best matching security."
290
+ />
291
+ <ShortcutRow
292
+ badges={["Up/Down", "Ctrl+P/N"]}
293
+ description="Move through command bar results."
168
294
  />
169
295
  <ShortcutRow
170
- badges={["help"]}
171
- description="Open this help window directly."
172
- compact={compact}
296
+ badges={["Enter", "Shift+Enter"]}
297
+ description="Run the selected result or its secondary action."
173
298
  />
174
299
  <ShortcutRow
175
- badges={["T <ticker>"]}
176
- description="Search for a ticker or company and open it."
177
- compact={compact}
300
+ badges={["Tab"]}
301
+ description="Accept an inferred shortcut argument from the focused ticker."
178
302
  />
179
303
  <ShortcutRow
180
- badges={["NP", "PS", "PL"]}
181
- description="Create panes, edit pane settings, and manage plugins."
182
- compact={compact}
304
+ badges={["Esc", "`"]}
305
+ description="Close the command bar."
183
306
  />
184
307
  </HelpSection>
185
308
 
309
+ <HelpSection title="Command Prefixes">
310
+ {commandShortcuts.map((shortcut) => (
311
+ <ShortcutRow
312
+ key={shortcut.id}
313
+ badges={shortcut.badges}
314
+ description={shortcut.description}
315
+ />
316
+ ))}
317
+ </HelpSection>
318
+
186
319
  <HelpSection title="Window Templates">
187
320
  {windowTemplates.length > 0 ? windowTemplates.map((template) => (
188
321
  <ShortcutRow
189
322
  key={template.id}
190
323
  badges={template.badges}
191
324
  description={template.description}
192
- compact={compact}
193
325
  />
194
326
  )) : (
195
- <text fg={colors.textDim}>No shortcut window templates are currently registered.</text>
327
+ <Text fg={colors.textDim}>No shortcut window templates are currently registered.</Text>
196
328
  )}
197
329
  </HelpSection>
198
330
 
199
- <HelpSection title="Move Around">
331
+ {pluginShortcuts.length > 0 && (
332
+ <HelpSection title="Plugin Shortcuts">
333
+ {pluginShortcuts.map((shortcut) => (
334
+ <ShortcutRow
335
+ key={shortcut.id}
336
+ badges={shortcut.badges}
337
+ description={shortcut.description}
338
+ />
339
+ ))}
340
+ </HelpSection>
341
+ )}
342
+
343
+ <HelpSection title="Global Keys">
200
344
  <ShortcutRow
201
345
  badges={["Tab", "Shift+Tab"]}
202
346
  description="Move focus between panes and floating windows."
203
- compact={compact}
204
347
  />
205
348
  <ShortcutRow
206
- badges={["Ctrl+W"]}
207
- description="Close the focused pane, docked or floating."
208
- compact={compact}
209
- />
210
- <ShortcutRow
211
- badges={["j", "k"]}
212
- description="Move through lists in most panes. Arrow keys also work."
213
- compact={compact}
349
+ badges={["Ctrl+1-9"]}
350
+ description="Switch saved layouts by number."
214
351
  />
215
352
  <ShortcutRow
216
353
  badges={["r", "Shift+R"]}
217
354
  description="Refresh the focused ticker or refresh everything."
218
- compact={compact}
355
+ />
356
+ <ShortcutRow
357
+ badges={["a"]}
358
+ description="Open ticker actions for the focused ticker."
359
+ />
360
+ <ShortcutRow
361
+ badges={["q"]}
362
+ description="Quit the terminal app."
363
+ />
364
+ <ShortcutRow
365
+ badges={copyBadges}
366
+ description="Copy the active terminal selection."
367
+ />
368
+ <ShortcutRow
369
+ badges={pasteBadges}
370
+ description="Paste clipboard text into the active input."
371
+ />
372
+ <ShortcutRow
373
+ badges={["u"]}
374
+ description="Install an available app update when one is shown."
219
375
  />
220
376
  </HelpSection>
221
377
 
222
- <HelpSection title="Layout System">
378
+ <HelpSection title="Pane Management">
223
379
  <ShortcutRow
224
- badges={["LAY"]}
380
+ badges={[platformShortcut("W")]}
381
+ description="Close the focused pane, docked or floating."
382
+ />
383
+ <ShortcutRow
384
+ badges={[platformShortcut(",")]}
385
+ description="Edit settings for the focused pane."
386
+ />
387
+ <ShortcutRow
388
+ badges={[platformShortcut(["Shift", "D"])]}
389
+ description="Dock or float the focused pane."
390
+ />
391
+ {isDesktopWeb && (
392
+ <ShortcutRow
393
+ badges={[platformShortcut(["Shift", "O"])]}
394
+ description="Pop the focused pane out to a desktop window."
395
+ />
396
+ )}
397
+ <ShortcutRow
398
+ badges={[platformShortcut(["Shift", "L"])]}
225
399
  description="Open layout actions."
226
- compact={compact}
227
400
  />
228
- <text fg={colors.text}>Docked panes stay in the saved layout.</text>
229
- <box flexDirection="column">
230
- <text fg={colors.text}>Floating panes can be dragged by the title bar</text>
231
- <text fg={colors.text}>and resized from the lower-right corner.</text>
232
- </box>
233
- <text fg={colors.text}>Run Gridlock All Windows if the layout gets messy.</text>
401
+ <ShortcutRow
402
+ badges={[platformShortcut(["Shift", "G"])]}
403
+ description="Gridlock all windows."
404
+ />
405
+ <ShortcutRow
406
+ badges={["Esc"]}
407
+ description="Cancel an active pane drag."
408
+ />
409
+ </HelpSection>
410
+
411
+ <HelpSection title="Layout System">
412
+ <Text fg={colors.text}>Docked panes stay in the saved layout.</Text>
413
+ <Box flexDirection="column">
414
+ <Text fg={colors.text}>Floating panes can be dragged by the title bar</Text>
415
+ <Text fg={colors.text}>and resized from the lower-right corner.</Text>
416
+ </Box>
417
+ <Text fg={colors.text}>Use Layout Actions for split, move, duplicate, undo, redo, and layout presets.</Text>
234
418
  </HelpSection>
235
419
 
236
420
  <HelpSection title="If There Is A Bug">
237
- <text fg={colors.text}>Open Debug Log, then run Export Debug Log from the command bar.</text>
238
- <box flexDirection="column">
239
- <text fg={colors.text}>The file lands in ~/Downloads. Include steps, ticker or layout, plugin,</text>
240
- <text fg={colors.text}>and a screenshot if it is visual.</text>
241
- </box>
421
+ <Text fg={colors.text}>Open Debug Log, then run Export Debug Log from the command bar.</Text>
422
+ <Box flexDirection="column">
423
+ <Text fg={colors.text}>The file lands in ~/Downloads. Include steps, ticker or layout, plugin,</Text>
424
+ <Text fg={colors.text}>and a screenshot if it is visual.</Text>
425
+ </Box>
242
426
  </HelpSection>
243
- </box>
244
- </scrollbox>
245
- </box>
427
+ </Box>
428
+ </ScrollBox>
429
+ </Box>
246
430
  );
247
431
  }
248
432
 
@@ -0,0 +1,45 @@
1
+ import { expect, test } from "bun:test";
2
+ import { buildTreemap, buildTreemapRects } from "./index";
3
+ import type { HolderRecord } from "../../../types/financials";
4
+
5
+ function holder(name: string, value: number): HolderRecord & { id: string } {
6
+ return {
7
+ id: name,
8
+ ownerType: "institution",
9
+ name,
10
+ value,
11
+ };
12
+ }
13
+
14
+ test("groups large holders using terminal cell aspect instead of full-height strips", () => {
15
+ const rows = [
16
+ holder("Vanguard", 56.19),
17
+ holder("BlackRock", 52.3),
18
+ holder("State Street", 26.56),
19
+ holder("Geode", 13.3),
20
+ holder("Morgan Stanley", 8.88),
21
+ holder("Norges Bank", 8.16),
22
+ holder("JPMorgan", 7.41),
23
+ holder("Price", 7.21),
24
+ holder("UBS", 6.6),
25
+ holder("Northern Trust", 5.89),
26
+ holder("Capital Research", 5),
27
+ holder("Wells Fargo", 4.8),
28
+ ];
29
+ const tiles = buildTreemap(rows, 150, 40, 18 / 8);
30
+
31
+ expect(tiles[0]).toMatchObject({ row: expect.objectContaining({ name: "Vanguard" }), x: 0, y: 0 });
32
+ expect(tiles[1]).toMatchObject({ row: expect.objectContaining({ name: "BlackRock" }), x: 0 });
33
+ expect(tiles[1]!.y).toBeGreaterThan(0);
34
+ expect(tiles[0]!.height).toBeLessThan(40);
35
+ expect(tiles[1]!.height).toBeLessThan(40);
36
+ expect(tiles[0]!.width).toBe(tiles[1]!.width);
37
+
38
+ const rects = buildTreemapRects(rows, 150, 40, 18 / 8);
39
+ expect(rects[0]).toMatchObject({ row: expect.objectContaining({ name: "Vanguard" }), x: 0, y: 0 });
40
+ expect(rects[1]).toMatchObject({ row: expect.objectContaining({ name: "BlackRock" }), x: 0 });
41
+ expect(rects[1]!.y).toBeGreaterThan(0);
42
+ expect(rects[0]!.height).toBeLessThan(40);
43
+ expect(rects[1]!.height).toBeLessThan(40);
44
+ expect(rects[0]!.width).toBeCloseTo(rects[1]!.width, 8);
45
+ });