gloomberb 0.6.1 → 0.7.1
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/package.json +1 -1
- package/patches/@opentui%2Fcore@0.1.90.patch +48 -24
- package/src/app.tsx +158 -57
- package/src/capabilities/factories.ts +3 -0
- package/src/cli/registry.test.ts +0 -1
- package/src/components/chart/chart-controller.ts +0 -1
- package/src/components/chart/chart-data.ts +1 -31
- package/src/components/chart/chart-renderer.ts +94 -12
- package/src/components/chart/chart-resolution.test.ts +0 -36
- package/src/components/chart/chart-resolution.ts +0 -27
- package/src/components/chart/chart.test.ts +60 -45
- package/src/components/chart/comparison-chart-data.test.ts +1 -5
- package/src/components/chart/comparison-chart-data.ts +1 -4
- package/src/components/chart/comparison-chart-renderer.test.ts +26 -0
- package/src/components/chart/comparison-chart-renderer.ts +13 -6
- package/src/components/chart/comparison-stock-chart.tsx +63 -39
- package/src/components/chart/price-axis-labels.tsx +114 -0
- package/src/components/chart/static-chart-surface.tsx +30 -4
- package/src/components/chart/stock-chart.test.ts +0 -16
- package/src/components/chart/stock-chart.tsx +69 -41
- package/src/components/chart/time-axis-label.tsx +120 -0
- package/src/components/command-bar/__snapshots__/command-bar.test.tsx.snap +0 -28
- package/src/components/command-bar/command-bar.test.tsx +182 -36
- package/src/components/command-bar/command-bar.tsx +943 -1016
- package/src/components/command-bar/command-registry.ts +0 -10
- package/src/components/command-bar/confirm-body.tsx +51 -0
- package/src/components/command-bar/helpers.ts +2 -5
- package/src/components/command-bar/list-model.ts +76 -0
- package/src/components/command-bar/theme-picker.tsx +254 -0
- package/src/components/command-bar/view-model.test.ts +4 -4
- package/src/components/command-bar/view-model.ts +1 -20
- package/src/components/command-bar/workflow-ops.ts +4 -0
- package/src/components/command-bar/workflow-types.ts +1 -2
- package/src/components/data-table-stack-view.test.tsx +39 -4
- package/src/components/data-table-stack-view.tsx +11 -104
- package/src/components/data-table-view.tsx +43 -79
- package/src/components/feed-data-table-stack-view.tsx +10 -17
- package/src/components/feed-stack-controller.ts +39 -0
- package/src/components/index.ts +5 -1
- package/src/components/layout/detached-pane-shell.tsx +29 -10
- package/src/components/layout/floating-pane.tsx +35 -4
- package/src/components/layout/header.tsx +12 -7
- package/src/components/layout/pane-content.test.tsx +64 -0
- package/src/components/layout/pane-content.tsx +2 -0
- package/src/components/layout/pane-cycle-order.test.ts +30 -0
- package/src/components/layout/pane-cycle-order.ts +32 -0
- package/src/components/layout/pane-footer.tsx +11 -4
- package/src/components/layout/pane-header.tsx +5 -5
- package/src/components/layout/pane-sizing.ts +9 -2
- package/src/components/layout/pane-title.ts +4 -1
- package/src/components/layout/pane.tsx +11 -5
- package/src/components/layout/shell.test.tsx +264 -22
- package/src/components/layout/shell.tsx +46 -8
- package/src/components/layout/status-bar.tsx +2 -0
- package/src/components/onboarding/onboarding-wizard.tsx +1 -2
- package/src/components/pane-settings-dialog.tsx +12 -4
- package/src/components/pane-template-wizard.test.tsx +3 -4
- package/src/components/speedometer-gauge.test.tsx +45 -0
- package/src/components/speedometer-gauge.tsx +562 -0
- package/src/components/table-view-shared.tsx +218 -0
- package/src/components/ticker-list-table-view.tsx +57 -73
- package/src/components/ticker-list-table.test.tsx +82 -1
- package/src/components/ticker-list-table.tsx +22 -9
- package/src/components/ui/confirm-dialog.tsx +66 -0
- package/src/components/ui/data-table.tsx +30 -19
- package/src/components/ui/frame.tsx +2 -0
- package/src/components/ui/index.ts +0 -1
- package/src/components/ui/page-stack-view.tsx +39 -10
- package/src/components/ui/table-layout.test.ts +42 -0
- package/src/components/ui/table-layout.ts +49 -4
- package/src/components/ui/tabs.tsx +34 -1
- package/src/components/ui/ui.test.tsx +168 -21
- package/src/core/state/app-state.ts +10 -17
- package/src/market-data/coordinator.test.ts +76 -1
- package/src/market-data/coordinator.ts +218 -49
- package/src/news/aggregator.test.ts +49 -1
- package/src/news/aggregator.ts +48 -3
- package/src/news/hooks.ts +6 -2
- package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +46 -16
- package/src/plugins/builtin/ai/providers.ts +0 -17
- package/src/plugins/builtin/ai/screener-pane.test.tsx +3 -4
- package/src/plugins/builtin/ai/screener-pane.tsx +3 -5
- package/src/plugins/builtin/analytics/index.tsx +1 -18
- package/src/plugins/builtin/ask-ai.test.tsx +89 -2
- package/src/plugins/builtin/broker-manager/index.test.tsx +2 -2
- package/src/plugins/builtin/broker-manager/index.tsx +19 -36
- package/src/plugins/builtin/chat-controller.test.ts +188 -1
- package/src/plugins/builtin/chat-controller.ts +537 -220
- package/src/plugins/builtin/chat.test.tsx +431 -13
- package/src/plugins/builtin/chat.tsx +1134 -227
- package/src/plugins/builtin/cloud-tweets.tsx +1 -1
- package/src/plugins/builtin/comparison-chart.test.tsx +53 -2
- package/src/plugins/builtin/comparison-chart.tsx +11 -5
- package/src/plugins/builtin/correlation/index.tsx +1 -6
- package/src/plugins/builtin/debug.tsx +1 -3
- package/src/plugins/builtin/econ/index.tsx +1 -2
- package/src/plugins/builtin/fear-greed/fear-greed-data.test.ts +72 -0
- package/src/plugins/builtin/fear-greed/fear-greed-data.ts +366 -0
- package/src/plugins/builtin/fear-greed/index.tsx +587 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
- package/src/plugins/builtin/help.test.tsx +2 -2
- package/src/plugins/builtin/help.tsx +2 -2
- package/src/plugins/builtin/holders/index.test.ts +13 -1
- package/src/plugins/builtin/holders/index.tsx +64 -29
- package/src/plugins/builtin/insider/index.tsx +22 -89
- package/src/plugins/builtin/market-movers/index.tsx +13 -19
- package/src/plugins/builtin/market-movers/screener.test.ts +39 -1
- package/src/plugins/builtin/market-movers/screener.ts +70 -17
- package/src/plugins/builtin/news-wire/breaking-pane.tsx +3 -2
- package/src/plugins/builtin/news-wire/index.tsx +41 -2
- package/src/plugins/builtin/news-wire/industry-pane.tsx +4 -18
- package/src/plugins/builtin/news-wire/news-detail-view.tsx +46 -4
- package/src/plugins/builtin/news-wire/news-preset-pane.tsx +3 -2
- package/src/plugins/builtin/news-wire/news-table.tsx +11 -11
- package/src/plugins/builtin/news.tsx +4 -16
- package/src/plugins/builtin/notes.tsx +19 -55
- package/src/plugins/builtin/options.test.tsx +58 -2
- package/src/plugins/builtin/options.tsx +110 -65
- package/src/plugins/builtin/plugin-groups.ts +2 -0
- package/src/plugins/builtin/portfolio-list/cli/commands.ts +1 -1
- package/src/plugins/builtin/portfolio-list/pane.tsx +16 -39
- package/src/plugins/builtin/research/index.test.ts +94 -0
- package/src/plugins/builtin/research/index.tsx +164 -33
- package/src/plugins/builtin/sec-filing-display.tsx +84 -0
- package/src/plugins/builtin/sec.tsx +13 -66
- package/src/plugins/builtin/sectors/index.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +0 -1
- package/src/plugins/builtin/ticker-detail/index.tsx +0 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +0 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +28 -22
- package/src/plugins/builtin/ticker-detail-chart-tab-switch.test.tsx +3 -4
- package/src/plugins/builtin/ticker-detail.test.tsx +38 -0
- package/src/plugins/builtin/world-indices/index.tsx +1 -1
- package/src/plugins/builtin/yield-curve/index.tsx +1 -1
- package/src/plugins/ibkr/broker-adapter.ts +1 -3
- package/src/plugins/ibkr/gateway-service-native.ts +1 -5
- package/src/plugins/ibkr/gateway-service.ts +1 -1
- package/src/plugins/ibkr/index.test.tsx +0 -2
- package/src/plugins/ibkr/trade-tab.test.tsx +3 -4
- package/src/plugins/pane-manager.ts +0 -13
- package/src/plugins/plugin-runtime.test.tsx +40 -6
- package/src/plugins/plugin-runtime.tsx +20 -10
- package/src/plugins/prediction-markets/cli.ts +0 -1
- package/src/plugins/prediction-markets/controller-data.ts +0 -2
- package/src/plugins/prediction-markets/controller.ts +0 -15
- package/src/plugins/prediction-markets/detail/pane.tsx +1 -0
- package/src/plugins/prediction-markets/pane.test.tsx +3 -11
- package/src/plugins/prediction-markets/test-helpers.tsx +1 -1
- package/src/plugins/registry.ts +13 -42
- package/src/plugins/ticker-navigation.test.ts +66 -0
- package/src/plugins/ticker-navigation.ts +44 -0
- package/src/react/input.ts +2 -1
- package/src/renderers/electrobun/bun/index.ts +32 -11
- package/src/renderers/electrobun/bun/window-focus.ts +37 -0
- package/src/renderers/electrobun/shared/protocol.ts +7 -1
- package/src/renderers/electrobun/view/app-services.ts +88 -31
- package/src/renderers/electrobun/view/backend-rpc.ts +15 -0
- package/src/renderers/electrobun/view/data-table.tsx +138 -101
- package/src/renderers/electrobun/view/desktop-controls.tsx +42 -20
- package/src/renderers/electrobun/view/desktop-window-bridge.ts +12 -1
- package/src/renderers/electrobun/view/dialog-host.tsx +13 -1
- package/src/renderers/electrobun/view/input-host.tsx +74 -10
- package/src/renderers/electrobun/view/key-event.ts +1 -1
- package/src/renderers/electrobun/view/main.tsx +18 -1
- package/src/renderers/electrobun/view/styles.css +329 -0
- package/src/renderers/electrobun/view/toast-host.tsx +29 -3
- package/src/renderers/electrobun/view/ui-host.tsx +42 -0
- package/src/renderers/opentui/chart-surface.tsx +179 -0
- package/src/renderers/opentui/input-host.tsx +1 -1
- package/src/renderers/opentui/start.tsx +48 -30
- package/src/renderers/opentui/test-utils.tsx +17 -0
- package/src/renderers/opentui/ui-host.tsx +5 -2
- package/src/sources/gloomberb-cloud.test.ts +86 -1
- package/src/sources/gloomberb-cloud.ts +117 -21
- package/src/sources/provider-router.test.ts +122 -0
- package/src/sources/provider-router.ts +198 -15
- package/src/sources/yahoo-finance.test.ts +44 -0
- package/src/sources/yahoo-finance.ts +6 -0
- package/src/state/app-bootstrap.ts +20 -7
- package/src/state/app-context-selector.test.tsx +189 -4
- package/src/state/app-context.test.ts +65 -0
- package/src/state/app-context.tsx +48 -16
- package/src/state/use-inline-tickers.test.tsx +73 -0
- package/src/state/use-inline-tickers.ts +19 -10
- package/src/test-support/plugin-runtime.ts +3 -2
- package/src/theme/colors.test.ts +75 -0
- package/src/theme/colors.ts +76 -6
- package/src/theme/theme-context.tsx +26 -0
- package/src/theme/themes.test.ts +23 -1
- package/src/theme/themes.ts +115 -0
- package/src/types/broker.ts +1 -1
- package/src/types/capability-route-source.ts +1 -0
- package/src/types/data-provider.ts +15 -2
- package/src/types/desktop-window.ts +6 -0
- package/src/types/financials.ts +2 -0
- package/src/types/plugin.ts +1 -3
- package/src/ui/host.tsx +7 -0
- package/src/utils/api-client.test.ts +32 -0
- package/src/utils/api-client.ts +119 -18
- package/src/utils/back-navigation.ts +24 -1
- package/src/utils/double-escape-close.test.ts +33 -0
- package/src/utils/double-escape-close.ts +40 -0
- package/src/utils/throttled-fetch.test.ts +22 -0
- package/src/utils/throttled-fetch.ts +3 -1
- package/src/utils/ticker-search.ts +53 -9
- package/src/version.ts +1 -1
- package/src/plugins/builtin/news-wire/feed-pane.tsx +0 -21
- package/src/plugins/builtin/news-wire/top-pane.tsx +0 -21
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
2
|
import { TextAttributes } from "@opentui/core";
|
|
3
|
-
import { testRender } from "../../renderers/opentui/test-utils";
|
|
4
|
-
import { DialogProvider } from "@opentui-ui/dialog/react";
|
|
3
|
+
import { TestDialogProvider, testRender } from "../../renderers/opentui/test-utils";
|
|
5
4
|
import type { ReactNode } from "react";
|
|
6
5
|
import { act, createElement, useReducer } from "react";
|
|
7
6
|
import { AppContext, appReducer, createInitialState } from "../../state/app-context";
|
|
8
7
|
import { MarketDataCoordinator, setSharedMarketDataCoordinator } from "../../market-data/coordinator";
|
|
9
8
|
import { setSharedMarketDataForTests } from "../../plugins/registry";
|
|
9
|
+
import { useShortcut } from "../../react/input";
|
|
10
10
|
import { cloneLayout, createDefaultConfig } from "../../types/config";
|
|
11
11
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
12
12
|
import { setSharedRegistryForTests } from "../../plugins/registry";
|
|
@@ -17,6 +17,7 @@ import type { PaneProps } from "../../types/plugin";
|
|
|
17
17
|
import { StockChart } from "../chart/stock-chart";
|
|
18
18
|
import { StatusBar } from "./status-bar";
|
|
19
19
|
import { Header } from "./header";
|
|
20
|
+
import { DetachedPaneShell } from "./detached-pane-shell";
|
|
20
21
|
import {
|
|
21
22
|
buildNativeWindowState,
|
|
22
23
|
constrainFloatingRectToBounds,
|
|
@@ -76,7 +77,7 @@ function createShellPluginRegistry(options?: {
|
|
|
76
77
|
openPaneSettingsFn: () => {},
|
|
77
78
|
openCommandBar: () => {},
|
|
78
79
|
updateLayoutFn: () => {},
|
|
79
|
-
|
|
80
|
+
hidePane: () => {},
|
|
80
81
|
} as unknown as PluginRegistry;
|
|
81
82
|
}
|
|
82
83
|
|
|
@@ -114,7 +115,7 @@ function createBrokerPortfolioRegistry(): PluginRegistry {
|
|
|
114
115
|
openPaneSettingsFn: () => {},
|
|
115
116
|
openCommandBar: () => {},
|
|
116
117
|
updateLayoutFn: () => {},
|
|
117
|
-
|
|
118
|
+
hidePane: () => {},
|
|
118
119
|
focusPaneFn: () => {},
|
|
119
120
|
pinTicker: () => {},
|
|
120
121
|
navigateTicker: () => {},
|
|
@@ -212,6 +213,31 @@ function createChartShellDataProvider(historyBySymbol: Record<string, PricePoint
|
|
|
212
213
|
};
|
|
213
214
|
}
|
|
214
215
|
|
|
216
|
+
async function emitKeypress(event: { name?: string; sequence?: string }) {
|
|
217
|
+
await act(async () => {
|
|
218
|
+
const keyEvent = {
|
|
219
|
+
ctrl: false,
|
|
220
|
+
meta: false,
|
|
221
|
+
option: false,
|
|
222
|
+
shift: false,
|
|
223
|
+
eventType: "press",
|
|
224
|
+
repeated: false,
|
|
225
|
+
defaultPrevented: false,
|
|
226
|
+
propagationStopped: false,
|
|
227
|
+
preventDefault() {
|
|
228
|
+
this.defaultPrevented = true;
|
|
229
|
+
},
|
|
230
|
+
stopPropagation() {
|
|
231
|
+
this.propagationStopped = true;
|
|
232
|
+
},
|
|
233
|
+
...event,
|
|
234
|
+
};
|
|
235
|
+
testSetup!.renderer.keyInput.emit("keypress", keyEvent as any);
|
|
236
|
+
await testSetup!.renderOnce();
|
|
237
|
+
await testSetup!.renderOnce();
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
215
241
|
function HeaderHarness({
|
|
216
242
|
updateAvailable = null,
|
|
217
243
|
updateProgress = null,
|
|
@@ -337,9 +363,9 @@ function ChartShellHarness({
|
|
|
337
363
|
|
|
338
364
|
return (
|
|
339
365
|
<AppContext value={{ state, dispatch }}>
|
|
340
|
-
<
|
|
366
|
+
<TestDialogProvider>
|
|
341
367
|
<Shell pluginRegistry={pluginRegistry} />
|
|
342
|
-
</
|
|
368
|
+
</TestDialogProvider>
|
|
343
369
|
</AppContext>
|
|
344
370
|
);
|
|
345
371
|
}
|
|
@@ -613,9 +639,9 @@ describe("Shell", () => {
|
|
|
613
639
|
|
|
614
640
|
testSetup = await testRender(
|
|
615
641
|
<AppContext value={{ state, dispatch: () => {} }}>
|
|
616
|
-
<
|
|
642
|
+
<TestDialogProvider>
|
|
617
643
|
<Shell pluginRegistry={pluginRegistry} />
|
|
618
|
-
</
|
|
644
|
+
</TestDialogProvider>
|
|
619
645
|
</AppContext>,
|
|
620
646
|
{ width: 40, height: 10 },
|
|
621
647
|
);
|
|
@@ -655,7 +681,7 @@ describe("Shell", () => {
|
|
|
655
681
|
|
|
656
682
|
testSetup = await testRender(
|
|
657
683
|
<AppContext value={{ state, dispatch: () => {} }}>
|
|
658
|
-
<
|
|
684
|
+
<TestDialogProvider>
|
|
659
685
|
<Shell
|
|
660
686
|
pluginRegistry={pluginRegistry}
|
|
661
687
|
desktopWindowBridge={{
|
|
@@ -665,7 +691,7 @@ describe("Shell", () => {
|
|
|
665
691
|
subscribeDockPreview: () => () => {},
|
|
666
692
|
}}
|
|
667
693
|
/>
|
|
668
|
-
</
|
|
694
|
+
</TestDialogProvider>
|
|
669
695
|
</AppContext>,
|
|
670
696
|
{ width: 40, height: 10 },
|
|
671
697
|
);
|
|
@@ -704,9 +730,9 @@ describe("Shell", () => {
|
|
|
704
730
|
|
|
705
731
|
testSetup = await testRender(
|
|
706
732
|
<AppContext value={{ state, dispatch: () => {} }}>
|
|
707
|
-
<
|
|
733
|
+
<TestDialogProvider>
|
|
708
734
|
<Shell pluginRegistry={pluginRegistry} />
|
|
709
|
-
</
|
|
735
|
+
</TestDialogProvider>
|
|
710
736
|
</AppContext>,
|
|
711
737
|
{ width: 40, height: 10 },
|
|
712
738
|
);
|
|
@@ -823,9 +849,9 @@ describe("Shell", () => {
|
|
|
823
849
|
setSharedRegistryForTests(pluginRegistry);
|
|
824
850
|
|
|
825
851
|
testSetup = await testRender(
|
|
826
|
-
<
|
|
852
|
+
<TestDialogProvider>
|
|
827
853
|
<BrokerShellHarness pluginRegistry={pluginRegistry} />
|
|
828
|
-
</
|
|
854
|
+
</TestDialogProvider>,
|
|
829
855
|
{ width: 100, height: 24 },
|
|
830
856
|
);
|
|
831
857
|
|
|
@@ -916,9 +942,9 @@ describe("Shell", () => {
|
|
|
916
942
|
|
|
917
943
|
testSetup = await testRender(
|
|
918
944
|
<AppContext value={{ state, dispatch: (action) => actions.push(action) }}>
|
|
919
|
-
<
|
|
945
|
+
<TestDialogProvider>
|
|
920
946
|
<Shell pluginRegistry={createShellPluginRegistry()} />
|
|
921
|
-
</
|
|
947
|
+
</TestDialogProvider>
|
|
922
948
|
</AppContext>,
|
|
923
949
|
{ width: 40, height: 10 },
|
|
924
950
|
);
|
|
@@ -936,6 +962,177 @@ describe("Shell", () => {
|
|
|
936
962
|
expect(updateLayout?.layout.dockRoot).toBeNull();
|
|
937
963
|
});
|
|
938
964
|
|
|
965
|
+
test("closes the focused pane after double Escape", async () => {
|
|
966
|
+
const config = createDefaultConfig("/tmp/gloomberb-shell-test");
|
|
967
|
+
const mainPane = config.layout.instances.find((instance) => instance.instanceId === "portfolio-list:main");
|
|
968
|
+
if (!mainPane) throw new Error("missing default portfolio pane");
|
|
969
|
+
|
|
970
|
+
const singlePaneLayout = {
|
|
971
|
+
dockRoot: { kind: "pane" as const, instanceId: "portfolio-list:main" },
|
|
972
|
+
instances: [{ ...mainPane }],
|
|
973
|
+
floating: [],
|
|
974
|
+
};
|
|
975
|
+
const state = {
|
|
976
|
+
...createInitialState({
|
|
977
|
+
...config,
|
|
978
|
+
layout: cloneLayout(singlePaneLayout),
|
|
979
|
+
layouts: [{ name: "Default", layout: cloneLayout(singlePaneLayout) }],
|
|
980
|
+
}),
|
|
981
|
+
focusedPaneId: "portfolio-list:main",
|
|
982
|
+
};
|
|
983
|
+
const actions: Array<any> = [];
|
|
984
|
+
|
|
985
|
+
testSetup = await testRender(
|
|
986
|
+
<AppContext value={{ state, dispatch: (action) => actions.push(action) }}>
|
|
987
|
+
<TestDialogProvider>
|
|
988
|
+
<Shell pluginRegistry={createShellPluginRegistry()} />
|
|
989
|
+
</TestDialogProvider>
|
|
990
|
+
</AppContext>,
|
|
991
|
+
{ width: 40, height: 10 },
|
|
992
|
+
);
|
|
993
|
+
|
|
994
|
+
await testSetup.renderOnce();
|
|
995
|
+
await emitKeypress({ name: "escape", sequence: "\u001b" });
|
|
996
|
+
expect(actions.some((action) => action.type === "UPDATE_LAYOUT")).toBe(false);
|
|
997
|
+
|
|
998
|
+
await emitKeypress({ name: "escape", sequence: "\u001b" });
|
|
999
|
+
|
|
1000
|
+
const updateLayout = actions.find((action) => action.type === "UPDATE_LAYOUT");
|
|
1001
|
+
expect(actions).toContainEqual({ type: "PUSH_LAYOUT_HISTORY" });
|
|
1002
|
+
expect(updateLayout?.layout.instances).toEqual([]);
|
|
1003
|
+
expect(updateLayout?.layout.floating).toEqual([]);
|
|
1004
|
+
expect(updateLayout?.layout.dockRoot).toBeNull();
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
test("closes on double Escape even when the focused pane handles the first Escape", async () => {
|
|
1008
|
+
const config = createDefaultConfig("/tmp/gloomberb-shell-test");
|
|
1009
|
+
const mainPane = config.layout.instances.find((instance) => instance.instanceId === "portfolio-list:main");
|
|
1010
|
+
if (!mainPane) throw new Error("missing default portfolio pane");
|
|
1011
|
+
|
|
1012
|
+
let consumedEscapes = 0;
|
|
1013
|
+
function EscapeConsumerPane() {
|
|
1014
|
+
useShortcut((event) => {
|
|
1015
|
+
if (event.name !== "escape") return;
|
|
1016
|
+
consumedEscapes += 1;
|
|
1017
|
+
event.preventDefault();
|
|
1018
|
+
event.stopPropagation();
|
|
1019
|
+
});
|
|
1020
|
+
return <text>Escape Consumer</text>;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
const singlePaneLayout = {
|
|
1024
|
+
dockRoot: { kind: "pane" as const, instanceId: "portfolio-list:main" },
|
|
1025
|
+
instances: [{ ...mainPane }],
|
|
1026
|
+
floating: [],
|
|
1027
|
+
};
|
|
1028
|
+
const state = {
|
|
1029
|
+
...createInitialState({
|
|
1030
|
+
...config,
|
|
1031
|
+
layout: cloneLayout(singlePaneLayout),
|
|
1032
|
+
layouts: [{ name: "Default", layout: cloneLayout(singlePaneLayout) }],
|
|
1033
|
+
}),
|
|
1034
|
+
focusedPaneId: "portfolio-list:main",
|
|
1035
|
+
};
|
|
1036
|
+
const actions: Array<any> = [];
|
|
1037
|
+
|
|
1038
|
+
testSetup = await testRender(
|
|
1039
|
+
<AppContext value={{ state, dispatch: (action) => actions.push(action) }}>
|
|
1040
|
+
<TestDialogProvider>
|
|
1041
|
+
<Shell
|
|
1042
|
+
pluginRegistry={createShellPluginRegistry({
|
|
1043
|
+
portfolioListComponent: EscapeConsumerPane,
|
|
1044
|
+
})}
|
|
1045
|
+
/>
|
|
1046
|
+
</TestDialogProvider>
|
|
1047
|
+
</AppContext>,
|
|
1048
|
+
{ width: 40, height: 10 },
|
|
1049
|
+
);
|
|
1050
|
+
|
|
1051
|
+
await testSetup.renderOnce();
|
|
1052
|
+
await emitKeypress({ name: "escape", sequence: "\u001b" });
|
|
1053
|
+
expect(consumedEscapes).toBe(1);
|
|
1054
|
+
expect(actions.some((action) => action.type === "UPDATE_LAYOUT")).toBe(false);
|
|
1055
|
+
|
|
1056
|
+
await emitKeypress({ name: "escape", sequence: "\u001b" });
|
|
1057
|
+
const updateLayout = actions.find((action) => action.type === "UPDATE_LAYOUT");
|
|
1058
|
+
expect(consumedEscapes).toBe(1);
|
|
1059
|
+
expect(updateLayout?.layout.dockRoot).toBeNull();
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
test("closes on double Escape while pane input is captured", async () => {
|
|
1063
|
+
const config = createDefaultConfig("/tmp/gloomberb-shell-test");
|
|
1064
|
+
const mainPane = config.layout.instances.find((instance) => instance.instanceId === "portfolio-list:main");
|
|
1065
|
+
if (!mainPane) throw new Error("missing default portfolio pane");
|
|
1066
|
+
|
|
1067
|
+
const singlePaneLayout = {
|
|
1068
|
+
dockRoot: { kind: "pane" as const, instanceId: "portfolio-list:main" },
|
|
1069
|
+
instances: [{ ...mainPane }],
|
|
1070
|
+
floating: [],
|
|
1071
|
+
};
|
|
1072
|
+
const state = {
|
|
1073
|
+
...createInitialState({
|
|
1074
|
+
...config,
|
|
1075
|
+
layout: cloneLayout(singlePaneLayout),
|
|
1076
|
+
layouts: [{ name: "Default", layout: cloneLayout(singlePaneLayout) }],
|
|
1077
|
+
}),
|
|
1078
|
+
focusedPaneId: "portfolio-list:main",
|
|
1079
|
+
inputCaptured: true,
|
|
1080
|
+
};
|
|
1081
|
+
const actions: Array<any> = [];
|
|
1082
|
+
|
|
1083
|
+
testSetup = await testRender(
|
|
1084
|
+
<AppContext value={{ state, dispatch: (action) => actions.push(action) }}>
|
|
1085
|
+
<TestDialogProvider>
|
|
1086
|
+
<Shell pluginRegistry={createShellPluginRegistry()} />
|
|
1087
|
+
</TestDialogProvider>
|
|
1088
|
+
</AppContext>,
|
|
1089
|
+
{ width: 40, height: 10 },
|
|
1090
|
+
);
|
|
1091
|
+
|
|
1092
|
+
await testSetup.renderOnce();
|
|
1093
|
+
await emitKeypress({ name: "escape", sequence: "\u001b" });
|
|
1094
|
+
expect(actions.some((action) => action.type === "UPDATE_LAYOUT")).toBe(false);
|
|
1095
|
+
|
|
1096
|
+
await emitKeypress({ name: "escape", sequence: "\u001b" });
|
|
1097
|
+
const updateLayout = actions.find((action) => action.type === "UPDATE_LAYOUT");
|
|
1098
|
+
expect(updateLayout?.layout.dockRoot).toBeNull();
|
|
1099
|
+
});
|
|
1100
|
+
|
|
1101
|
+
test("closes a detached pane window after double Escape", async () => {
|
|
1102
|
+
const config = createDefaultConfig("/tmp/gloomberb-shell-test");
|
|
1103
|
+
const state = {
|
|
1104
|
+
...createInitialState(config),
|
|
1105
|
+
focusedPaneId: "portfolio-list:main",
|
|
1106
|
+
};
|
|
1107
|
+
const closedPaneIds: string[] = [];
|
|
1108
|
+
|
|
1109
|
+
testSetup = await testRender(
|
|
1110
|
+
<AppContext value={{ state, dispatch: () => {} }}>
|
|
1111
|
+
<TestDialogProvider>
|
|
1112
|
+
<DetachedPaneShell
|
|
1113
|
+
pluginRegistry={createShellPluginRegistry()}
|
|
1114
|
+
desktopWindowBridge={{
|
|
1115
|
+
kind: "detached",
|
|
1116
|
+
paneId: "portfolio-list:main",
|
|
1117
|
+
subscribeState: () => () => {},
|
|
1118
|
+
closeDetachedPane: async (paneId) => {
|
|
1119
|
+
closedPaneIds.push(paneId);
|
|
1120
|
+
},
|
|
1121
|
+
}}
|
|
1122
|
+
/>
|
|
1123
|
+
</TestDialogProvider>
|
|
1124
|
+
</AppContext>,
|
|
1125
|
+
{ width: 40, height: 10 },
|
|
1126
|
+
);
|
|
1127
|
+
|
|
1128
|
+
await testSetup.renderOnce();
|
|
1129
|
+
await emitKeypress({ name: "escape", sequence: "\u001b" });
|
|
1130
|
+
expect(closedPaneIds).toEqual([]);
|
|
1131
|
+
|
|
1132
|
+
await emitKeypress({ name: "escape", sequence: "\u001b" });
|
|
1133
|
+
expect(closedPaneIds).toEqual(["portfolio-list:main"]);
|
|
1134
|
+
});
|
|
1135
|
+
|
|
939
1136
|
test("closes the focused floating pane with Ctrl+W", async () => {
|
|
940
1137
|
const config = createDefaultConfig("/tmp/gloomberb-shell-test");
|
|
941
1138
|
const mainPane = config.layout.instances.find((instance) => instance.instanceId === "portfolio-list:main");
|
|
@@ -946,6 +1143,7 @@ describe("Shell", () => {
|
|
|
946
1143
|
dockRoot: { kind: "pane" as const, instanceId: "portfolio-list:main" },
|
|
947
1144
|
instances: [{ ...mainPane }, { ...detailPane }],
|
|
948
1145
|
floating: [{ instanceId: "ticker-detail:main", x: 4, y: 2, width: 30, height: 8 }],
|
|
1146
|
+
detached: [],
|
|
949
1147
|
};
|
|
950
1148
|
const state = {
|
|
951
1149
|
...createInitialState({
|
|
@@ -959,9 +1157,9 @@ describe("Shell", () => {
|
|
|
959
1157
|
|
|
960
1158
|
testSetup = await testRender(
|
|
961
1159
|
<AppContext value={{ state, dispatch: (action) => actions.push(action) }}>
|
|
962
|
-
<
|
|
1160
|
+
<TestDialogProvider>
|
|
963
1161
|
<Shell pluginRegistry={createShellPluginRegistry()} />
|
|
964
|
-
</
|
|
1162
|
+
</TestDialogProvider>
|
|
965
1163
|
</AppContext>,
|
|
966
1164
|
{ width: 40, height: 12 },
|
|
967
1165
|
);
|
|
@@ -989,6 +1187,7 @@ describe("Shell", () => {
|
|
|
989
1187
|
dockRoot: { kind: "pane" as const, instanceId: "portfolio-list:main" },
|
|
990
1188
|
instances: [{ ...mainPane }, { ...detailPane }],
|
|
991
1189
|
floating: [{ instanceId: "ticker-detail:main", x: 4, y: 2, width: 30, height: 8 }],
|
|
1190
|
+
detached: [],
|
|
992
1191
|
};
|
|
993
1192
|
const state = {
|
|
994
1193
|
...createInitialState({
|
|
@@ -1002,9 +1201,9 @@ describe("Shell", () => {
|
|
|
1002
1201
|
|
|
1003
1202
|
testSetup = await testRender(
|
|
1004
1203
|
<AppContext value={{ state, dispatch: (action) => actions.push(action) }}>
|
|
1005
|
-
<
|
|
1204
|
+
<TestDialogProvider>
|
|
1006
1205
|
<Shell pluginRegistry={createShellPluginRegistry()} />
|
|
1007
|
-
</
|
|
1206
|
+
</TestDialogProvider>
|
|
1008
1207
|
</AppContext>,
|
|
1009
1208
|
{ width: 40, height: 12 },
|
|
1010
1209
|
);
|
|
@@ -1020,6 +1219,49 @@ describe("Shell", () => {
|
|
|
1020
1219
|
expect(updateLayout?.layout.floating).toEqual([]);
|
|
1021
1220
|
});
|
|
1022
1221
|
|
|
1222
|
+
test("closes the focused floating pane with Ctrl+W while plugin input is captured", async () => {
|
|
1223
|
+
const config = createDefaultConfig("/tmp/gloomberb-shell-test");
|
|
1224
|
+
const mainPane = config.layout.instances.find((instance) => instance.instanceId === "portfolio-list:main");
|
|
1225
|
+
const detailPane = config.layout.instances.find((instance) => instance.instanceId === "ticker-detail:main");
|
|
1226
|
+
if (!mainPane || !detailPane) throw new Error("missing default panes");
|
|
1227
|
+
|
|
1228
|
+
const mixedLayout = {
|
|
1229
|
+
dockRoot: { kind: "pane" as const, instanceId: "portfolio-list:main" },
|
|
1230
|
+
instances: [{ ...mainPane }, { ...detailPane }],
|
|
1231
|
+
floating: [{ instanceId: "ticker-detail:main", x: 4, y: 2, width: 30, height: 8 }],
|
|
1232
|
+
detached: [],
|
|
1233
|
+
};
|
|
1234
|
+
const state = {
|
|
1235
|
+
...createInitialState({
|
|
1236
|
+
...config,
|
|
1237
|
+
layout: cloneLayout(mixedLayout),
|
|
1238
|
+
layouts: [{ name: "Default", layout: cloneLayout(mixedLayout) }],
|
|
1239
|
+
}),
|
|
1240
|
+
focusedPaneId: "ticker-detail:main",
|
|
1241
|
+
inputCaptured: true,
|
|
1242
|
+
};
|
|
1243
|
+
const actions: Array<any> = [];
|
|
1244
|
+
|
|
1245
|
+
testSetup = await testRender(
|
|
1246
|
+
<AppContext value={{ state, dispatch: (action) => actions.push(action) }}>
|
|
1247
|
+
<TestDialogProvider>
|
|
1248
|
+
<Shell pluginRegistry={createShellPluginRegistry()} />
|
|
1249
|
+
</TestDialogProvider>
|
|
1250
|
+
</AppContext>,
|
|
1251
|
+
{ width: 40, height: 12 },
|
|
1252
|
+
);
|
|
1253
|
+
|
|
1254
|
+
await testSetup.renderOnce();
|
|
1255
|
+
await act(async () => {
|
|
1256
|
+
testSetup!.mockInput.pressKey("w", { ctrl: true });
|
|
1257
|
+
await testSetup!.renderOnce();
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
const updateLayout = actions.find((action) => action.type === "UPDATE_LAYOUT");
|
|
1261
|
+
expect(updateLayout?.layout.instances.map((instance: { instanceId: string }) => instance.instanceId)).toEqual(["portfolio-list:main"]);
|
|
1262
|
+
expect(updateLayout?.layout.floating).toEqual([]);
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1023
1265
|
test("keeps a floating pane at the preview rect after a free drag release", () => {
|
|
1024
1266
|
const config = createDefaultConfig("/tmp/gloomberb-shell-drag-test");
|
|
1025
1267
|
const detailPane = config.layout.instances.find((instance) => instance.instanceId === "ticker-detail:main");
|
|
@@ -1153,7 +1395,7 @@ describe("Shell", () => {
|
|
|
1153
1395
|
|
|
1154
1396
|
testSetup = await testRender(
|
|
1155
1397
|
<AppContext value={{ state, dispatch: () => {} }}>
|
|
1156
|
-
<
|
|
1398
|
+
<TestDialogProvider>
|
|
1157
1399
|
<Shell pluginRegistry={createShellPluginRegistry({
|
|
1158
1400
|
tickerDetailComponent: ({ width, height }) => (
|
|
1159
1401
|
<box flexDirection="column" width={width} height={height}>
|
|
@@ -1164,7 +1406,7 @@ describe("Shell", () => {
|
|
|
1164
1406
|
</box>
|
|
1165
1407
|
),
|
|
1166
1408
|
})} />
|
|
1167
|
-
</
|
|
1409
|
+
</TestDialogProvider>
|
|
1168
1410
|
</AppContext>,
|
|
1169
1411
|
{ width: 40, height: 12 },
|
|
1170
1412
|
);
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
MIN_FLOAT_HEIGHT,
|
|
10
10
|
MIN_FLOAT_WIDTH,
|
|
11
11
|
applyDrop,
|
|
12
|
-
findDockLeaf,
|
|
13
12
|
floatAtRect,
|
|
14
13
|
floatPane,
|
|
15
14
|
getDockDividerLayouts,
|
|
@@ -22,7 +21,6 @@ import {
|
|
|
22
21
|
resolveDocked,
|
|
23
22
|
resolveFloating,
|
|
24
23
|
simulateDrop,
|
|
25
|
-
swapPanes,
|
|
26
24
|
type DockDividerLayout,
|
|
27
25
|
type DockLeafLayout,
|
|
28
26
|
type DropTarget,
|
|
@@ -45,6 +43,7 @@ import {
|
|
|
45
43
|
selectStatusBarVisible,
|
|
46
44
|
} from "../../state/selectors-ui";
|
|
47
45
|
import { colors } from "../../theme/colors";
|
|
46
|
+
import { useThemeColors } from "../../theme/theme-context";
|
|
48
47
|
import { PANE_HEADER_ACTION, PANE_HEADER_CLOSE } from "./pane-header";
|
|
49
48
|
import { getNativeSurfaceManager, type NativeOccluder, type NativePaneLayer } from "../chart/native/surface-manager";
|
|
50
49
|
import { FloatingPaneWrapper } from "./floating-pane";
|
|
@@ -60,6 +59,11 @@ import {
|
|
|
60
59
|
getShortcutDisplayMode,
|
|
61
60
|
type ShortcutDisplayMode,
|
|
62
61
|
} from "../../utils/shortcut-labels";
|
|
62
|
+
import {
|
|
63
|
+
createDoubleEscapeCloseState,
|
|
64
|
+
recordDoubleEscapeClose,
|
|
65
|
+
resetDoubleEscapeClose,
|
|
66
|
+
} from "../../utils/double-escape-close";
|
|
63
67
|
|
|
64
68
|
interface ShellProps {
|
|
65
69
|
pluginRegistry: PluginRegistry;
|
|
@@ -218,6 +222,14 @@ export function resolvePaneManagementShortcut(
|
|
|
218
222
|
return null;
|
|
219
223
|
}
|
|
220
224
|
|
|
225
|
+
function inputCaptureAllowsPaneManagementShortcut(
|
|
226
|
+
shortcut: PaneManagementShortcut,
|
|
227
|
+
event: Pick<KeyEventLike, "meta" | "super" | "targetEditable">,
|
|
228
|
+
): boolean {
|
|
229
|
+
if (shortcut !== "close") return false;
|
|
230
|
+
return event.meta || event.super || event.targetEditable !== true;
|
|
231
|
+
}
|
|
232
|
+
|
|
221
233
|
export function resolveAppHeaderHeightCells(options: { titleBarOverlay?: boolean; cellHeightPx?: number }): number {
|
|
222
234
|
if (!options.titleBarOverlay || !options.cellHeightPx || options.cellHeightPx <= 0) return DEFAULT_HEADER_HEIGHT;
|
|
223
235
|
return TITLEBAR_OVERLAY_HEIGHT_PX / options.cellHeightPx;
|
|
@@ -588,8 +600,8 @@ function resolveHeaderHitAreas(
|
|
|
588
600
|
actionStart: number | null;
|
|
589
601
|
closeStart: number | null;
|
|
590
602
|
} {
|
|
591
|
-
//
|
|
592
|
-
let rightEdge = options.focused ? width - 2 : width;
|
|
603
|
+
// Focused panes and all floating panes render ┌─...─┐, adding 2 chars on each side.
|
|
604
|
+
let rightEdge = options.focused || options.floating ? width - 2 : width;
|
|
593
605
|
let closeStart: number | null = null;
|
|
594
606
|
let actionStart: number | null = null;
|
|
595
607
|
|
|
@@ -771,6 +783,7 @@ function resolveExternalDockPreview(
|
|
|
771
783
|
}
|
|
772
784
|
|
|
773
785
|
export function Shell({ pluginRegistry, desktopWindowBridge, desktopDockPreview }: ShellProps) {
|
|
786
|
+
useThemeColors();
|
|
774
787
|
const dispatch = useAppDispatch();
|
|
775
788
|
const config = useAppSelector((state) => state.config);
|
|
776
789
|
const paneState = useAppSelector((state) => state.paneState);
|
|
@@ -802,6 +815,7 @@ export function Shell({ pluginRegistry, desktopWindowBridge, desktopDockPreview
|
|
|
802
815
|
const overlayOpen = commandBarOpen || dialogOpen || !!menuState;
|
|
803
816
|
|
|
804
817
|
const dragRef = useRef<DragMode | null>(null);
|
|
818
|
+
const doubleEscapeCloseRef = useRef(createDoubleEscapeCloseState());
|
|
805
819
|
const [dragFloatingRect, setDragFloatingRect] = useState<{ paneId: string; rect: FloatingRect } | null>(null);
|
|
806
820
|
const [dragCursor, setDragCursor] = useState<{ x: number; y: number } | null>(null);
|
|
807
821
|
const [dividerPreview, setDividerPreview] = useState<DividerPreviewState | null>(null);
|
|
@@ -958,15 +972,39 @@ export function Shell({ pluginRegistry, desktopWindowBridge, desktopDockPreview
|
|
|
958
972
|
return true;
|
|
959
973
|
}, [contentHeight, persistLayout, visibleLayout, width]);
|
|
960
974
|
|
|
975
|
+
useEffect(() => {
|
|
976
|
+
if (overlayOpen) {
|
|
977
|
+
resetDoubleEscapeClose(doubleEscapeCloseRef.current);
|
|
978
|
+
}
|
|
979
|
+
}, [overlayOpen]);
|
|
980
|
+
|
|
961
981
|
useShortcut((event) => {
|
|
962
|
-
|
|
982
|
+
const isEscape = event.name === "escape" || event.name === "esc";
|
|
983
|
+
if (isEscape) {
|
|
984
|
+
const doubleEscapeState = doubleEscapeCloseRef.current;
|
|
985
|
+
if (!dragRef.current && !overlayOpen) {
|
|
986
|
+
if (recordDoubleEscapeClose(doubleEscapeState, focusedPaneId, Date.now()) && closeFocusedPane()) {
|
|
987
|
+
event.preventDefault();
|
|
988
|
+
event.stopPropagation();
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
} else {
|
|
992
|
+
resetDoubleEscapeClose(doubleEscapeState);
|
|
993
|
+
}
|
|
994
|
+
|
|
963
995
|
if (!dragRef.current) return;
|
|
964
996
|
cancelActiveDrag();
|
|
965
|
-
|
|
997
|
+
event.preventDefault();
|
|
998
|
+
event.stopPropagation();
|
|
999
|
+
} else {
|
|
1000
|
+
resetDoubleEscapeClose(doubleEscapeCloseRef.current);
|
|
966
1001
|
}
|
|
1002
|
+
}, { phase: "before" });
|
|
967
1003
|
|
|
1004
|
+
useShortcut((event) => {
|
|
968
1005
|
const shortcut = resolvePaneManagementShortcut(event);
|
|
969
|
-
if (!shortcut || dragRef.current || overlayOpen
|
|
1006
|
+
if (!shortcut || dragRef.current || overlayOpen) return;
|
|
1007
|
+
if (inputCaptured && !inputCaptureAllowsPaneManagementShortcut(shortcut, event)) return;
|
|
970
1008
|
|
|
971
1009
|
let handled = false;
|
|
972
1010
|
switch (shortcut) {
|
|
@@ -1701,7 +1739,7 @@ export function Shell({ pluginRegistry, desktopWindowBridge, desktopDockPreview
|
|
|
1701
1739
|
"data-active": active ? "true" : "false",
|
|
1702
1740
|
style: { "--divider-color": active ? colors.borderFocused : colors.border } as any,
|
|
1703
1741
|
} : {})}
|
|
1704
|
-
onMouseDown={nativePaneChrome ? (event) => startNativeDividerDrag(divider, event) : undefined}
|
|
1742
|
+
onMouseDown={nativePaneChrome ? (event: ShellMouseEvent) => startNativeDividerDrag(divider, event) : undefined}
|
|
1705
1743
|
onMouseDrag={nativePaneChrome ? handleNativeDrag : undefined}
|
|
1706
1744
|
onMouseDragEnd={nativePaneChrome ? handleNativeDrag : undefined}
|
|
1707
1745
|
/>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Box, Span, Text, TextAttributes, contextMenuDivider, useContextMenu, useUiCapabilities } from "../../ui";
|
|
2
2
|
import { useCallback, useEffect, useState } from "react";
|
|
3
3
|
import { colors, hoverBg } from "../../theme/colors";
|
|
4
|
+
import { useThemeColors } from "../../theme/theme-context";
|
|
4
5
|
import { useAppDispatch, useAppSelector, useFocusedTicker } from "../../state/app-context";
|
|
5
6
|
import {
|
|
6
7
|
selectActiveLayoutIndex,
|
|
@@ -57,6 +58,7 @@ function truncate(text: string, width: number): string {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
export function StatusBar() {
|
|
61
|
+
useThemeColors();
|
|
60
62
|
const { nativePaneChrome, nativeContextMenu } = useUiCapabilities();
|
|
61
63
|
const { showContextMenu } = useContextMenu();
|
|
62
64
|
const registry = getSharedRegistry();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AsciiText, Box,
|
|
1
|
+
import { AsciiText, Box, Span, Strong, Text, Underline, useUiHost } from "../../ui";
|
|
2
2
|
import { useState, useCallback, useEffect, useRef, useMemo, type RefObject } from "react";
|
|
3
3
|
import { useShortcut, useViewport } from "../../react/input";
|
|
4
4
|
import { TextAttributes } from "../../ui";
|
|
@@ -963,7 +963,6 @@ function PortfolioStep({
|
|
|
963
963
|
|
|
964
964
|
// broker-fields: dynamically render fields from the broker's configSchema
|
|
965
965
|
if (!selectedBrokerId) return null;
|
|
966
|
-
const currentField = brokerFields[brokerFieldIdx];
|
|
967
966
|
const values = brokerValues[selectedBrokerId] ?? {};
|
|
968
967
|
|
|
969
968
|
return (
|
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
PaneSettingTextField,
|
|
10
10
|
} from "../types/plugin";
|
|
11
11
|
import type { PluginRegistry } from "../plugins/registry";
|
|
12
|
-
import { colors } from "../theme/colors";
|
|
12
|
+
import { blendHex, colors } from "../theme/colors";
|
|
13
13
|
import { Button, DialogFrame, ListView, MultiSelectDialogContent, TextField } from "./ui";
|
|
14
14
|
import { NativeSelect, openNativeSelect, type NativeSelectElement } from "./ui/native-select";
|
|
15
15
|
|
|
@@ -80,6 +80,14 @@ function desktopText(weight?: number) {
|
|
|
80
80
|
return weight ? { ...DESKTOP_TEXT_STYLE, fontWeight: weight } : DESKTOP_TEXT_STYLE;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
function desktopSubtleSurface(): string {
|
|
84
|
+
return blendHex(colors.panel, colors.bg, 0.22);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function desktopHoverSurface(): string {
|
|
88
|
+
return blendHex(colors.bg, colors.borderFocused, 0.08);
|
|
89
|
+
}
|
|
90
|
+
|
|
83
91
|
function DesktopDialogSurface({
|
|
84
92
|
title,
|
|
85
93
|
subtitle,
|
|
@@ -149,7 +157,7 @@ function DesktopSwitch({
|
|
|
149
157
|
flexDirection="row"
|
|
150
158
|
alignItems="center"
|
|
151
159
|
justifyContent={checked ? "flex-end" : "flex-start"}
|
|
152
|
-
backgroundColor={checked ? colors.selected :
|
|
160
|
+
backgroundColor={checked ? colors.selected : desktopSubtleSurface()}
|
|
153
161
|
onMouseDown={(event: any) => {
|
|
154
162
|
event.stopPropagation?.();
|
|
155
163
|
event.preventDefault?.();
|
|
@@ -182,7 +190,7 @@ function DesktopValuePill({ value }: { value: string }) {
|
|
|
182
190
|
<Box
|
|
183
191
|
flexDirection="row"
|
|
184
192
|
alignItems="center"
|
|
185
|
-
backgroundColor=
|
|
193
|
+
backgroundColor={desktopSubtleSurface()}
|
|
186
194
|
style={{
|
|
187
195
|
border: `1px solid ${colors.border}`,
|
|
188
196
|
borderRadius: 6,
|
|
@@ -236,7 +244,7 @@ function DesktopSettingsRow({
|
|
|
236
244
|
<Box
|
|
237
245
|
flexDirection="column"
|
|
238
246
|
minHeight={field.description ? undefined : 2}
|
|
239
|
-
backgroundColor={hovered || selected ?
|
|
247
|
+
backgroundColor={hovered || selected ? desktopHoverSurface() : "transparent"}
|
|
240
248
|
onMouseMove={onHover}
|
|
241
249
|
onMouseDown={field.type === "select" ? undefined : (event: any) => {
|
|
242
250
|
event.stopPropagation?.();
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
2
|
import { act } from "react";
|
|
3
|
-
import { testRender } from "../renderers/opentui/test-utils";
|
|
4
|
-
import { DialogProvider } from "@opentui-ui/dialog/react";
|
|
3
|
+
import { TestDialogProvider, testRender } from "../renderers/opentui/test-utils";
|
|
5
4
|
import { PaneTemplateTextareaStep } from "./pane-template-wizard";
|
|
6
5
|
|
|
7
6
|
let testSetup: Awaited<ReturnType<typeof testRender>> | undefined;
|
|
@@ -18,7 +17,7 @@ describe("PaneTemplateTextareaStep", () => {
|
|
|
18
17
|
let resolved = "";
|
|
19
18
|
|
|
20
19
|
testSetup = await testRender(
|
|
21
|
-
<
|
|
20
|
+
<TestDialogProvider>
|
|
22
21
|
<PaneTemplateTextareaStep
|
|
23
22
|
resolve={(value) => { resolved = value; }}
|
|
24
23
|
dialogId="textarea-test"
|
|
@@ -30,7 +29,7 @@ describe("PaneTemplateTextareaStep", () => {
|
|
|
30
29
|
defaultValue: "Find cash-generative semiconductor suppliers",
|
|
31
30
|
}}
|
|
32
31
|
/>
|
|
33
|
-
</
|
|
32
|
+
</TestDialogProvider>,
|
|
34
33
|
{ width: 80, height: 16 },
|
|
35
34
|
);
|
|
36
35
|
|