gloomberb 0.15.6 → 0.15.7

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 (259) hide show
  1. package/README.md +2 -2
  2. package/README.zh-CN.md +1 -1
  3. package/package.json +1 -1
  4. package/src/api-client/data.ts +37 -10
  5. package/src/api-client/estimate-revisions.ts +1 -1
  6. package/src/api-client/futures-curve.ts +1 -1
  7. package/src/api-client/index.ts +6 -0
  8. package/src/api-client/market-discovery.ts +84 -0
  9. package/src/api-client/paths.ts +14 -11
  10. package/src/api-client/request.ts +12 -2
  11. package/src/api-client/tape.ts +2 -2
  12. package/src/api-client/types.ts +2 -0
  13. package/src/app/dialog-bridge.tsx +33 -0
  14. package/src/app/pane-runtime/index.tsx +7 -2
  15. package/src/app/pane-runtime/pane-settings-runtime.tsx +2 -1
  16. package/src/app/pane-runtime/pane-template-runtime.ts +8 -28
  17. package/src/app/pane-runtime/plugin-bindings.ts +34 -12
  18. package/src/app/pane-runtime/ticker-open-runtime.ts +5 -3
  19. package/src/app/runtime/broker-import.ts +1 -1
  20. package/src/app/runtime/desktop-menu.ts +9 -6
  21. package/src/app/runtime/plugin-bindings.ts +7 -1
  22. package/src/app/runtime/ticker-refresh.ts +1 -1
  23. package/src/app.tsx +12 -0
  24. package/src/brokers/directory.ts +11 -2
  25. package/src/brokers/profile-form.ts +2 -1
  26. package/src/brokers/remove-profile.ts +61 -0
  27. package/src/brokers/signed-in/connect.ts +20 -7
  28. package/src/brokers/signed-in/sign-in-dialog.tsx +68 -30
  29. package/src/cli/desktop-pane-shot.ts +2 -2
  30. package/src/cli/history-rows.ts +1 -1
  31. package/src/cli/pane-functions/function-help.ts +3 -3
  32. package/src/cli/ticker-resolution.ts +2 -2
  33. package/src/components/command-bar/action-runtime.ts +7 -67
  34. package/src/components/command-bar/commands/direct/index.ts +9 -5
  35. package/src/components/command-bar/commands/direct/runtime.ts +12 -11
  36. package/src/components/command-bar/commands/plugin/actions.ts +6 -35
  37. package/src/components/command-bar/helpers.ts +3 -31
  38. package/src/components/command-bar/keyboard-handlers.ts +7 -219
  39. package/src/components/command-bar/keyboard-shortcuts.ts +25 -99
  40. package/src/components/command-bar/layout-items/current-layout.ts +2 -1
  41. package/src/components/command-bar/layout-items/focused-pane.ts +2 -1
  42. package/src/components/command-bar/layout-items/types.ts +2 -0
  43. package/src/components/command-bar/layout-items.ts +3 -0
  44. package/src/components/command-bar/list/model.ts +1 -1
  45. package/src/components/command-bar/list/navigation.ts +5 -12
  46. package/src/components/command-bar/pane-actions.ts +17 -14
  47. package/src/components/command-bar/pane-settings.ts +7 -250
  48. package/src/components/command-bar/pane-templates/workflow.ts +6 -13
  49. package/src/components/command-bar/panel/index.tsx +4 -110
  50. package/src/components/command-bar/panel/layout.ts +4 -16
  51. package/src/components/command-bar/panel/runtime.ts +1 -87
  52. package/src/components/command-bar/panel/state.ts +12 -29
  53. package/src/components/command-bar/panel/types.ts +2 -26
  54. package/src/components/command-bar/picker-activation.ts +7 -56
  55. package/src/components/command-bar/routes/root/plugin-install.ts +3 -3
  56. package/src/components/command-bar/routes/root/search-report.ts +2 -1
  57. package/src/components/command-bar/routing/actions.ts +21 -59
  58. package/src/components/command-bar/routing/confirm.ts +15 -88
  59. package/src/components/command-bar/routing/effects.ts +1 -20
  60. package/src/components/command-bar/routing/launch-request.ts +1 -36
  61. package/src/components/command-bar/routing/list-state.ts +5 -36
  62. package/src/components/command-bar/selection-runtime.ts +6 -35
  63. package/src/components/command-bar/surface/environment.ts +10 -6
  64. package/src/components/command-bar/surface/index.tsx +4 -37
  65. package/src/components/command-bar/workflow/alert.ts +23 -17
  66. package/src/components/command-bar/workflow/broker.ts +30 -40
  67. package/src/components/command-bar/workflow/builtin.ts +33 -38
  68. package/src/components/command-bar/workflow/collection-actions.ts +18 -21
  69. package/src/components/command-bar/workflow/collection-ticker.ts +4 -4
  70. package/src/components/command-bar/workflow/coordinator.ts +27 -156
  71. package/src/components/command-bar/workflow/fields.ts +6 -22
  72. package/src/components/command-bar/workflow/submit.ts +28 -43
  73. package/src/components/command-bar/workflow/ticker-listing-picker.ts +12 -9
  74. package/src/components/command-bar/workflow/tickers.ts +2 -2
  75. package/src/components/command-bar/workflow/types.ts +10 -35
  76. package/src/components/feedback-dialog.tsx +1 -13
  77. package/src/components/form-modal/broker-step.tsx +119 -0
  78. package/src/components/form-modal/confirm.tsx +145 -0
  79. package/src/components/form-modal/content.tsx +698 -0
  80. package/src/components/form-modal/deps.ts +77 -0
  81. package/src/components/form-modal/field-row.tsx +318 -0
  82. package/src/components/form-modal/host.tsx +169 -0
  83. package/src/components/form-modal/index.ts +2 -0
  84. package/src/components/form-modal/model.ts +131 -0
  85. package/src/components/form-modal/request.ts +67 -0
  86. package/src/components/form-modal/routes.ts +139 -0
  87. package/src/components/layout/shell/index.tsx +11 -0
  88. package/src/components/layout/status-bar.tsx +2 -1
  89. package/src/components/onboarding/first-run-workspace.ts +1 -1
  90. package/src/components/onboarding/onboarding-frame.tsx +4 -1
  91. package/src/components/onboarding/onboarding-wizard.tsx +1 -1
  92. package/src/components/pane-settings-dialog.tsx +7 -1
  93. package/src/components/pane-template-wizard.tsx +4 -149
  94. package/src/components/sign-in-code-panel.tsx +22 -9
  95. package/src/components/textarea-keys.ts +13 -0
  96. package/src/components/ui/button.tsx +3 -2
  97. package/src/components/ui/confirm-dialog.tsx +62 -10
  98. package/src/components/ui/frame.tsx +7 -2
  99. package/src/components/ui/list-view.tsx +16 -9
  100. package/src/core/app-runtime.ts +1 -1
  101. package/src/core/state/app/types.ts +1 -15
  102. package/src/data/config/home.ts +82 -11
  103. package/src/data/config/store/node.ts +29 -16
  104. package/src/i18n/es.ts +53 -7
  105. package/src/i18n/ja.ts +53 -7
  106. package/src/i18n/ko.ts +53 -7
  107. package/src/i18n/zh-cn.ts +53 -7
  108. package/src/i18n/zh-tw.ts +53 -7
  109. package/src/layout-marketplace/gallery.tsx +11 -3
  110. package/src/market-data/coordinator/fx-legs.ts +1 -1
  111. package/src/market-data/history-session.ts +3 -5
  112. package/src/market-data/market/freshness.ts +1 -1
  113. package/src/market-data/market/status.ts +1 -1
  114. package/src/market-data/quotes/metadata.ts +3 -3
  115. package/src/market-data/quotes/resolution.ts +3 -3
  116. package/src/news/types.ts +1 -1
  117. package/src/plugins/builtin/alerts/alert-engine.ts +2 -0
  118. package/src/plugins/builtin/alerts/cli.ts +2 -2
  119. package/src/plugins/builtin/alerts/types.ts +2 -0
  120. package/src/plugins/builtin/broker-manager/add-flow.tsx +625 -0
  121. package/src/plugins/builtin/broker-manager/add-request.ts +34 -0
  122. package/src/plugins/builtin/broker-manager/detail.tsx +128 -50
  123. package/src/plugins/builtin/broker-manager/footer.ts +6 -5
  124. package/src/plugins/builtin/broker-manager/index.tsx +62 -21
  125. package/src/plugins/builtin/broker-manager/model.ts +7 -0
  126. package/src/plugins/builtin/broker-manager/pane-actions.tsx +47 -44
  127. package/src/plugins/builtin/cloud/company-picker.tsx +658 -0
  128. package/src/plugins/builtin/credit-boards/model.ts +1 -1
  129. package/src/plugins/builtin/dividend-yield/browser.tsx +2 -25
  130. package/src/plugins/builtin/dividend-yield/client.ts +34 -122
  131. package/src/plugins/builtin/dividend-yield/index.tsx +9 -9
  132. package/src/plugins/builtin/dividend-yield/provider-client.ts +1 -1
  133. package/src/plugins/builtin/estimate-revisions/client.ts +3 -3
  134. package/src/plugins/builtin/estimate-revisions/pane.tsx +2 -2
  135. package/src/plugins/builtin/executives/pane.tsx +13 -4
  136. package/src/plugins/builtin/filing-events/pane.tsx +23 -6
  137. package/src/plugins/builtin/futures/contracts.ts +3 -3
  138. package/src/plugins/builtin/futures/returns.ts +1 -1
  139. package/src/plugins/builtin/futures-curve/client.ts +1 -1
  140. package/src/plugins/builtin/futures-curve/model.ts +1 -1
  141. package/src/plugins/builtin/help/index.tsx +8 -3
  142. package/src/plugins/builtin/market-heatmap/data.ts +13 -256
  143. package/src/plugins/builtin/market-heatmap/index.tsx +5 -15
  144. package/src/plugins/builtin/market-movers/client.ts +1 -1
  145. package/src/plugins/builtin/market-movers/screener.ts +60 -142
  146. package/src/plugins/builtin/market-valuation/defs.ts +1 -2
  147. package/src/plugins/builtin/market-valuation/sources.ts +1 -3
  148. package/src/plugins/builtin/news/wire/default-feeds.ts +2 -8
  149. package/src/plugins/builtin/news/wire/feed-config.ts +5 -1
  150. package/src/plugins/builtin/news/wire/persisted-articles.ts +9 -5
  151. package/src/plugins/builtin/news/wire/rss/cli.ts +1 -1
  152. package/src/plugins/builtin/news/wire/rss/managed-feed.ts +19 -0
  153. package/src/plugins/builtin/news/wire/rss/parser.ts +18 -9
  154. package/src/plugins/builtin/news/wire/rss/source.ts +1 -1
  155. package/src/plugins/builtin/plugin-marketplace/model.ts +5 -0
  156. package/src/plugins/builtin/plugin-marketplace/pane.tsx +4 -2
  157. package/src/plugins/builtin/relative-rotation/client.ts +1 -1
  158. package/src/plugins/builtin/research/analyst-model.ts +1 -1
  159. package/src/plugins/builtin/research/event-model.ts +1 -1
  160. package/src/plugins/builtin/risk-factors/pane.tsx +25 -10
  161. package/src/plugins/builtin/shared/pro-feature-wall.tsx +47 -0
  162. package/src/plugins/builtin/short-interest/client.ts +14 -106
  163. package/src/plugins/builtin/short-interest/headless.ts +2 -2
  164. package/src/plugins/builtin/short-interest/index.tsx +5 -5
  165. package/src/plugins/builtin/short-interest/pane.tsx +4 -4
  166. package/src/plugins/builtin/ticker-detail/pane.tsx +2 -2
  167. package/src/plugins/builtin/time-sales/client.ts +3 -3
  168. package/src/plugins/builtin/vol-surface/model.ts +1 -1
  169. package/src/plugins/builtin/volatility/model.ts +1 -1
  170. package/src/plugins/bundle.ts +2 -2
  171. package/src/plugins/catalog-manifest.ts +0 -1
  172. package/src/plugins/catalog-ui.ts +1 -1
  173. package/src/plugins/catalog.ts +0 -2
  174. package/src/plugins/host-link.ts +1 -1
  175. package/src/plugins/host-resolver.ts +1 -1
  176. package/src/plugins/loader.ts +6 -5
  177. package/src/plugins/registry/host-actions.ts +6 -1
  178. package/src/plugins/registry/index.ts +1 -0
  179. package/src/plugins/runtime/context.tsx +2 -0
  180. package/src/plugins/runtime/index.tsx +2 -0
  181. package/src/public/__snapshots__/public-api.test.ts.snap +0 -1
  182. package/src/public/market-data.ts +0 -7
  183. package/src/remote/controller-utils.ts +1 -1
  184. package/src/remote/controller.ts +27 -3
  185. package/src/remote/form.ts +20 -0
  186. package/src/remote/resources.ts +7 -0
  187. package/src/remote/schema.ts +25 -3
  188. package/src/remote/semantic-tree.tsx +15 -0
  189. package/src/remote/types.ts +2 -0
  190. package/src/renderers/browser/research-entry.ts +2 -2
  191. package/src/renderers/cloudflare/http-proxy.ts +157 -33
  192. package/src/renderers/cloudflare/proxy-session.ts +270 -0
  193. package/src/renderers/cloudflare/worker.ts +4 -1
  194. package/src/renderers/dom/desktop/controls.tsx +2 -1
  195. package/src/renderers/dom/dialog-host.tsx +17 -8
  196. package/src/renderers/dom/styles.css +4 -0
  197. package/src/renderers/electrobun/bun/application-menu/index.ts +4 -4
  198. package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
  199. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -2
  200. package/src/renderers/electrobun/bun/external-plugins.ts +1 -1
  201. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +3 -6
  202. package/src/renderers/electrobun/view/cli-pane-shot-health.ts +3 -3
  203. package/src/renderers/opentui/dialog-host.tsx +14 -2
  204. package/src/renderers/opentui/start.tsx +2 -2
  205. package/src/renderers/opentui/terminal-media.ts +2 -2
  206. package/src/renderers/opentui/test-utils.tsx +9 -2
  207. package/src/renderers/opentui/toast-host.tsx +1 -1
  208. package/src/sources/gloomberb-cloud/index.ts +23 -24
  209. package/src/sources/gloomberb-cloud/news.ts +3 -3
  210. package/src/sources/history-retention.ts +6 -6
  211. package/src/sources/{yahoo-finance/symbols.ts → listing-symbols.ts} +16 -41
  212. package/src/sources/listing-target.ts +4 -4
  213. package/src/sources/provider-router/cache.ts +16 -6
  214. package/src/sources/provider-router/financials.ts +1 -1
  215. package/src/sources/provider-router/history.ts +3 -2
  216. package/src/state/app/bootstrap.ts +1 -1
  217. package/src/state/app/context/index.tsx +13 -0
  218. package/src/sync/core-contributors.ts +163 -16
  219. package/src/test-support/plugin-runtime.ts +1 -0
  220. package/src/tickers/instrument-kind.ts +1 -1
  221. package/src/tickers/open-target.ts +4 -4
  222. package/src/tickers/search/index.ts +3 -3
  223. package/src/tickers/search/ranking.ts +4 -4
  224. package/src/time-series/resolution.ts +1 -1
  225. package/src/time-series/resolve.ts +1 -1
  226. package/src/types/financials.ts +5 -5
  227. package/src/types/instrument.ts +1 -1
  228. package/src/types/plugin.ts +3 -3
  229. package/src/types/price-history.ts +1 -1
  230. package/src/ui/dialog-bridge.tsx +50 -0
  231. package/src/ui/dialog-context.ts +24 -0
  232. package/src/ui/dialog-stack.ts +47 -1
  233. package/src/ui/dialog.tsx +2 -10
  234. package/src/ui/host.tsx +6 -1
  235. package/src/ui/index.tsx +7 -2
  236. package/src/utils/latest-financial-period.ts +1 -1
  237. package/src/utils/plugin-proxy-hosts.json +1 -5
  238. package/src/utils/sec.ts +1 -1
  239. package/src/app/pane-template-dialog-wizard.tsx +0 -67
  240. package/src/components/command-bar/multi-select-picker.tsx +0 -225
  241. package/src/components/command-bar/multi-select-runtime.ts +0 -91
  242. package/src/components/command-bar/workflow/body.tsx +0 -164
  243. package/src/components/command-bar/workflow/field-row.tsx +0 -231
  244. package/src/components/command-bar/workflow/route-actions.ts +0 -235
  245. package/src/components/command-bar/workflow/runtime.ts +0 -247
  246. package/src/plugins/builtin/yahoo/index.ts +0 -38
  247. package/src/sources/yahoo-finance/chart-period.ts +0 -138
  248. package/src/sources/yahoo-finance/financials.ts +0 -370
  249. package/src/sources/yahoo-finance/history.ts +0 -150
  250. package/src/sources/yahoo-finance/http.ts +0 -119
  251. package/src/sources/yahoo-finance/mappers.ts +0 -549
  252. package/src/sources/yahoo-finance/names.ts +0 -28
  253. package/src/sources/yahoo-finance/options.ts +0 -190
  254. package/src/sources/yahoo-finance/quote-summary.ts +0 -176
  255. package/src/sources/yahoo-finance/requests.ts +0 -245
  256. package/src/sources/yahoo-finance/snapshots.ts +0 -336
  257. package/src/sources/yahoo-finance/types.ts +0 -198
  258. package/src/sources/yahoo-finance/yahoo-chart-interval.ts +0 -18
  259. package/src/sources/yahoo-finance.ts +0 -339
package/README.md CHANGED
@@ -38,7 +38,7 @@ The desktop app and TUI share the command language and plugin system. The [brows
38
38
  On **macOS (Apple Silicon)**:
39
39
 
40
40
  ```bash
41
- brew install --cask vincelwt/tap/gloomberb
41
+ brew install --cask gloomberb
42
42
  ```
43
43
 
44
44
  On **Windows 11**, [download the installer](https://github.com/gloom-sh/gloomberb/releases/latest/download/stable-win-x64-GloomberbSetup.exe). It supports x64, and ARM64 through x64 emulation.
@@ -117,7 +117,7 @@ When the app hits an uncaught error, a render crash, or a plugin that fails to l
117
117
 
118
118
  The app also counts how often you open each function, from the command bar, a menu or a link in another pane, and which functions are on screen when a workspace is restored at launch. It sends each function's mnemonic (such as `DES` or `GP`) with those two counts, the surface, the app version and the operating system, a minute after the first count, then every 15 minutes, and when you quit. Functions from plugins other than the official gloom-sh ones are sent as `plugin`, so their names never leave your machine. The server adds your plan (signed out, Free or Pro); the counts are never tied to your account.
119
119
 
120
- Neither contains anything from your workspace: no tickers, arguments, portfolios, watchlists, layouts, settings or queries. Both carry a random install id stored in `~/.gloomberb/install-id` (in the browser, in local storage).
120
+ Neither contains anything from your workspace: no tickers, arguments, portfolios, watchlists, layouts, settings or queries. Both carry a random install id stored in `install-id` in the data folder, `~/.gloomberb` by default (in the browser, in local storage).
121
121
 
122
122
  The usage setting also covers command bar searches. When you are signed in, a search you finish in the command bar (you pause typing or run something, never each keystroke) is stored with your account to improve search: its text, the AI suggestions it got and the result you picked. Searches are deleted with your account.
123
123
 
package/README.zh-CN.md CHANGED
@@ -40,7 +40,7 @@
40
40
  在 **macOS(Apple Silicon)** 上:
41
41
 
42
42
  ```bash
43
- brew install --cask vincelwt/tap/gloomberb
43
+ brew install --cask gloomberb
44
44
  ```
45
45
 
46
46
  在 **Windows 11** 上,[下载安装程序](https://github.com/gloom-sh/gloomberb/releases/latest/download/stable-win-x64-GloomberbSetup.exe)。支持 x64,也可通过 x64 仿真在 ARM64 上运行。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gloomberb",
3
- "version": "0.15.6",
3
+ "version": "0.15.7",
4
4
  "packageManager": "bun@1.3.11",
5
5
  "description": "Market research and portfolio tracker for the terminal",
6
6
  "module": "src/public/index.ts",
@@ -1,3 +1,5 @@
1
+ import type { EarningsEvent } from "../types/data-provider";
2
+ import type { MarketDividendsPayload, MarketHeatmapResult, MarketHeatmapUniverseId, MarketMoversPayload } from "./market-discovery";
1
3
  import type { DebtMaturitiesPayload } from "./debt-maturities";
2
4
  import type { RevenueBreakdownPayload, RevenueBreakdownView } from "./revenue-breakdown";
3
5
  import type { MnaDealPayload, MnaDealsParams, MnaDealsPayload } from "./mna";
@@ -39,11 +41,11 @@ import {
39
41
  cloudJobsPath,
40
42
  cloudJobsPostingsPath,
41
43
  type CloudJobsPostingsParams,
42
- publicProxyStatementPath,
43
- publicFilingEventsPath,
44
- publicRiskReportPath,
45
- publicRiskReportsPath,
46
- publicProxyStatementsPath,
44
+ cloudProxyStatementPath,
45
+ cloudFilingEventsPath,
46
+ cloudRiskReportPath,
47
+ cloudRiskReportsPath,
48
+ cloudProxyStatementsPath,
47
49
  cloudExchangeRatePath,
48
50
  cloudSec13FPath,
49
51
  cloudSecFilingContentPath,
@@ -213,6 +215,31 @@ export class CloudDataApi {
213
215
  );
214
216
  }
215
217
 
218
+ getMarketEarningsCalendar(symbols: string[]): Promise<CloudMarketResponse<Array<Omit<EarningsEvent, "earningsDate" | "earningsCallDate"> & { earningsDate: string; earningsCallDate?: string | null }>>> {
219
+ return this.request(`/market/earnings-calendar?${new URLSearchParams({ symbols: symbols.join(",") })}`);
220
+ }
221
+
222
+ getCloudArticleSummary(url: string): Promise<{ summary: string | null }> {
223
+ return this.request(`/news/article-summary?${new URLSearchParams({ url })}`);
224
+ }
225
+
226
+ getMarketMovers(category: "day_gainers" | "day_losers" | "most_actives", count = 25, refresh = false): Promise<CloudMarketResponse<MarketMoversPayload>> {
227
+ const params = new URLSearchParams({ category, count: String(count), mode: refresh ? "refresh" : "cache-first" });
228
+ return this.request(`/market/movers?${params}`);
229
+ }
230
+
231
+ getMarketTrending(count = 25): Promise<CloudMarketResponse<Array<{ symbol: string }>>> {
232
+ return this.request(`/market/trending?count=${encodeURIComponent(count)}`);
233
+ }
234
+
235
+ getMarketHeatmap(universe: MarketHeatmapUniverseId, count = 80): Promise<CloudMarketResponse<MarketHeatmapResult>> {
236
+ return this.request(`/market/heatmap?${new URLSearchParams({ universe, count: String(count) })}`);
237
+ }
238
+
239
+ getMarketDividends(symbol: string, exchange = ""): Promise<CloudMarketResponse<MarketDividendsPayload>> {
240
+ return this.requestMarketSymbol("/market/dividends", symbol, exchange);
241
+ }
242
+
216
243
  /** Gainers, losers and most active, or the pre-market, after-hours and gap lists with a `side`. */
217
244
  async getCloudMarketScreener<Category extends CloudMarketScreenerCategory | CloudSessionMoversCategory>(
218
245
  category: Category,
@@ -521,7 +548,7 @@ export class CloudDataApi {
521
548
  ticker: string,
522
549
  ): Promise<CloudProxyStatementListPayload> {
523
550
  return this.request<CloudProxyStatementListPayload>(
524
- publicProxyStatementsPath(ticker),
551
+ cloudProxyStatementsPath(ticker),
525
552
  );
526
553
  }
527
554
 
@@ -530,7 +557,7 @@ export class CloudDataApi {
530
557
  year: number,
531
558
  ): Promise<CloudProxyStatementPayload> {
532
559
  return this.request<CloudProxyStatementPayload>(
533
- publicProxyStatementPath(ticker, year),
560
+ cloudProxyStatementPath(ticker, year),
534
561
  );
535
562
  }
536
563
 
@@ -539,13 +566,13 @@ export class CloudDataApi {
539
566
  limit?: number,
540
567
  ): Promise<{ ticker: string; events: CloudFilingEventPayload[] }> {
541
568
  return this.request<{ ticker: string; events: CloudFilingEventPayload[] }>(
542
- publicFilingEventsPath(ticker, limit),
569
+ cloudFilingEventsPath(ticker, limit),
543
570
  );
544
571
  }
545
572
 
546
573
  async getRiskReports(ticker: string): Promise<CloudRiskReportListPayload> {
547
574
  return this.request<CloudRiskReportListPayload>(
548
- publicRiskReportsPath(ticker),
575
+ cloudRiskReportsPath(ticker),
549
576
  );
550
577
  }
551
578
 
@@ -554,7 +581,7 @@ export class CloudDataApi {
554
581
  year: number,
555
582
  ): Promise<CloudRiskReportPayload> {
556
583
  return this.request<CloudRiskReportPayload>(
557
- publicRiskReportPath(ticker, year),
584
+ cloudRiskReportPath(ticker, year),
558
585
  );
559
586
  }
560
587
 
@@ -22,7 +22,7 @@ export interface EstimateObservation {
22
22
  analysts: number | null;
23
23
  range: number | null;
24
24
  relativeRange: number | null;
25
- source: "yahoo" | "yahoo-eps-trend";
25
+ source: "gloom" | "gloom-eps-trend";
26
26
  }
27
27
  interface RevisionBreadth {
28
28
  days: 7 | 30;
@@ -22,7 +22,7 @@ export interface FuturesContract {
22
22
  export interface FuturesCurvePayload {
23
23
  root: string
24
24
  name: string
25
- source: "yahoo" | "cboe"
25
+ source: "gloom" | "cboe"
26
26
  currency: string | null
27
27
  quoteUnit: string | null
28
28
  asOf: string | null
@@ -677,6 +677,12 @@ class GloomApiClient {
677
677
  getCloudQuote = this.data.getCloudQuote.bind(this.data);
678
678
  getCloudQuotesBatch = this.data.getCloudQuotesBatch.bind(this.data);
679
679
  getCloudWorldVenues = this.data.getCloudWorldVenues.bind(this.data);
680
+ getMarketEarningsCalendar = this.data.getMarketEarningsCalendar.bind(this.data);
681
+ getCloudArticleSummary = this.data.getCloudArticleSummary.bind(this.data);
682
+ getMarketMovers = this.data.getMarketMovers.bind(this.data);
683
+ getMarketTrending = this.data.getMarketTrending.bind(this.data);
684
+ getMarketHeatmap = this.data.getMarketHeatmap.bind(this.data);
685
+ getMarketDividends = this.data.getMarketDividends.bind(this.data);
680
686
  getCloudMarketScreener = this.data.getCloudMarketScreener.bind(this.data);
681
687
  getCloudOptionsChain = this.data.getCloudOptionsChain.bind(this.data);
682
688
  getCloudFinancials = this.data.getCloudFinancials.bind(this.data);
@@ -0,0 +1,84 @@
1
+ import type { CloudCorporateActionsPayload, CloudQuotePayload } from "./types";
2
+
3
+ export interface ScreenerQuote {
4
+ symbol: string;
5
+ name: string;
6
+ price: number | null;
7
+ change: number | null;
8
+ changePercent: number | null;
9
+ volume: number | null;
10
+ avgVolume: number | null;
11
+ volumeRatio: number | null; // volume / avgVolume
12
+ marketCap: number | undefined;
13
+ currency: string;
14
+ fiftyTwoWeekHigh: number | undefined;
15
+ fiftyTwoWeekLow: number | undefined;
16
+ dayHigh: number | undefined;
17
+ dayLow: number | undefined;
18
+ exchange: string;
19
+ lastUpdated?: number;
20
+ /** Session-fixed, so the price column reads its decimals from it rather than from each tick. */
21
+ previousClose?: number;
22
+ }
23
+
24
+
25
+ export type MarketHeatmapUniverseId = "us-equity" | "us-etf";
26
+ type MarketHeatmapSource = "gloom";
27
+ type MarketHeatmapSizeKind = "market-cap" | "net-assets";
28
+ export interface MarketHeatmapAsset {
29
+ symbol: string;
30
+ name: string;
31
+ price: number;
32
+ change: number;
33
+ changePercent: number;
34
+ /**
35
+ * Whether the source actually carried a session change. `changePercent` keeps
36
+ * a numeric 0 for the shared screener row shape, so tiles must read this
37
+ * before showing a flat session that was really missing data.
38
+ */
39
+ hasChange: boolean;
40
+ size: number | null;
41
+ sizeKind: MarketHeatmapSizeKind;
42
+ volume: number | null;
43
+ currency: string;
44
+ exchange: string;
45
+ sector: string | null;
46
+ industry: string | null;
47
+ marketState: string | null;
48
+ source: MarketHeatmapSource;
49
+ }
50
+
51
+ export interface MarketHeatmapResult {
52
+ stale?: boolean;
53
+ universe: MarketHeatmapUniverseId;
54
+ source: MarketHeatmapSource;
55
+ fetchedAt: number;
56
+ assets: MarketHeatmapAsset[];
57
+ }
58
+
59
+
60
+ export interface MarketMoversPayload {
61
+ quotes: ScreenerQuote[];
62
+ source: "gloom";
63
+ stale: boolean;
64
+ asOf: string;
65
+ }
66
+
67
+ export interface DividendSummary {
68
+ trailingAnnualDividendRate: number | null;
69
+ trailingAnnualDividendYield: number | null;
70
+ forwardAnnualDividendRate: number | null;
71
+ payoutRatio: number | null;
72
+ exDividendDate: number | null;
73
+ dividendDate: number | null;
74
+ currency: string | null;
75
+ }
76
+
77
+ export interface MarketDividendsPayload {
78
+ stale?: boolean;
79
+ actions: CloudCorporateActionsPayload | null;
80
+ quote: CloudQuotePayload | null;
81
+ summary: DividendSummary | null;
82
+ historyError?: string;
83
+ summaryError?: string;
84
+ }
@@ -311,30 +311,33 @@ export function cloudEarningsTranscriptPath(id: string): string {
311
311
  return `/cloud/transcripts/${encodeURIComponent(id)}`;
312
312
  }
313
313
 
314
- /** Executive compensation reads are open: no account, no plan. */
315
- export function publicProxyStatementsPath(ticker: string): string {
316
- return `/public/proxies/${encodeURIComponent(normalizeIssuerResearchTicker(ticker))}`;
314
+ /**
315
+ * RISK, EXEC and EK are Pro: these reads need a verified Pro account. The same
316
+ * risk factors and pay are open under /public for the gloom.sh pages.
317
+ */
318
+ export function cloudProxyStatementsPath(ticker: string): string {
319
+ return `/cloud/proxies/${encodeURIComponent(normalizeIssuerResearchTicker(ticker))}`;
317
320
  }
318
321
 
319
- export function publicFilingEventsPath(ticker: string, limit?: number): string {
322
+ export function cloudFilingEventsPath(ticker: string, limit?: number): string {
320
323
  const search = new URLSearchParams();
321
324
  if (limit != null) search.set("limit", String(limit));
322
325
  return appendQuery(
323
- `/public/events/${encodeURIComponent(normalizeIssuerResearchTicker(ticker))}`,
326
+ `/cloud/events/${encodeURIComponent(normalizeIssuerResearchTicker(ticker))}`,
324
327
  search,
325
328
  );
326
329
  }
327
330
 
328
- export function publicRiskReportsPath(ticker: string): string {
329
- return `/public/risks/${encodeURIComponent(normalizeIssuerResearchTicker(ticker))}`;
331
+ export function cloudRiskReportsPath(ticker: string): string {
332
+ return `/cloud/risks/${encodeURIComponent(normalizeIssuerResearchTicker(ticker))}`;
330
333
  }
331
334
 
332
- export function publicRiskReportPath(ticker: string, year: number): string {
333
- return `/public/risks/${encodeURIComponent(normalizeIssuerResearchTicker(ticker))}/${year}`;
335
+ export function cloudRiskReportPath(ticker: string, year: number): string {
336
+ return `/cloud/risks/${encodeURIComponent(normalizeIssuerResearchTicker(ticker))}/${year}`;
334
337
  }
335
338
 
336
- export function publicProxyStatementPath(ticker: string, year: number): string {
337
- return `/public/proxies/${encodeURIComponent(normalizeIssuerResearchTicker(ticker))}/${year}`;
339
+ export function cloudProxyStatementPath(ticker: string, year: number): string {
340
+ return `/cloud/proxies/${encodeURIComponent(normalizeIssuerResearchTicker(ticker))}/${year}`;
338
341
  }
339
342
 
340
343
  export function cloudSecFilingsPath(params: CloudSecFilingsParams): string {
@@ -17,6 +17,12 @@ import { SESSION_COOKIE_NAMES } from "./session-cookie";
17
17
 
18
18
  const DEFAULT_API_URL = "https://api.gloom.sh";
19
19
  const DEFAULT_MARKET_REQUEST_TIMEOUT_MS = 10_000;
20
+ const DEFAULT_RESEARCH_REQUEST_TIMEOUT_MS = 45_000;
21
+ // Research may fill missing sections after a primary read; quote/history reads remain latency bounded.
22
+ const RESEARCH_MARKET_PATHS = new Set([
23
+ "/market/dividends", "/market/short-interest", "/market/movers", "/market/trending",
24
+ "/market/heatmap", "/market/earnings-calendar",
25
+ ]);
20
26
  /** Local status for "this runtime cannot stream", never returned by the server. */
21
27
  export const STREAMING_UNSUPPORTED_STATUS = 0;
22
28
 
@@ -140,6 +146,7 @@ export class CloudApiRequestTransport {
140
146
  private cookieSessionMode = false;
141
147
  private readonly fetchTransport: CloudApiFetchTransport | null;
142
148
  private readonly marketRequestTimeoutMs: number;
149
+ private readonly researchRequestTimeoutMs: number;
143
150
  private readonly connectionHealth: ConnectionHealthRegistry;
144
151
 
145
152
  readonly baseUrl = getCloudApiBaseUrl();
@@ -154,6 +161,7 @@ export class CloudApiRequestTransport {
154
161
  this.fetchTransport = options.fetchTransport ?? null;
155
162
  this.marketRequestTimeoutMs =
156
163
  options.marketRequestTimeoutMs ?? DEFAULT_MARKET_REQUEST_TIMEOUT_MS;
164
+ this.researchRequestTimeoutMs = options.marketRequestTimeoutMs ?? DEFAULT_RESEARCH_REQUEST_TIMEOUT_MS;
157
165
  this.connectionHealth = options.connectionHealth ?? connectionHealth;
158
166
  }
159
167
 
@@ -257,6 +265,8 @@ export class CloudApiRequestTransport {
257
265
  return this.performRequest<T>(path, options, canApplySession);
258
266
  }
259
267
 
268
+ const timeoutMs = RESEARCH_MARKET_PATHS.has(path.split("?")[0]!)
269
+ ? this.researchRequestTimeoutMs : this.marketRequestTimeoutMs;
260
270
  const controller = new AbortController();
261
271
  const callerSignal = options?.signal;
262
272
  const abortFromCaller = () => controller.abort(callerSignal?.reason);
@@ -273,8 +283,8 @@ export class CloudApiRequestTransport {
273
283
  }, canApplySession);
274
284
  return await withDeadline(
275
285
  request,
276
- this.marketRequestTimeoutMs,
277
- `Cloud market request timed out after ${this.marketRequestTimeoutMs}ms: ${path}`,
286
+ timeoutMs,
287
+ `Cloud market request timed out after ${timeoutMs}ms: ${path}`,
278
288
  (error) => controller.abort(error),
279
289
  );
280
290
  } finally {
@@ -11,7 +11,7 @@ export interface TapeQuote {
11
11
  timestamp: string;
12
12
  bid: number | null;
13
13
  ask: number | null;
14
- /** Alpaca quote sizes are round lots. */
14
+ /** Quote sizes are round lots. */
15
15
  bidSize: number;
16
16
  askSize: number;
17
17
  bidExchange: string;
@@ -20,7 +20,7 @@ export interface TapeQuote {
20
20
  tape: string;
21
21
  }
22
22
  export interface TapeSnapshot {
23
- source: "Alpaca";
23
+ source: "Gloom Cloud";
24
24
  symbol: string;
25
25
  exchange: string;
26
26
  access: "realtime" | "delayed";
@@ -454,6 +454,7 @@ export interface CloudCdsHistoryResponse {
454
454
 
455
455
  interface CloudShortInterestPointPayload {
456
456
  settlementDate: string;
457
+ shortPercentFloat?: number | null;
457
458
  sharesShort: number;
458
459
  previousSharesShort: number | null;
459
460
  averageDailyVolume: number | null;
@@ -463,6 +464,7 @@ interface CloudShortInterestPointPayload {
463
464
  }
464
465
 
465
466
  export interface CloudShortInterestPayload {
467
+ source?: "finra" | "gloom";
466
468
  symbol: string;
467
469
  issueName: string | null;
468
470
  points: CloudShortInterestPointPayload[];
@@ -0,0 +1,33 @@
1
+ import { useCallback, useContext, type ReactNode } from "react";
2
+ import { RemoteUiRegistryProvider, useRemoteUiRegistry } from "../remote/semantic-tree";
3
+ import { AppContext, getEffectiveThemeId, useAppSelector } from "../state/app/context";
4
+ import { ThemeProvider } from "../theme/theme-context";
5
+ import { useRegisterDialogBridge } from "../ui/dialog-bridge";
6
+ import { KeybindingsProvider, useKeybindings } from "./keybindings";
7
+
8
+ function DialogTheme({ children }: { children: ReactNode }) {
9
+ const themeId = useAppSelector(getEffectiveThemeId);
10
+ return <ThemeProvider themeId={themeId}>{children}</ThemeProvider>;
11
+ }
12
+
13
+ /**
14
+ * Gives every dialog what the app gives its panes: the store, the theme, the
15
+ * keybindings and the remote registry, so a form in a dialog reads state and
16
+ * registers nodes like one in the app. Renders nothing.
17
+ */
18
+ export function AppDialogBridge() {
19
+ const store = useContext(AppContext);
20
+ const registry = useRemoteUiRegistry();
21
+ const keybindings = useKeybindings();
22
+ const bridge = useCallback((content: ReactNode) => {
23
+ const withKeybindings = <KeybindingsProvider value={keybindings}>{content}</KeybindingsProvider>;
24
+ const withRegistry = registry
25
+ ? <RemoteUiRegistryProvider registry={registry}>{withKeybindings}</RemoteUiRegistryProvider>
26
+ : withKeybindings;
27
+ return store
28
+ ? <AppContext value={store}><DialogTheme>{withRegistry}</DialogTheme></AppContext>
29
+ : withRegistry;
30
+ }, [keybindings, registry, store]);
31
+ useRegisterDialogBridge(bridge);
32
+ return null;
33
+ }
@@ -6,6 +6,7 @@ import {
6
6
  bringToFront,
7
7
  findDockLeaf,
8
8
  getDockedPaneIds,
9
+ isPaneDetached,
9
10
  isPaneInLayout,
10
11
  } from "../../layout/pane-manager";
11
12
  import type { PluginRegistry } from "../../plugins/registry";
@@ -52,6 +53,8 @@ interface AppPaneRuntimeArgs {
52
53
  dialog: DialogApi;
53
54
  dispatch: Dispatch<AppAction>;
54
55
  externalPlugins: readonly LoadedExternalPlugin[];
56
+ /** Desktop main window: raises a detached pane's own window. */
57
+ focusDetachedPane?: (paneId: string) => void;
55
58
  isDetachedWindow: boolean;
56
59
  notify: (body: string, options?: { type?: "info" | "success" | "error" }) => void;
57
60
  persistConfig: (nextConfig: AppState["config"]) => void;
@@ -67,6 +70,7 @@ export function useAppPaneRuntime({
67
70
  dialog,
68
71
  dispatch,
69
72
  externalPlugins,
73
+ focusDetachedPane,
70
74
  isDetachedWindow,
71
75
  notify,
72
76
  persistConfig,
@@ -159,7 +163,9 @@ export function useAppPaneRuntime({
159
163
  persistLayout(nextLayout, { pushHistory: false });
160
164
  }
161
165
  activatePane(paneId);
162
- }, [activatePane, persistLayout, state.config.layout]);
166
+ // Focus in this window's state alone would leave the pane's window behind this one.
167
+ if (isPaneDetached(nextLayout, paneId)) focusDetachedPane?.(paneId);
168
+ }, [activatePane, focusDetachedPane, persistLayout, state.config.layout]);
163
169
 
164
170
  const placePaneInstance = useCallback((
165
171
  instance: PaneInstanceConfig,
@@ -240,7 +246,6 @@ export function useAppPaneRuntime({
240
246
  const { createPaneFromTemplate } = useAppPaneTemplateRuntime({
241
247
  buildPaneInstance,
242
248
  dataProvider,
243
- dialog,
244
249
  dispatch,
245
250
  notify,
246
251
  placePaneInstance,
@@ -32,7 +32,7 @@ export function useAppPaneSettingsRuntime({
32
32
  stateRef,
33
33
  tickerRepository,
34
34
  }: UseAppPaneSettingsRuntimeOptions) {
35
- const openPaneSettings = useCallback(async (paneId?: string) => {
35
+ const openPaneSettings = useCallback(async (paneId?: string, options?: { fieldKey?: string }) => {
36
36
  const targetPaneId = paneId
37
37
  ? resolvePaneTarget(paneId)
38
38
  : stateRef.current.focusedPaneId;
@@ -59,6 +59,7 @@ export function useAppPaneSettingsRuntime({
59
59
  paneId={targetPaneId}
60
60
  pluginRegistry={pluginRegistry}
61
61
  applyFieldValue={applyFieldValue}
62
+ initialFieldKey={options?.fieldKey}
62
63
  />
63
64
  ),
64
65
  });
@@ -1,6 +1,7 @@
1
1
  import { useCallback, type Dispatch } from "react";
2
2
  import { getPaneTemplateDisplayLabel } from "../../components/command-bar/pane-templates/items";
3
3
  import { createPaneTemplateOrThrow } from "../../components/command-bar/workflow/ops";
4
+ import { openFormModal } from "../../components/form-modal";
4
5
  import type { AppTickerRepositoryPort } from "../../core/app-service-ports";
5
6
  import type { PluginRegistry } from "../../plugins/registry";
6
7
  import type { AppAction, AppState } from "../../state/app/context";
@@ -10,10 +11,7 @@ import type {
10
11
  PaneDef,
11
12
  PaneTemplateCreateOptions,
12
13
  PaneTemplateInstanceConfig,
13
- WizardStep,
14
14
  } from "../../types/plugin";
15
- import type { DialogApi } from "../../ui/dialog";
16
- import { runPaneTemplateDialogWizard } from "../pane-template-dialog-wizard";
17
15
 
18
16
  interface UseAppPaneTemplateRuntimeOptions {
19
17
  buildPaneInstance: (paneType: string, options?: {
@@ -24,7 +22,6 @@ interface UseAppPaneTemplateRuntimeOptions {
24
22
  instanceId?: string;
25
23
  }) => PaneInstanceConfig | null;
26
24
  dataProvider: DataProvider;
27
- dialog: DialogApi;
28
25
  dispatch: Dispatch<AppAction>;
29
26
  notify: (body: string, options?: { type?: "info" | "success" | "error" }) => void;
30
27
  placePaneInstance: (
@@ -40,7 +37,6 @@ interface UseAppPaneTemplateRuntimeOptions {
40
37
  export function useAppPaneTemplateRuntime({
41
38
  buildPaneInstance,
42
39
  dataProvider,
43
- dialog,
44
40
  dispatch,
45
41
  notify,
46
42
  placePaneInstance,
@@ -48,38 +44,23 @@ export function useAppPaneTemplateRuntime({
48
44
  stateRef,
49
45
  tickerRepository,
50
46
  }: UseAppPaneTemplateRuntimeOptions) {
51
- const runPaneTemplateWizard = useCallback((steps: WizardStep[]) => (
52
- runPaneTemplateDialogWizard(dialog, steps)
53
- ), [dialog]);
54
-
55
47
  const createPaneFromTemplate = useCallback(async (templateId: string, options?: PaneTemplateCreateOptions) => {
56
48
  const template = pluginRegistry.paneTemplates.get(templateId);
57
49
  if (!template) return;
58
50
 
59
- let resolvedOptions = options;
60
- const shouldRunDialogWizard = !!template.wizard
51
+ // A template that asks for settings asks in the same form the command bar
52
+ // opens, which creates the pane when it is sent.
53
+ const asksForSettings = !!template.wizard
61
54
  && template.wizard.length > 0
62
55
  && !options?.values
63
56
  && (!options?.arg || template.wizard.some((step) => step.type === "textarea"));
64
- if (shouldRunDialogWizard && template.wizard) {
65
- const wizardSteps = options?.arg && template.shortcut?.argPlaceholder
66
- ? template.wizard.map((step) => (
67
- step.key === template.shortcut?.argPlaceholder
68
- ? { ...step, defaultValue: options.arg }
69
- : step
70
- ))
71
- : template.wizard;
72
- const values = await runPaneTemplateWizard(wizardSteps);
73
- if (!values) return;
74
- resolvedOptions = {
75
- ...options,
76
- values,
77
- arg: template.shortcut?.argPlaceholder ? values[template.shortcut.argPlaceholder] : options?.arg,
78
- };
57
+ if (asksForSettings) {
58
+ openFormModal({ kind: "pane-template", templateId, arg: options?.arg, options });
59
+ return;
79
60
  }
80
61
 
81
62
  try {
82
- await createPaneTemplateOrThrow(templateId, resolvedOptions, {
63
+ await createPaneTemplateOrThrow(templateId, options, {
83
64
  dataProvider,
84
65
  tickerRepository,
85
66
  pluginRegistry,
@@ -101,7 +82,6 @@ export function useAppPaneTemplateRuntime({
101
82
  notify,
102
83
  placePaneInstance,
103
84
  pluginRegistry,
104
- runPaneTemplateWizard,
105
85
  stateRef,
106
86
  tickerRepository,
107
87
  ]);
@@ -4,6 +4,9 @@ import {
4
4
  createPaneTemplateOrThrow,
5
5
  } from "../../components/command-bar/workflow/ops";
6
6
  import type { AppTickerRepositoryPort } from "../../core/app-service-ports";
7
+ import { openFormModal } from "../../components/form-modal";
8
+ import { t } from "../../i18n";
9
+ import { openBrokerAddFlow } from "../../plugins/builtin/broker-manager/add-request";
7
10
  import { getPanelFocusTarget } from "../../core/state/app/layout";
8
11
  import { setLayoutManagerDispatch } from "../../plugins/builtin/layout-manager";
9
12
  import { setMarketplaceHost } from "../../plugins/builtin/plugin-marketplace/store";
@@ -48,6 +51,7 @@ import type {
48
51
  import type { TickerOpenTarget } from "../../tickers/open-target";
49
52
  import { instrumentFromTicker } from "../../market-data/request-types";
50
53
  import { tickerInstrumentLabel } from "../../tickers/instrument-label";
54
+ import { isDialogOpen } from "../../ui/dialog-stack";
51
55
  import { stableStringify } from "../../utils/hash";
52
56
 
53
57
  // Registry callbacks are rebound on renders. Request ownership must survive
@@ -70,7 +74,7 @@ interface BindAppPanePluginRegistryOptions {
70
74
  externalPlugins: readonly LoadedExternalPlugin[];
71
75
  focusVisiblePane: (paneId: string, layout?: LayoutConfig) => void;
72
76
  isDetachedWindow: boolean;
73
- openPaneSettings: (paneId?: string) => Promise<void>;
77
+ openPaneSettings: (paneId?: string, options?: { fieldKey?: string }) => Promise<void>;
74
78
  openPinnedTicker: (rawSymbol: string, options?: PinTickerOptions) => Promise<void>;
75
79
  persistConfig: (nextConfig: AppState["config"]) => void;
76
80
  persistLayout: (layout: LayoutConfig, options?: { pushHistory?: boolean }) => void;
@@ -131,25 +135,43 @@ export function bindAppPanePluginRegistry({
131
135
  if (paneId) dispatch({ type: "FOCUS_PANE", paneId });
132
136
  },
133
137
  switchTab: (tabId, paneId) => switchTickerResearchTab(tabId, paneId),
138
+ // The bar would open over a dialog without its keys, so it waits for none to be open.
134
139
  openCommandBar: (query) => {
135
- if (isDetachedWindow) return;
140
+ if (isDetachedWindow || isDialogOpen()) return;
136
141
  dispatch({ type: "SET_COMMAND_BAR", open: true, query });
137
142
  },
143
+ // Forms submit through the main window's state, so a detached window says where to go.
138
144
  openPluginCommandWorkflow: (commandId) => {
139
- if (isDetachedWindow) return;
140
- dispatch({
141
- type: "SET_COMMAND_BAR",
142
- open: true,
143
- query: "",
144
- launch: { kind: "plugin-command", commandId },
145
- });
145
+ if (isDetachedWindow) {
146
+ pluginRegistry.notify({ body: t("Open this from the main window."), type: "info" });
147
+ return;
148
+ }
149
+ const command = pluginRegistry.commands.get(commandId);
150
+ if (!command?.wizard || command.wizard.length === 0) {
151
+ // Nothing to fill in: the bar opens, as it always has for such a command.
152
+ if (!isDialogOpen()) dispatch({ type: "SET_COMMAND_BAR", open: true, query: "" });
153
+ return;
154
+ }
155
+ openFormModal({ kind: "plugin-command", commandId });
156
+ },
157
+ openBuiltInWorkflow: (actionId) => {
158
+ if (isDetachedWindow) {
159
+ pluginRegistry.notify({ body: t("Open this from the main window."), type: "info" });
160
+ return;
161
+ }
162
+ // Profiles are added in the Brokers pane, not in a form.
163
+ if (actionId === "add-broker-account") {
164
+ openBrokerAddFlow(pluginRegistry);
165
+ return;
166
+ }
167
+ openFormModal({ kind: "builtin", actionId });
146
168
  },
147
169
  getLayout: () => stateRef.current.config.layout,
148
170
  updateLayout: (layout) => {
149
171
  if (isDetachedWindow) return;
150
172
  persistLayout(layout);
151
173
  },
152
- openPaneSettings: (paneId) => { void openPaneSettings(paneId); },
174
+ openPaneSettings: (paneId, options) => { void openPaneSettings(paneId, options); },
153
175
  showPane: (paneId) => {
154
176
  if (isDetachedWindow) return;
155
177
  showPane(paneId);
@@ -335,7 +357,7 @@ export function bindAppPanePluginRegistry({
335
357
  toggleable: plugin.toggleable === true,
336
358
  enabled: !disabled.has(plugin.id),
337
359
  source: external ? "external" : "builtin",
338
- ...(external?.directory ? { directory: external.directory } : {}),
360
+ ...(external?.directory ? { directory: external.directory, path: external.path } : {}),
339
361
  ...(external?.commit ? { commit: external.commit } : {}),
340
362
  ...(external?.linked ? { linked: true } : {}),
341
363
  hasSetup,
@@ -358,7 +380,7 @@ export function bindAppPanePluginRegistry({
358
380
  toggleable: true,
359
381
  enabled: !disabled.has(entry.plugin.id),
360
382
  source: "external",
361
- ...(entry.directory ? { directory: entry.directory } : {}),
383
+ ...(entry.directory ? { directory: entry.directory, path: entry.path } : {}),
362
384
  ...(entry.commit ? { commit: entry.commit } : {}),
363
385
  ...(entry.linked ? { linked: true } : {}),
364
386
  ...(entry.unsupportedTarget ? { unsupportedTarget: entry.unsupportedTarget } : {}),