gloomberb 0.9.3 → 0.9.5
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.
- package/README.md +1 -0
- package/package.json +3 -2
- package/patches/@opentui%2Fcore@0.3.2.patch +231 -29
- package/patches/react-reconciler@0.33.0.patch +10 -0
- package/src/app/runtime/desktop-menu.ts +5 -1
- package/src/app/runtime/ticker-refresh.ts +4 -1
- package/src/app.tsx +10 -1
- package/src/brokers/account-cache.ts +1 -1
- package/src/brokers/sync-broker-instance.test.ts +107 -0
- package/src/brokers/sync-broker-instance.ts +22 -10
- package/src/cli/desktop-pane-shot.ts +2 -1
- package/src/cli/entry.ts +8 -1
- package/src/cli/native-smoke.ts +5 -0
- package/src/cli/pane-functions/screenshot.ts +13 -1
- package/src/components/chart/bar-chart-renderer.test.ts +108 -3
- package/src/components/chart/bar-chart-renderer.ts +226 -27
- package/src/components/chart/core/pointer.ts +32 -6
- package/src/components/chart/native/bitmap-support.test.ts +27 -0
- package/src/components/chart/native/bitmap-support.ts +55 -0
- package/src/components/chart/native/kitty/index.test.ts +47 -0
- package/src/components/chart/native/surface/visibility.ts +8 -1
- package/src/components/chart/price-axis-labels.tsx +3 -1
- package/src/components/chart/static/bar-chart-surface.test.tsx +97 -0
- package/src/components/chart/static/bar-chart-surface.tsx +151 -28
- package/src/components/chart/static/chart/axis-overlays.tsx +243 -0
- package/src/components/chart/static/chart/bitmap.ts +16 -9
- package/src/components/chart/static/chart/surface.test.tsx +92 -0
- package/src/components/chart/static/chart/surface.tsx +167 -24
- package/src/components/chart/stock-chart/index.test.ts +25 -0
- package/src/components/command-bar/layout-items/current-layout.ts +5 -1
- package/src/components/command-bar/list/view.tsx +1 -1
- package/src/components/command-bar/theme-picker.tsx +1 -1
- package/src/components/data-table/view.tsx +0 -12
- package/src/components/layout/detached-pane-shell.tsx +6 -3
- package/src/components/layout/floating-pane.tsx +6 -5
- package/src/components/layout/pane/content.tsx +20 -8
- package/src/components/layout/pane/footer/registration.tsx +6 -2
- package/src/components/layout/pane/frame.tsx +4 -4
- package/src/components/layout/pane/header.tsx +45 -4
- package/src/components/layout/pane/index.tsx +6 -4
- package/src/components/layout/pane/sizing.test.ts +43 -0
- package/src/components/layout/pane/sizing.ts +13 -4
- package/src/components/layout/shell/action-menu-overlay.tsx +1 -1
- package/src/components/layout/shell/cursor-occlusion.ts +204 -0
- package/src/components/layout/shell/drag/runtime.ts +9 -2
- package/src/components/layout/shell/index.test.tsx +92 -2
- package/src/components/layout/shell/index.tsx +45 -3
- package/src/components/layout/shell/layout-state.test.ts +33 -0
- package/src/components/layout/shell/layout-state.ts +4 -9
- package/src/components/layout/shell/pane/layers.tsx +10 -4
- package/src/components/layout/shell/visible-layout.ts +17 -0
- package/src/components/layout/status-bar.test.tsx +6 -1
- package/src/components/layout/status-bar.tsx +10 -6
- package/src/components/onboarding/onboarding-steps.tsx +8 -28
- package/src/components/onboarding/onboarding-wizard.test.tsx +13 -12
- package/src/components/onboarding/onboarding-wizard.tsx +2 -4
- package/src/components/onboarding/portfolio-step/index.tsx +0 -12
- package/src/components/onboarding/portfolio-step/types.ts +1 -3
- package/src/components/onboarding/wizard-keyboard.ts +2 -5
- package/src/components/pane-settings-dialog/desktop.tsx +1 -1
- package/src/components/price-performance.tsx +1 -1
- package/src/components/price-sparkline/view.tsx +15 -14
- package/src/components/speedometer-gauge/terminal.tsx +18 -15
- package/src/components/table-view-shared.tsx +12 -14
- package/src/components/ticker/list-table-view.tsx +1 -7
- package/src/components/ticker/list-table.test.tsx +0 -4
- package/src/components/ui/data-table/opentui/index.tsx +29 -22
- package/src/components/ui/data-table/types.ts +10 -4
- package/src/components/ui/fields.tsx +10 -3
- package/src/components/ui/list-view.tsx +1 -1
- package/src/components/ui/page-stack-view.tsx +6 -2
- package/src/components/ui/table-layout.test.ts +25 -0
- package/src/components/ui/table-layout.ts +22 -6
- package/src/components/ui/tabs.tsx +27 -12
- package/src/components/ui/ui.test.tsx +87 -9
- package/src/core/state/app/layout-reducer.ts +2 -2
- package/src/plugins/builtin/account-management/form-components.tsx +2 -2
- package/src/plugins/builtin/alerts/pane.tsx +1 -1
- package/src/plugins/builtin/analytics/index.test.tsx +6 -2
- package/src/plugins/builtin/analytics/pane-model.ts +43 -8
- package/src/plugins/builtin/buildout/cells.tsx +1 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +1 -1
- package/src/plugins/builtin/chat/message/desktop.tsx +1 -1
- package/src/plugins/builtin/chat/message/inline-tokens.tsx +1 -1
- package/src/plugins/builtin/chat/message/terminal.tsx +2 -2
- package/src/plugins/builtin/chat/sidebar.tsx +1 -1
- package/src/plugins/builtin/chat/status-widget.tsx +1 -1
- package/src/plugins/builtin/cloud/auth-actions.tsx +2 -2
- package/src/plugins/builtin/correlation/matrix/symbol-cell.tsx +1 -1
- package/src/plugins/builtin/debug/index.tsx +3 -4
- package/src/plugins/builtin/help/components.tsx +1 -1
- package/src/plugins/builtin/holders/treemap.tsx +1 -1
- package/src/plugins/builtin/kelly-sizer/constants.ts +3 -0
- package/src/plugins/builtin/kelly-sizer/fields.ts +214 -0
- package/src/plugins/builtin/kelly-sizer/index.test.tsx +197 -0
- package/src/plugins/builtin/kelly-sizer/index.tsx +47 -0
- package/src/plugins/builtin/kelly-sizer/model.test.ts +152 -0
- package/src/plugins/builtin/kelly-sizer/model.ts +580 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +558 -0
- package/src/plugins/builtin/kelly-sizer/portfolio.ts +71 -0
- package/src/plugins/builtin/kelly-sizer/sections.tsx +142 -0
- package/src/plugins/builtin/kelly-sizer/state.ts +67 -0
- package/src/plugins/builtin/kelly-sizer/test-support.tsx +174 -0
- package/src/plugins/builtin/kelly-sizer/types.ts +156 -0
- package/src/plugins/builtin/kelly-sizer/view.test.tsx +77 -0
- package/src/plugins/builtin/kelly-sizer/view.tsx +375 -0
- package/src/plugins/builtin/layout-manager/index.tsx +5 -1
- package/src/plugins/builtin/options/table.ts +5 -5
- package/src/plugins/builtin/options/view.test.tsx +141 -4
- package/src/plugins/builtin/options/view.tsx +76 -15
- package/src/plugins/builtin/portfolio-list/account-metrics.test.ts +26 -1
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +14 -0
- package/src/plugins/builtin/portfolio-list/index.test.ts +23 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +2 -0
- package/src/plugins/builtin/portfolio-list/summary/index.test.ts +59 -1
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +36 -5
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/content.tsx +149 -48
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/model.ts +25 -2
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph.tsx +5 -4
- package/src/plugins/builtin/ticker-detail/data-panes.test.ts +16 -0
- package/src/plugins/builtin/ticker-detail/index.test.tsx +143 -0
- package/src/plugins/catalog-ui.ts +2 -0
- package/src/plugins/ibkr/broker-adapter.ts +19 -0
- package/src/plugins/ibkr/config.test.ts +31 -2
- package/src/plugins/ibkr/config.ts +9 -2
- package/src/plugins/ibkr/flex/index.test.ts +18 -9
- package/src/plugins/ibkr/flex/index.ts +30 -0
- package/src/plugins/ibkr/flex/statement-client.ts +68 -14
- package/src/plugins/ibkr/gateway/account-loaders.ts +1 -1
- package/src/plugins/ibkr/gateway/account-summary.ts +1 -0
- package/src/plugins/ibkr/gateway/service/index.test.ts +8 -0
- package/src/plugins/ibkr/trade/ticket-panel.tsx +1 -1
- package/src/plugins/pane-manager/gridlock.ts +13 -5
- package/src/plugins/pane-manager/layout-state.ts +25 -0
- package/src/plugins/pane-manager.test.ts +33 -0
- package/src/plugins/pane-manager.ts +6 -1
- package/src/plugins/prediction-markets/chart.tsx +10 -4
- package/src/plugins/prediction-markets/detail/book.tsx +141 -96
- package/src/plugins/prediction-markets/detail/overview.tsx +3 -0
- package/src/plugins/prediction-markets/detail/pane.tsx +71 -64
- package/src/plugins/prediction-markets/detail/shared.tsx +19 -0
- package/src/plugins/prediction-markets/detail/trades.tsx +64 -19
- package/src/plugins/prediction-markets/detail.test.tsx +50 -0
- package/src/plugins/prediction-markets/pane.test.tsx +43 -0
- package/src/plugins/prediction-markets/pane.tsx +50 -22
- package/src/plugins/prediction-markets/test-helpers.tsx +41 -23
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +13 -2
- package/src/renderers/electrobun/view/data-table/index.tsx +19 -12
- package/src/renderers/electrobun/view/data-table/row.tsx +32 -18
- package/src/renderers/electrobun/view/desktop/controls.tsx +11 -5
- package/src/renderers/electrobun/view/host/box.tsx +6 -1
- package/src/renderers/electrobun/view/host/input.tsx +115 -18
- package/src/renderers/electrobun/view/host/style.ts +1 -0
- package/src/renderers/electrobun/view/host/tabs.tsx +7 -2
- package/src/renderers/electrobun/view/styles.css +18 -0
- package/src/renderers/opentui/host.tsx +17 -1
- package/src/renderers/opentui/native-smoke.ts +1 -0
- package/src/renderers/opentui/ui-host.tsx +57 -25
- package/src/sources/provider-router/batches.ts +16 -2
- package/src/sources/provider-router/financial-routes.ts +5 -0
- package/src/sources/provider-router/financials.ts +11 -23
- package/src/sources/provider-router/index.test.ts +189 -0
- package/src/sources/provider-router/primary.ts +3 -2
- package/src/sources/sec-edgar.test.ts +83 -0
- package/src/sources/sec-edgar.ts +231 -0
- package/src/sources/yahoo-finance.test.ts +51 -0
- package/src/sources/yahoo-finance.ts +49 -1
- package/src/state/app/context/index.test.tsx +5 -8
- package/src/types/broker.ts +1 -0
- package/src/types/config.ts +10 -0
- package/src/types/trading.ts +2 -0
- package/src/ui/host.tsx +16 -0
- package/src/ui/index.tsx +2 -0
- package/src/utils/financial-statements.ts +23 -0
- package/src/version.ts +1 -1
- package/src/components/onboarding/portfolio-step/manual-name-panel.tsx +0 -62
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { useMemo } from "react";
|
|
2
1
|
import { AsciiText, Box, Span, Strong, Text, TextAttributes, useUiHost } from "../../ui";
|
|
3
2
|
import { colors } from "../../theme/colors";
|
|
4
3
|
import { themes } from "../../theme/themes";
|
|
5
|
-
import type { PluginRegistry } from "../../plugins/registry";
|
|
6
4
|
import { detectShortcutPlatform, formatPrimaryShortcut, getShortcutDisplayMode } from "../../utils/shortcut-labels";
|
|
7
5
|
export { PortfolioStep, type PortfolioSub } from "./portfolio-step";
|
|
8
6
|
|
|
@@ -95,7 +93,7 @@ export function ThemeStep({ themeIds, selectedIdx, height }: { themeIds: string[
|
|
|
95
93
|
);
|
|
96
94
|
}
|
|
97
95
|
|
|
98
|
-
export function ShortcutsStep(
|
|
96
|
+
export function ShortcutsStep() {
|
|
99
97
|
const uiHost = useUiHost();
|
|
100
98
|
const shortcutPlatform = detectShortcutPlatform();
|
|
101
99
|
const shortcutDisplayMode = getShortcutDisplayMode(uiHost.kind);
|
|
@@ -109,30 +107,12 @@ export function ShortcutsStep({ pluginRegistry }: { pluginRegistry: PluginRegist
|
|
|
109
107
|
{ key: "q", desc: "Quit" },
|
|
110
108
|
];
|
|
111
109
|
|
|
112
|
-
const commandPrefixes =
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
{ key: "HELP", desc: "Open the help window" },
|
|
119
|
-
];
|
|
120
|
-
|
|
121
|
-
const builtInKeys = new Set(builtIn.map((entry) => entry.key.split(" ")[0]));
|
|
122
|
-
const pluginPrefixes: { key: string; desc: string }[] = [];
|
|
123
|
-
|
|
124
|
-
for (const [, template] of pluginRegistry.paneTemplates) {
|
|
125
|
-
if (!template.shortcut) continue;
|
|
126
|
-
if (builtInKeys.has(template.shortcut.prefix)) continue;
|
|
127
|
-
const label = template.shortcut.argPlaceholder
|
|
128
|
-
? `${template.shortcut.prefix} <${template.shortcut.argPlaceholder}>`
|
|
129
|
-
: template.shortcut.prefix;
|
|
130
|
-
pluginPrefixes.push({ key: label, desc: template.label });
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
pluginPrefixes.sort((a, b) => a.key.localeCompare(b.key));
|
|
134
|
-
return [...builtIn, ...pluginPrefixes];
|
|
135
|
-
}, [pluginRegistry]);
|
|
110
|
+
const commandPrefixes = [
|
|
111
|
+
{ key: "DES AAPL", desc: "Open security details" },
|
|
112
|
+
{ key: "TH", desc: "Switch theme" },
|
|
113
|
+
{ key: "PL", desc: "Toggle plugins" },
|
|
114
|
+
{ key: "HELP", desc: "Open the help window" },
|
|
115
|
+
];
|
|
136
116
|
|
|
137
117
|
const COL = 20;
|
|
138
118
|
|
|
@@ -152,7 +132,7 @@ export function ShortcutsStep({ pluginRegistry }: { pluginRegistry: PluginRegist
|
|
|
152
132
|
|
|
153
133
|
<Box height={2} />
|
|
154
134
|
<Box height={1}>
|
|
155
|
-
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{"
|
|
135
|
+
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{"Basic command prefixes"}</Text>
|
|
156
136
|
</Box>
|
|
157
137
|
<Box height={1} />
|
|
158
138
|
<Box height={1}>
|
|
@@ -93,11 +93,14 @@ afterEach(async () => {
|
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
describe("OnboardingWizard", () => {
|
|
96
|
-
test("
|
|
96
|
+
test("skips manual portfolio naming and shows compact starter shortcuts", async () => {
|
|
97
97
|
const pluginRegistry = {
|
|
98
98
|
allPlugins: new Map(),
|
|
99
99
|
brokers: new Map(),
|
|
100
|
-
paneTemplates: new Map(
|
|
100
|
+
paneTemplates: new Map([[
|
|
101
|
+
"ai",
|
|
102
|
+
{ label: "AI Screener", shortcut: { prefix: "AI", argPlaceholder: "prompt" } },
|
|
103
|
+
]]),
|
|
101
104
|
getPaneTemplatePluginId: () => undefined,
|
|
102
105
|
tickerRepository: createTickerRepository(),
|
|
103
106
|
persistence: { resources: undefined },
|
|
@@ -132,16 +135,14 @@ describe("OnboardingWizard", () => {
|
|
|
132
135
|
expect(frame).toContain("Create Manual Portfolio");
|
|
133
136
|
|
|
134
137
|
await emitKeypress(testSetup, { name: "return", sequence: "\r" });
|
|
135
|
-
frame = await waitForFrameToContain("
|
|
136
|
-
expect(frame).toContain("
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
frame
|
|
143
|
-
expect(frame).toContain("Main Portfoli");
|
|
144
|
-
expect(frame).not.toContain("Create Manual Portfolio");
|
|
138
|
+
frame = await waitForFrameToContain("After launch shortcuts");
|
|
139
|
+
expect(frame).toContain("Basic command prefixes");
|
|
140
|
+
expect(frame).toContain("DES AAPL");
|
|
141
|
+
expect(frame).toContain("HELP");
|
|
142
|
+
expect(frame).not.toContain("Name your portfolio");
|
|
143
|
+
expect(frame).not.toContain("Command-bar prefixes");
|
|
144
|
+
expect(frame).not.toContain("AI <prompt>");
|
|
145
|
+
expect(frame).not.toContain("AI Screener");
|
|
145
146
|
});
|
|
146
147
|
|
|
147
148
|
test("waits for broker positions to import before completing onboarding", async () => {
|
|
@@ -36,7 +36,7 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
36
36
|
const [themeIdx, setThemeIdx] = useState(0);
|
|
37
37
|
const [portfolioSub, setPortfolioSub] = useState<PortfolioSub>("choose");
|
|
38
38
|
const [portfolioOptionIdx, setPortfolioOptionIdx] = useState(0);
|
|
39
|
-
const
|
|
39
|
+
const portfolioName = "Main Portfolio";
|
|
40
40
|
const [brokerValues, setBrokerValues] = useState<Record<string, Record<string, string>>>({});
|
|
41
41
|
const [selectedBrokerId, setSelectedBrokerId] = useState<string | null>(null);
|
|
42
42
|
const [brokerFieldIdx, setBrokerFieldIdx] = useState(0);
|
|
@@ -255,8 +255,6 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
255
255
|
choices={portfolioChoices}
|
|
256
256
|
optionIdx={portfolioOptionIdx}
|
|
257
257
|
onOptionSelect={setPortfolioOptionIdx}
|
|
258
|
-
portfolioName={portfolioName}
|
|
259
|
-
onNameChange={setPortfolioName}
|
|
260
258
|
selectedBrokerId={selectedBrokerId}
|
|
261
259
|
brokerFields={activeBrokerFields}
|
|
262
260
|
brokerFieldIdx={brokerFieldIdx}
|
|
@@ -270,7 +268,7 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
270
268
|
/>
|
|
271
269
|
)}
|
|
272
270
|
{step === "shortcuts" && (
|
|
273
|
-
<ShortcutsStep
|
|
271
|
+
<ShortcutsStep />
|
|
274
272
|
)}
|
|
275
273
|
{step === "ready" && (
|
|
276
274
|
<ReadyStep
|
|
@@ -2,7 +2,6 @@ import { BrokerFieldsPanel } from "./broker-fields-panel";
|
|
|
2
2
|
import { BrokerSetupPanel } from "./broker-setup-panel";
|
|
3
3
|
import { BrokerSyncPanel } from "./broker-sync-panel";
|
|
4
4
|
import { PortfolioChoicePanel } from "./choose-panel";
|
|
5
|
-
import { ManualPortfolioNamePanel } from "./manual-name-panel";
|
|
6
5
|
import type { PortfolioStepProps, PortfolioSub } from "./types";
|
|
7
6
|
|
|
8
7
|
export type { PortfolioSub };
|
|
@@ -18,17 +17,6 @@ export function PortfolioStep(props: PortfolioStepProps) {
|
|
|
18
17
|
);
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
if (props.sub === "manual-name") {
|
|
22
|
-
return (
|
|
23
|
-
<ManualPortfolioNamePanel
|
|
24
|
-
portfolioName={props.portfolioName}
|
|
25
|
-
onNameChange={props.onNameChange}
|
|
26
|
-
editing={props.editing}
|
|
27
|
-
inputRef={props.inputRef}
|
|
28
|
-
/>
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
20
|
if (props.sub === "broker-setup" && props.selectedBrokerId) {
|
|
33
21
|
return (
|
|
34
22
|
<BrokerSetupPanel
|
|
@@ -3,15 +3,13 @@ import type { InputRenderable } from "../../../ui";
|
|
|
3
3
|
import type { BrokerConfigField } from "../../../types/broker";
|
|
4
4
|
import type { ListViewItem } from "../../ui";
|
|
5
5
|
|
|
6
|
-
export type PortfolioSub = "choose" | "
|
|
6
|
+
export type PortfolioSub = "choose" | "broker-setup" | "broker-fields" | "broker-sync";
|
|
7
7
|
|
|
8
8
|
export interface PortfolioStepProps {
|
|
9
9
|
sub: PortfolioSub;
|
|
10
10
|
choices: ListViewItem[];
|
|
11
11
|
optionIdx: number;
|
|
12
12
|
onOptionSelect: (idx: number) => void;
|
|
13
|
-
portfolioName: string;
|
|
14
|
-
onNameChange: (name: string) => void;
|
|
15
13
|
selectedBrokerId: string | null;
|
|
16
14
|
brokerFields: BrokerConfigField[];
|
|
17
15
|
brokerFieldIdx: number;
|
|
@@ -74,9 +74,7 @@ export function useOnboardingKeyboard({
|
|
|
74
74
|
if (editingField) {
|
|
75
75
|
if (event.name === "return") {
|
|
76
76
|
setEditingField(false);
|
|
77
|
-
if (portfolioSub === "
|
|
78
|
-
nextStep();
|
|
79
|
-
} else if (portfolioSub === "broker-fields" && selectedBrokerId) {
|
|
77
|
+
if (portfolioSub === "broker-fields" && selectedBrokerId) {
|
|
80
78
|
const currentField = activeBrokerFields[brokerFieldIdx];
|
|
81
79
|
if (!currentField) {
|
|
82
80
|
void syncSelectedBroker();
|
|
@@ -125,8 +123,7 @@ export function useOnboardingKeyboard({
|
|
|
125
123
|
if (choice.id === "manual") {
|
|
126
124
|
resetBrokerSync();
|
|
127
125
|
setSelectedBrokerId(null);
|
|
128
|
-
|
|
129
|
-
setEditingField(true);
|
|
126
|
+
nextStep();
|
|
130
127
|
} else {
|
|
131
128
|
resetBrokerSync();
|
|
132
129
|
setSelectedBrokerId(choice.id);
|
|
@@ -183,7 +183,7 @@ function DesktopSettingsRow({
|
|
|
183
183
|
flexDirection="column"
|
|
184
184
|
minHeight={field.description ? undefined : 2}
|
|
185
185
|
backgroundColor={hovered || selected ? desktopHoverSurface() : "transparent"}
|
|
186
|
-
|
|
186
|
+
onMouseOver={onHover}
|
|
187
187
|
onMouseDown={field.type === "select" ? undefined : (event: any) => {
|
|
188
188
|
event.stopPropagation?.();
|
|
189
189
|
event.preventDefault?.();
|
|
@@ -157,7 +157,7 @@ export function PriceReturnTable({
|
|
|
157
157
|
height={1}
|
|
158
158
|
width={width}
|
|
159
159
|
backgroundColor={row.selected ? blendHex(colors.panel, colors.borderFocused, 0.18) : colors.panel}
|
|
160
|
-
|
|
160
|
+
onMouseOver={() => onSelectSymbol(row.symbol)}
|
|
161
161
|
onMouseDown={(event: any) => {
|
|
162
162
|
onFocusInteraction?.(event);
|
|
163
163
|
onSelectSymbol(row.symbol);
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import { colors } from "../../theme/colors";
|
|
11
11
|
import { useThemeColors } from "../../theme/theme-context";
|
|
12
12
|
import type { PricePoint } from "../../types/financials";
|
|
13
|
-
import {
|
|
13
|
+
import { resolveNativeBitmapSize, shouldRenderNativeBitmap } from "../chart/native/bitmap-support";
|
|
14
14
|
import { renderSparklineBitmap } from "./bitmap";
|
|
15
15
|
import {
|
|
16
16
|
buildSamples,
|
|
@@ -90,22 +90,21 @@ function TerminalPriceSparkline({
|
|
|
90
90
|
const { nativeCharts, cellWidthPx = 8, cellHeightPx = 18, pixelRatio = 1 } = useUiCapabilities();
|
|
91
91
|
const nativeRenderer = useNativeRenderer();
|
|
92
92
|
const rendererResolution = nativeRenderer.resolution;
|
|
93
|
+
const rendererCapabilities = nativeRenderer.capabilities;
|
|
93
94
|
const rendererTerminalWidth = nativeRenderer.terminalWidth;
|
|
94
95
|
const rendererTerminalHeight = nativeRenderer.terminalHeight;
|
|
95
96
|
const bitmap = useMemo(() => {
|
|
96
|
-
if (
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
pixelHeight: Math.max(1, Math.round(height * cellHeightPx * scale)),
|
|
108
|
-
};
|
|
97
|
+
if (values.length < 2 || !shouldRenderNativeBitmap(nativeRenderer, nativeCharts === true)) return null;
|
|
98
|
+
const bitmapSize = resolveNativeBitmapSize({
|
|
99
|
+
width,
|
|
100
|
+
height,
|
|
101
|
+
resolution: rendererResolution,
|
|
102
|
+
terminalWidth: rendererTerminalWidth,
|
|
103
|
+
terminalHeight: rendererTerminalHeight,
|
|
104
|
+
cellWidthPx,
|
|
105
|
+
cellHeightPx,
|
|
106
|
+
pixelRatio,
|
|
107
|
+
});
|
|
109
108
|
return renderSparklineBitmap(values, bitmapSize.pixelWidth, bitmapSize.pixelHeight, color, { area, compact: height <= 1 });
|
|
110
109
|
}, [
|
|
111
110
|
area,
|
|
@@ -114,7 +113,9 @@ function TerminalPriceSparkline({
|
|
|
114
113
|
color,
|
|
115
114
|
height,
|
|
116
115
|
nativeCharts,
|
|
116
|
+
nativeRenderer,
|
|
117
117
|
pixelRatio,
|
|
118
|
+
rendererCapabilities,
|
|
118
119
|
rendererResolution,
|
|
119
120
|
rendererTerminalHeight,
|
|
120
121
|
rendererTerminalWidth,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useMemo } from "react";
|
|
2
2
|
import { Box, ChartSurface, Span, Text, TextAttributes, useNativeRenderer, useUiCapabilities } from "../../ui";
|
|
3
|
-
import {
|
|
3
|
+
import type { NativeChartBitmap } from "../chart/native/chart-rasterizer";
|
|
4
|
+
import { resolveNativeBitmapSize, shouldRenderNativeBitmap } from "../chart/native/bitmap-support";
|
|
4
5
|
import { drawCircle, drawLine, parseHex } from "../chart/native/raster/primitives";
|
|
5
6
|
import { colors } from "../../theme/colors";
|
|
6
7
|
import {
|
|
@@ -203,29 +204,29 @@ export function TerminalSpeedometerGauge({
|
|
|
203
204
|
const labelRow = useMemo(() => renderLabelRow(gaugeWidth, min, max, segments), [gaugeWidth, max, min, segments]);
|
|
204
205
|
const tickRow = useMemo(() => renderTickRow(gaugeWidth, min, max), [gaugeWidth, max, min]);
|
|
205
206
|
const arcRows = useMemo(() => renderArcRows(value, min, max, gaugeWidth, segments), [gaugeWidth, max, min, segments, value]);
|
|
207
|
+
const rendererCapabilities = nativeRenderer.capabilities;
|
|
206
208
|
const rendererResolution = nativeRenderer.resolution;
|
|
207
209
|
const rendererTerminalWidth = nativeRenderer.terminalWidth;
|
|
208
210
|
const rendererTerminalHeight = nativeRenderer.terminalHeight;
|
|
209
211
|
const bitmap = useMemo<NativeChartBitmap | null>(() => {
|
|
210
|
-
if (!nativeCharts) return null;
|
|
211
|
-
const bitmapSize =
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const pixelHeight = bitmapSize?.pixelHeight ?? Math.max(1, Math.round(arcRows.length * cellHeightPx * scale));
|
|
212
|
+
if (!shouldRenderNativeBitmap(nativeRenderer, nativeCharts === true)) return null;
|
|
213
|
+
const bitmapSize = resolveNativeBitmapSize({
|
|
214
|
+
width: gaugeWidth,
|
|
215
|
+
height: arcRows.length,
|
|
216
|
+
resolution: rendererResolution,
|
|
217
|
+
terminalWidth: rendererTerminalWidth,
|
|
218
|
+
terminalHeight: rendererTerminalHeight,
|
|
219
|
+
cellWidthPx,
|
|
220
|
+
cellHeightPx,
|
|
221
|
+
pixelRatio,
|
|
222
|
+
});
|
|
222
223
|
return renderGaugeBitmap(
|
|
223
224
|
value,
|
|
224
225
|
min,
|
|
225
226
|
max,
|
|
226
227
|
segments,
|
|
227
|
-
pixelWidth,
|
|
228
|
-
pixelHeight,
|
|
228
|
+
bitmapSize.pixelWidth,
|
|
229
|
+
bitmapSize.pixelHeight,
|
|
229
230
|
);
|
|
230
231
|
}, [
|
|
231
232
|
arcRows.length,
|
|
@@ -235,7 +236,9 @@ export function TerminalSpeedometerGauge({
|
|
|
235
236
|
max,
|
|
236
237
|
min,
|
|
237
238
|
nativeCharts,
|
|
239
|
+
nativeRenderer,
|
|
238
240
|
pixelRatio,
|
|
241
|
+
rendererCapabilities,
|
|
239
242
|
rendererResolution,
|
|
240
243
|
rendererTerminalHeight,
|
|
241
244
|
rendererTerminalWidth,
|
|
@@ -2,11 +2,10 @@ import {
|
|
|
2
2
|
useCallback,
|
|
3
3
|
useEffect,
|
|
4
4
|
useRef,
|
|
5
|
-
useState,
|
|
6
5
|
type ReactNode,
|
|
7
6
|
type RefObject,
|
|
8
7
|
} from "react";
|
|
9
|
-
import { Box, type ScrollBoxRenderable } from "../ui";
|
|
8
|
+
import { Box, useUiCapabilities, type ScrollBoxRenderable } from "../ui";
|
|
10
9
|
import { isPlainKeyboardEvent } from "../utils/keyboard";
|
|
11
10
|
|
|
12
11
|
function listenToScrollBarChange(
|
|
@@ -56,15 +55,23 @@ export function TableViewFrame({
|
|
|
56
55
|
after,
|
|
57
56
|
children,
|
|
58
57
|
}: TableViewFrameProps) {
|
|
58
|
+
const { nativePaneChrome } = useUiCapabilities();
|
|
59
|
+
const nativeFlexibleFrame = nativePaneChrome === true;
|
|
60
|
+
|
|
59
61
|
return (
|
|
60
62
|
<Box
|
|
61
63
|
flexDirection="column"
|
|
62
|
-
flexGrow={height == null ? 1 : undefined}
|
|
63
|
-
flexShrink={height == null ? undefined : 0}
|
|
64
|
+
flexGrow={nativeFlexibleFrame || height == null ? 1 : undefined}
|
|
65
|
+
flexShrink={nativeFlexibleFrame ? 1 : height == null ? undefined : 0}
|
|
66
|
+
flexBasis={nativeFlexibleFrame ? 0 : undefined}
|
|
64
67
|
width={width}
|
|
65
|
-
height={height}
|
|
68
|
+
height={nativeFlexibleFrame ? undefined : height}
|
|
69
|
+
minWidth={nativeFlexibleFrame ? 0 : undefined}
|
|
70
|
+
minHeight={nativeFlexibleFrame ? 0 : undefined}
|
|
71
|
+
maxHeight={nativeFlexibleFrame ? "100%" : undefined}
|
|
66
72
|
backgroundColor={backgroundColor}
|
|
67
73
|
overflow="hidden"
|
|
74
|
+
data-gloom-role="table-view-frame"
|
|
68
75
|
>
|
|
69
76
|
{before}
|
|
70
77
|
{children}
|
|
@@ -106,23 +113,16 @@ export function useTableViewState({
|
|
|
106
113
|
headerScrollRef,
|
|
107
114
|
scrollRef,
|
|
108
115
|
syncHeaderScroll,
|
|
109
|
-
hoveredIdx,
|
|
110
|
-
setHoveredIdx,
|
|
111
116
|
}: {
|
|
112
117
|
headerScrollRef?: RefObject<ScrollBoxRenderable | null>;
|
|
113
118
|
scrollRef?: RefObject<ScrollBoxRenderable | null>;
|
|
114
119
|
syncHeaderScroll?: () => void;
|
|
115
|
-
hoveredIdx?: number | null;
|
|
116
|
-
setHoveredIdx?: (index: number | null) => void;
|
|
117
120
|
}) {
|
|
118
121
|
const internalHeaderScrollRef = useRef<ScrollBoxRenderable>(null);
|
|
119
122
|
const internalScrollRef = useRef<ScrollBoxRenderable>(null);
|
|
120
|
-
const [internalHoveredIdx, setInternalHoveredIdx] = useState<number | null>(null);
|
|
121
123
|
|
|
122
124
|
const effectiveHeaderScrollRef = headerScrollRef ?? internalHeaderScrollRef;
|
|
123
125
|
const effectiveScrollRef = scrollRef ?? internalScrollRef;
|
|
124
|
-
const effectiveHoveredIdx = hoveredIdx !== undefined ? hoveredIdx : internalHoveredIdx;
|
|
125
|
-
const effectiveSetHoveredIdx = setHoveredIdx ?? setInternalHoveredIdx;
|
|
126
126
|
|
|
127
127
|
const defaultSyncHeaderScroll = useCallback(() => {
|
|
128
128
|
const body = effectiveScrollRef.current;
|
|
@@ -136,8 +136,6 @@ export function useTableViewState({
|
|
|
136
136
|
return {
|
|
137
137
|
effectiveHeaderScrollRef,
|
|
138
138
|
effectiveScrollRef,
|
|
139
|
-
effectiveHoveredIdx,
|
|
140
|
-
effectiveSetHoveredIdx,
|
|
141
139
|
effectiveSyncHeaderScroll: syncHeaderScroll ?? defaultSyncHeaderScroll,
|
|
142
140
|
};
|
|
143
141
|
}
|
|
@@ -56,8 +56,6 @@ export interface TickerListTableViewProps {
|
|
|
56
56
|
scrollRef?: RefObject<ScrollBoxRenderable | null>;
|
|
57
57
|
syncHeaderScroll?: () => void;
|
|
58
58
|
onBodyScrollActivity?: () => void;
|
|
59
|
-
hoveredIdx?: number | null;
|
|
60
|
-
setHoveredIdx?: (index: number | null) => void;
|
|
61
59
|
keyboardNavigation?: boolean;
|
|
62
60
|
onRootKeyDown?: (event: DataTableKeyEvent) => boolean | void;
|
|
63
61
|
onVisibleRangeChange?: (range: TickerListVisibleRange) => void;
|
|
@@ -119,8 +117,6 @@ export function TickerListTableView({
|
|
|
119
117
|
scrollRef,
|
|
120
118
|
syncHeaderScroll,
|
|
121
119
|
onBodyScrollActivity,
|
|
122
|
-
hoveredIdx,
|
|
123
|
-
setHoveredIdx,
|
|
124
120
|
keyboardNavigation = true,
|
|
125
121
|
onRootKeyDown,
|
|
126
122
|
onVisibleRangeChange,
|
|
@@ -187,7 +183,7 @@ export function TickerListTableView({
|
|
|
187
183
|
ticker: TickerRecord,
|
|
188
184
|
column: ColumnConfig,
|
|
189
185
|
_index: number,
|
|
190
|
-
rowState: { selected: boolean
|
|
186
|
+
rowState: { selected: boolean },
|
|
191
187
|
) => {
|
|
192
188
|
const financials = financialsMap.get(ticker.metadata.ticker);
|
|
193
189
|
if (column.id === PRICE_SPARKLINE_COLUMN_ID) {
|
|
@@ -279,8 +275,6 @@ export function TickerListTableView({
|
|
|
279
275
|
scrollRef={effectiveScrollRef}
|
|
280
276
|
syncHeaderScroll={syncHeaderScroll}
|
|
281
277
|
onBodyScrollActivity={handleBodyScrollActivity}
|
|
282
|
-
hoveredIdx={hoveredIdx}
|
|
283
|
-
setHoveredIdx={setHoveredIdx}
|
|
284
278
|
keyboardNavigation={keyboardNavigation}
|
|
285
279
|
onRootKeyDown={onRootKeyDown}
|
|
286
280
|
resetScrollKey={resetScrollKey}
|
|
@@ -32,8 +32,6 @@ const manyTickers: TickerRecord[] = Array.from({ length: 1000 }, (_, index) => (
|
|
|
32
32
|
},
|
|
33
33
|
}));
|
|
34
34
|
|
|
35
|
-
function noop(_index: number | null): void {}
|
|
36
|
-
|
|
37
35
|
function resolveCell(_column: ColumnConfig, ticker: TickerRecord, _financials: TickerFinancials | undefined): TickerTableCell {
|
|
38
36
|
resolveCellCallCount += 1;
|
|
39
37
|
return { text: ticker.metadata.ticker };
|
|
@@ -69,8 +67,6 @@ function LargeTickerListTableHarness() {
|
|
|
69
67
|
columns={columns}
|
|
70
68
|
tickers={manyTickers}
|
|
71
69
|
cursorSymbol={cursorSymbol}
|
|
72
|
-
hoveredIdx={null}
|
|
73
|
-
setHoveredIdx={noop}
|
|
74
70
|
setCursorSymbol={setCursorSymbol}
|
|
75
71
|
resolveCell={resolveCell}
|
|
76
72
|
financialsMap={financialsMap}
|
|
@@ -40,14 +40,13 @@ export function OpenTuiDataTable<T, C extends DataTableColumn = DataTableColumn>
|
|
|
40
40
|
scrollRef,
|
|
41
41
|
syncHeaderScroll,
|
|
42
42
|
onBodyScrollActivity,
|
|
43
|
-
hoveredIdx,
|
|
44
|
-
setHoveredIdx,
|
|
45
43
|
headerScrollId,
|
|
46
44
|
bodyScrollId,
|
|
47
45
|
getItemKey,
|
|
48
46
|
isSelected,
|
|
49
47
|
onSelect,
|
|
50
48
|
onActivate,
|
|
49
|
+
onTableMouseDown,
|
|
51
50
|
onRowMouseDown,
|
|
52
51
|
onRowContextMenu,
|
|
53
52
|
rowContextMenuSurface = false,
|
|
@@ -60,6 +59,9 @@ export function OpenTuiDataTable<T, C extends DataTableColumn = DataTableColumn>
|
|
|
60
59
|
emptyStateHint,
|
|
61
60
|
virtualize = true,
|
|
62
61
|
overscan = 3,
|
|
62
|
+
columnGap = 1,
|
|
63
|
+
horizontalPadding = 1,
|
|
64
|
+
fillAvailableWidth = true,
|
|
63
65
|
showHorizontalScrollbar = true,
|
|
64
66
|
scrollToIndex,
|
|
65
67
|
scrollToIndexAlign = "nearest",
|
|
@@ -105,17 +107,21 @@ export function OpenTuiDataTable<T, C extends DataTableColumn = DataTableColumn>
|
|
|
105
107
|
],
|
|
106
108
|
);
|
|
107
109
|
const { endIndex, startIndex, viewportHeight, visibleItems } = tableWindow;
|
|
108
|
-
const tableWidth = useMemo(
|
|
110
|
+
const tableWidth = useMemo(
|
|
111
|
+
() => getTableWidth(columns, columnGap, horizontalPadding),
|
|
112
|
+
[columnGap, columns, horizontalPadding],
|
|
113
|
+
);
|
|
109
114
|
const {
|
|
110
115
|
contentWidth: measuredContentWidth,
|
|
111
116
|
viewportWidth: measuredViewportWidth,
|
|
112
117
|
measureContentWidth,
|
|
113
118
|
} = useMeasuredTableContentWidth(tableWidth, headerScrollRef, scrollRef);
|
|
114
119
|
const horizontalScrollbarVisible = showHorizontalScrollbar
|
|
115
|
-
&& hasMeaningfulTableHorizontalOverflow(tableWidth, measuredViewportWidth);
|
|
120
|
+
&& hasMeaningfulTableHorizontalOverflow(tableWidth, measuredViewportWidth, columnGap);
|
|
121
|
+
const contentWidth = fillAvailableWidth ? measuredContentWidth : tableWidth;
|
|
116
122
|
const displayColumns = useMemo(
|
|
117
|
-
() => expandTableColumns(columns,
|
|
118
|
-
[columns,
|
|
123
|
+
() => expandTableColumns(columns, contentWidth, columnGap, horizontalPadding),
|
|
124
|
+
[columnGap, columns, contentWidth, horizontalPadding],
|
|
119
125
|
);
|
|
120
126
|
const handleRowMouseDown =
|
|
121
127
|
useDoubleClickActivation<DataTableRowPointerTarget<T>>({
|
|
@@ -264,8 +270,8 @@ export function OpenTuiDataTable<T, C extends DataTableColumn = DataTableColumn>
|
|
|
264
270
|
<Box
|
|
265
271
|
flexDirection="row"
|
|
266
272
|
height={1}
|
|
267
|
-
{...tableContentWidthProps(
|
|
268
|
-
paddingX={
|
|
273
|
+
{...tableContentWidthProps(contentWidth)}
|
|
274
|
+
paddingX={horizontalPadding}
|
|
269
275
|
backgroundColor={colors.panel}
|
|
270
276
|
>
|
|
271
277
|
{displayColumns.map((column) => {
|
|
@@ -283,10 +289,11 @@ export function OpenTuiDataTable<T, C extends DataTableColumn = DataTableColumn>
|
|
|
283
289
|
return (
|
|
284
290
|
<Box
|
|
285
291
|
key={column.id}
|
|
286
|
-
width={column.width +
|
|
292
|
+
width={column.width + columnGap}
|
|
287
293
|
backgroundColor={column.headerBackgroundColor ?? colors.panel}
|
|
288
294
|
onMouseDown={(event: any) => {
|
|
289
295
|
focusPane();
|
|
296
|
+
onTableMouseDown?.(event);
|
|
290
297
|
event.preventDefault();
|
|
291
298
|
onHeaderClick(column.id);
|
|
292
299
|
}}
|
|
@@ -315,6 +322,7 @@ export function OpenTuiDataTable<T, C extends DataTableColumn = DataTableColumn>
|
|
|
315
322
|
focusable={false}
|
|
316
323
|
onMouseDown={() => {
|
|
317
324
|
focusPane();
|
|
325
|
+
onTableMouseDown?.({});
|
|
318
326
|
}}
|
|
319
327
|
onSizeChange={measureContentWidth}
|
|
320
328
|
>
|
|
@@ -339,11 +347,12 @@ export function OpenTuiDataTable<T, C extends DataTableColumn = DataTableColumn>
|
|
|
339
347
|
key={getItemKey(item, index)}
|
|
340
348
|
flexDirection="row"
|
|
341
349
|
height={1}
|
|
342
|
-
{...tableContentWidthProps(
|
|
343
|
-
paddingX={
|
|
350
|
+
{...tableContentWidthProps(contentWidth)}
|
|
351
|
+
paddingX={horizontalPadding}
|
|
344
352
|
backgroundColor={sectionHeader.backgroundColor ?? colors.bg}
|
|
345
353
|
onMouseDown={(event: any) => {
|
|
346
354
|
focusPane();
|
|
355
|
+
onTableMouseDown?.(event);
|
|
347
356
|
event.preventDefault();
|
|
348
357
|
}}
|
|
349
358
|
>
|
|
@@ -358,8 +367,7 @@ export function OpenTuiDataTable<T, C extends DataTableColumn = DataTableColumn>
|
|
|
358
367
|
}
|
|
359
368
|
|
|
360
369
|
const selected = isSelected(item, index);
|
|
361
|
-
const
|
|
362
|
-
const rowState = { selected, hovered };
|
|
370
|
+
const rowState = { selected };
|
|
363
371
|
const rowBackgroundColor = getRowBackgroundColor?.(
|
|
364
372
|
item,
|
|
365
373
|
index,
|
|
@@ -367,24 +375,22 @@ export function OpenTuiDataTable<T, C extends DataTableColumn = DataTableColumn>
|
|
|
367
375
|
);
|
|
368
376
|
const rowBg = selected
|
|
369
377
|
? colors.selected
|
|
370
|
-
:
|
|
371
|
-
|
|
372
|
-
: rowBackgroundColor ?? colors.bg;
|
|
378
|
+
: rowBackgroundColor ?? colors.bg;
|
|
379
|
+
const rowHoverBg = selected ? undefined : hoverBg();
|
|
373
380
|
|
|
374
381
|
return (
|
|
375
382
|
<Box
|
|
376
383
|
key={getItemKey(item, index)}
|
|
377
384
|
flexDirection="row"
|
|
378
385
|
height={1}
|
|
379
|
-
{...tableContentWidthProps(
|
|
380
|
-
paddingX={
|
|
386
|
+
{...tableContentWidthProps(contentWidth)}
|
|
387
|
+
paddingX={horizontalPadding}
|
|
381
388
|
backgroundColor={rowBg}
|
|
389
|
+
hoverBackgroundColor={rowHoverBg}
|
|
382
390
|
data-gloom-context-menu-surface={rowContextMenuSurface ? "true" : undefined}
|
|
383
|
-
onMouseMove={() => {
|
|
384
|
-
if (hoveredIdx !== index) setHoveredIdx(index);
|
|
385
|
-
}}
|
|
386
391
|
onMouseDown={(event: any) => {
|
|
387
392
|
focusPane();
|
|
393
|
+
onTableMouseDown?.(event);
|
|
388
394
|
if (onRowMouseDown?.(item, index, event) === true) {
|
|
389
395
|
return;
|
|
390
396
|
}
|
|
@@ -404,10 +410,11 @@ export function OpenTuiDataTable<T, C extends DataTableColumn = DataTableColumn>
|
|
|
404
410
|
return (
|
|
405
411
|
<Box
|
|
406
412
|
key={column.id}
|
|
407
|
-
width={column.width +
|
|
413
|
+
width={column.width + columnGap}
|
|
408
414
|
backgroundColor={cell.backgroundColor ?? rowBg}
|
|
409
415
|
onMouseDown={(event: any) => {
|
|
410
416
|
focusPane();
|
|
417
|
+
onTableMouseDown?.(event);
|
|
411
418
|
if (cell.onMouseDown) {
|
|
412
419
|
cell.onMouseDown(event);
|
|
413
420
|
return;
|