gloomberb 0.9.2 → 0.9.4
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 +24 -2
- package/bin/gloomberb +1 -1
- package/package.json +1 -1
- package/src/app/runtime/desktop-menu.ts +5 -1
- package/src/app/runtime/ticker-refresh.ts +4 -1
- package/src/app.tsx +20 -8
- 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/capabilities/registry.ts +1 -0
- package/src/capabilities/types.ts +23 -0
- package/src/cli/commands/api.ts +119 -0
- package/src/cli/commands/automation.ts +173 -0
- package/src/cli/commands/command-utils.ts +53 -0
- package/src/cli/commands/market.ts +527 -0
- package/src/cli/commands/overview.ts +222 -0
- package/src/cli/commands/search.ts +39 -0
- package/src/cli/commands/system.ts +553 -0
- package/src/cli/commands/ticker.ts +104 -12
- package/src/cli/context.ts +35 -2
- package/src/cli/desktop-pane-shot.ts +2 -1
- package/src/cli/entry.ts +51 -0
- package/src/cli/errors.ts +56 -5
- package/src/cli/index.ts +73 -11
- package/src/cli/options.test.ts +38 -0
- package/src/cli/options.ts +95 -0
- package/src/cli/pane-functions/index.ts +10 -1
- package/src/cli/pane-functions/screenshot.ts +13 -1
- package/src/cli/registry.test.ts +40 -0
- package/src/cli/registry.ts +46 -5
- package/src/cli/result.test.ts +82 -0
- package/src/cli/result.ts +149 -0
- package/src/cli/types.ts +11 -0
- package/src/cli.test.ts +11 -11
- 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/comparison-stock-chart/interactions.ts +7 -1
- package/src/components/chart/comparison-stock-chart/legend.tsx +4 -0
- package/src/components/chart/comparison-stock-chart/render-data.ts +37 -8
- package/src/components/chart/comparison-stock-chart/selection-runtime.ts +19 -2
- package/src/components/chart/comparison-stock-chart/sources.ts +8 -4
- package/src/components/chart/comparison-stock-chart/toolbar.tsx +13 -3
- package/src/components/chart/comparison-stock-chart/view.tsx +12 -2
- package/src/components/chart/core/pane-settings.test.ts +27 -0
- package/src/components/chart/core/pane-settings.ts +60 -1
- package/src/components/chart/core/pointer.ts +34 -6
- 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/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/chart/stock-chart/resolved/runtime.ts +1 -1
- package/src/components/chart/stock-chart/settings.ts +7 -3
- package/src/components/chart/stock-chart/viewport/runtime.ts +5 -5
- package/src/components/command-bar/layout-items/current-layout.ts +5 -1
- package/src/components/command-bar/pane-templates/workflow-route.ts +10 -0
- package/src/components/command-bar/surface/pane.test.tsx +76 -0
- package/src/components/data-table/view.tsx +13 -2
- package/src/components/feed-data-table/stack-view.tsx +9 -1
- package/src/components/index.ts +1 -1
- package/src/components/input-search-bar.test.tsx +103 -0
- package/src/components/input-search-bar.tsx +16 -0
- package/src/components/layout/detached-pane-shell.tsx +6 -3
- package/src/components/layout/floating-pane.tsx +3 -2
- 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 +40 -4
- package/src/components/layout/pane/index.tsx +3 -1
- package/src/components/layout/pane/sizing.test.ts +43 -0
- package/src/components/layout/pane/sizing.ts +13 -4
- package/src/components/layout/shell/active-drag.ts +5 -1
- package/src/components/layout/shell/cursor-occlusion.ts +196 -0
- package/src/components/layout/shell/drag/runtime.ts +28 -3
- package/src/components/layout/shell/fullscreen.ts +10 -0
- package/src/components/layout/shell/index.test.tsx +487 -4
- package/src/components/layout/shell/index.tsx +165 -4
- 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/native/pointer-runtime.ts +10 -4
- package/src/components/layout/shell/pane/layers.tsx +34 -16
- package/src/components/layout/shell/pane/management-shortcuts.ts +19 -0
- package/src/components/layout/shell/shortcuts.ts +4 -0
- package/src/components/layout/shell/terminal-pointer-runtime.ts +29 -19
- package/src/components/layout/shell/visible-layout.ts +17 -0
- package/src/components/layout/shell/window-mode/index.ts +11 -3
- package/src/components/layout/shell/window-mode/overlays.tsx +8 -1
- package/src/components/layout/status-bar.test.tsx +111 -1
- package/src/components/layout/status-bar.tsx +37 -7
- package/src/components/layout/transient-layout.tsx +57 -0
- package/src/components/layout/window-edit/mode.test.ts +76 -5
- package/src/components/layout/window-edit/mode.ts +47 -2
- package/src/components/layout/window-edit/presentation.ts +2 -1
- 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/price-performance.tsx +9 -1
- package/src/components/table-view-shared.tsx +12 -4
- package/src/components/ui/data-table/opentui/index.tsx +25 -12
- package/src/components/ui/data-table/types.ts +4 -0
- package/src/components/ui/fields.tsx +10 -3
- 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 +26 -10
- package/src/components/ui/ui.test.tsx +87 -0
- package/src/core/app-services.ts +7 -4
- package/src/core/state/app/layout-reducer.ts +2 -2
- package/src/data/config/store/index.ts +1 -2
- package/src/plugins/builtin/analytics/index.test.tsx +6 -2
- package/src/plugins/builtin/analytics/pane-model.ts +43 -8
- package/src/plugins/builtin/cloud-tweets/pane.tsx +2 -0
- package/src/plugins/builtin/cloud-tweets/search-bar.tsx +3 -0
- package/src/plugins/builtin/cloud-tweets/table.tsx +14 -2
- package/src/plugins/builtin/comparison-chart/index.test.tsx +130 -6
- 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/news/wire/read-state.ts +9 -32
- 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/cli/portfolio-command.ts +23 -0
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +19 -2
- package/src/plugins/builtin/portfolio-list/index.test.ts +23 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +3 -1
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -7
- 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/shared/read-state.ts +43 -0
- package/src/plugins/builtin/substack/article-detail.test.tsx +10 -0
- package/src/plugins/builtin/substack/article-detail.tsx +39 -15
- package/src/plugins/builtin/substack/article-stack.tsx +6 -2
- package/src/plugins/builtin/substack/feed-tabs.tsx +12 -9
- package/src/plugins/builtin/substack/pane.tsx +8 -3
- package/src/plugins/builtin/substack/read-state.ts +55 -0
- package/src/plugins/builtin/thirteenf/pane.test.tsx +26 -0
- package/src/plugins/builtin/thirteenf/pane.tsx +12 -1
- 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/pane-manager/docking.ts +9 -2
- package/src/plugins/pane-manager/gridlock.ts +13 -5
- package/src/plugins/pane-manager/layout-state.ts +25 -0
- package/src/plugins/pane-manager/queries.ts +8 -2
- 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/controller/index.ts +2 -0
- package/src/plugins/prediction-markets/controller/keyboard.ts +34 -5
- 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 +75 -0
- package/src/plugins/prediction-markets/pane.tsx +71 -23
- package/src/plugins/prediction-markets/test-helpers.tsx +42 -23
- package/src/renderers/electrobun/bun/application-menu/click.test.ts +9 -0
- package/src/renderers/electrobun/bun/application-menu/click.ts +1 -0
- package/src/renderers/electrobun/bun/application-menu/index.test.ts +9 -0
- package/src/renderers/electrobun/bun/application-menu/index.ts +3 -2
- package/src/renderers/electrobun/bun/index.ts +13 -0
- package/src/renderers/electrobun/bun/window/focus.test.ts +35 -0
- package/src/renderers/electrobun/bun/window/focus.ts +5 -0
- package/src/renderers/electrobun/view/app-services.ts +6 -4
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +13 -2
- package/src/renderers/electrobun/view/data-table/index.tsx +19 -4
- package/src/renderers/electrobun/view/data-table/row.tsx +30 -6
- package/src/renderers/electrobun/view/desktop/controls.tsx +11 -5
- package/src/renderers/electrobun/view/host/ascii-text.ts +50 -0
- package/src/renderers/electrobun/view/host/input.tsx +115 -18
- package/src/renderers/electrobun/view/host/tabs.tsx +7 -2
- package/src/renderers/electrobun/view/host/text.test.ts +22 -0
- package/src/renderers/electrobun/view/host/text.tsx +24 -20
- package/src/renderers/electrobun/view/styles.css +11 -0
- package/src/renderers/electrobun/view/ui-host.tsx +1 -1
- package/src/renderers/opentui/host.tsx +17 -1
- package/src/renderers/opentui/start.tsx +13 -5
- 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/state/app/input-capture.ts +12 -0
- package/src/types/broker.ts +1 -0
- package/src/types/config.ts +10 -0
- package/src/types/plugin.ts +28 -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/cli-output.ts +6 -0
- package/src/utils/financial-statements.ts +23 -0
- package/src/utils/search-focus-navigation.ts +24 -0
- package/src/version.ts +1 -1
- package/src/components/onboarding/portfolio-step/manual-name-panel.tsx +0 -62
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { resolvePaneBodyFrame, shouldReservePaneFooter } from "./sizing";
|
|
3
|
+
|
|
4
|
+
describe("pane sizing", () => {
|
|
5
|
+
test("lets native pane chrome lay out footer bars in normal flex flow", () => {
|
|
6
|
+
expect(shouldReservePaneFooter(true, true)).toBe(false);
|
|
7
|
+
|
|
8
|
+
const bodyFrame = resolvePaneBodyFrame({
|
|
9
|
+
width: 80,
|
|
10
|
+
height: 30,
|
|
11
|
+
nativePaneChrome: true,
|
|
12
|
+
footerVisible: true,
|
|
13
|
+
reserveFooter: false,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
expect(bodyFrame.height).toBe(28);
|
|
17
|
+
expect(bodyFrame.layoutProps).toEqual({
|
|
18
|
+
flexGrow: 1,
|
|
19
|
+
flexShrink: 1,
|
|
20
|
+
flexBasis: 0,
|
|
21
|
+
minWidth: 0,
|
|
22
|
+
minHeight: 0,
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("keeps terminal pane footer rows reserved for border rendering", () => {
|
|
27
|
+
expect(shouldReservePaneFooter(false, false)).toBe(true);
|
|
28
|
+
|
|
29
|
+
const bodyFrame = resolvePaneBodyFrame({
|
|
30
|
+
width: 80,
|
|
31
|
+
height: 30,
|
|
32
|
+
nativePaneChrome: false,
|
|
33
|
+
reserveFooter: true,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
expect(bodyFrame.height).toBe(28);
|
|
37
|
+
expect(bodyFrame.layoutProps).toEqual({
|
|
38
|
+
height: 28,
|
|
39
|
+
flexGrow: 0,
|
|
40
|
+
flexBasis: undefined,
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -4,28 +4,35 @@ const PANE_FOOTER_ROWS = 1;
|
|
|
4
4
|
|
|
5
5
|
const NATIVE_PANE_BODY_LAYOUT_PROPS = {
|
|
6
6
|
flexGrow: 1,
|
|
7
|
+
flexShrink: 1,
|
|
7
8
|
flexBasis: 0,
|
|
9
|
+
minWidth: 0,
|
|
8
10
|
minHeight: 0,
|
|
9
11
|
} as const;
|
|
10
12
|
|
|
11
|
-
export function shouldReservePaneFooter(nativePaneChrome: boolean | undefined,
|
|
12
|
-
return !nativePaneChrome
|
|
13
|
+
export function shouldReservePaneFooter(nativePaneChrome: boolean | undefined, _showFooter: boolean): boolean {
|
|
14
|
+
return !nativePaneChrome;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
function resolvePaneBodyHeight({
|
|
16
18
|
height,
|
|
17
19
|
nativePaneChrome,
|
|
20
|
+
footerVisible,
|
|
18
21
|
reserveFooter = true,
|
|
19
22
|
headerRows = PANE_HEADER_ROWS,
|
|
20
23
|
}: {
|
|
21
24
|
height: number;
|
|
22
25
|
nativePaneChrome?: boolean;
|
|
26
|
+
footerVisible?: boolean;
|
|
23
27
|
reserveFooter?: boolean;
|
|
24
28
|
headerRows?: number;
|
|
25
29
|
}): number {
|
|
26
30
|
const finiteHeight = Number.isFinite(height) ? height : 1;
|
|
27
31
|
const normalizedHeight = nativePaneChrome ? finiteHeight : Math.max(1, Math.floor(finiteHeight));
|
|
28
|
-
|
|
32
|
+
const footerRows = nativePaneChrome
|
|
33
|
+
? footerVisible ? PANE_FOOTER_ROWS : 0
|
|
34
|
+
: reserveFooter ? PANE_FOOTER_ROWS : 0;
|
|
35
|
+
return Math.max(1, normalizedHeight - headerRows - footerRows);
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
function getPaneBodyLayoutProps(nativePaneChrome: boolean | undefined, bodyHeight: number | undefined) {
|
|
@@ -41,17 +48,19 @@ export function resolvePaneBodyFrame({
|
|
|
41
48
|
width,
|
|
42
49
|
height,
|
|
43
50
|
nativePaneChrome,
|
|
51
|
+
footerVisible,
|
|
44
52
|
reserveFooter = true,
|
|
45
53
|
headerRows = PANE_HEADER_ROWS,
|
|
46
54
|
}: {
|
|
47
55
|
width?: number;
|
|
48
56
|
height?: number;
|
|
49
57
|
nativePaneChrome?: boolean;
|
|
58
|
+
footerVisible?: boolean;
|
|
50
59
|
reserveFooter?: boolean;
|
|
51
60
|
headerRows?: number;
|
|
52
61
|
}) {
|
|
53
62
|
const bodyHeight = typeof height === "number"
|
|
54
|
-
? resolvePaneBodyHeight({ height, nativePaneChrome, reserveFooter, headerRows })
|
|
63
|
+
? resolvePaneBodyHeight({ height, nativePaneChrome, footerVisible, reserveFooter, headerRows })
|
|
55
64
|
: undefined;
|
|
56
65
|
return {
|
|
57
66
|
width: typeof width === "number" ? resolvePaneBodyWidth(width, nativePaneChrome) : undefined,
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
getRememberedFloatingRect,
|
|
5
5
|
resizeSplitAtPath,
|
|
6
6
|
simulateDrop,
|
|
7
|
+
type DockGeometryOptions,
|
|
7
8
|
type DockLeafLayout,
|
|
8
9
|
type DropTarget,
|
|
9
10
|
type LayoutBounds,
|
|
@@ -32,6 +33,7 @@ interface UseShellActiveDragOptions {
|
|
|
32
33
|
bounds: LayoutBounds;
|
|
33
34
|
contentHeight: number;
|
|
34
35
|
dispatch: Dispatch<AppAction>;
|
|
36
|
+
dockGeometryOptions: DockGeometryOptions;
|
|
35
37
|
dockLeafLayouts: DockLeafLayout[];
|
|
36
38
|
dragRuntime: ShellDragRuntimeState;
|
|
37
39
|
focusPane: (paneId: string) => void;
|
|
@@ -51,6 +53,7 @@ export function useShellActiveDrag({
|
|
|
51
53
|
bounds,
|
|
52
54
|
contentHeight,
|
|
53
55
|
dispatch,
|
|
56
|
+
dockGeometryOptions,
|
|
54
57
|
dockLeafLayouts,
|
|
55
58
|
dragRuntime,
|
|
56
59
|
focusPane,
|
|
@@ -117,7 +120,7 @@ export function useShellActiveDrag({
|
|
|
117
120
|
const hoveredCell = hoveredOverlay.cells.find((cell) => pointInRect(cell.rect, hitX, hitShellY));
|
|
118
121
|
if (hoveredCell) {
|
|
119
122
|
const target: DropTarget = { kind: "leaf", targetId: hoveredOverlay.targetId, position: hoveredCell.position };
|
|
120
|
-
const simulation = simulateDrop(baseLayout, drag.paneId, target, bounds);
|
|
123
|
+
const simulation = simulateDrop(baseLayout, drag.paneId, target, bounds, dockGeometryOptions);
|
|
121
124
|
if (simulation.previewRect) {
|
|
122
125
|
updateDockPreview({ kind: "dock", target, rect: simulation.previewRect });
|
|
123
126
|
} else {
|
|
@@ -199,6 +202,7 @@ export function useShellActiveDrag({
|
|
|
199
202
|
bounds,
|
|
200
203
|
contentHeight,
|
|
201
204
|
dispatch,
|
|
205
|
+
dockGeometryOptions,
|
|
202
206
|
dividerPreviewRef,
|
|
203
207
|
dockLeafLayouts,
|
|
204
208
|
dockPreviewRef,
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { useEffect, useRef, type RefObject } from "react";
|
|
2
|
+
import { useNativeRenderer, type NativeCursorState, type NativePostProcessFn } from "../../../ui";
|
|
3
|
+
import type { FloatingRect, LayoutBounds, ResolvedPane } from "../../../plugins/pane-manager";
|
|
4
|
+
import { constrainFloatingRectToBounds } from "./drag";
|
|
5
|
+
|
|
6
|
+
interface VisibleFloatingPane {
|
|
7
|
+
pane: ResolvedPane;
|
|
8
|
+
rect: FloatingRect;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ShellCursorOcclusionRect extends LayoutBounds {
|
|
12
|
+
paneId: string;
|
|
13
|
+
zIndex: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ResolveShellCursorOcclusionRectsOptions {
|
|
17
|
+
contentHeight: number;
|
|
18
|
+
dragFloatingRect: { paneId: string; rect: FloatingRect } | null;
|
|
19
|
+
nativePaneChrome: boolean;
|
|
20
|
+
overlayOpen: boolean;
|
|
21
|
+
transientFocusActive: boolean;
|
|
22
|
+
visibleFloatingPanes: readonly VisibleFloatingPane[];
|
|
23
|
+
width: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function resolveShellCursorOcclusionRects({
|
|
27
|
+
contentHeight,
|
|
28
|
+
dragFloatingRect,
|
|
29
|
+
nativePaneChrome,
|
|
30
|
+
overlayOpen,
|
|
31
|
+
transientFocusActive,
|
|
32
|
+
visibleFloatingPanes,
|
|
33
|
+
width,
|
|
34
|
+
}: ResolveShellCursorOcclusionRectsOptions): ShellCursorOcclusionRect[] {
|
|
35
|
+
if (nativePaneChrome || overlayOpen || transientFocusActive || visibleFloatingPanes.length === 0) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return visibleFloatingPanes.map(({ pane, rect }) => {
|
|
40
|
+
const paneId = pane.instance.instanceId;
|
|
41
|
+
const visibleRect = dragFloatingRect?.paneId === paneId
|
|
42
|
+
? constrainFloatingRectToBounds(dragFloatingRect.rect, width, contentHeight)
|
|
43
|
+
: rect;
|
|
44
|
+
return {
|
|
45
|
+
paneId,
|
|
46
|
+
zIndex: pane.floating?.zIndex ?? 50,
|
|
47
|
+
x: Math.floor(visibleRect.x),
|
|
48
|
+
y: Math.floor(visibleRect.y),
|
|
49
|
+
width: Math.max(0, Math.ceil(visibleRect.width)),
|
|
50
|
+
height: Math.max(0, Math.ceil(visibleRect.height)),
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function readRenderableBounds(renderable: unknown): LayoutBounds | null {
|
|
56
|
+
if (!renderable || typeof renderable !== "object") return null;
|
|
57
|
+
const record = renderable as Record<string, unknown>;
|
|
58
|
+
const getRect = record.getBoundingClientRect;
|
|
59
|
+
if (typeof getRect === "function") {
|
|
60
|
+
const rect = getRect.call(renderable) as Partial<LayoutBounds> | null | undefined;
|
|
61
|
+
if (
|
|
62
|
+
rect
|
|
63
|
+
&& typeof rect.x === "number"
|
|
64
|
+
&& typeof rect.y === "number"
|
|
65
|
+
&& typeof rect.width === "number"
|
|
66
|
+
&& typeof rect.height === "number"
|
|
67
|
+
) {
|
|
68
|
+
return rect as LayoutBounds;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const absoluteBounds = record.absoluteBounds as Partial<LayoutBounds> | null | undefined;
|
|
73
|
+
if (
|
|
74
|
+
absoluteBounds
|
|
75
|
+
&& typeof absoluteBounds.x === "number"
|
|
76
|
+
&& typeof absoluteBounds.y === "number"
|
|
77
|
+
&& typeof absoluteBounds.width === "number"
|
|
78
|
+
&& typeof absoluteBounds.height === "number"
|
|
79
|
+
) {
|
|
80
|
+
return absoluteBounds as LayoutBounds;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const x = typeof record.screenX === "number"
|
|
84
|
+
? record.screenX
|
|
85
|
+
: typeof record.absoluteX === "number"
|
|
86
|
+
? record.absoluteX
|
|
87
|
+
: typeof record.x === "number"
|
|
88
|
+
? record.x
|
|
89
|
+
: null;
|
|
90
|
+
const y = typeof record.screenY === "number"
|
|
91
|
+
? record.screenY
|
|
92
|
+
: typeof record.absoluteY === "number"
|
|
93
|
+
? record.absoluteY
|
|
94
|
+
: typeof record.y === "number"
|
|
95
|
+
? record.y
|
|
96
|
+
: null;
|
|
97
|
+
const width = typeof record.width === "number" ? record.width : null;
|
|
98
|
+
const height = typeof record.height === "number" ? record.height : null;
|
|
99
|
+
if (x == null || y == null || width == null || height == null) return null;
|
|
100
|
+
return { x, y, width, height };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function readRenderablePaneId(renderable: unknown): string | null {
|
|
104
|
+
let current = renderable;
|
|
105
|
+
while (current && typeof current === "object") {
|
|
106
|
+
const record = current as Record<string, unknown>;
|
|
107
|
+
const paneId = record["data-gloom-pane-id"];
|
|
108
|
+
if (typeof paneId === "string") return paneId;
|
|
109
|
+
current = record.parent;
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function rectContainsCursor(rect: LayoutBounds, cursor: NativeCursorState): boolean {
|
|
115
|
+
return cursor.x >= rect.x
|
|
116
|
+
&& cursor.x < rect.x + rect.width
|
|
117
|
+
&& cursor.y >= rect.y
|
|
118
|
+
&& cursor.y < rect.y + rect.height;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function toTerminalRect(shellBounds: LayoutBounds, rect: ShellCursorOcclusionRect): ShellCursorOcclusionRect {
|
|
122
|
+
return {
|
|
123
|
+
...rect,
|
|
124
|
+
x: Math.floor(shellBounds.x + rect.x + 1),
|
|
125
|
+
y: Math.floor(shellBounds.y + rect.y + 1),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function resolveCursorOwnerZIndex(
|
|
130
|
+
cursorOwnerPaneId: string | null,
|
|
131
|
+
rects: readonly ShellCursorOcclusionRect[],
|
|
132
|
+
): number {
|
|
133
|
+
if (!cursorOwnerPaneId) return Number.NEGATIVE_INFINITY;
|
|
134
|
+
return rects.find((rect) => rect.paneId === cursorOwnerPaneId)?.zIndex ?? 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function shouldHideCursor({
|
|
138
|
+
cursor,
|
|
139
|
+
cursorOwnerPaneId,
|
|
140
|
+
terminalRects,
|
|
141
|
+
}: {
|
|
142
|
+
cursor: NativeCursorState;
|
|
143
|
+
cursorOwnerPaneId: string | null;
|
|
144
|
+
terminalRects: readonly ShellCursorOcclusionRect[];
|
|
145
|
+
}): boolean {
|
|
146
|
+
const ownerZIndex = resolveCursorOwnerZIndex(cursorOwnerPaneId, terminalRects);
|
|
147
|
+
return terminalRects.some((rect) => {
|
|
148
|
+
if (rect.paneId === cursorOwnerPaneId) return false;
|
|
149
|
+
if (rect.zIndex <= ownerZIndex) return false;
|
|
150
|
+
return rectContainsCursor(rect, cursor);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function useShellCursorOcclusionGuard({
|
|
155
|
+
occlusionRects,
|
|
156
|
+
shellRef,
|
|
157
|
+
}: {
|
|
158
|
+
occlusionRects: readonly ShellCursorOcclusionRect[];
|
|
159
|
+
shellRef: RefObject<unknown>;
|
|
160
|
+
}) {
|
|
161
|
+
const nativeRenderer = useNativeRenderer();
|
|
162
|
+
const stateRef = useRef({ occlusionRects, shellRef });
|
|
163
|
+
stateRef.current = { occlusionRects, shellRef };
|
|
164
|
+
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
if (
|
|
167
|
+
!nativeRenderer.addPostProcessFn
|
|
168
|
+
|| !nativeRenderer.removePostProcessFn
|
|
169
|
+
|| !nativeRenderer.getCursorState
|
|
170
|
+
|| !nativeRenderer.setCursorPosition
|
|
171
|
+
) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const guardCursor: NativePostProcessFn = () => {
|
|
176
|
+
const cursor = nativeRenderer.getCursorState?.();
|
|
177
|
+
if (!cursor?.visible) return;
|
|
178
|
+
const shellBounds = readRenderableBounds(stateRef.current.shellRef.current);
|
|
179
|
+
if (!shellBounds) return;
|
|
180
|
+
|
|
181
|
+
const terminalRects = stateRef.current.occlusionRects.map((rect) => toTerminalRect(shellBounds, rect));
|
|
182
|
+
if (terminalRects.length === 0) return;
|
|
183
|
+
|
|
184
|
+
const cursorOwnerPaneId = readRenderablePaneId(nativeRenderer.currentFocusedEditor);
|
|
185
|
+
if (!shouldHideCursor({ cursor, cursorOwnerPaneId, terminalRects })) return;
|
|
186
|
+
nativeRenderer.setCursorPosition?.(0, 0, false);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
nativeRenderer.addPostProcessFn(guardCursor);
|
|
190
|
+
nativeRenderer.requestRender();
|
|
191
|
+
return () => {
|
|
192
|
+
nativeRenderer.removePostProcessFn?.(guardCursor);
|
|
193
|
+
nativeRenderer.requestRender();
|
|
194
|
+
};
|
|
195
|
+
}, [nativeRenderer]);
|
|
196
|
+
}
|
|
@@ -6,8 +6,10 @@ import {
|
|
|
6
6
|
type MutableRefObject,
|
|
7
7
|
type SetStateAction,
|
|
8
8
|
} from "react";
|
|
9
|
+
import { useRafCallback } from "../../../../react/use-raf-callback";
|
|
9
10
|
import {
|
|
10
11
|
type DockDividerLayout,
|
|
12
|
+
type DockGeometryOptions,
|
|
11
13
|
type DockLeafLayout,
|
|
12
14
|
type FloatingRect,
|
|
13
15
|
type LayoutBounds,
|
|
@@ -84,24 +86,40 @@ export interface ShellDragRuntimeState {
|
|
|
84
86
|
|
|
85
87
|
export function useShellDragRuntimeState({
|
|
86
88
|
contentHeight,
|
|
89
|
+
throttleFloatingPreview = false,
|
|
87
90
|
width,
|
|
88
91
|
}: {
|
|
89
92
|
contentHeight: number;
|
|
93
|
+
throttleFloatingPreview?: boolean;
|
|
90
94
|
width: number;
|
|
91
95
|
}): ShellDragRuntimeState {
|
|
92
96
|
const dragRef = useRef<DragMode | null>(null);
|
|
93
97
|
const [dragFloatingRect, setDragFloatingRect] = useState<{ paneId: string; rect: FloatingRect } | null>(null);
|
|
98
|
+
const pendingDragFloatingRectRef = useRef<{ paneId: string; rect: FloatingRect } | null>(null);
|
|
94
99
|
const [dragCursor, setDragCursor] = useState<{ x: number; y: number } | null>(null);
|
|
95
100
|
const [dividerPreview, setDividerPreview] = useState<DividerPreviewState | null>(null);
|
|
96
101
|
const [dockPreview, setDockPreview] = useState<DragPreview | null>(null);
|
|
97
102
|
const dividerPreviewRef = useRef<DividerPreviewState | null>(null);
|
|
98
103
|
const dockPreviewRef = useRef<DragPreview | null>(null);
|
|
104
|
+
const flushDragFloatingRect = useRafCallback(() => {
|
|
105
|
+
setDragFloatingRect(pendingDragFloatingRectRef.current);
|
|
106
|
+
});
|
|
99
107
|
|
|
100
108
|
const updateDragFloatingRect = useCallback((next: { paneId: string; rect: FloatingRect } | null) => {
|
|
101
|
-
|
|
109
|
+
const constrainedNext = next
|
|
102
110
|
? { paneId: next.paneId, rect: constrainFloatingRectToBounds(next.rect, width, contentHeight) }
|
|
103
|
-
: null
|
|
104
|
-
|
|
111
|
+
: null;
|
|
112
|
+
pendingDragFloatingRectRef.current = constrainedNext;
|
|
113
|
+
if (!next) {
|
|
114
|
+
setDragFloatingRect(null);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (!throttleFloatingPreview) {
|
|
118
|
+
setDragFloatingRect(constrainedNext);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
flushDragFloatingRect();
|
|
122
|
+
}, [contentHeight, flushDragFloatingRect, throttleFloatingPreview, width]);
|
|
105
123
|
|
|
106
124
|
const updateDividerPreview = useCallback((next: DividerPreviewState | null) => {
|
|
107
125
|
dividerPreviewRef.current = next;
|
|
@@ -146,6 +164,7 @@ interface UseShellPointerRuntimeOptions {
|
|
|
146
164
|
closePaneMenu: () => void;
|
|
147
165
|
contentHeight: number;
|
|
148
166
|
dispatch: Dispatch<AppAction>;
|
|
167
|
+
dockGeometryOptions: DockGeometryOptions;
|
|
149
168
|
dockDividerLayouts: DockDividerLayout[];
|
|
150
169
|
dockLeafLayouts: DockLeafLayout[];
|
|
151
170
|
dragRuntime: ShellDragRuntimeState;
|
|
@@ -166,6 +185,7 @@ interface UseShellPointerRuntimeOptions {
|
|
|
166
185
|
setHoveredMenuItemId: Dispatch<SetStateAction<string | null>>;
|
|
167
186
|
setMenuState: Dispatch<SetStateAction<ActionMenuState | null>>;
|
|
168
187
|
snapGuides: ReturnType<typeof makeSnapGuides>;
|
|
188
|
+
transientFocusActive: boolean;
|
|
169
189
|
updateWindowModePreviewLayout: (nextLayout: LayoutConfig, paneId?: string) => void;
|
|
170
190
|
visibleFloatingPanes: VisibleFloatingPane[];
|
|
171
191
|
visibleLayout: LayoutConfig;
|
|
@@ -179,6 +199,7 @@ export function useShellPointerRuntime({
|
|
|
179
199
|
closePaneMenu,
|
|
180
200
|
contentHeight,
|
|
181
201
|
dispatch,
|
|
202
|
+
dockGeometryOptions,
|
|
182
203
|
dockDividerLayouts,
|
|
183
204
|
dockLeafLayouts,
|
|
184
205
|
dragRuntime,
|
|
@@ -195,6 +216,7 @@ export function useShellPointerRuntime({
|
|
|
195
216
|
setHoveredMenuItemId,
|
|
196
217
|
setMenuState,
|
|
197
218
|
snapGuides,
|
|
219
|
+
transientFocusActive,
|
|
198
220
|
updateWindowModePreviewLayout,
|
|
199
221
|
visibleFloatingPanes,
|
|
200
222
|
visibleLayout,
|
|
@@ -206,6 +228,7 @@ export function useShellPointerRuntime({
|
|
|
206
228
|
bounds,
|
|
207
229
|
contentHeight,
|
|
208
230
|
dispatch,
|
|
231
|
+
dockGeometryOptions,
|
|
209
232
|
dockLeafLayouts,
|
|
210
233
|
focusPane,
|
|
211
234
|
dragRuntime,
|
|
@@ -237,6 +260,7 @@ export function useShellPointerRuntime({
|
|
|
237
260
|
selectWindowModePane,
|
|
238
261
|
setHoveredMenuItemId,
|
|
239
262
|
setMenuState,
|
|
263
|
+
transientFocusActive,
|
|
240
264
|
visibleFloatingPanes,
|
|
241
265
|
width,
|
|
242
266
|
windowMode,
|
|
@@ -254,6 +278,7 @@ export function useShellPointerRuntime({
|
|
|
254
278
|
selectWindowModePane,
|
|
255
279
|
setHoveredMenuItemId,
|
|
256
280
|
setMenuState,
|
|
281
|
+
transientFocusActive,
|
|
257
282
|
windowMode,
|
|
258
283
|
});
|
|
259
284
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { isPaneInLayout } from "../../../plugins/pane-manager";
|
|
2
|
+
import { cloneLayout, type LayoutConfig } from "../../../types/config";
|
|
3
|
+
|
|
4
|
+
export function resolvePaneFocusSourceLayout(
|
|
5
|
+
layout: LayoutConfig,
|
|
6
|
+
paneId: string | null,
|
|
7
|
+
): LayoutConfig | null {
|
|
8
|
+
if (!paneId || !isPaneInLayout(layout, paneId)) return null;
|
|
9
|
+
return cloneLayout(layout);
|
|
10
|
+
}
|