gloomberb 0.9.7 → 0.9.9

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 (276) hide show
  1. package/package.json +1 -1
  2. package/src/api-client/auth.ts +20 -0
  3. package/src/api-client/index.test.ts +3 -3
  4. package/src/api-client/index.ts +48 -6
  5. package/src/api-client/request.test.ts +63 -0
  6. package/src/api-client/request.ts +53 -5
  7. package/src/api-client/types.ts +45 -2
  8. package/src/app/pane-runtime/layout-placement.ts +0 -1
  9. package/src/app/runtime/desktop-deeplink.test.ts +87 -0
  10. package/src/app/runtime/desktop-deeplink.ts +496 -0
  11. package/src/app.tsx +34 -0
  12. package/src/brokers/account-cache.ts +2 -10
  13. package/src/brokers/broker-portfolio-sync.ts +0 -1
  14. package/src/brokers/broker-ticker-sync.ts +20 -16
  15. package/src/brokers/remote-broker-adapter.ts +3 -0
  16. package/src/capabilities/registry.test.ts +25 -0
  17. package/src/capabilities/registry.ts +22 -5
  18. package/src/cli/commands/automation.ts +1 -3
  19. package/src/cli/commands/command-utils.ts +0 -7
  20. package/src/cli/commands/overview.ts +2 -2
  21. package/src/cli/commands/plugins.ts +34 -17
  22. package/src/cli/index.ts +2 -2
  23. package/src/cli.test.ts +0 -157
  24. package/src/components/chart/bar-chart-renderer.test.ts +87 -12
  25. package/src/components/chart/bar-chart-renderer.ts +111 -55
  26. package/src/components/chart/comparison/renderer.ts +0 -1
  27. package/src/components/chart/comparison-stock-chart/render-output.ts +3 -8
  28. package/src/components/chart/comparison-stock-chart/view.tsx +0 -2
  29. package/src/components/chart/core/index.test.ts +0 -24
  30. package/src/components/chart/core/renderer.ts +0 -4
  31. package/src/components/chart/core/resolution.test.ts +0 -17
  32. package/src/components/chart/core/resolution.ts +0 -26
  33. package/src/components/chart/core/scroll.test.ts +0 -6
  34. package/src/components/chart/core/scroll.ts +0 -22
  35. package/src/components/chart/core/viewport.test.ts +2 -23
  36. package/src/components/chart/core/viewport.ts +0 -27
  37. package/src/components/chart/market-session.ts +1 -1
  38. package/src/components/chart/native/surface/hooks.ts +10 -3
  39. package/src/components/chart/static/bar-chart-surface.tsx +104 -20
  40. package/src/components/chart/stock-chart/data-runtime.ts +0 -1
  41. package/src/components/chart/stock-chart/footer.test.ts +65 -0
  42. package/src/components/chart/stock-chart/footer.ts +68 -13
  43. package/src/components/chart/stock-chart/resolved.tsx +18 -4
  44. package/src/components/chart/stock-chart/viewport/effects.ts +0 -3
  45. package/src/components/command-bar/helpers.test.ts +1 -80
  46. package/src/components/command-bar/helpers.ts +1 -36
  47. package/src/components/command-bar/pane-settings.ts +1 -1
  48. package/src/components/command-bar/pane-templates/items.ts +11 -3
  49. package/src/components/command-bar/panel/runtime.ts +0 -2
  50. package/src/components/command-bar/routes/root/results.ts +1 -1
  51. package/src/components/command-bar/routes/ticker-search/results.ts +0 -11
  52. package/src/components/command-bar/surface/index.test.tsx +52 -16
  53. package/src/components/command-bar/surface/index.tsx +0 -1
  54. package/src/components/command-bar/workflow/ops.ts +81 -20
  55. package/src/components/feed-data-table/stack-view.test.tsx +36 -130
  56. package/src/components/feed-data-table/stack-view.tsx +0 -1
  57. package/src/components/index.ts +1 -0
  58. package/src/components/layout/shell/index.test.tsx +2 -113
  59. package/src/components/layout/shell/index.tsx +0 -1
  60. package/src/components/layout/shell/menu.ts +0 -1
  61. package/src/components/layout/shell/terminal-pointer-runtime.ts +0 -1
  62. package/src/components/onboarding/onboarding-wizard.tsx +3 -3
  63. package/src/components/onboarding/wizard-keyboard.ts +2 -23
  64. package/src/components/price-selector-dialog.tsx +3 -3
  65. package/src/components/ticker/badge/text.tsx +0 -1
  66. package/src/components/toggle-list.tsx +29 -73
  67. package/src/components/ui/button.tsx +4 -1
  68. package/src/components/ui/checkbox.tsx +76 -0
  69. package/src/components/ui/choice-dialog.tsx +15 -3
  70. package/src/components/ui/frame.tsx +1 -1
  71. package/src/components/ui/index.ts +2 -0
  72. package/src/components/ui/message-composer.tsx +4 -0
  73. package/src/components/ui/native-select.tsx +4 -2
  74. package/src/components/ui/page-stack-view.tsx +1 -1
  75. package/src/components/ui/ui.test.tsx +0 -78
  76. package/src/core/state/app/layout-reducer.ts +0 -1
  77. package/src/data/config/layout.ts +3 -3
  78. package/src/data/resource-store.ts +6 -0
  79. package/src/data/sqlite/cache.test.ts +5 -0
  80. package/src/data/ticker-store.ts +0 -7
  81. package/src/market-data/coordinator/auxiliary.ts +1 -44
  82. package/src/market-data/coordinator/entries.ts +0 -1
  83. package/src/market-data/coordinator/events.test.ts +49 -0
  84. package/src/market-data/coordinator/events.ts +25 -4
  85. package/src/market-data/coordinator/index.test.ts +223 -10
  86. package/src/market-data/coordinator/index.ts +41 -21
  87. package/src/market-data/coordinator/quotes.ts +13 -11
  88. package/src/market-data/coordinator/subscriptions.test.ts +1 -0
  89. package/src/market-data/hooks.tsx +17 -13
  90. package/src/market-data/market/freshness.ts +58 -1
  91. package/src/market-data/market/status.ts +19 -1
  92. package/src/market-data/query-store.ts +0 -4
  93. package/src/market-data/quotes/freshness.test.ts +49 -1
  94. package/src/market-data/quotes/freshness.ts +8 -0
  95. package/src/market-data/quotes/resolution.test.ts +185 -0
  96. package/src/market-data/quotes/resolution.ts +96 -10
  97. package/src/market-data/request-types.test.ts +61 -1
  98. package/src/market-data/request-types.ts +42 -8
  99. package/src/market-data/result-types.ts +0 -11
  100. package/src/market-data/selectors.ts +1 -5
  101. package/src/news/aggregator.ts +5 -1
  102. package/src/pane-settings.test.ts +0 -18
  103. package/src/plugins/builtin/account-management/footer.ts +2 -5
  104. package/src/plugins/builtin/account-management/form-components.tsx +175 -59
  105. package/src/plugins/builtin/account-management/keyboard.ts +32 -2
  106. package/src/plugins/builtin/account-management/model.test.ts +99 -4
  107. package/src/plugins/builtin/account-management/model.ts +259 -23
  108. package/src/plugins/builtin/account-management/pane.tsx +784 -155
  109. package/src/plugins/builtin/broker-manager/index.test.tsx +0 -32
  110. package/src/plugins/builtin/buildout/cells.tsx +1 -1
  111. package/src/plugins/builtin/buildout/detail/satellite-images.tsx +1 -1
  112. package/src/plugins/builtin/buildout/detail/ui.tsx +0 -1
  113. package/src/plugins/builtin/buildout/format.ts +2 -11
  114. package/src/plugins/builtin/chat/chat.test.tsx +127 -1
  115. package/src/plugins/builtin/chat/content/channel-navigation.ts +1 -21
  116. package/src/plugins/builtin/chat/content/composer-cursor.ts +29 -0
  117. package/src/plugins/builtin/chat/content/composer-runtime.ts +22 -24
  118. package/src/plugins/builtin/chat/content/composer.tsx +84 -1
  119. package/src/plugins/builtin/chat/content/index.tsx +153 -16
  120. package/src/plugins/builtin/chat/content/layout-metrics.ts +10 -1
  121. package/src/plugins/builtin/chat/content/mentions.test.ts +74 -0
  122. package/src/plugins/builtin/chat/content/mentions.ts +88 -0
  123. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +264 -0
  124. package/src/plugins/builtin/chat/content/shortcuts.ts +47 -1
  125. package/src/plugins/builtin/chat/content/snapshot.ts +7 -0
  126. package/src/plugins/builtin/chat/content/transcript.tsx +0 -5
  127. package/src/plugins/builtin/chat/controller/index.ts +0 -10
  128. package/src/plugins/builtin/chat/controller.test.ts +5 -5
  129. package/src/plugins/builtin/chat/message/profile-popover.test.tsx +36 -0
  130. package/src/plugins/builtin/chat/message/profile-popover.tsx +124 -20
  131. package/src/plugins/builtin/chat/sidebar.test.tsx +83 -6
  132. package/src/plugins/builtin/chat/sidebar.tsx +26 -2
  133. package/src/plugins/builtin/chat/test-harness.tsx +22 -12
  134. package/src/plugins/builtin/cloud/plugin.tsx +8 -1
  135. package/src/plugins/builtin/cloud-tweets/pane.tsx +0 -1
  136. package/src/plugins/builtin/congress-trades/model.ts +2 -7
  137. package/src/plugins/builtin/congress-trades/pane.tsx +0 -1
  138. package/src/plugins/builtin/correlation/index.tsx +4 -1
  139. package/src/plugins/builtin/correlation/relationship/controls.tsx +8 -2
  140. package/src/plugins/builtin/correlation/relationship/model.ts +22 -2
  141. package/src/plugins/builtin/correlation/relationship/pane.tsx +9 -3
  142. package/src/plugins/builtin/earnings/index.tsx +0 -9
  143. package/src/plugins/builtin/econ/index.tsx +1 -5
  144. package/src/plugins/builtin/help/index.tsx +2 -2
  145. package/src/plugins/builtin/holders/pane.tsx +0 -5
  146. package/src/plugins/builtin/insider/index.tsx +1 -8
  147. package/src/plugins/builtin/kelly-sizer/index.test.tsx +0 -26
  148. package/src/plugins/builtin/kelly-sizer/index.tsx +0 -2
  149. package/src/plugins/builtin/market-movers/index.tsx +0 -2
  150. package/src/plugins/builtin/news/index.tsx +0 -1
  151. package/src/plugins/builtin/news/wire/news/table.tsx +0 -11
  152. package/src/plugins/builtin/options/view.test.tsx +1 -38
  153. package/src/plugins/builtin/plugin-groups.ts +17 -3
  154. package/src/plugins/builtin/portfolio-list/column-values.ts +15 -8
  155. package/src/plugins/builtin/portfolio-list/index.test.ts +52 -25
  156. package/src/plugins/builtin/portfolio-list/metrics.test.ts +44 -7
  157. package/src/plugins/builtin/portfolio-list/pane/data.test.ts +54 -0
  158. package/src/plugins/builtin/portfolio-list/pane/data.ts +50 -3
  159. package/src/plugins/builtin/portfolio-list/pane/index.test.tsx +314 -16
  160. package/src/plugins/builtin/portfolio-list/pane/index.tsx +11 -4
  161. package/src/plugins/builtin/portfolio-list/pane/streaming.ts +83 -24
  162. package/src/plugins/builtin/sec/filing-documents.ts +0 -11
  163. package/src/plugins/builtin/sec/index.tsx +0 -2
  164. package/src/plugins/builtin/substack/article-detail.tsx +1 -1
  165. package/src/plugins/builtin/thirteenf/api.ts +0 -15
  166. package/src/plugins/builtin/thirteenf/pane.tsx +1 -3
  167. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/content.tsx +42 -6
  168. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/model.ts +415 -64
  169. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/settings.ts +23 -2
  170. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/types.ts +8 -1
  171. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph.tsx +3 -1
  172. package/src/plugins/builtin/ticker-detail/data-panes.test.ts +95 -1
  173. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +0 -7
  174. package/src/plugins/builtin/ticker-detail/index.tsx +2 -0
  175. package/src/plugins/builtin/ticker-detail/quote-monitor/index.test.tsx +5 -34
  176. package/src/plugins/builtin/world-indices/index.tsx +0 -4
  177. package/src/plugins/builtin/world-indices/model.ts +0 -7
  178. package/src/plugins/ibkr/account-cache.ts +1 -1
  179. package/src/plugins/ibkr/gateway/market-data.ts +12 -5
  180. package/src/plugins/ibkr/gateway/service/index.test.ts +31 -0
  181. package/src/plugins/ibkr/portfolio-performance.ts +1 -1
  182. package/src/plugins/ibkr/trade/utils.ts +1 -7
  183. package/src/plugins/loader.ts +2 -1
  184. package/src/plugins/prediction-markets/detail.test.tsx +0 -34
  185. package/src/plugins/registry/context.ts +15 -0
  186. package/src/plugins/registry/contributions.ts +22 -11
  187. package/src/plugins/registry/index.test.ts +44 -0
  188. package/src/plugins/registry/index.ts +75 -26
  189. package/src/remote/controller.ts +1 -7
  190. package/src/remote/json-patch.ts +15 -1
  191. package/src/remote/server.ts +16 -6
  192. package/src/renderers/electrobun/bun/application-menu/index.ts +1 -1
  193. package/src/renderers/electrobun/bun/core-capabilities.ts +2 -0
  194. package/src/renderers/electrobun/bun/desktop/http-fetch.test.ts +14 -0
  195. package/src/renderers/electrobun/bun/desktop/http-fetch.ts +8 -0
  196. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -1
  197. package/src/renderers/electrobun/bun/index.ts +42 -0
  198. package/src/renderers/electrobun/shared/protocol.ts +5 -0
  199. package/src/renderers/electrobun/view/backend-rpc.ts +29 -0
  200. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -0
  201. package/src/renderers/electrobun/view/data-table/dom.ts +0 -1
  202. package/src/renderers/electrobun/view/desktop/controls.tsx +135 -4
  203. package/src/renderers/electrobun/view/desktop/list-view.tsx +38 -25
  204. package/src/renderers/electrobun/view/desktop-deeplink-bridge.ts +10 -0
  205. package/src/renderers/electrobun/view/host/box.tsx +0 -2
  206. package/src/renderers/electrobun/view/host/mouse.ts +0 -11
  207. package/src/renderers/electrobun/view/host/scroll-box.tsx +1 -2
  208. package/src/renderers/electrobun/view/host/text.tsx +1 -2
  209. package/src/renderers/electrobun/view/http-fetch.ts +15 -2
  210. package/src/renderers/electrobun/view/main.tsx +3 -0
  211. package/src/renderers/electrobun/view/remote/asset-data-client.ts +9 -18
  212. package/src/renderers/electrobun/view/remote/capability-invoker.test.ts +50 -0
  213. package/src/renderers/electrobun/view/remote/capability-invoker.ts +36 -0
  214. package/src/renderers/electrobun/view/ui-host.tsx +2 -0
  215. package/src/renderers/opentui/host.tsx +11 -3
  216. package/src/renderers/opentui/input-host.tsx +5 -1
  217. package/src/sources/gloomberb-cloud/index.test.ts +0 -72
  218. package/src/sources/gloomberb-cloud/index.ts +1 -30
  219. package/src/sources/gloomberb-cloud/news.ts +0 -12
  220. package/src/sources/provider-router/batches.ts +11 -3
  221. package/src/sources/provider-router/cached-financials.test.ts +123 -0
  222. package/src/sources/provider-router/financial-routes.ts +77 -5
  223. package/src/sources/provider-router/financials.test.ts +49 -0
  224. package/src/sources/provider-router/financials.ts +40 -3
  225. package/src/sources/provider-router/index.test.ts +314 -0
  226. package/src/sources/provider-router/primary.ts +17 -3
  227. package/src/sources/provider-router/search.ts +5 -0
  228. package/src/sources/provider-router/streaming.ts +6 -1
  229. package/src/sources/provider-router/supplemental.ts +4 -3
  230. package/src/sources/sec-edgar.test.ts +42 -0
  231. package/src/sources/sec-edgar.ts +1 -2
  232. package/src/sources/yahoo-finance/history.ts +5 -1
  233. package/src/sources/yahoo-finance/mappers.test.ts +29 -0
  234. package/src/sources/yahoo-finance/mappers.ts +2 -1
  235. package/src/sources/yahoo-finance/options.ts +59 -14
  236. package/src/sources/yahoo-finance/quote-summary.ts +4 -1
  237. package/src/sources/yahoo-finance/requests.ts +2 -1
  238. package/src/sources/yahoo-finance/snapshots.ts +13 -2
  239. package/src/sources/yahoo-finance.test.ts +40 -21
  240. package/src/sources/yahoo-finance.ts +19 -10
  241. package/src/state/app/context/index.test.tsx +0 -81
  242. package/src/state/app/context/selector.test.tsx +6 -2
  243. package/src/state/hooks/quote-streaming.test.ts +14 -1
  244. package/src/state/hooks/quote-streaming.ts +5 -4
  245. package/src/sync/controller.test.ts +31 -0
  246. package/src/sync/controller.ts +300 -0
  247. package/src/sync/core-contributors.test.ts +251 -0
  248. package/src/sync/core-contributors.ts +400 -0
  249. package/src/sync/profile-analytics.ts +39 -0
  250. package/src/sync/react.ts +53 -0
  251. package/src/sync/types.ts +77 -0
  252. package/src/test-support/data-provider.ts +0 -1
  253. package/src/types/desktop-deeplink.ts +7 -0
  254. package/src/types/plugin.ts +3 -0
  255. package/src/ui/host.tsx +15 -1
  256. package/src/ui/index.tsx +2 -0
  257. package/src/updater.test.ts +0 -21
  258. package/src/updater.ts +20 -16
  259. package/src/utils/async-deadline.ts +26 -0
  260. package/src/utils/exchanges.test.ts +11 -1
  261. package/src/utils/exchanges.ts +5 -1
  262. package/src/utils/selection-clipboard.ts +2 -2
  263. package/src/version.ts +1 -1
  264. package/src/components/layout/pane/header.test.tsx +0 -39
  265. package/src/plugins/builtin/chat/channels.test.ts +0 -28
  266. package/src/plugins/builtin/chat/index.tsx +0 -4
  267. package/src/plugins/builtin/cloud-tweets/index.ts +0 -1
  268. package/src/plugins/builtin/earnings/index.test.ts +0 -12
  269. package/src/plugins/builtin/help/index.test.tsx +0 -242
  270. package/src/plugins/builtin/substack/article-detail.test.tsx +0 -10
  271. package/src/renderers/electrobun/bun/application-menu/index.test.ts +0 -21
  272. package/src/renderers/electrobun/bun/window/focus.test.ts +0 -35
  273. package/src/renderers/electrobun/view/host/mouse.test.ts +0 -37
  274. package/src/renderers/electrobun/view/host/text.test.ts +0 -22
  275. package/src/renderers/opentui/dialog-host.test.tsx +0 -44
  276. /package/src/{plugins/ibkr → utils}/hash.ts +0 -0
@@ -3,7 +3,7 @@ import type { PluginRegistry } from "../../plugins/registry";
3
3
  import { fuzzyFilter } from "../../utils/fuzzy-search";
4
4
  import type { ResultItem } from "./list/model";
5
5
  import type { CommandBarRoute, CommandBarWorkflowRoute } from "./workflow/types";
6
- import { summarizePaneSettingValue } from "./helpers";
6
+ import { summarizePaneSettingValue } from "../pane-settings-dialog/value";
7
7
 
8
8
  type NotifyFn = (body: string, options?: { type?: "info" | "success" | "error" }) => void;
9
9
 
@@ -139,6 +139,14 @@ export function buildPaneTemplateItem(options: {
139
139
  .join(" ")
140
140
  : null;
141
141
  const arg = options.createOptions?.arg;
142
+ const searchText = [
143
+ options.template.keywords?.join(" ") || "",
144
+ displayLabel,
145
+ options.template.label,
146
+ options.template.paneId,
147
+ shortcutLabel || "",
148
+ pluginName || "",
149
+ ].filter(Boolean).join(" ");
142
150
 
143
151
  const action = () => {
144
152
  if (
@@ -163,7 +171,7 @@ export function buildPaneTemplateItem(options: {
163
171
  kind: "action",
164
172
  right: options.showShortcut ? options.template.shortcut?.prefix : undefined,
165
173
  shortcutQuery: options.template.shortcut?.prefix,
166
- searchText: `${displayLabel} ${options.template.label} ${options.template.paneId} ${options.template.keywords?.join(" ") || ""} ${shortcutLabel || ""} ${pluginName || ""}`,
174
+ searchText,
167
175
  action,
168
176
  };
169
177
  }
@@ -187,7 +195,7 @@ export function buildPaneShortcutItems(options: {
187
195
  }));
188
196
 
189
197
  return options.filterQuery
190
- ? fuzzyFilter(items, options.filterQuery, (item) => `${item.label} ${item.detail} ${item.searchText || ""} ${item.right || ""}`)
198
+ ? fuzzyFilter(items, options.filterQuery, (item) => `${item.label} ${item.searchText || ""} ${item.detail} ${item.right || ""}`)
191
199
  : items;
192
200
  }
193
201
 
@@ -201,6 +209,6 @@ export function buildNonShortcutPaneTemplateItems(options: {
201
209
  .map((template) => options.createItem(template, { category: "Panes" }));
202
210
 
203
211
  return options.filterQuery
204
- ? fuzzyFilter(items, options.filterQuery, (item) => `${item.label} ${item.detail} ${item.searchText || ""} ${item.right || ""}`)
212
+ ? fuzzyFilter(items, options.filterQuery, (item) => `${item.label} ${item.searchText || ""} ${item.detail} ${item.right || ""}`)
205
213
  : items;
206
214
  }
@@ -37,7 +37,6 @@ interface CommandBarPanelRuntimeOptions {
37
37
  applyThemePreview: (themeId: string | null) => void;
38
38
  cellHeightPx: number;
39
39
  cellWidthPx: number;
40
- clearThemePreview: (themeId: string | null | undefined) => void;
41
40
  closeAll: (options?: { revertThemePreview?: boolean }) => void;
42
41
  commitTheme: (themeId: string) => void;
43
42
  committedThemeId: string;
@@ -92,7 +91,6 @@ export function useCommandBarPanelRuntime({
92
91
  applyThemePreview,
93
92
  cellHeightPx,
94
93
  cellWidthPx,
95
- clearThemePreview,
96
94
  closeAll,
97
95
  commitTheme,
98
96
  committedThemeId,
@@ -196,7 +196,7 @@ export function buildRootResultModel(options: RootResultModelOptions): RootResul
196
196
  ...tickerActionItems(),
197
197
  ...pluginCommandItems(),
198
198
  ];
199
- items.push(...fuzzyFilter(allItems, rootQuery, (item) => `${item.label} ${item.detail} ${item.searchText || ""} ${item.right || ""}`));
199
+ items.push(...fuzzyFilter(allItems, rootQuery, (item) => `${item.label} ${item.searchText || ""} ${item.detail} ${item.right || ""}`));
200
200
  }
201
201
 
202
202
  return { items, initialIdx };
@@ -24,17 +24,6 @@ export function normalizeCommandTickerSearchText(value: string): string {
24
24
  return value.trim().toUpperCase().replace(/[^A-Z0-9]+/g, "");
25
25
  }
26
26
 
27
- export function isLikelyPlainTickerSearch(query: string): boolean {
28
- const trimmed = query.trim();
29
- if (!trimmed || /\s/.test(trimmed)) return false;
30
- if (!/^[A-Za-z0-9.^=\-/]+$/.test(trimmed)) return false;
31
-
32
- const compact = normalizeCommandTickerSearchText(trimmed);
33
- if (compact.length < 2) return false;
34
- if (compact.length <= 6) return true;
35
- return /[.^=\-/]/.test(trimmed) && compact.length <= 10;
36
- }
37
-
38
27
  function isExactTickerResultMatch(item: ResultItem, query: string): boolean {
39
28
  if (item.kind !== "ticker" && item.kind !== "search") return false;
40
29
  const normalizedQuery = normalizeCommandTickerSearchText(query);
@@ -6,6 +6,7 @@ import type { CommandDef, CommandShortcutArgContext, PaneTemplateCreateOptions,
6
6
  import {
7
7
  CommandBarHarness,
8
8
  createCommandBarTestControls,
9
+ emitKeypress,
9
10
  expectSingleBackControl,
10
11
  makeDataProvider,
11
12
  makeTicker,
@@ -26,6 +27,11 @@ type MutableCommandRegistry = {
26
27
  commands: ReadonlyMap<string, CommandDef>;
27
28
  };
28
29
 
30
+ type MutablePaneRegistry = {
31
+ panes: ReadonlyMap<string, unknown>;
32
+ paneTemplates: ReadonlyMap<string, unknown>;
33
+ };
34
+
29
35
  const DEFAULT_ALERT_OPTIONS = [
30
36
  { label: "Above", value: "above" },
31
37
  { label: "Below", value: "below" },
@@ -70,23 +76,11 @@ function registerAlertCommand(
70
76
  });
71
77
  }
72
78
 
73
- describe("CommandBar", () => {
74
- test("renders the default layout with opencode-style chrome", async () => {
75
- testSetup = await testRender(<CommandBarHarness query="" selectedTicker="AAPL" />, {
76
- width: 80,
77
- height: 24,
78
- });
79
-
80
- await testSetup.renderOnce();
81
-
82
- const frame = testSetup.captureCharFrame();
83
- expect(frame).toContain("Commands");
84
- expect(frame).not.toContain("Tickers");
85
- expect(frame).toContain("Panes");
86
- expect(frame).toContain("Portfolio");
87
- expect(frame).toContain("Help");
88
- });
79
+ function mutablePaneRegistryMap(map: ReadonlyMap<string, unknown>): Map<string, unknown> {
80
+ return map as Map<string, unknown>;
81
+ }
89
82
 
83
+ describe("CommandBar", () => {
90
84
  test("keeps generic command filtering separate from ticker search", async () => {
91
85
  const searchQueries: string[] = [];
92
86
  testSetup = await testRender(<CommandBarHarness
@@ -129,6 +123,48 @@ describe("CommandBar", () => {
129
123
  expect(testSetup.captureCharFrame()).not.toContain("Commands");
130
124
  });
131
125
 
126
+ test("shows one account management result when searching profile", async () => {
127
+ const created: Array<{ templateId: string; options?: PaneTemplateCreateOptions }> = [];
128
+
129
+ testSetup = await testRender(<CommandBarHarness
130
+ query="profile"
131
+ live
132
+ configurePluginRegistry={(pluginRegistry) => {
133
+ const registry = pluginRegistry as MutablePaneRegistry;
134
+ mutablePaneRegistryMap(registry.panes).set("account-management", {
135
+ id: "account-management",
136
+ name: "Account Management",
137
+ component: () => null,
138
+ defaultPosition: "right",
139
+ defaultMode: "floating",
140
+ });
141
+ mutablePaneRegistryMap(registry.paneTemplates).set("account-management-pane", {
142
+ id: "account-management-pane",
143
+ paneId: "account-management",
144
+ label: "Account Management",
145
+ description: "Edit your Gloom Cloud profile, password, and public portfolio sharing settings",
146
+ keywords: ["account", "profile", "cloud", "acm", "password", "settings"],
147
+ shortcut: { prefix: "ACM" },
148
+ });
149
+ pluginRegistry.createPaneFromTemplateAsyncFn = async (templateId, options) => {
150
+ created.push({ templateId, options });
151
+ };
152
+ }}
153
+ />, {
154
+ width: 80,
155
+ height: 24,
156
+ });
157
+
158
+ await testSetup.renderOnce();
159
+ const frame = await waitForFrameToContain("Account Management");
160
+ expect(frame).not.toMatch(/\n\s*Profile\s*(?:\n|$)/);
161
+ expect(frame.indexOf("Account Management")).toBeLessThan(frame.indexOf("Add Broker Account"));
162
+
163
+ await emitKeypress(testSetup, { name: "return", sequence: "\r" });
164
+
165
+ expect(created).toEqual([{ templateId: "account-management-pane", options: undefined }]);
166
+ });
167
+
132
168
  test("shows theme picker rows and commits a filtered light theme", async () => {
133
169
  testSetup = await testRender(<CommandBarHarness query="TH light" live />, {
134
170
  width: 80,
@@ -291,7 +291,6 @@ export function CommandBar({
291
291
  applyThemePreview,
292
292
  cellHeightPx,
293
293
  cellWidthPx,
294
- clearThemePreview,
295
294
  closeAll,
296
295
  commitTheme,
297
296
  committedThemeId: state.config.theme,
@@ -6,11 +6,22 @@ import { formatTickerListInput } from "../../../tickers/list";
6
6
  import { updatePaneInstance, setPaneSettings } from "../../../pane-settings";
7
7
  import { TICKER_RESEARCH_PANE_ID } from "../../../types/config";
8
8
  import { cleanPortfolioPaneSettings, resolvePortfolioPaneCollectionId } from "../../../plugins/builtin/portfolio-list/settings";
9
+ import {
10
+ DEFAULT_RELATIONSHIP_SECOND_SYMBOL,
11
+ RELATIONSHIP_GRAPH_PANE_ID,
12
+ buildRelationshipGraphPaneTitle,
13
+ } from "../../../plugins/builtin/correlation/relationship/model";
9
14
  import {
10
15
  buildComparisonChartPaneTitle,
11
16
  COMPARISON_CHART_PANE_ID,
12
17
  MIN_COMPARISON_CHART_SYMBOLS,
13
18
  } from "../../../plugins/builtin/comparison-chart";
19
+ import {
20
+ FUNDAMENTAL_GRAPH_PANE_ID,
21
+ graphKindFromSettings,
22
+ graphShortcutForKind,
23
+ graphTemplateTitle,
24
+ } from "../../../plugins/builtin/ticker-detail/data-panes/fundamental-graph/settings";
14
25
  import { buildQuoteMonitorPaneTitle } from "../../../plugins/builtin/ticker-detail/settings";
15
26
  import { getPaneTemplateDisplayLabel } from "../pane-templates/items";
16
27
  import {
@@ -54,6 +65,30 @@ interface ApplyPaneSettingDeps extends SharedWorkflowDeps {
54
65
  persistLayout: (layout: LayoutConfig, options?: { pushHistory?: boolean }) => void;
55
66
  }
56
67
 
68
+ function updateTickerListPane(
69
+ layout: LayoutConfig,
70
+ targetId: string,
71
+ options: {
72
+ title: string;
73
+ symbols: readonly string[];
74
+ primarySymbol?: string;
75
+ settings?: Record<string, unknown>;
76
+ },
77
+ ): LayoutConfig {
78
+ const symbols = [...options.symbols];
79
+ return updatePaneInstance(layout, targetId, (instance) => ({
80
+ ...instance,
81
+ title: options.title,
82
+ ...(options.primarySymbol ? { binding: { kind: "fixed", symbol: options.primarySymbol } as PaneBinding } : {}),
83
+ settings: {
84
+ ...(instance.settings ?? {}),
85
+ symbols,
86
+ symbolsText: formatTickerListInput(symbols),
87
+ ...(options.settings ?? {}),
88
+ },
89
+ }));
90
+ }
91
+
57
92
  async function resolvePaneTemplateOptions(
58
93
  template: PaneTemplateDef,
59
94
  options: PaneTemplateCreateOptions | undefined,
@@ -180,19 +215,50 @@ export async function applyPaneSettingFieldValue(
180
215
  const rawQuery = typeof value === "string" ? value.trim() : "";
181
216
  const symbols = await resolveTickerListInput(rawQuery, null, deps);
182
217
  const primarySymbol = symbols[0]!;
183
- const symbolsText = formatTickerListInput(symbols);
184
-
185
- const nextLayout = updatePaneInstance(state.config.layout, targetId, (instance) => ({
186
- ...instance,
218
+ const nextLayout = updateTickerListPane(state.config.layout, targetId, {
187
219
  title: buildQuoteMonitorPaneTitle(symbols),
188
- binding: { kind: "fixed", symbol: primarySymbol },
189
- settings: {
190
- ...(instance.settings ?? {}),
191
- symbol: primarySymbol,
192
- symbols,
193
- symbolsText,
194
- },
195
- }));
220
+ symbols,
221
+ primarySymbol,
222
+ settings: { symbol: primarySymbol },
223
+ });
224
+ deps.persistLayout(nextLayout, { pushHistory: shouldPushHistory });
225
+ return;
226
+ }
227
+
228
+ if (descriptor.pane.paneId === FUNDAMENTAL_GRAPH_PANE_ID && field.key === "symbolsText") {
229
+ const rawInput = typeof value === "string" ? value : "";
230
+ const symbols = await resolveTickerListInput(
231
+ rawInput,
232
+ descriptor.context.activeCollectionId,
233
+ deps,
234
+ );
235
+ const primarySymbol = symbols[0]!;
236
+ const chartKind = graphKindFromSettings(descriptor.context.settings, "fundamental");
237
+ const nextLayout = updateTickerListPane(state.config.layout, targetId, {
238
+ title: graphTemplateTitle(graphShortcutForKind(chartKind), symbols),
239
+ symbols,
240
+ primarySymbol,
241
+ });
242
+ deps.persistLayout(nextLayout, { pushHistory: shouldPushHistory });
243
+ return;
244
+ }
245
+
246
+ if (descriptor.pane.paneId === RELATIONSHIP_GRAPH_PANE_ID && field.key === "symbolsText") {
247
+ const rawInput = typeof value === "string" ? value : "";
248
+ const symbols = await resolveTickerListInput(
249
+ rawInput,
250
+ descriptor.context.activeCollectionId,
251
+ deps,
252
+ );
253
+ if (symbols.length > 2) {
254
+ throw new Error("Enter one or two tickers.");
255
+ }
256
+ const pair: [string, string] = [symbols[0]!, symbols[1] ?? DEFAULT_RELATIONSHIP_SECOND_SYMBOL];
257
+ const nextLayout = updateTickerListPane(state.config.layout, targetId, {
258
+ title: buildRelationshipGraphPaneTitle(pair),
259
+ symbols: pair,
260
+ primarySymbol: pair[0],
261
+ });
196
262
  deps.persistLayout(nextLayout, { pushHistory: shouldPushHistory });
197
263
  return;
198
264
  }
@@ -207,15 +273,10 @@ export async function applyPaneSettingFieldValue(
207
273
  if (symbols.length < MIN_COMPARISON_CHART_SYMBOLS) {
208
274
  throw new Error(`Enter at least ${MIN_COMPARISON_CHART_SYMBOLS} tickers.`);
209
275
  }
210
- const nextLayout = updatePaneInstance(state.config.layout, targetId, (instance) => ({
211
- ...instance,
276
+ const nextLayout = updateTickerListPane(state.config.layout, targetId, {
212
277
  title: buildComparisonChartPaneTitle(symbols),
213
- settings: {
214
- ...(instance.settings ?? {}),
215
- symbols,
216
- symbolsText: formatTickerListInput(symbols),
217
- },
218
- }));
278
+ symbols,
279
+ });
219
280
  deps.persistLayout(nextLayout, { pushHistory: shouldPushHistory });
220
281
  return;
221
282
  }
@@ -1,148 +1,54 @@
1
- import { afterEach, describe, expect, test } from "bun:test";
2
- import { act, useState } from "react";
1
+ import { afterEach, expect, test } from "bun:test";
2
+ import { act } from "react";
3
3
  import { testRender } from "../../renderers/opentui/test-utils";
4
4
  import { AppContext, PaneInstanceProvider, createInitialState } from "../../state/app/context";
5
5
  import { createDefaultConfig } from "../../types/config";
6
- import {
7
- FeedDataTableStackView,
8
- type FeedDataTableItem,
9
- } from "./stack-view";
10
6
  import { Box, Text } from "../../ui";
7
+ import { FeedDataTableStackView } from "./stack-view";
11
8
 
12
9
  let testSetup: Awaited<ReturnType<typeof testRender>> | undefined;
13
- let rootKeyHits = 0;
14
-
15
- const items: FeedDataTableItem[] = [
16
- {
17
- id: "1",
18
- eyebrow: "Reuters",
19
- title:
20
- "Long headline with enough words to prove the table truncates cleanly",
21
- timestamp: new Date("2024-08-01T12:00:00Z"),
22
- preview: "Preview text with a little more color.",
23
- detailTitle: "Long headline with enough words",
24
- detailMeta: ["Reuters", "Published Aug 1, 2024"],
25
- detailBody:
26
- "This body should stay readable when the pane opens the detail page.",
27
- detailNote: "https://example.com/story",
28
- },
29
- {
30
- id: "2",
31
- eyebrow: "10-Q",
32
- title: "10-Q filing",
33
- timestamp: new Date("2024-08-02T12:00:00Z"),
34
- preview: "Quarterly report",
35
- detailTitle: "10-Q filing",
36
- detailMeta: ["Filed Aug 2, 2024"],
37
- detailBody: "Quarterly report details.",
38
- },
39
- ];
40
10
 
41
11
  afterEach(async () => {
42
- if (testSetup) {
43
- await act(async () => {
44
- testSetup!.renderer.destroy();
45
- });
46
- testSetup = undefined;
47
- }
48
- rootKeyHits = 0;
12
+ await act(async () => testSetup?.renderer.destroy());
13
+ testSetup = undefined;
49
14
  });
50
15
 
51
- function Harness({
52
- width,
53
- height,
54
- withRootControls = false,
55
- }: {
56
- width: number;
57
- height: number;
58
- withRootControls?: boolean;
59
- }) {
60
- const [selectedIdx, setSelectedIdx] = useState(0);
61
-
62
- const state = createInitialState(
63
- createDefaultConfig("/tmp/gloomberb-detail-data-table-test"),
64
- );
65
-
66
- return (
67
- <AppContext value={{ state, dispatch: () => {} }}>
68
- <PaneInstanceProvider paneId="portfolio-list:main">
69
- <FeedDataTableStackView
70
- width={width}
71
- height={height}
72
- focused
73
- items={items}
74
- selectedIdx={selectedIdx}
75
- onSelect={setSelectedIdx}
76
- rootBefore={withRootControls ? (
77
- <Box height={1}>
78
- <Text>Feed controls</Text>
79
- </Box>
80
- ) : undefined}
81
- onRootKeyDown={withRootControls ? (event) => {
82
- if (event.name !== "f") return false;
83
- rootKeyHits += 1;
84
- return true;
85
- } : undefined}
86
- sourceLabel="Source"
87
- titleLabel="Headline"
88
- />
89
- </PaneInstanceProvider>
90
- </AppContext>
91
- );
92
- }
93
-
94
- async function renderSettled() {
16
+ test("delegates root key handling", async () => {
17
+ let keyHits = 0;
18
+ const state = createInitialState(createDefaultConfig("/tmp/gloomberb-feed-table-test"));
95
19
  await act(async () => {
96
- await testSetup!.renderOnce();
97
- await testSetup!.renderOnce();
20
+ testSetup = await testRender(
21
+ <AppContext value={{ state, dispatch: () => {} }}>
22
+ <PaneInstanceProvider paneId="portfolio-list:main">
23
+ <FeedDataTableStackView
24
+ width={80}
25
+ height={12}
26
+ focused
27
+ items={[]}
28
+ selectedIdx={0}
29
+ onSelect={() => {}}
30
+ rootBefore={<Box height={1}><Text>Feed controls</Text></Box>}
31
+ onRootKeyDown={(event) => {
32
+ if (event.name !== "f") return false;
33
+ keyHits += 1;
34
+ return true;
35
+ }}
36
+ />
37
+ </PaneInstanceProvider>
38
+ </AppContext>,
39
+ { width: 80, height: 12 },
40
+ );
41
+ await testSetup.renderOnce();
98
42
  });
99
- }
100
43
 
101
- async function emitKeypress(event: { name?: string; sequence?: string }) {
102
44
  await act(async () => {
103
- testSetup!.renderer.keyInput.emit("keypress", {
104
- ctrl: false,
105
- meta: false,
106
- option: false,
107
- shift: false,
108
- eventType: "press",
109
- repeated: false,
110
- preventDefault: () => {},
111
- stopPropagation: () => {},
112
- ...event,
113
- } as any);
114
- await testSetup!.renderOnce();
115
- });
116
- }
117
-
118
- describe("FeedDataTableStackView", () => {
119
- test("renders a DataTable list by default", async () => {
120
- testSetup = await testRender(<Harness width={90} height={16} />, {
121
- width: 90,
122
- height: 16,
45
+ (testSetup!.renderer.keyInput as any).emit("keypress", {
46
+ name: "f", sequence: "f", eventType: "press",
47
+ ctrl: false, meta: false, option: false, shift: false, repeated: false,
48
+ preventDefault() {}, stopPropagation() {},
123
49
  });
124
-
125
- await renderSettled();
126
- const frame = testSetup.captureCharFrame();
127
-
128
- expect(frame).toContain("Time");
129
- expect(frame).toContain("Source");
130
- expect(frame).toContain("Headline");
131
- expect(frame).toContain("10-Q filing");
132
- expect(frame).not.toContain("j/k move");
133
- expect(frame).not.toContain("Quarterly report details.");
50
+ await testSetup!.renderOnce();
134
51
  });
135
52
 
136
- test("renders root controls and delegates root key handling", async () => {
137
- testSetup = await testRender(<Harness width={90} height={16} withRootControls />, {
138
- width: 90,
139
- height: 16,
140
- });
141
-
142
- await renderSettled();
143
- expect(testSetup.captureCharFrame()).toContain("Feed controls");
144
-
145
- await emitKeypress({ name: "f", sequence: "f" });
146
- expect(rootKeyHits).toBe(1);
147
- });
53
+ expect(keyHits).toBe(1);
148
54
  });
@@ -21,7 +21,6 @@ export interface FeedDataTableItem {
21
21
  eyebrow?: string;
22
22
  title: string;
23
23
  timestamp?: Date | string | null;
24
- preview?: string | null;
25
24
  detailTitle?: string;
26
25
  detailMeta?: string[];
27
26
  detailBody?: string | null;
@@ -25,6 +25,7 @@ export { PaneFooterScope, usePaneFooter } from "./layout/pane/footer";
25
25
  export type { PaneFooterSegment, PaneHint } from "./layout/pane/footer";
26
26
  export { useExternalLinkFooter } from "./use-external-link-footer";
27
27
  export { Button } from "./ui/button";
28
+ export { Checkbox } from "./ui/checkbox";
28
29
  export { ConfirmDialog } from "./ui/confirm-dialog";
29
30
  export { ChoiceDialog } from "./ui/choice-dialog";
30
31
  export type { ChoiceDialogChoice } from "./ui/choice-dialog";