gloomberb 0.7.2 → 0.7.3

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 (129) hide show
  1. package/README.md +79 -132
  2. package/package.json +1 -1
  3. package/src/app.tsx +73 -67
  4. package/src/components/chart/chart-data.ts +7 -2
  5. package/src/components/chart/chart-pane-settings.ts +7 -4
  6. package/src/components/chart/chart-renderer.ts +49 -15
  7. package/src/components/chart/chart-types.ts +2 -2
  8. package/src/components/chart/chart.test.ts +27 -8
  9. package/src/components/chart/comparison-stock-chart.tsx +84 -121
  10. package/src/components/chart/native/chart-rasterizer.ts +43 -17
  11. package/src/components/chart/price-axis-labels.tsx +25 -9
  12. package/src/components/chart/stock-chart-renderer-switch.test.tsx +1 -1
  13. package/src/components/chart/stock-chart.tsx +59 -41
  14. package/src/components/command-bar/command-bar.test.tsx +446 -120
  15. package/src/components/command-bar/command-bar.tsx +528 -383
  16. package/src/components/command-bar/command-registry.ts +16 -6
  17. package/src/components/command-bar/root-shortcuts.ts +4 -5
  18. package/src/components/command-bar/view-model.test.ts +1 -0
  19. package/src/components/command-bar/view-model.ts +2 -1
  20. package/src/components/command-bar/workflow-ops.ts +11 -32
  21. package/src/components/data-table-view.test.tsx +24 -1
  22. package/src/components/data-table-view.tsx +3 -2
  23. package/src/components/feed-data-table-stack-view.tsx +3 -2
  24. package/src/components/layout/detached-pane-shell.tsx +2 -2
  25. package/src/components/layout/floating-pane.tsx +9 -2
  26. package/src/components/layout/pane-footer.tsx +1 -1
  27. package/src/components/layout/pane-header.tsx +4 -2
  28. package/src/components/layout/pane-sizing.ts +12 -0
  29. package/src/components/layout/pane.tsx +24 -2
  30. package/src/components/layout/shell.tsx +24 -10
  31. package/src/components/layout/status-bar.test.tsx +4 -1
  32. package/src/components/layout/status-bar.tsx +4 -24
  33. package/src/components/markdown-text.tsx +53 -8
  34. package/src/components/onboarding/onboarding-wizard.tsx +7 -6
  35. package/src/components/pane-settings-dialog.tsx +9 -6
  36. package/src/components/pane-template-wizard.tsx +3 -2
  37. package/src/components/price-selector-dialog.tsx +3 -2
  38. package/src/components/price-sparkline-view.tsx +463 -0
  39. package/src/components/price-sparkline.ts +46 -0
  40. package/src/components/table-view-shared.tsx +21 -3
  41. package/src/components/ticker-list-table-view.tsx +3 -2
  42. package/src/components/ticker-list-table.tsx +11 -51
  43. package/src/components/toggle-list.tsx +3 -3
  44. package/src/components/ui/choice-dialog.tsx +3 -2
  45. package/src/components/ui/multi-select-dialog.tsx +3 -2
  46. package/src/components/ui/native-select.tsx +3 -3
  47. package/src/core/state/app-state.ts +210 -80
  48. package/src/data/config-store-node.ts +113 -25
  49. package/src/data/config-store.test.ts +65 -3
  50. package/src/data/plugin-state-store.ts +39 -13
  51. package/src/data/sqlite-cache.test.ts +13 -0
  52. package/src/market-data/coordinator.ts +8 -1
  53. package/src/news/aggregator.test.ts +41 -0
  54. package/src/news/aggregator.ts +61 -9
  55. package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +25 -6
  56. package/src/plugins/builtin/ai/settings.ts +3 -0
  57. package/src/plugins/builtin/broker-manager/index.tsx +4 -3
  58. package/src/plugins/builtin/chat-controller.test.ts +398 -17
  59. package/src/plugins/builtin/chat-controller.ts +258 -44
  60. package/src/plugins/builtin/chat.test.tsx +257 -6
  61. package/src/plugins/builtin/chat.tsx +225 -60
  62. package/src/plugins/builtin/comparison-chart.test.tsx +117 -3
  63. package/src/plugins/builtin/correlation/colors.test.ts +39 -0
  64. package/src/plugins/builtin/correlation/colors.ts +87 -0
  65. package/src/plugins/builtin/correlation/index.test.tsx +151 -0
  66. package/src/plugins/builtin/correlation/index.tsx +92 -21
  67. package/src/plugins/builtin/debug.tsx +4 -3
  68. package/src/plugins/builtin/econ/index.tsx +3 -2
  69. package/src/plugins/builtin/help.test.tsx +2 -2
  70. package/src/plugins/builtin/holders/index.tsx +25 -13
  71. package/src/plugins/builtin/layout-manager.tsx +1 -7
  72. package/src/plugins/builtin/news-wire/index.tsx +1 -1
  73. package/src/plugins/builtin/news-wire/news-detail-view.tsx +3 -2
  74. package/src/plugins/builtin/news-wire/news-preset-pane.tsx +4 -1
  75. package/src/plugins/builtin/news-wire/persisted-articles.ts +7 -1
  76. package/src/plugins/builtin/options.tsx +9 -17
  77. package/src/plugins/builtin/portfolio-list/cli/commands.ts +6 -10
  78. package/src/plugins/builtin/portfolio-list/metrics.ts +11 -0
  79. package/src/plugins/builtin/portfolio-list/mutations.ts +17 -0
  80. package/src/plugins/builtin/portfolio-list/pane.tsx +117 -18
  81. package/src/plugins/builtin/portfolio-list/quick-add.tsx +422 -0
  82. package/src/plugins/builtin/portfolio-list/settings.ts +20 -12
  83. package/src/plugins/builtin/portfolio-list/table.tsx +3 -3
  84. package/src/plugins/builtin/portfolio-list.test.tsx +282 -2
  85. package/src/plugins/builtin/quote-monitor.test.tsx +158 -25
  86. package/src/plugins/builtin/sectors/index.tsx +252 -41
  87. package/src/plugins/builtin/sectors/sector-data.ts +41 -1
  88. package/src/plugins/builtin/ticker-detail/chart-tab.tsx +3 -2
  89. package/src/plugins/builtin/ticker-detail/index.tsx +30 -9
  90. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +25 -15
  91. package/src/plugins/builtin/ticker-detail/pane.tsx +0 -6
  92. package/src/plugins/builtin/ticker-detail/quote-monitor.tsx +442 -63
  93. package/src/plugins/builtin/ticker-detail/settings.ts +84 -3
  94. package/src/plugins/builtin/ticker-detail.test.tsx +2 -2
  95. package/src/plugins/ibkr/trading-pane.tsx +3 -2
  96. package/src/plugins/plugin-runtime.tsx +2 -2
  97. package/src/plugins/prediction-markets/settings.ts +4 -0
  98. package/src/plugins/registry.test.ts +55 -0
  99. package/src/plugins/registry.ts +11 -4
  100. package/src/plugins/ticker-navigation.test.ts +35 -5
  101. package/src/plugins/ticker-navigation.ts +16 -22
  102. package/src/renderers/electrobun/bun/application-menu-click.test.ts +9 -0
  103. package/src/renderers/electrobun/bun/application-menu-click.ts +7 -4
  104. package/src/renderers/electrobun/bun/application-menu.ts +6 -1
  105. package/src/renderers/electrobun/bun/desktop-workspace.ts +47 -29
  106. package/src/renderers/electrobun/bun/index.ts +52 -13
  107. package/src/renderers/electrobun/view/app-services.ts +51 -9
  108. package/src/renderers/electrobun/view/backend-rpc.ts +5 -8
  109. package/src/renderers/electrobun/view/desktop-controls.tsx +4 -4
  110. package/src/renderers/electrobun/view/main.tsx +0 -16
  111. package/src/renderers/electrobun/view/styles.css +23 -21
  112. package/src/renderers/electrobun/view/ui-host.tsx +6 -4
  113. package/src/renderers/opentui/host.tsx +2 -1
  114. package/src/renderers/opentui/start.tsx +1 -1
  115. package/src/renderers/opentui/test-utils.tsx +6 -5
  116. package/src/state/app-bootstrap.ts +3 -3
  117. package/src/state/app-context.test.ts +113 -0
  118. package/src/state/app-context.tsx +8 -4
  119. package/src/theme/colors.ts +13 -14
  120. package/src/types/config.ts +9 -4
  121. package/src/types/plugin.ts +8 -1
  122. package/src/ui/context-menu.tsx +4 -4
  123. package/src/utils/api-client.test.ts +128 -0
  124. package/src/utils/api-client.ts +112 -7
  125. package/src/utils/fuzzy-search.ts +12 -1
  126. package/src/utils/keyboard.ts +31 -0
  127. package/src/utils/ticker-search.test.ts +2 -2
  128. package/src/utils/ticker-search.ts +48 -1
  129. package/src/version.ts +1 -1
@@ -18,8 +18,7 @@ import type { ColumnConfig } from "../types/config";
18
18
  import type { TickerFinancials, PricePoint } from "../types/financials";
19
19
  import type { TickerRecord } from "../types/ticker";
20
20
  import { padTo } from "../utils/format";
21
- import { renderChart, resolveChartPalette, type StyledContent } from "./chart/chart-renderer";
22
- import type { ProjectedChartPoint } from "./chart/chart-data";
21
+ import { PRICE_SPARKLINE_COLUMN_ID, PriceSparkline } from "./price-sparkline-view";
23
22
  import { tableContentWidthProps, useMeasuredTableContentWidth } from "./ui/table-layout";
24
23
  import { useViewport } from "../react/input";
25
24
  import { measurePerf } from "../utils/perf-marks";
@@ -88,7 +87,6 @@ export interface TickerListTableProps {
88
87
  onRowActivate?: (ticker: TickerRecord) => void;
89
88
  emptyTitle?: string;
90
89
  emptyHint?: string;
91
- showSparklines?: boolean;
92
90
  virtualize?: boolean;
93
91
  overscan?: number;
94
92
  }
@@ -155,38 +153,6 @@ const TickerListHeader = memo(function TickerListHeader({
155
153
  );
156
154
  });
157
155
 
158
- function renderSparkline(priceHistory: PricePoint[]): StyledContent | null {
159
- if (priceHistory.length < 5) return null;
160
-
161
- const last20 = priceHistory.slice(-20);
162
- const points: ProjectedChartPoint[] = last20.map((pt) => ({
163
- date: pt.date,
164
- open: pt.open ?? pt.close,
165
- high: pt.high ?? pt.close,
166
- low: pt.low ?? pt.close,
167
- close: pt.close,
168
- volume: pt.volume ?? 0,
169
- }));
170
-
171
- const first = last20[0]?.close ?? 0;
172
- const last = last20[last20.length - 1]?.close ?? 0;
173
- const trend = last >= first ? "positive" : "negative";
174
- const palette = resolveChartPalette(colors, trend);
175
-
176
- const result = renderChart(points, {
177
- width: 10,
178
- height: 1,
179
- showVolume: false,
180
- volumeHeight: 0,
181
- cursorX: null,
182
- cursorY: null,
183
- mode: "line",
184
- colors: palette,
185
- });
186
-
187
- return result.lines[0] ?? null;
188
- }
189
-
190
156
  const TickerListRow = memo(function TickerListRow({
191
157
  columns,
192
158
  ticker,
@@ -201,7 +167,6 @@ const TickerListRow = memo(function TickerListRow({
201
167
  onRowActivate,
202
168
  onRowContextMenu,
203
169
  openContextMenuOnMouseDown,
204
- showSparklines,
205
170
  priceHistory,
206
171
  contentWidth,
207
172
  }: {
@@ -218,7 +183,6 @@ const TickerListRow = memo(function TickerListRow({
218
183
  onRowActivate?: (ticker: TickerRecord) => void;
219
184
  onRowContextMenu: (ticker: TickerRecord, financials: TickerFinancials | undefined, event: unknown) => void;
220
185
  openContextMenuOnMouseDown: boolean;
221
- showSparklines?: boolean;
222
186
  priceHistory?: PricePoint[];
223
187
  contentWidth: number;
224
188
  }) {
@@ -226,10 +190,6 @@ const TickerListRow = memo(function TickerListRow({
226
190
  const lastActivatedAtRef = useRef<number | null>(null);
227
191
  const rowBg = isSelected ? colors.selected : isHovered ? hoverBg() : colors.bg;
228
192
 
229
- const sparkline = showSparklines && priceHistory && priceHistory.length >= 5
230
- ? renderSparkline(priceHistory)
231
- : null;
232
-
233
193
  return (
234
194
  <Box
235
195
  flexDirection="row"
@@ -268,6 +228,14 @@ const TickerListRow = memo(function TickerListRow({
268
228
  }}
269
229
  >
270
230
  {columns.map((column) => {
231
+ if (column.id === PRICE_SPARKLINE_COLUMN_ID) {
232
+ return (
233
+ <Box key={column.id} width={column.width + 1} overflow="hidden">
234
+ <PriceSparkline priceHistory={priceHistory} width={column.width} />
235
+ </Box>
236
+ );
237
+ }
238
+
271
239
  const { text, color } = resolveCell(column, ticker, financials);
272
240
  const baseFg = color || (isSelected ? colors.selectedText : colors.text);
273
241
  const shouldFlash = flashDirection != null && FLASHABLE_QUOTE_COLUMN_IDS.has(column.id);
@@ -283,11 +251,6 @@ const TickerListRow = memo(function TickerListRow({
283
251
  </Box>
284
252
  );
285
253
  })}
286
- {sparkline && (
287
- <Box width={12}>
288
- <Text content={sparkline} />
289
- </Box>
290
- )}
291
254
  </Box>
292
255
  );
293
256
  }, (previous, next) => (
@@ -304,7 +267,6 @@ const TickerListRow = memo(function TickerListRow({
304
267
  && previous.onRowActivate === next.onRowActivate
305
268
  && previous.onRowContextMenu === next.onRowContextMenu
306
269
  && previous.openContextMenuOnMouseDown === next.openContextMenuOnMouseDown
307
- && previous.showSparklines === next.showSparklines
308
270
  && previous.priceHistory === next.priceHistory
309
271
  && previous.contentWidth === next.contentWidth
310
272
  ));
@@ -329,7 +291,6 @@ export function TickerListTable({
329
291
  onRowActivate,
330
292
  emptyTitle = "No tickers.",
331
293
  emptyHint = "Press Ctrl+P to add one.",
332
- showSparklines,
333
294
  virtualize = true,
334
295
  overscan = 4,
335
296
  }: TickerListTableProps) {
@@ -347,8 +308,8 @@ export function TickerListTable({
347
308
  hoveredIdxRef.current = hoveredIdx;
348
309
  const safeFlashSymbols = flashSymbols ?? EMPTY_FLASH_SYMBOLS;
349
310
  const tableWidth = useMemo(
350
- () => columns.reduce((sum, column) => sum + column.width + 1, 2) + (showSparklines ? 12 : 0),
351
- [columns, showSparklines],
311
+ () => columns.reduce((sum, column) => sum + column.width + 1, 2),
312
+ [columns],
352
313
  );
353
314
  const { contentWidth, measureContentWidth } = useMeasuredTableContentWidth(
354
315
  tableWidth,
@@ -492,7 +453,6 @@ export function TickerListTable({
492
453
  onRowActivate={onRowActivate}
493
454
  onRowContextMenu={handleRowContextMenu}
494
455
  openContextMenuOnMouseDown={nativeContextMenu !== true}
495
- showSparklines={showSparklines}
496
456
  priceHistory={financialsMap.get(ticker.metadata.ticker)?.priceHistory}
497
457
  contentWidth={contentWidth}
498
458
  />
@@ -1,6 +1,6 @@
1
1
  import { Box, Text, useUiHost } from "../ui";
2
2
  import { TextAttributes } from "../ui";
3
- import { colors } from "../theme/colors";
3
+ import { blendHex, colors } from "../theme/colors";
4
4
  import { ListView, type ListRowState, type ListViewItem } from "./ui/list-view";
5
5
 
6
6
  export interface ToggleListItem {
@@ -76,8 +76,8 @@ function DesktopToggleRow({
76
76
  border: `1px solid ${checkboxBorder}`,
77
77
  borderRadius: 4,
78
78
  boxShadow: enabled
79
- ? "inset 0 1px 0 rgba(255,255,255,0.28)"
80
- : "inset 0 1px 0 rgba(255,255,255,0.05)",
79
+ ? `inset 0 1px 0 ${blendHex(colors.borderFocused, colors.textBright, 0.28)}`
80
+ : `inset 0 1px 0 ${blendHex(colors.bg, colors.textBright, 0.05)}`,
81
81
  }}
82
82
  >
83
83
  {enabled && (
@@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from "react";
2
2
  import { Box, Text } from "../../ui";
3
3
  import { type PromptContext, useDialogKeyboard } from "../../ui/dialog";
4
4
  import { colors } from "../../theme/colors";
5
+ import { isPlainKey } from "../../utils/keyboard";
5
6
  import { DialogFrame } from "./frame";
6
7
  import { ListView, type ListViewItem } from "./list-view";
7
8
 
@@ -58,9 +59,9 @@ export function ChoiceDialog({
58
59
 
59
60
  useDialogKeyboard((event) => {
60
61
  event.stopPropagation();
61
- if (event.name === "up" || event.name === "k") {
62
+ if (isPlainKey(event, "up", "k")) {
62
63
  setIndex((current) => clampChoiceIndex(current - 1, choices.length));
63
- } else if (event.name === "down" || event.name === "j") {
64
+ } else if (isPlainKey(event, "down", "j")) {
64
65
  setIndex((current) => clampChoiceIndex(current + 1, choices.length));
65
66
  } else if (event.name === "enter" || event.name === "return") {
66
67
  activateChoice(selectedChoice);
@@ -5,6 +5,7 @@ import { type AlertContext, useDialog, useDialogKeyboard } from "../../ui/dialog
5
5
  import { type ReactNode, useEffect, useMemo, useState } from "react";
6
6
  import { colors } from "../../theme/colors";
7
7
  import { isDetailBackNavigationKey } from "../../utils/back-navigation";
8
+ import { isPlainKey } from "../../utils/keyboard";
8
9
  import { ToggleList } from "../toggle-list";
9
10
  import { Button } from "./button";
10
11
  import { DialogFrame } from "./frame";
@@ -176,10 +177,10 @@ export function MultiSelectDialogContent({
176
177
 
177
178
  useDialogKeyboard((event) => {
178
179
  event.stopPropagation();
179
- if (event.name === "up" || event.name === "k") {
180
+ if (isPlainKey(event, "up", "k")) {
180
181
  const nextIndex = Math.max(0, selectedIndex - 1);
181
182
  setSelectedOptionId(displayOptions[nextIndex]?.value ?? selectedOptionId);
182
- } else if (event.name === "down" || event.name === "j") {
183
+ } else if (isPlainKey(event, "down", "j")) {
183
184
  const nextIndex = Math.min(displayOptions.length - 1, selectedIndex + 1);
184
185
  setSelectedOptionId(displayOptions[nextIndex]?.value ?? selectedOptionId);
185
186
  } else if (isSpaceKey(event)) {
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { type CSSProperties } from "react";
4
4
  import { Box } from "../../ui";
5
- import { colors } from "../../theme/colors";
5
+ import { blendHex, colors } from "../../theme/colors";
6
6
 
7
7
  export type NativeSelectElement = HTMLSelectElement & { showPicker?: () => void };
8
8
 
@@ -51,11 +51,11 @@ export function NativeSelect({
51
51
  width,
52
52
  height: 28,
53
53
  color: colors.text,
54
- backgroundColor: "rgba(255, 255, 255, 0.06)",
54
+ backgroundColor: blendHex(colors.panel, colors.textBright, 0.06),
55
55
  border: `1px solid ${colors.border}`,
56
56
  borderRadius: 6,
57
57
  padding: "0 8px",
58
- boxShadow: "inset 0 1px 0 rgba(255,255,255,0.05)",
58
+ boxShadow: `inset 0 1px 0 ${blendHex(colors.bg, colors.textBright, 0.05)}`,
59
59
  cursor: "pointer",
60
60
  font: "inherit",
61
61
  letterSpacing: 0,