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
|
@@ -0,0 +1,204 @@
|
|
|
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
|
+
const MAX_RENDERABLE_ANCESTOR_DEPTH = 256;
|
|
27
|
+
|
|
28
|
+
export function resolveShellCursorOcclusionRects({
|
|
29
|
+
contentHeight,
|
|
30
|
+
dragFloatingRect,
|
|
31
|
+
nativePaneChrome,
|
|
32
|
+
overlayOpen,
|
|
33
|
+
transientFocusActive,
|
|
34
|
+
visibleFloatingPanes,
|
|
35
|
+
width,
|
|
36
|
+
}: ResolveShellCursorOcclusionRectsOptions): ShellCursorOcclusionRect[] {
|
|
37
|
+
if (nativePaneChrome || overlayOpen || transientFocusActive || visibleFloatingPanes.length === 0) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return visibleFloatingPanes.map(({ pane, rect }) => {
|
|
42
|
+
const paneId = pane.instance.instanceId;
|
|
43
|
+
const visibleRect = dragFloatingRect?.paneId === paneId
|
|
44
|
+
? constrainFloatingRectToBounds(dragFloatingRect.rect, width, contentHeight)
|
|
45
|
+
: rect;
|
|
46
|
+
return {
|
|
47
|
+
paneId,
|
|
48
|
+
zIndex: pane.floating?.zIndex ?? 50,
|
|
49
|
+
x: Math.floor(visibleRect.x),
|
|
50
|
+
y: Math.floor(visibleRect.y),
|
|
51
|
+
width: Math.max(0, Math.ceil(visibleRect.width)),
|
|
52
|
+
height: Math.max(0, Math.ceil(visibleRect.height)),
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readRenderableBounds(renderable: unknown): LayoutBounds | null {
|
|
58
|
+
if (!renderable || typeof renderable !== "object") return null;
|
|
59
|
+
const record = renderable as Record<string, unknown>;
|
|
60
|
+
const getRect = record.getBoundingClientRect;
|
|
61
|
+
if (typeof getRect === "function") {
|
|
62
|
+
const rect = getRect.call(renderable) as Partial<LayoutBounds> | null | undefined;
|
|
63
|
+
if (
|
|
64
|
+
rect
|
|
65
|
+
&& typeof rect.x === "number"
|
|
66
|
+
&& typeof rect.y === "number"
|
|
67
|
+
&& typeof rect.width === "number"
|
|
68
|
+
&& typeof rect.height === "number"
|
|
69
|
+
) {
|
|
70
|
+
return rect as LayoutBounds;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const absoluteBounds = record.absoluteBounds as Partial<LayoutBounds> | null | undefined;
|
|
75
|
+
if (
|
|
76
|
+
absoluteBounds
|
|
77
|
+
&& typeof absoluteBounds.x === "number"
|
|
78
|
+
&& typeof absoluteBounds.y === "number"
|
|
79
|
+
&& typeof absoluteBounds.width === "number"
|
|
80
|
+
&& typeof absoluteBounds.height === "number"
|
|
81
|
+
) {
|
|
82
|
+
return absoluteBounds as LayoutBounds;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const x = typeof record.screenX === "number"
|
|
86
|
+
? record.screenX
|
|
87
|
+
: typeof record.absoluteX === "number"
|
|
88
|
+
? record.absoluteX
|
|
89
|
+
: typeof record.x === "number"
|
|
90
|
+
? record.x
|
|
91
|
+
: null;
|
|
92
|
+
const y = typeof record.screenY === "number"
|
|
93
|
+
? record.screenY
|
|
94
|
+
: typeof record.absoluteY === "number"
|
|
95
|
+
? record.absoluteY
|
|
96
|
+
: typeof record.y === "number"
|
|
97
|
+
? record.y
|
|
98
|
+
: null;
|
|
99
|
+
const width = typeof record.width === "number" ? record.width : null;
|
|
100
|
+
const height = typeof record.height === "number" ? record.height : null;
|
|
101
|
+
if (x == null || y == null || width == null || height == null) return null;
|
|
102
|
+
return { x, y, width, height };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function readRenderablePaneId(renderable: unknown): string | null {
|
|
106
|
+
let current = renderable;
|
|
107
|
+
const seen = new Set<object>();
|
|
108
|
+
let depth = 0;
|
|
109
|
+
|
|
110
|
+
while (current && typeof current === "object" && !seen.has(current)) {
|
|
111
|
+
if (depth >= MAX_RENDERABLE_ANCESTOR_DEPTH) return null;
|
|
112
|
+
seen.add(current);
|
|
113
|
+
depth += 1;
|
|
114
|
+
const record = current as Record<string, unknown>;
|
|
115
|
+
const paneId = record["data-gloom-pane-id"];
|
|
116
|
+
if (typeof paneId === "string") return paneId;
|
|
117
|
+
current = record.parent;
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function rectContainsCursor(rect: LayoutBounds, cursor: NativeCursorState): boolean {
|
|
123
|
+
return cursor.x >= rect.x
|
|
124
|
+
&& cursor.x < rect.x + rect.width
|
|
125
|
+
&& cursor.y >= rect.y
|
|
126
|
+
&& cursor.y < rect.y + rect.height;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function toTerminalRect(shellBounds: LayoutBounds, rect: ShellCursorOcclusionRect): ShellCursorOcclusionRect {
|
|
130
|
+
return {
|
|
131
|
+
...rect,
|
|
132
|
+
x: Math.floor(shellBounds.x + rect.x + 1),
|
|
133
|
+
y: Math.floor(shellBounds.y + rect.y + 1),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function resolveCursorOwnerZIndex(
|
|
138
|
+
cursorOwnerPaneId: string | null,
|
|
139
|
+
rects: readonly ShellCursorOcclusionRect[],
|
|
140
|
+
): number {
|
|
141
|
+
if (!cursorOwnerPaneId) return Number.NEGATIVE_INFINITY;
|
|
142
|
+
return rects.find((rect) => rect.paneId === cursorOwnerPaneId)?.zIndex ?? 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function shouldHideCursor({
|
|
146
|
+
cursor,
|
|
147
|
+
cursorOwnerPaneId,
|
|
148
|
+
terminalRects,
|
|
149
|
+
}: {
|
|
150
|
+
cursor: NativeCursorState;
|
|
151
|
+
cursorOwnerPaneId: string | null;
|
|
152
|
+
terminalRects: readonly ShellCursorOcclusionRect[];
|
|
153
|
+
}): boolean {
|
|
154
|
+
const ownerZIndex = resolveCursorOwnerZIndex(cursorOwnerPaneId, terminalRects);
|
|
155
|
+
return terminalRects.some((rect) => {
|
|
156
|
+
if (rect.paneId === cursorOwnerPaneId) return false;
|
|
157
|
+
if (rect.zIndex <= ownerZIndex) return false;
|
|
158
|
+
return rectContainsCursor(rect, cursor);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function useShellCursorOcclusionGuard({
|
|
163
|
+
occlusionRects,
|
|
164
|
+
shellRef,
|
|
165
|
+
}: {
|
|
166
|
+
occlusionRects: readonly ShellCursorOcclusionRect[];
|
|
167
|
+
shellRef: RefObject<unknown>;
|
|
168
|
+
}) {
|
|
169
|
+
const nativeRenderer = useNativeRenderer();
|
|
170
|
+
const stateRef = useRef({ occlusionRects, shellRef });
|
|
171
|
+
stateRef.current = { occlusionRects, shellRef };
|
|
172
|
+
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
if (
|
|
175
|
+
!nativeRenderer.addPostProcessFn
|
|
176
|
+
|| !nativeRenderer.removePostProcessFn
|
|
177
|
+
|| !nativeRenderer.getCursorState
|
|
178
|
+
|| !nativeRenderer.setCursorPosition
|
|
179
|
+
) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const guardCursor: NativePostProcessFn = () => {
|
|
184
|
+
const cursor = nativeRenderer.getCursorState?.();
|
|
185
|
+
if (!cursor?.visible) return;
|
|
186
|
+
const shellBounds = readRenderableBounds(stateRef.current.shellRef.current);
|
|
187
|
+
if (!shellBounds) return;
|
|
188
|
+
|
|
189
|
+
const terminalRects = stateRef.current.occlusionRects.map((rect) => toTerminalRect(shellBounds, rect));
|
|
190
|
+
if (terminalRects.length === 0) return;
|
|
191
|
+
|
|
192
|
+
const cursorOwnerPaneId = readRenderablePaneId(nativeRenderer.currentFocusedEditor);
|
|
193
|
+
if (!shouldHideCursor({ cursor, cursorOwnerPaneId, terminalRects })) return;
|
|
194
|
+
nativeRenderer.setCursorPosition?.(0, 0, false);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
nativeRenderer.addPostProcessFn(guardCursor);
|
|
198
|
+
nativeRenderer.requestRender();
|
|
199
|
+
return () => {
|
|
200
|
+
nativeRenderer.removePostProcessFn?.(guardCursor);
|
|
201
|
+
nativeRenderer.requestRender();
|
|
202
|
+
};
|
|
203
|
+
}, [nativeRenderer]);
|
|
204
|
+
}
|
|
@@ -86,9 +86,11 @@ export interface ShellDragRuntimeState {
|
|
|
86
86
|
|
|
87
87
|
export function useShellDragRuntimeState({
|
|
88
88
|
contentHeight,
|
|
89
|
+
throttleFloatingPreview = false,
|
|
89
90
|
width,
|
|
90
91
|
}: {
|
|
91
92
|
contentHeight: number;
|
|
93
|
+
throttleFloatingPreview?: boolean;
|
|
92
94
|
width: number;
|
|
93
95
|
}): ShellDragRuntimeState {
|
|
94
96
|
const dragRef = useRef<DragMode | null>(null);
|
|
@@ -104,15 +106,20 @@ export function useShellDragRuntimeState({
|
|
|
104
106
|
});
|
|
105
107
|
|
|
106
108
|
const updateDragFloatingRect = useCallback((next: { paneId: string; rect: FloatingRect } | null) => {
|
|
107
|
-
|
|
109
|
+
const constrainedNext = next
|
|
108
110
|
? { paneId: next.paneId, rect: constrainFloatingRectToBounds(next.rect, width, contentHeight) }
|
|
109
111
|
: null;
|
|
112
|
+
pendingDragFloatingRectRef.current = constrainedNext;
|
|
110
113
|
if (!next) {
|
|
111
114
|
setDragFloatingRect(null);
|
|
112
115
|
return;
|
|
113
116
|
}
|
|
117
|
+
if (!throttleFloatingPreview) {
|
|
118
|
+
setDragFloatingRect(constrainedNext);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
114
121
|
flushDragFloatingRect();
|
|
115
|
-
}, [contentHeight, flushDragFloatingRect, width]);
|
|
122
|
+
}, [contentHeight, flushDragFloatingRect, throttleFloatingPreview, width]);
|
|
116
123
|
|
|
117
124
|
const updateDividerPreview = useCallback((next: DividerPreviewState | null) => {
|
|
118
125
|
dividerPreviewRef.current = next;
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { cloneLayout, createDefaultConfig, TICKER_RESEARCH_PANE_ID, type LayoutConfig } from "../../../types/config";
|
|
14
14
|
import type { PluginRegistry } from "../../../plugins/registry";
|
|
15
15
|
import type { PaneProps } from "../../../types/plugin";
|
|
16
|
+
import { Textarea } from "../../../ui";
|
|
16
17
|
import { Header } from "../header";
|
|
17
18
|
import {
|
|
18
19
|
buildNativeWindowState,
|
|
@@ -222,7 +223,7 @@ describe("Header", () => {
|
|
|
222
223
|
);
|
|
223
224
|
|
|
224
225
|
await testSetup.renderOnce();
|
|
225
|
-
const frame = testSetup
|
|
226
|
+
const frame = testSetup!.captureCharFrame();
|
|
226
227
|
|
|
227
228
|
expect(frame).toContain("v0.3.0 available");
|
|
228
229
|
expect(frame).toContain("starting download");
|
|
@@ -242,7 +243,7 @@ describe("Header", () => {
|
|
|
242
243
|
);
|
|
243
244
|
|
|
244
245
|
await testSetup.renderOnce();
|
|
245
|
-
const frame = testSetup
|
|
246
|
+
const frame = testSetup!.captureCharFrame();
|
|
246
247
|
|
|
247
248
|
expect(frame).toContain("v0.3.0 available");
|
|
248
249
|
expect(frame).toContain("run npm install -g gloomberb@latest");
|
|
@@ -564,6 +565,95 @@ describe("Shell", () => {
|
|
|
564
565
|
expect(actions.some((action) => action.type === "UPDATE_LAYOUT")).toBe(false);
|
|
565
566
|
});
|
|
566
567
|
|
|
568
|
+
test("updates the floating pane preview before mouse release", async () => {
|
|
569
|
+
const config = createDefaultConfig("/tmp/gloomberb-shell-live-floating-drag-test");
|
|
570
|
+
const floatingLayout = cloneLayout(config.layout);
|
|
571
|
+
floatingLayout.dockRoot = { kind: "pane", instanceId: "portfolio-list:main" };
|
|
572
|
+
floatingLayout.floating = [{ instanceId: "ticker-detail:main", x: 8, y: 2, width: 32, height: 10, zIndex: 75 }];
|
|
573
|
+
await renderShellForWindowModeTest(
|
|
574
|
+
createShellStateWithLayout(config, floatingLayout, "ticker-detail:main"),
|
|
575
|
+
{ width: 80, height: 18 },
|
|
576
|
+
);
|
|
577
|
+
|
|
578
|
+
await act(async () => {
|
|
579
|
+
await testSetup!.mockMouse.pressDown(10, 3);
|
|
580
|
+
await testSetup!.renderOnce();
|
|
581
|
+
await testSetup!.mockMouse.moveTo(16, 6);
|
|
582
|
+
await testSetup!.renderOnce();
|
|
583
|
+
await testSetup!.renderOnce();
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
const frame = testSetup!.captureCharFrame();
|
|
587
|
+
const rows = frame.split("\n");
|
|
588
|
+
expect(rows[2]?.indexOf("┌─:: Main Portfolio") ?? -1).toBeLessThan(0);
|
|
589
|
+
expect(rows[5]?.indexOf("┌─:: Main Portfolio")).toBeGreaterThanOrEqual(14);
|
|
590
|
+
|
|
591
|
+
await act(async () => {
|
|
592
|
+
await testSetup!.mockMouse.release(16, 6);
|
|
593
|
+
await testSetup!.renderOnce();
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
test("keeps the focused textarea cursor visible when it is not covered", async () => {
|
|
598
|
+
const config = createDefaultConfig("/tmp/gloomberb-shell-cursor-visible-test");
|
|
599
|
+
const mainPane = requireLayoutInstance(config, "portfolio-list:main");
|
|
600
|
+
const detailPane = requireLayoutInstance(config, "ticker-detail:main");
|
|
601
|
+
const registry = createShellPluginRegistry({
|
|
602
|
+
portfolioListComponent: ({ focused, width, height }) => (
|
|
603
|
+
<Textarea
|
|
604
|
+
initialValue=""
|
|
605
|
+
focused={focused}
|
|
606
|
+
width={width}
|
|
607
|
+
height={Math.max(1, height)}
|
|
608
|
+
/>
|
|
609
|
+
),
|
|
610
|
+
tickerDetailComponent: () => <text>Chart Body</text>,
|
|
611
|
+
});
|
|
612
|
+
const layout = {
|
|
613
|
+
dockRoot: { kind: "pane" as const, instanceId: "portfolio-list:main" },
|
|
614
|
+
instances: [{ ...mainPane }, { ...detailPane }],
|
|
615
|
+
floating: [{ instanceId: "ticker-detail:main", x: 30, y: 2, width: 24, height: 8, zIndex: 75 }],
|
|
616
|
+
detached: [],
|
|
617
|
+
};
|
|
618
|
+
await renderShellForWindowModeTest(
|
|
619
|
+
createShellStateWithLayout(config, layout, "portfolio-list:main"),
|
|
620
|
+
{ registry, width: 80, height: 18 },
|
|
621
|
+
);
|
|
622
|
+
await testSetup!.renderOnce();
|
|
623
|
+
|
|
624
|
+
expect(testSetup!.renderer.getCursorState().visible).toBe(true);
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
test("hides the textarea cursor when a higher floating pane covers it", async () => {
|
|
628
|
+
const config = createDefaultConfig("/tmp/gloomberb-shell-cursor-occlusion-test");
|
|
629
|
+
const mainPane = requireLayoutInstance(config, "portfolio-list:main");
|
|
630
|
+
const detailPane = requireLayoutInstance(config, "ticker-detail:main");
|
|
631
|
+
const registry = createShellPluginRegistry({
|
|
632
|
+
portfolioListComponent: ({ focused, width, height }) => (
|
|
633
|
+
<Textarea
|
|
634
|
+
initialValue=""
|
|
635
|
+
focused={focused}
|
|
636
|
+
width={width}
|
|
637
|
+
height={Math.max(1, height)}
|
|
638
|
+
/>
|
|
639
|
+
),
|
|
640
|
+
tickerDetailComponent: () => <text>Chart Body</text>,
|
|
641
|
+
});
|
|
642
|
+
const layout = {
|
|
643
|
+
dockRoot: { kind: "pane" as const, instanceId: "portfolio-list:main" },
|
|
644
|
+
instances: [{ ...mainPane }, { ...detailPane }],
|
|
645
|
+
floating: [{ instanceId: "ticker-detail:main", x: 0, y: 0, width: 40, height: 8, zIndex: 75 }],
|
|
646
|
+
detached: [],
|
|
647
|
+
};
|
|
648
|
+
await renderShellForWindowModeTest(
|
|
649
|
+
createShellStateWithLayout(config, layout, "portfolio-list:main"),
|
|
650
|
+
{ registry, width: 80, height: 18 },
|
|
651
|
+
);
|
|
652
|
+
await testSetup!.renderOnce();
|
|
653
|
+
|
|
654
|
+
expect(testSetup!.renderer.getCursorState().visible).toBe(false);
|
|
655
|
+
});
|
|
656
|
+
|
|
567
657
|
test("keeps focused pane local detail state when maximizing a floating pane", async () => {
|
|
568
658
|
const config = createDefaultConfig("/tmp/gloomberb-shell-fullscreen-local-state-test");
|
|
569
659
|
const detailPane = requireLayoutInstance(config, "ticker-detail:main");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AsciiText, Box, Text, compactContextMenuItems, useContextMenu, useUiHost } from "../../../ui";
|
|
1
|
+
import { AsciiText, Box, Text, compactContextMenuItems, useContextMenu, useUiHost, type BoxRenderable } from "../../../ui";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { useRendererHost, useUiCapabilities } from "../../../ui";
|
|
4
4
|
import { useViewport } from "../../../react/input";
|
|
@@ -61,6 +61,10 @@ import {
|
|
|
61
61
|
import { useShellPaneActions } from "./pane/actions";
|
|
62
62
|
import { resolvePaneFocusSourceLayout } from "./fullscreen";
|
|
63
63
|
import { useTransientLayout } from "../transient-layout";
|
|
64
|
+
import {
|
|
65
|
+
resolveShellCursorOcclusionRects,
|
|
66
|
+
useShellCursorOcclusionGuard,
|
|
67
|
+
} from "./cursor-occlusion";
|
|
64
68
|
|
|
65
69
|
export { resolveAppHeaderHeightCells } from "./chrome";
|
|
66
70
|
export { buildNativeWindowState } from "./native/window-state";
|
|
@@ -102,6 +106,7 @@ export function Shell({
|
|
|
102
106
|
const { nativePaneChrome = false, nativeContextMenu, precisePointer, titleBarOverlay, cellHeightPx } = useUiCapabilities();
|
|
103
107
|
const { showContextMenu } = useContextMenu();
|
|
104
108
|
const { width, height } = useViewport();
|
|
109
|
+
const shellRef = useRef<BoxRenderable | null>(null);
|
|
105
110
|
|
|
106
111
|
const appHeaderHeight = resolveAppHeaderHeightCells({ titleBarOverlay, cellHeightPx });
|
|
107
112
|
const contentHeight = Math.max(1, height - appHeaderHeight - (statusBarVisible ? 1 : 0));
|
|
@@ -124,7 +129,11 @@ export function Shell({
|
|
|
124
129
|
}, []);
|
|
125
130
|
const overlayOpen = commandBarOpen || dialogOpen || !!menuState;
|
|
126
131
|
|
|
127
|
-
const dragRuntime = useShellDragRuntimeState({
|
|
132
|
+
const dragRuntime = useShellDragRuntimeState({
|
|
133
|
+
contentHeight,
|
|
134
|
+
throttleFloatingPreview: nativePaneChrome,
|
|
135
|
+
width,
|
|
136
|
+
});
|
|
128
137
|
const {
|
|
129
138
|
cancelActiveDrag,
|
|
130
139
|
dividerPreview,
|
|
@@ -209,6 +218,27 @@ export function Shell({
|
|
|
209
218
|
pluginRegistry,
|
|
210
219
|
width,
|
|
211
220
|
});
|
|
221
|
+
const cursorOcclusionRects = useMemo(() => resolveShellCursorOcclusionRects({
|
|
222
|
+
contentHeight,
|
|
223
|
+
dragFloatingRect,
|
|
224
|
+
nativePaneChrome,
|
|
225
|
+
overlayOpen,
|
|
226
|
+
transientFocusActive,
|
|
227
|
+
visibleFloatingPanes,
|
|
228
|
+
width,
|
|
229
|
+
}), [
|
|
230
|
+
contentHeight,
|
|
231
|
+
dragFloatingRect,
|
|
232
|
+
nativePaneChrome,
|
|
233
|
+
overlayOpen,
|
|
234
|
+
transientFocusActive,
|
|
235
|
+
visibleFloatingPanes,
|
|
236
|
+
width,
|
|
237
|
+
]);
|
|
238
|
+
useShellCursorOcclusionGuard({
|
|
239
|
+
occlusionRects: cursorOcclusionRects,
|
|
240
|
+
shellRef,
|
|
241
|
+
});
|
|
212
242
|
|
|
213
243
|
const {
|
|
214
244
|
closeAllFloatingPanes,
|
|
@@ -485,12 +515,24 @@ export function Shell({
|
|
|
485
515
|
|
|
486
516
|
return (
|
|
487
517
|
<Box
|
|
518
|
+
ref={shellRef}
|
|
488
519
|
flexDirection="row"
|
|
489
520
|
flexGrow={1}
|
|
521
|
+
flexShrink={1}
|
|
522
|
+
flexBasis={0}
|
|
523
|
+
minWidth={0}
|
|
524
|
+
minHeight={0}
|
|
490
525
|
height={nativePaneChrome ? undefined : contentHeight}
|
|
491
526
|
position={nativePaneChrome ? "relative" : undefined}
|
|
492
527
|
overflow="hidden"
|
|
493
|
-
{...(!nativePaneChrome
|
|
528
|
+
{...(!nativePaneChrome
|
|
529
|
+
? {
|
|
530
|
+
onMouseDown: handleMouse,
|
|
531
|
+
onMouseDrag: handleMouse,
|
|
532
|
+
onMouseDragEnd: handleMouse,
|
|
533
|
+
onMouseUp: handleMouse,
|
|
534
|
+
}
|
|
535
|
+
: {})}
|
|
494
536
|
>
|
|
495
537
|
<Box
|
|
496
538
|
position="absolute"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { getDockedPaneIds } from "../../../plugins/pane-manager";
|
|
3
|
+
import { createPaneInstance, type LayoutConfig } from "../../../types/config";
|
|
4
|
+
import { resolveShellVisibleLayout } from "./visible-layout";
|
|
5
|
+
|
|
6
|
+
describe("shell visible layout", () => {
|
|
7
|
+
test("removes unregistered pane types before resolving dock geometry", () => {
|
|
8
|
+
const layout: LayoutConfig = {
|
|
9
|
+
dockRoot: {
|
|
10
|
+
kind: "split",
|
|
11
|
+
axis: "horizontal",
|
|
12
|
+
ratio: 0.5,
|
|
13
|
+
first: { kind: "pane", instanceId: "missing:main" },
|
|
14
|
+
second: { kind: "pane", instanceId: "chat:main" },
|
|
15
|
+
},
|
|
16
|
+
instances: [
|
|
17
|
+
createPaneInstance("missing-plugin", { instanceId: "missing:main" }),
|
|
18
|
+
createPaneInstance("chat", { instanceId: "chat:main" }),
|
|
19
|
+
],
|
|
20
|
+
floating: [],
|
|
21
|
+
detached: [],
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const visibleLayout = resolveShellVisibleLayout(
|
|
25
|
+
layout,
|
|
26
|
+
new Set(),
|
|
27
|
+
new Map([["chat", true]]),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
expect(visibleLayout.instances.map((instance) => instance.instanceId)).toEqual(["chat:main"]);
|
|
31
|
+
expect(getDockedPaneIds(visibleLayout)).toEqual(["chat:main"]);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -6,8 +6,9 @@ import {
|
|
|
6
6
|
type ResolvedPane,
|
|
7
7
|
} from "../../../plugins/pane-manager";
|
|
8
8
|
import type { PluginRegistry } from "../../../plugins/registry";
|
|
9
|
-
import {
|
|
9
|
+
import type { LayoutConfig } from "../../../types/config";
|
|
10
10
|
import { constrainFloatingRectToBounds } from "./drag";
|
|
11
|
+
import { resolveShellVisibleLayout } from "./visible-layout";
|
|
11
12
|
|
|
12
13
|
interface ShellVisibleLayoutOptions {
|
|
13
14
|
disabledPlugins: readonly string[];
|
|
@@ -34,15 +35,9 @@ export function useShellVisibleLayout({
|
|
|
34
35
|
return paneIds;
|
|
35
36
|
}, [disabledPlugins, pluginRegistry]);
|
|
36
37
|
|
|
37
|
-
const hiddenInstanceIds = useMemo(() => (
|
|
38
|
-
layout.instances
|
|
39
|
-
.filter((instance) => disabledPaneIds.has(instance.paneId))
|
|
40
|
-
.map((instance) => instance.instanceId)
|
|
41
|
-
), [disabledPaneIds, layout.instances]);
|
|
42
|
-
|
|
43
38
|
const visibleLayout = useMemo(
|
|
44
|
-
() => (
|
|
45
|
-
[
|
|
39
|
+
() => resolveShellVisibleLayout(layout, disabledPaneIds, pluginRegistry.panes),
|
|
40
|
+
[disabledPaneIds, layout, pluginRegistry.panes],
|
|
46
41
|
);
|
|
47
42
|
|
|
48
43
|
return { disabledPaneIds, visibleLayout };
|
|
@@ -111,11 +111,14 @@ export function ShellPaneLayers({
|
|
|
111
111
|
>
|
|
112
112
|
<PaneFooterProvider>
|
|
113
113
|
{(footer) => {
|
|
114
|
-
const
|
|
114
|
+
const showFooter = hasPaneFooterContent(footer);
|
|
115
|
+
const reserveFooter = shouldReservePaneFooter(nativePaneChrome, showFooter);
|
|
116
|
+
const renderFooter = reserveFooter || showFooter;
|
|
115
117
|
const bodyFrame = resolvePaneBodyFrame({
|
|
116
118
|
width: rect.width,
|
|
117
119
|
height: rect.height,
|
|
118
120
|
nativePaneChrome,
|
|
121
|
+
footerVisible: renderFooter,
|
|
119
122
|
reserveFooter,
|
|
120
123
|
});
|
|
121
124
|
return (
|
|
@@ -129,7 +132,7 @@ export function ShellPaneLayers({
|
|
|
129
132
|
windowModeSelected={windowModeSelected}
|
|
130
133
|
footer={footer}
|
|
131
134
|
onMouseDown={nativePaneChrome ? (event) => handleNativePaneMouseDown(leaf.instanceId, event) : undefined}
|
|
132
|
-
|
|
135
|
+
onHeaderMouseMove={() => setHoveredPaneIfChanged(leaf.instanceId)}
|
|
133
136
|
onHeaderMouseDown={nativePaneChrome && !transientFocusActive ? (event) => startNativeDockedDrag(leaf.instanceId, rect, event) : undefined}
|
|
134
137
|
onHeaderMouseDrag={nativePaneChrome && !transientFocusActive ? handleNativeDrag : undefined}
|
|
135
138
|
onHeaderMouseDragEnd={nativePaneChrome && !transientFocusActive ? handleNativeDrag : undefined}
|
|
@@ -165,11 +168,14 @@ export function ShellPaneLayers({
|
|
|
165
168
|
return (
|
|
166
169
|
<PaneFooterProvider key={`float:${pane.instance.instanceId}`}>
|
|
167
170
|
{(footer) => {
|
|
168
|
-
const
|
|
171
|
+
const showFooter = hasPaneFooterContent(footer);
|
|
172
|
+
const reserveFooter = shouldReservePaneFooter(nativePaneChrome, showFooter);
|
|
173
|
+
const renderFooter = reserveFooter || showFooter;
|
|
169
174
|
const bodyFrame = resolvePaneBodyFrame({
|
|
170
175
|
width: preview.width,
|
|
171
176
|
height: preview.height,
|
|
172
177
|
nativePaneChrome,
|
|
178
|
+
footerVisible: renderFooter,
|
|
173
179
|
reserveFooter,
|
|
174
180
|
});
|
|
175
181
|
return (
|
|
@@ -186,7 +192,7 @@ export function ShellPaneLayers({
|
|
|
186
192
|
showActions={showActions}
|
|
187
193
|
footer={footer}
|
|
188
194
|
onMouseDown={nativePaneChrome ? (event) => handleNativePaneMouseDown(pane.instance.instanceId, event) : undefined}
|
|
189
|
-
|
|
195
|
+
onHeaderMouseMove={() => setHoveredPaneIfChanged(pane.instance.instanceId)}
|
|
190
196
|
onHeaderMouseDown={nativePaneChrome ? (event) => startNativeFloatingDrag(pane.instance.instanceId, preview, event) : undefined}
|
|
191
197
|
onHeaderMouseDrag={nativePaneChrome ? handleNativeDrag : undefined}
|
|
192
198
|
onHeaderMouseDragEnd={nativePaneChrome ? handleNativeDrag : undefined}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
removeUnavailablePaneTypes,
|
|
3
|
+
type PaneTypeAvailability,
|
|
4
|
+
} from "../../../plugins/pane-manager";
|
|
5
|
+
import type { LayoutConfig } from "../../../types/config";
|
|
6
|
+
|
|
7
|
+
export function resolveShellVisibleLayout(
|
|
8
|
+
layout: LayoutConfig,
|
|
9
|
+
disabledPaneIds: ReadonlySet<string>,
|
|
10
|
+
registeredPaneIds: PaneTypeAvailability,
|
|
11
|
+
): LayoutConfig {
|
|
12
|
+
return removeUnavailablePaneTypes(
|
|
13
|
+
layout,
|
|
14
|
+
registeredPaneIds,
|
|
15
|
+
{ disabledPaneIds },
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
2
|
import { testRender } from "../../renderers/opentui/test-utils";
|
|
3
3
|
import { AppContext, createInitialState } from "../../state/app/context";
|
|
4
|
-
import { cloneLayout, createDefaultConfig, type LayoutConfig } from "../../types/config";
|
|
4
|
+
import { cloneLayout, createDefaultConfig, TICKER_RESEARCH_PANE_ID, type LayoutConfig } from "../../types/config";
|
|
5
5
|
import type { AppNotificationRequest } from "../../types/plugin";
|
|
6
6
|
import { StatusBar } from "./status-bar";
|
|
7
7
|
import { setSharedRegistryForTests } from "../../plugins/registry";
|
|
@@ -174,6 +174,10 @@ describe("StatusBar", () => {
|
|
|
174
174
|
const notifications: AppNotificationRequest[] = [];
|
|
175
175
|
|
|
176
176
|
setSharedRegistryForTests({
|
|
177
|
+
panes: new Map([
|
|
178
|
+
["portfolio-list", {}],
|
|
179
|
+
[TICKER_RESEARCH_PANE_ID, {}],
|
|
180
|
+
]),
|
|
177
181
|
getLayoutFn: () => state.config.layout,
|
|
178
182
|
getTermSizeFn: () => ({ width: 120, height: 40 }),
|
|
179
183
|
updateLayoutFn: (layout: LayoutConfig) => { updatedLayout = layout; },
|
|
@@ -233,6 +237,7 @@ describe("StatusBar", () => {
|
|
|
233
237
|
globalThis.clearTimeout = (() => {}) as typeof clearTimeout;
|
|
234
238
|
|
|
235
239
|
setSharedRegistryForTests({
|
|
240
|
+
panes: new Map(),
|
|
236
241
|
getLayoutFn: () => state.config.layout,
|
|
237
242
|
getTermSizeFn: () => ({ width: 120, height: 40 }),
|
|
238
243
|
updateLayoutFn: () => {},
|
|
@@ -114,7 +114,11 @@ export function StatusBar() {
|
|
|
114
114
|
event?.stopPropagation?.();
|
|
115
115
|
if (!registry) return;
|
|
116
116
|
const { width, height } = registry.getTermSizeFn();
|
|
117
|
-
registry.updateLayoutFn(gridlockAllPanes(
|
|
117
|
+
registry.updateLayoutFn(gridlockAllPanes(
|
|
118
|
+
registry.getLayoutFn(),
|
|
119
|
+
{ x: 0, y: 0, width, height },
|
|
120
|
+
registry.panes,
|
|
121
|
+
));
|
|
118
122
|
notifyGridlockComplete(registry.notify.bind(registry), () => {
|
|
119
123
|
dispatch({ type: "UNDO_LAYOUT" });
|
|
120
124
|
});
|
|
@@ -360,7 +364,7 @@ function CommandBarHint({
|
|
|
360
364
|
<Text
|
|
361
365
|
fg={hovered ? colors.text : colors.textDim}
|
|
362
366
|
{...(!nativePaneChrome ? { bg: hovered ? hoverBg() : undefined } : {})}
|
|
363
|
-
|
|
367
|
+
onMouseOver={() => setHoveredControl((current) => (current === "command-bar" ? current : "command-bar"))}
|
|
364
368
|
onMouseDown={openCommandBar}
|
|
365
369
|
{...(nativePaneChrome ? { "data-gloom-interactive": "true" } : {})}
|
|
366
370
|
>
|
|
@@ -381,7 +385,7 @@ function NativeGridlockTip({
|
|
|
381
385
|
<Text
|
|
382
386
|
fg={hoveredControl === "gridlock-tip" ? colors.textBright : colors.borderFocused}
|
|
383
387
|
attributes={TextAttributes.BOLD}
|
|
384
|
-
|
|
388
|
+
onMouseOver={() => setHoveredControl((current) => (current === "gridlock-tip" ? current : "gridlock-tip"))}
|
|
385
389
|
onMouseDown={handleGridlockTip}
|
|
386
390
|
data-gloom-interactive="true"
|
|
387
391
|
>
|
|
@@ -389,7 +393,7 @@ function NativeGridlockTip({
|
|
|
389
393
|
</Text>
|
|
390
394
|
<Text
|
|
391
395
|
fg={hoveredControl === "gridlock-tip-dismiss" ? colors.text : colors.textDim}
|
|
392
|
-
|
|
396
|
+
onMouseOver={() => setHoveredControl((current) => (current === "gridlock-tip-dismiss" ? current : "gridlock-tip-dismiss"))}
|
|
393
397
|
onMouseDown={dismissGridlockTip}
|
|
394
398
|
data-gloom-interactive="true"
|
|
395
399
|
>
|
|
@@ -411,14 +415,14 @@ function TerminalGridlockTip({
|
|
|
411
415
|
<Box width={1} />
|
|
412
416
|
<Box
|
|
413
417
|
backgroundColor={hoveredControl === "gridlock-tip" ? hoverBg() : colors.header}
|
|
414
|
-
|
|
418
|
+
onMouseOver={() => setHoveredControl((current) => (current === "gridlock-tip" ? current : "gridlock-tip"))}
|
|
415
419
|
onMouseDown={handleGridlockTip}
|
|
416
420
|
>
|
|
417
421
|
<Text fg={colors.headerText}> Gridlock All </Text>
|
|
418
422
|
</Box>
|
|
419
423
|
<Text
|
|
420
424
|
fg={hoveredControl === "gridlock-tip-dismiss" ? colors.text : colors.textDim}
|
|
421
|
-
|
|
425
|
+
onMouseOver={() => setHoveredControl((current) => (current === "gridlock-tip-dismiss" ? current : "gridlock-tip-dismiss"))}
|
|
422
426
|
onMouseDown={dismissGridlockTip}
|
|
423
427
|
>
|
|
424
428
|
{" x"}
|