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
@@ -1,7 +1,14 @@
1
- import { useCallback, useMemo, useRef, useState, type ReactNode } from "react";
2
- import { Box, ChartSurface, Span, Text, useNativeRenderer, type BoxRenderable } from "../../../ui";
1
+ import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
2
+ import { Box, ChartSurface, Text, TextAttributes, useNativeRenderer, type BoxRenderable } from "../../../ui";
3
3
  import { colors } from "../../../theme/colors";
4
- import { consumeChartMouseEvent, getLocalPlotPointer, type ChartMouseEvent } from "../core/pointer";
4
+ import {
5
+ consumeChartMouseEvent,
6
+ getLocalPlotPointer,
7
+ getRenderablePixelSize,
8
+ scaleLocalPixelCoordinate,
9
+ type ChartMouseEvent,
10
+ type LocalPlotPointer,
11
+ } from "../core/pointer";
5
12
  import type { NativeChartBitmap } from "../native/chart-rasterizer";
6
13
  import { truncateWithEllipsis } from "../../../utils/text-wrap";
7
14
  import { useStaticChartBitmapSize } from "./chart/bitmap";
@@ -12,11 +19,36 @@ import {
12
19
  renderBarChartYAxis,
13
20
  renderNativeBarChart,
14
21
  resolveBarChartHover,
22
+ resolveNativeBarChartHover,
15
23
  type BarChartColors,
16
24
  type BarChartHover,
25
+ type BarChartScene,
17
26
  type BarChartSeries,
18
27
  } from "../bar-chart-renderer";
19
28
 
29
+ export interface BarChartSurfaceHoverGeometry {
30
+ bitmapPixelWidth: number;
31
+ renderablePixelWidth?: number | null;
32
+ }
33
+
34
+ export function resolveBarChartSurfaceHover(
35
+ scene: BarChartScene,
36
+ pointer: Pick<LocalPlotPointer, "cellX" | "pixelX">,
37
+ geometry?: BarChartSurfaceHoverGeometry | null,
38
+ ): BarChartHover | null {
39
+ if (geometry && pointer.pixelX !== null) {
40
+ const pixelX = scaleLocalPixelCoordinate(
41
+ pointer.pixelX,
42
+ geometry.renderablePixelWidth ?? geometry.bitmapPixelWidth,
43
+ geometry.bitmapPixelWidth,
44
+ );
45
+ if (pixelX !== null) {
46
+ return resolveNativeBarChartHover(scene, pixelX, geometry.bitmapPixelWidth);
47
+ }
48
+ }
49
+ return resolveBarChartHover(scene, pointer.cellX);
50
+ }
51
+
20
52
  export interface StaticBarChartSurfaceProps {
21
53
  series: BarChartSeries[];
22
54
  width: number;
@@ -25,6 +57,8 @@ export interface StaticBarChartSurfaceProps {
25
57
  title?: string;
26
58
  header?: ReactNode;
27
59
  formatValue?: (value: number) => string;
60
+ hiddenSeriesIds?: readonly string[] | ReadonlySet<string>;
61
+ onToggleSeries?: (seriesId: string) => void;
28
62
  onHoverChange?: (hover: BarChartHover | null) => void;
29
63
  onMouseDown?: (event: ChartMouseEvent) => void;
30
64
  }
@@ -36,6 +70,8 @@ export function StaticBarChartSurface({
36
70
  title,
37
71
  header,
38
72
  formatValue,
73
+ hiddenSeriesIds,
74
+ onToggleSeries,
39
75
  onHoverChange,
40
76
  onMouseDown,
41
77
  colors: chartColors,
@@ -59,11 +95,18 @@ export function StaticBarChartSurface({
59
95
  negativeColor: chartColors?.negativeColor ?? colors.negative,
60
96
  hoverColor: chartColors?.hoverColor ?? colors.textBright,
61
97
  }), [chartColors]);
62
- const scene = useMemo(() => buildBarChartScene(series, {
98
+ const hiddenSeriesIdSet = useMemo(() => (
99
+ hiddenSeriesIds instanceof Set ? hiddenSeriesIds : new Set(hiddenSeriesIds ?? [])
100
+ ), [hiddenSeriesIds]);
101
+ const visibleSeries = useMemo(
102
+ () => series.filter((item) => !hiddenSeriesIdSet.has(item.id)),
103
+ [hiddenSeriesIdSet, series],
104
+ );
105
+ const scene = useMemo(() => buildBarChartScene(visibleSeries, {
63
106
  width: plotWidth,
64
107
  height: plotHeight,
65
108
  colors: resolvedChartColors,
66
- }), [plotHeight, plotWidth, resolvedChartColors, series]);
109
+ }), [plotHeight, plotWidth, resolvedChartColors, visibleSeries]);
67
110
 
68
111
  const bitmapSize = useStaticChartBitmapSize(plotWidth, plotHeight);
69
112
 
@@ -78,8 +121,8 @@ export function StaticBarChartSurface({
78
121
  const hoverLabel = useMemo(() => {
79
122
  if (!hover) return null;
80
123
  const value = formatValue ? formatValue(hover.value) : String(hover.value);
81
- return series.length > 1 ? `${hover.category} ${hover.seriesLabel}: ${value}` : `${hover.category}: ${value}`;
82
- }, [formatValue, hover, series.length]);
124
+ return visibleSeries.length > 1 ? `${hover.category} ${hover.seriesLabel}: ${value}` : `${hover.category}: ${value}`;
125
+ }, [formatValue, hover, visibleSeries.length]);
83
126
  const readout = useMemo(() => {
84
127
  if (!hoverLabel) return title ?? "";
85
128
  return title ? `${title} ${hoverLabel}` : hoverLabel;
@@ -98,6 +141,17 @@ export function StaticBarChartSurface({
98
141
  return { labelLeft, labelTop, labelWidth, text };
99
142
  }, [hover, hoverLabel, plotHeight, plotWidth]);
100
143
 
144
+ const resolvePointerHover = useCallback((pointer: LocalPlotPointer): BarChartHover | null => {
145
+ if (!scene) return null;
146
+ const renderablePixelSize = bitmapSize ? getRenderablePixelSize(plotRef.current, renderer) : null;
147
+ return resolveBarChartSurfaceHover(scene, pointer, bitmapSize
148
+ ? {
149
+ bitmapPixelWidth: bitmapSize.pixelWidth,
150
+ renderablePixelWidth: renderablePixelSize?.pixelWidth ?? bitmapSize.pixelWidth,
151
+ }
152
+ : null);
153
+ }, [bitmapSize, renderer, scene]);
154
+
101
155
  const handleMouseMove = useCallback((event: ChartMouseEvent) => {
102
156
  if (!scene) return;
103
157
  const pointer = getLocalPlotPointer(event, plotRef.current, renderer);
@@ -106,11 +160,11 @@ export function StaticBarChartSurface({
106
160
  onHoverChange?.(null);
107
161
  return;
108
162
  }
109
- const nextHover = resolveBarChartHover(scene, pointer.cellX);
163
+ const nextHover = resolvePointerHover(pointer);
110
164
  setHover(nextHover);
111
165
  onHoverChange?.(nextHover);
112
166
  consumeChartMouseEvent(event);
113
- }, [onHoverChange, renderer, scene]);
167
+ }, [onHoverChange, renderer, resolvePointerHover, scene]);
114
168
 
115
169
  const handleMouseOut = useCallback(() => {
116
170
  setHover(null);
@@ -122,16 +176,54 @@ export function StaticBarChartSurface({
122
176
  if (!scene) return;
123
177
  const pointer = getLocalPlotPointer(event, plotRef.current, renderer);
124
178
  if (!pointer) return;
125
- const nextHover = resolveBarChartHover(scene, pointer.cellX);
179
+ const nextHover = resolvePointerHover(pointer);
126
180
  setHover(nextHover);
127
181
  onHoverChange?.(nextHover);
128
182
  consumeChartMouseEvent(event);
129
- }, [onHoverChange, onMouseDown, renderer, scene]);
183
+ }, [onHoverChange, onMouseDown, renderer, resolvePointerHover, scene]);
184
+
185
+ useEffect(() => {
186
+ if (hover && hiddenSeriesIdSet.has(hover.seriesId)) {
187
+ setHover(null);
188
+ onHoverChange?.(null);
189
+ }
190
+ }, [hiddenSeriesIdSet, hover, onHoverChange]);
191
+
192
+ const legend = legendRows ? (
193
+ <Box flexDirection="row" height={1}>
194
+ {series.map((item, index) => {
195
+ const hidden = hiddenSeriesIdSet.has(item.id);
196
+ const legendColor = hidden ? colors.textDim : item.color;
197
+ const legendAttributes = hidden ? TextAttributes.STRIKETHROUGH | TextAttributes.DIM : TextAttributes.NONE;
198
+ const handleLegendMouseDown = onToggleSeries
199
+ ? (event: any) => {
200
+ event.stopPropagation?.();
201
+ onToggleSeries(item.id);
202
+ }
203
+ : undefined;
204
+ return (
205
+ <Box
206
+ key={item.id}
207
+ flexDirection="row"
208
+ height={1}
209
+ onMouseDown={handleLegendMouseDown}
210
+ cursor={onToggleSeries ? "pointer" : undefined}
211
+ data-gloom-interactive={onToggleSeries ? "true" : undefined}
212
+ >
213
+ <Text>{index > 0 ? " " : " "}</Text>
214
+ <Text fg={legendColor} bg={legendColor} attributes={legendAttributes}> </Text>
215
+ <Text fg={legendColor} attributes={legendAttributes}>{` ${item.label}`}</Text>
216
+ </Box>
217
+ );
218
+ })}
219
+ </Box>
220
+ ) : null;
130
221
 
131
222
  if (!scene) {
132
223
  return (
133
224
  <Box flexDirection="column" width={totalWidth} height={totalHeight} onMouseDown={onMouseDown}>
134
225
  {header ?? (title ? <Text fg={colors.textBright} bold>{truncateWithEllipsis(title, totalWidth)}</Text> : null)}
226
+ {legend}
135
227
  <Text fg={colors.textDim}>No chart data</Text>
136
228
  </Box>
137
229
  );
@@ -146,15 +238,7 @@ export function StaticBarChartSurface({
146
238
  </Text>
147
239
  )
148
240
  ) : null}
149
- {legendRows ? (
150
- <Text>
151
- {series.map((item, index) => (
152
- <Span key={item.id} fg={item.color}>
153
- {index > 0 ? " " : ""}{"■ "}{item.label}
154
- </Span>
155
- ))}
156
- </Text>
157
- ) : null}
241
+ {legend}
158
242
  <Box flexDirection="row" width={totalWidth} height={plotHeight}>
159
243
  {yAxisWidth ? (
160
244
  <Box flexDirection="column" width={yAxisWidth} height={plotHeight}>
@@ -3,7 +3,6 @@ import type { InstrumentRef } from "../../../market-data/request-types";
3
3
  import type { DataProvider } from "../../../types/data-provider";
4
4
  import type { PricePoint, TickerFinancials } from "../../../types/financials";
5
5
  import { appendLiveQuotePoint, resolveStableOhlcProjectionOptions } from "../core/data";
6
- import type { DateWindowRange } from "../core/controller";
7
6
  import {
8
7
  getChartResolutionLabel,
9
8
  sortChartResolutions,
@@ -0,0 +1,65 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import type { ProjectedChartPoint } from "../core/data";
3
+ import { resolveStockChartFooterOhlcReadout } from "./footer";
4
+
5
+ function point(
6
+ close: number,
7
+ overrides: Partial<ProjectedChartPoint> = {},
8
+ ): ProjectedChartPoint {
9
+ return {
10
+ date: new Date("2026-07-03T10:00:00Z"),
11
+ open: close,
12
+ high: close,
13
+ low: close,
14
+ close,
15
+ volume: 100,
16
+ ...overrides,
17
+ };
18
+ }
19
+
20
+ describe("stock chart footer OHLC readout", () => {
21
+ test("summarizes the visible OHLC window for idle chart readouts", () => {
22
+ const readout = resolveStockChartFooterOhlcReadout({
23
+ activePoint: null,
24
+ hasDisplayCursor: false,
25
+ points: [
26
+ point(100, { open: 99, high: 101, low: 98, volume: 10 }),
27
+ point(104, { open: 100, high: 108, low: 99, volume: 20 }),
28
+ point(106, { open: 104, high: 107, low: 103, volume: 30 }),
29
+ ],
30
+ });
31
+
32
+ expect(readout).toMatchObject({
33
+ open: 99,
34
+ high: 108,
35
+ low: 98,
36
+ close: 106,
37
+ volume: 60,
38
+ });
39
+ expect(readout?.changePercent).toBeCloseTo(((106 - 99) / 99) * 100);
40
+ });
41
+
42
+ test("uses the summary only when there is no chart cursor", () => {
43
+ const activePoint = point(104, { open: 103, high: 105, low: 102 });
44
+ const points = [
45
+ point(100, { open: 99, high: 101, low: 98 }),
46
+ point(106, { open: 104, high: 107, low: 103 }),
47
+ ];
48
+
49
+ const idleReadout = resolveStockChartFooterOhlcReadout({
50
+ activePoint,
51
+ hasDisplayCursor: false,
52
+ points,
53
+ });
54
+ const cursorReadout = resolveStockChartFooterOhlcReadout({
55
+ activePoint,
56
+ hasDisplayCursor: true,
57
+ points,
58
+ });
59
+
60
+ expect(idleReadout?.close).toBe(106);
61
+ expect(idleReadout?.changePercent).not.toBeNull();
62
+ expect(cursorReadout).toMatchObject(activePoint);
63
+ expect(cursorReadout?.changePercent).toBeNull();
64
+ });
65
+ });
@@ -1,7 +1,8 @@
1
1
  import { useMemo, type Dispatch, type MutableRefObject, type SetStateAction } from "react";
2
- import { formatCompact } from "../../../utils/format";
2
+ import { formatCompact, formatPercentRaw } from "../../../utils/format";
3
3
  import { formatMarketPriceWithCurrency } from "../../../market-data/market/format";
4
4
  import { usePaneFooter, type PaneFooterSegment, type PaneHint } from "../../layout/pane/footer";
5
+ import { priceColor } from "../../../theme/colors";
5
6
  import type { PricePoint } from "../../../types/financials";
6
7
  import type { ProjectedChartPoint } from "../core/data";
7
8
  import type { DateWindowRange } from "../core/controller";
@@ -24,8 +25,53 @@ import {
24
25
  type StockChartViewportState,
25
26
  } from "./viewport";
26
27
 
27
- interface StockChartFooterOptions {
28
+ interface StockChartOhlcReadout extends ProjectedChartPoint {
29
+ changePercent: number | null;
30
+ }
31
+
32
+ function summarizeOhlcWindow(points: readonly ProjectedChartPoint[]): StockChartOhlcReadout | null {
33
+ const first = points[0];
34
+ const last = points.at(-1);
35
+ if (!first || !last) return null;
36
+
37
+ let high = first.high;
38
+ let low = first.low;
39
+ let volume = 0;
40
+ for (const point of points) {
41
+ high = Math.max(high, point.high);
42
+ low = Math.min(low, point.low);
43
+ volume += point.volume;
44
+ }
45
+
46
+ return {
47
+ date: last.date,
48
+ open: first.open,
49
+ high,
50
+ low,
51
+ close: last.close,
52
+ volume,
53
+ changePercent: Number.isFinite(first.open) && first.open !== 0
54
+ ? ((last.close - first.open) / first.open) * 100
55
+ : null,
56
+ };
57
+ }
58
+
59
+ export function resolveStockChartFooterOhlcReadout({
60
+ activePoint,
61
+ hasDisplayCursor,
62
+ points,
63
+ }: {
28
64
  activePoint: ProjectedChartPoint | null;
65
+ hasDisplayCursor: boolean;
66
+ points: readonly ProjectedChartPoint[];
67
+ }): StockChartOhlcReadout | null {
68
+ if (hasDisplayCursor) {
69
+ return activePoint ? { ...activePoint, changePercent: null } : null;
70
+ }
71
+ return summarizeOhlcWindow(points);
72
+ }
73
+
74
+ interface StockChartFooterOptions {
29
75
  activePreset: TimeRange | null;
30
76
  baseDateBounds: DateWindowRange | null;
31
77
  boundsHistoryDates: Date[];
@@ -37,6 +83,7 @@ interface StockChartFooterOptions {
37
83
  history: PricePoint[];
38
84
  manualMinimumSpanMs: number | null;
39
85
  navigableDateWindow: DateWindowRange | null;
86
+ ohlcReadout: StockChartOhlcReadout | null;
40
87
  pendingAutoWindowRef: MutableRefObject<DateWindowRange | null>;
41
88
  pendingCanonicalResetRef: MutableRefObject<number>;
42
89
  persistRenderMode: (mode: ChartRenderMode) => void;
@@ -50,7 +97,6 @@ interface StockChartFooterOptions {
50
97
  setRenderedAutoView: Dispatch<SetStateAction<AutoRenderedView | null>>;
51
98
  setResolution: (resolution: ChartResolution) => void;
52
99
  setViewState: Dispatch<SetStateAction<StockChartViewportState>>;
53
- showOhlcSummary: boolean;
54
100
  updateDisplayCursorTarget: (next: DisplayCursorState, motionKind: ChartCursorMotionKind) => void;
55
101
  visibleDateWindow: DateWindowRange | null;
56
102
  visiblePriceRange: number | undefined;
@@ -58,7 +104,6 @@ interface StockChartFooterOptions {
58
104
  }
59
105
 
60
106
  export function useStockChartFooter({
61
- activePoint,
62
107
  activePreset,
63
108
  baseDateBounds,
64
109
  boundsHistoryDates,
@@ -70,6 +115,7 @@ export function useStockChartFooter({
70
115
  history,
71
116
  manualMinimumSpanMs,
72
117
  navigableDateWindow,
118
+ ohlcReadout,
73
119
  pendingAutoWindowRef,
74
120
  pendingCanonicalResetRef,
75
121
  persistRenderMode,
@@ -83,7 +129,6 @@ export function useStockChartFooter({
83
129
  setRenderedAutoView,
84
130
  setResolution,
85
131
  setViewState,
86
- showOhlcSummary,
87
132
  updateDisplayCursorTarget,
88
133
  visibleDateWindow,
89
134
  visiblePriceRange,
@@ -196,7 +241,7 @@ export function useStockChartFooter({
196
241
  ]);
197
242
 
198
243
  const chartFooterInfo = useMemo<PaneFooterSegment[]>(() => {
199
- if (compact || !showOhlcSummary || !activePoint) return [];
244
+ if (compact || !ohlcReadout) return [];
200
245
 
201
246
  const formatPrice = (value: number) => formatMarketPriceWithCurrency(value, chartCurrency, {
202
247
  assetCategory: chartAssetCategory,
@@ -210,16 +255,27 @@ export function useStockChartFooter({
210
255
  ? []
211
256
  : [
212
257
  { text: "O", tone: "label" as const },
213
- { text: formatPrice(activePoint.open), tone: "value" as const },
258
+ { text: formatPrice(ohlcReadout.open), tone: "value" as const },
214
259
  ]),
215
260
  { text: "H", tone: "label" },
216
- { text: formatPrice(activePoint.high), tone: "value" },
261
+ { text: formatPrice(ohlcReadout.high), tone: "value" },
217
262
  { text: "L", tone: "label" },
218
- { text: formatPrice(activePoint.low), tone: "value" },
263
+ { text: formatPrice(ohlcReadout.low), tone: "value" },
219
264
  { text: "C", tone: "label" },
220
- { text: formatPrice(activePoint.close), tone: "value" },
265
+ { text: formatPrice(ohlcReadout.close), tone: "value" },
266
+ ...(ohlcReadout.changePercent === null
267
+ ? []
268
+ : [
269
+ { text: "%", tone: "label" as const },
270
+ {
271
+ text: formatPercentRaw(ohlcReadout.changePercent),
272
+ tone: "value" as const,
273
+ color: priceColor(ohlcReadout.changePercent),
274
+ bold: true,
275
+ },
276
+ ]),
221
277
  { text: "V", tone: "label" },
222
- { text: formatCompact(activePoint.volume), tone: "value" },
278
+ { text: formatCompact(ohlcReadout.volume), tone: "value" },
223
279
  ];
224
280
 
225
281
  return [{
@@ -227,12 +283,11 @@ export function useStockChartFooter({
227
283
  parts,
228
284
  }];
229
285
  }, [
230
- activePoint,
231
286
  chartAssetCategory,
232
287
  chartCurrency,
233
288
  compact,
289
+ ohlcReadout,
234
290
  projectionMode,
235
- showOhlcSummary,
236
291
  visiblePriceRange,
237
292
  ]);
238
293
 
@@ -1,5 +1,5 @@
1
- import { memo, type ComponentProps } from "react";
2
- import { useStockChartFooter } from "./footer";
1
+ import { memo, useMemo, type ComponentProps } from "react";
2
+ import { resolveStockChartFooterOhlcReadout, useStockChartFooter } from "./footer";
3
3
  import { useStockChartKeyboardShortcuts } from "./keyboard";
4
4
  import { useStockChartControlRenderInvalidation } from "./rendering/invalidation";
5
5
  import { useStockChartRenderOutput } from "./rendering/output";
@@ -139,9 +139,23 @@ function useResolvedStockChartControls(
139
139
  } = runtime;
140
140
  const { output, status } = render;
141
141
  const manualMinimumSpanMs = getChartResolutionStepMs(dataRuntime.renderedResolution);
142
+ const ohlcReadout = useMemo(
143
+ () => output.showOhlcSummary
144
+ ? resolveStockChartFooterOhlcReadout({
145
+ activePoint: output.activePoint,
146
+ hasDisplayCursor: output.hasDisplayCursor,
147
+ points: projectionModel.projection.points,
148
+ })
149
+ : null,
150
+ [
151
+ output.activePoint,
152
+ output.hasDisplayCursor,
153
+ output.showOhlcSummary,
154
+ projectionModel.projection.points,
155
+ ],
156
+ );
142
157
 
143
158
  useStockChartFooter({
144
- activePoint: output.activePoint,
145
159
  activePreset: viewportRuntime.activePreset,
146
160
  baseDateBounds: dataRuntime.baseDateBounds,
147
161
  boundsHistoryDates: dataRuntime.boundsHistoryDates,
@@ -153,6 +167,7 @@ function useResolvedStockChartControls(
153
167
  history: dataRuntime.history,
154
168
  manualMinimumSpanMs,
155
169
  navigableDateWindow: dataRuntime.navigableDateWindow,
170
+ ohlcReadout,
156
171
  pendingAutoWindowRef: interactionRuntime.pendingAutoWindowRef,
157
172
  pendingCanonicalResetRef: interactionRuntime.pendingCanonicalResetRef,
158
173
  persistRenderMode: viewportRuntime.persistRenderMode,
@@ -166,7 +181,6 @@ function useResolvedStockChartControls(
166
181
  setRenderedAutoView: dataRuntime.setRenderedAutoView,
167
182
  setResolution: viewportRuntime.setResolution,
168
183
  setViewState: settings.setViewState,
169
- showOhlcSummary: output.showOhlcSummary,
170
184
  updateDisplayCursorTarget: interactionRuntime.updateDisplayCursorTarget,
171
185
  visibleDateWindow: dataRuntime.visibleDateWindow,
172
186
  visiblePriceRange: output.visiblePriceRange,
@@ -1,5 +1,4 @@
1
1
  import { useEffect, useRef, type Dispatch, type MutableRefObject, type SetStateAction } from "react";
2
- import { applyBufferedPanExpansion } from "../../core/scroll";
3
2
  import {
4
3
  applyDateWindowViewport,
5
4
  buildVisibleDateWindow,
@@ -15,7 +14,6 @@ import {
15
14
  type StoredChartSelectionSyncState,
16
15
  } from "../../core/pane-settings";
17
16
  import {
18
- getNextBufferRange,
19
17
  getSupportMaxRange,
20
18
  type ManualChartResolution,
21
19
  } from "../../core/resolution";
@@ -31,7 +29,6 @@ import type {
31
29
  import type { ChartCursorMotionKind } from "../../cursor-motion";
32
30
  import type { AutoRenderedView } from "../auto";
33
31
  import {
34
- clearAutoViewportState,
35
32
  resolveViewportPresetSelection,
36
33
  resolveViewportStoredSelection,
37
34
  type PendingExpansionAction,
@@ -1,20 +1,14 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
  import type { PaneTemplateDef, WizardStep } from "../../types/plugin";
3
- import type {
4
- CommandBarRoute,
5
- CommandBarWorkflowField,
6
- } from "./workflow/types";
3
+ import type { CommandBarWorkflowField } from "./workflow/types";
7
4
  import {
8
5
  buildGeneratedTemplateField,
9
6
  getCollectionCommandAction,
10
7
  getCollectionCommandKind,
11
8
  getCollectionCommandVerb,
12
9
  getFirstVisibleFieldId,
13
- getScreenFooterLeft,
14
- getScreenFooterRight,
15
10
  getVisibleWorkflowFields,
16
11
  isCollectionCommand,
17
- isRootParsedCommand,
18
12
  isRouteCommandId,
19
13
  isWorkflowTextField,
20
14
  looksDestructiveCommand,
@@ -22,7 +16,6 @@ import {
22
16
  routeCommandIdToScreen,
23
17
  slugifyName,
24
18
  summarizeError,
25
- summarizePaneSettingValue,
26
19
  summarizeWorkflowFieldValue,
27
20
  } from "./helpers";
28
21
 
@@ -79,26 +72,6 @@ describe("command-bar helpers", () => {
79
72
  }, ["symbol", "price", "pnl", "volume"])).toBe("Symbol, Price, P&L +1");
80
73
  });
81
74
 
82
- test("summarizes pane setting values consistently", () => {
83
- expect(summarizePaneSettingValue({
84
- key: "theme",
85
- label: "Theme",
86
- type: "select",
87
- options: [{ label: "Nord", value: "nord" }],
88
- }, "nord")).toBe("Nord");
89
- expect(summarizePaneSettingValue({
90
- key: "columns",
91
- label: "Columns",
92
- type: "ordered-multi-select",
93
- options: [
94
- { label: "Symbol", value: "symbol" },
95
- { label: "Price", value: "price" },
96
- { label: "P&L", value: "pnl" },
97
- { label: "Volume", value: "volume" },
98
- ],
99
- }, ["symbol", "price", "pnl", "volume"])).toBe("Symbol, Price, P&L +1");
100
- });
101
-
102
75
  test("normalizes wizard fields and derives defaults from steps", () => {
103
76
  const steps: WizardStep[] = [
104
77
  { key: "intro", label: "Intro", type: "info", body: ["Line one", "Line two"] },
@@ -157,61 +130,9 @@ describe("command-bar helpers", () => {
157
130
  });
158
131
  });
159
132
 
160
- test("formats screen footers based on route context", () => {
161
- const orderedPickerRoute: CommandBarRoute = {
162
- kind: "picker",
163
- pickerId: "field-multi-select",
164
- title: "Columns",
165
- query: "",
166
- selectedIdx: 0,
167
- hoveredIdx: null,
168
- options: [],
169
- payload: { fieldType: "ordered-multi-select" },
170
- };
171
- const filteredPickerRoute: CommandBarRoute = {
172
- ...orderedPickerRoute,
173
- query: "vol",
174
- };
175
- const workflowRoute: CommandBarRoute = {
176
- kind: "workflow",
177
- workflowId: "create-pane",
178
- title: "Create Pane",
179
- fields: [],
180
- values: {},
181
- activeFieldId: null,
182
- submitLabel: "Create",
183
- pending: false,
184
- error: null,
185
- payload: {
186
- kind: "builtin",
187
- actionId: "create-pane",
188
- },
189
- };
190
- const confirmRoute: CommandBarRoute = {
191
- kind: "confirm",
192
- confirmId: "delete",
193
- title: "Delete",
194
- body: ["Delete item?"],
195
- confirmLabel: "Delete",
196
- cancelLabel: "Back",
197
- tone: "danger",
198
- onConfirm: () => {},
199
- pending: false,
200
- error: null,
201
- };
202
-
203
- expect(getScreenFooterLeft(null)).toBe("up/down move enter select");
204
- expect(getScreenFooterLeft(orderedPickerRoute)).toBe("up/down move space toggle [ ] reorder enter done");
205
- expect(getScreenFooterRight(orderedPickerRoute)).toBe("backspace/esc back");
206
- expect(getScreenFooterRight(filteredPickerRoute)).toBe("backspace delete esc back");
207
- expect(getScreenFooterRight(workflowRoute)).toBe("backspace/esc back");
208
- expect(getScreenFooterRight(confirmRoute)).toBe("backspace/esc back");
209
- });
210
-
211
133
  test("maps route and collection command ids", () => {
212
134
  expect(isRouteCommandId("plugins")).toBe(true);
213
135
  expect(routeCommandIdToScreen("plugins")).toBe("plugins");
214
- expect(isRootParsedCommand("remove-portfolio")).toBe(true);
215
136
  expect(isCollectionCommand("remove-portfolio")).toBe(true);
216
137
  expect(getCollectionCommandKind("remove-portfolio")).toBe("portfolio");
217
138
  expect(getCollectionCommandAction("remove-portfolio")).toBe("remove");
@@ -1,4 +1,4 @@
1
- import type { CommandDef, PaneSettingField } from "../../types/plugin";
1
+ import type { CommandDef } from "../../types/plugin";
2
2
  import type { CommandBarRoute } from "./workflow/types";
3
3
  import type { CollectionKind, CollectionMembershipAction } from "./workflow/ops";
4
4
 
@@ -37,14 +37,6 @@ export function routeCommandIdToScreen(commandId: RouteCommandId): "ticker-searc
37
37
  }
38
38
  }
39
39
 
40
- export function isRootParsedCommand(commandId: string): boolean {
41
- return commandId === "security-description"
42
- || commandId === "add-watchlist"
43
- || commandId === "add-portfolio"
44
- || commandId === "remove-watchlist"
45
- || commandId === "remove-portfolio";
46
- }
47
-
48
40
  export function isCollectionCommand(commandId: string): commandId is CollectionCommandId {
49
41
  return commandId === "add-watchlist"
50
42
  || commandId === "add-portfolio"
@@ -64,33 +56,6 @@ export function getCollectionCommandVerb(action: CollectionMembershipAction): st
64
56
  return action === "add" ? "Add" : "Remove";
65
57
  }
66
58
 
67
- export function summarizePaneSettingValue(field: PaneSettingField, value: unknown): string {
68
- switch (field.type) {
69
- case "toggle":
70
- return value === true ? "On" : "Off";
71
- case "text":
72
- return typeof value === "string" && value.trim().length > 0 ? value : "Unset";
73
- case "select": {
74
- const option = field.options.find((entry) => entry.value === value);
75
- return option?.label ?? "Unset";
76
- }
77
- case "multi-select":
78
- case "ordered-multi-select": {
79
- const selected = Array.isArray(value)
80
- ? value.filter((entry): entry is string => typeof entry === "string")
81
- : [];
82
- if (selected.length === 0) return "None";
83
- const labels = selected
84
- .map((entry) => field.options.find((option) => option.value === entry)?.label ?? entry)
85
- .slice(0, 3);
86
- const suffix = selected.length > 3 ? ` +${selected.length - 3}` : "";
87
- return `${labels.join(", ")}${suffix}`;
88
- }
89
- default:
90
- return "";
91
- }
92
- }
93
-
94
59
  export function getScreenFooterLeft(route: CommandBarRoute | null): string {
95
60
  if (!route) return "up/down move enter select";
96
61
  switch (route.kind) {