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
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AsciiText, Box, Text, compactContextMenuItems, useContextMenu, useUiHost } from "../../../ui";
|
|
2
|
-
import { useCallback, useMemo, useState } from "react";
|
|
1
|
+
import { AsciiText, Box, Text, compactContextMenuItems, useContextMenu, useUiHost, type BoxRenderable } from "../../../ui";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { useRendererHost, useUiCapabilities } from "../../../ui";
|
|
4
4
|
import { useViewport } from "../../../react/input";
|
|
5
5
|
import { useDialogState } from "../../../ui/dialog";
|
|
@@ -8,6 +8,7 @@ import type { DesktopDockPreviewState, DesktopWindowBridge } from "../../../type
|
|
|
8
8
|
import {
|
|
9
9
|
getDockDividerLayouts,
|
|
10
10
|
getDockLeafLayouts,
|
|
11
|
+
isPaneInLayout,
|
|
11
12
|
type DockGeometryOptions,
|
|
12
13
|
type LayoutBounds,
|
|
13
14
|
type ResolvedPane,
|
|
@@ -58,6 +59,12 @@ import {
|
|
|
58
59
|
useShellVisibleLayout,
|
|
59
60
|
} from "./layout-state";
|
|
60
61
|
import { useShellPaneActions } from "./pane/actions";
|
|
62
|
+
import { resolvePaneFocusSourceLayout } from "./fullscreen";
|
|
63
|
+
import { useTransientLayout } from "../transient-layout";
|
|
64
|
+
import {
|
|
65
|
+
resolveShellCursorOcclusionRects,
|
|
66
|
+
useShellCursorOcclusionGuard,
|
|
67
|
+
} from "./cursor-occlusion";
|
|
61
68
|
|
|
62
69
|
export { resolveAppHeaderHeightCells } from "./chrome";
|
|
63
70
|
export { buildNativeWindowState } from "./native/window-state";
|
|
@@ -70,6 +77,13 @@ interface ShellProps {
|
|
|
70
77
|
commandBarNativeOccluder?: LayoutBounds | null;
|
|
71
78
|
}
|
|
72
79
|
|
|
80
|
+
interface TransientFocusLayoutState {
|
|
81
|
+
paneId: string;
|
|
82
|
+
layout: LayoutConfig;
|
|
83
|
+
sourceLayoutIndex: number;
|
|
84
|
+
active: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
73
87
|
export function Shell({
|
|
74
88
|
pluginRegistry,
|
|
75
89
|
desktopWindowBridge,
|
|
@@ -86,11 +100,13 @@ export function Shell({
|
|
|
86
100
|
const inputCaptured = useAppSelector((state) => state.inputCaptured);
|
|
87
101
|
const statusBarVisible = useAppSelector(selectStatusBarVisible);
|
|
88
102
|
const rendererHost = useRendererHost();
|
|
103
|
+
const { setTransientLayout } = useTransientLayout();
|
|
89
104
|
const uiKind = useUiHost().kind;
|
|
90
105
|
const shortcutDisplayMode = getShortcutDisplayMode(uiKind);
|
|
91
106
|
const { nativePaneChrome = false, nativeContextMenu, precisePointer, titleBarOverlay, cellHeightPx } = useUiCapabilities();
|
|
92
107
|
const { showContextMenu } = useContextMenu();
|
|
93
108
|
const { width, height } = useViewport();
|
|
109
|
+
const shellRef = useRef<BoxRenderable | null>(null);
|
|
94
110
|
|
|
95
111
|
const appHeaderHeight = resolveAppHeaderHeightCells({ titleBarOverlay, cellHeightPx });
|
|
96
112
|
const contentHeight = Math.max(1, height - appHeaderHeight - (statusBarVisible ? 1 : 0));
|
|
@@ -103,6 +119,9 @@ export function Shell({
|
|
|
103
119
|
setHoveredPaneId((current) => (current === paneId ? current : paneId));
|
|
104
120
|
}, []);
|
|
105
121
|
const [menuState, setMenuState] = useState<ActionMenuState | null>(null);
|
|
122
|
+
const [transientFocusLayoutState, setTransientFocusLayoutState] = useState<TransientFocusLayoutState | null>(null);
|
|
123
|
+
const transientFocusLayoutStateRef = useRef<TransientFocusLayoutState | null>(null);
|
|
124
|
+
transientFocusLayoutStateRef.current = transientFocusLayoutState;
|
|
106
125
|
const [hoveredMenuItemId, setHoveredMenuItemId] = useState<string | null>(null);
|
|
107
126
|
const closePaneMenu = useCallback(() => {
|
|
108
127
|
setMenuState(null);
|
|
@@ -110,7 +129,11 @@ export function Shell({
|
|
|
110
129
|
}, []);
|
|
111
130
|
const overlayOpen = commandBarOpen || dialogOpen || !!menuState;
|
|
112
131
|
|
|
113
|
-
const dragRuntime = useShellDragRuntimeState({
|
|
132
|
+
const dragRuntime = useShellDragRuntimeState({
|
|
133
|
+
contentHeight,
|
|
134
|
+
throttleFloatingPreview: nativePaneChrome,
|
|
135
|
+
width,
|
|
136
|
+
});
|
|
114
137
|
const {
|
|
115
138
|
cancelActiveDrag,
|
|
116
139
|
dividerPreview,
|
|
@@ -149,7 +172,7 @@ export function Shell({
|
|
|
149
172
|
}, [dispatch]);
|
|
150
173
|
|
|
151
174
|
const {
|
|
152
|
-
activeLayout,
|
|
175
|
+
activeLayout: windowModeLayout,
|
|
153
176
|
nativeWindowModePanelRect,
|
|
154
177
|
selectWindowModePane,
|
|
155
178
|
startWindowMode,
|
|
@@ -171,6 +194,17 @@ export function Shell({
|
|
|
171
194
|
visibleLayout,
|
|
172
195
|
width,
|
|
173
196
|
});
|
|
197
|
+
const transientFocusActive = !windowMode && transientFocusLayoutState?.active === true;
|
|
198
|
+
const activeLayout = transientFocusActive && transientFocusLayoutState
|
|
199
|
+
? transientFocusLayoutState.layout
|
|
200
|
+
: windowModeLayout;
|
|
201
|
+
const transientFocusPaneId = transientFocusActive ? transientFocusLayoutState?.paneId ?? null : null;
|
|
202
|
+
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
if (!windowMode || !transientFocusLayoutState) return;
|
|
205
|
+
transientFocusLayoutStateRef.current = null;
|
|
206
|
+
setTransientFocusLayoutState(null);
|
|
207
|
+
}, [transientFocusLayoutState, windowMode]);
|
|
174
208
|
|
|
175
209
|
const {
|
|
176
210
|
dockedPanes,
|
|
@@ -184,6 +218,27 @@ export function Shell({
|
|
|
184
218
|
pluginRegistry,
|
|
185
219
|
width,
|
|
186
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
|
+
});
|
|
187
242
|
|
|
188
243
|
const {
|
|
189
244
|
closeAllFloatingPanes,
|
|
@@ -211,6 +266,102 @@ export function Shell({
|
|
|
211
266
|
visibleLayout,
|
|
212
267
|
width,
|
|
213
268
|
});
|
|
269
|
+
const setTransientFocusLayout = useCallback((next: TransientFocusLayoutState | null) => {
|
|
270
|
+
transientFocusLayoutStateRef.current = next;
|
|
271
|
+
setTransientFocusLayoutState(next);
|
|
272
|
+
}, []);
|
|
273
|
+
const activateTransientFocusState = useCallback((current: TransientFocusLayoutState) => {
|
|
274
|
+
closePaneMenu();
|
|
275
|
+
setTransientFocusLayout({ ...current, active: true });
|
|
276
|
+
const sourceLayout = config.layouts[current.sourceLayoutIndex]?.layout;
|
|
277
|
+
if (
|
|
278
|
+
current.sourceLayoutIndex !== config.activeLayoutIndex
|
|
279
|
+
&& sourceLayout
|
|
280
|
+
&& isPaneInLayout(sourceLayout, current.paneId)
|
|
281
|
+
) {
|
|
282
|
+
dispatch({ type: "SWITCH_LAYOUT", index: current.sourceLayoutIndex });
|
|
283
|
+
}
|
|
284
|
+
focusPane(current.paneId);
|
|
285
|
+
}, [closePaneMenu, config.activeLayoutIndex, config.layouts, dispatch, focusPane, setTransientFocusLayout]);
|
|
286
|
+
const toggleFocusedPaneFullscreen = useCallback(() => {
|
|
287
|
+
const current = transientFocusLayoutStateRef.current;
|
|
288
|
+
if (current?.active) {
|
|
289
|
+
setTransientFocusLayout(null);
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (
|
|
294
|
+
current
|
|
295
|
+
&& current.paneId === focusedPaneId
|
|
296
|
+
&& current.sourceLayoutIndex === config.activeLayoutIndex
|
|
297
|
+
) {
|
|
298
|
+
activateTransientFocusState(current);
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const nextLayout = resolvePaneFocusSourceLayout(visibleLayout, focusedPaneId);
|
|
303
|
+
if (!focusedPaneId || !nextLayout) {
|
|
304
|
+
pluginRegistry.notify({ body: "Focus a pane to make it fullscreen", type: "info" });
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
closePaneMenu();
|
|
309
|
+
setTransientFocusLayout({
|
|
310
|
+
paneId: focusedPaneId,
|
|
311
|
+
layout: nextLayout,
|
|
312
|
+
sourceLayoutIndex: config.activeLayoutIndex,
|
|
313
|
+
active: true,
|
|
314
|
+
});
|
|
315
|
+
focusPane(focusedPaneId);
|
|
316
|
+
return true;
|
|
317
|
+
}, [
|
|
318
|
+
activateTransientFocusState,
|
|
319
|
+
closePaneMenu,
|
|
320
|
+
config.activeLayoutIndex,
|
|
321
|
+
focusedPaneId,
|
|
322
|
+
focusPane,
|
|
323
|
+
pluginRegistry,
|
|
324
|
+
setTransientFocusLayout,
|
|
325
|
+
visibleLayout,
|
|
326
|
+
]);
|
|
327
|
+
const activateTransientFocusLayout = useCallback(() => {
|
|
328
|
+
const current = transientFocusLayoutStateRef.current;
|
|
329
|
+
if (!current) return;
|
|
330
|
+
activateTransientFocusState(current);
|
|
331
|
+
}, [activateTransientFocusState]);
|
|
332
|
+
const deactivateTransientFocusLayout = useCallback(() => {
|
|
333
|
+
const current = transientFocusLayoutStateRef.current;
|
|
334
|
+
if (!current || !current.active) return;
|
|
335
|
+
closePaneMenu();
|
|
336
|
+
setTransientFocusLayout({ ...current, active: false });
|
|
337
|
+
}, [closePaneMenu, setTransientFocusLayout]);
|
|
338
|
+
const exitTransientFocusLayout = useCallback(() => {
|
|
339
|
+
closePaneMenu();
|
|
340
|
+
setTransientFocusLayout(null);
|
|
341
|
+
}, [closePaneMenu, setTransientFocusLayout]);
|
|
342
|
+
|
|
343
|
+
useEffect(() => {
|
|
344
|
+
setTransientLayout(
|
|
345
|
+
transientFocusLayoutState
|
|
346
|
+
? {
|
|
347
|
+
id: "pane-focus",
|
|
348
|
+
label: "^F Focus",
|
|
349
|
+
active: transientFocusActive,
|
|
350
|
+
onActivate: activateTransientFocusLayout,
|
|
351
|
+
onDeactivate: deactivateTransientFocusLayout,
|
|
352
|
+
onExit: exitTransientFocusLayout,
|
|
353
|
+
}
|
|
354
|
+
: null,
|
|
355
|
+
);
|
|
356
|
+
return () => setTransientLayout(null);
|
|
357
|
+
}, [
|
|
358
|
+
activateTransientFocusLayout,
|
|
359
|
+
deactivateTransientFocusLayout,
|
|
360
|
+
exitTransientFocusLayout,
|
|
361
|
+
setTransientLayout,
|
|
362
|
+
transientFocusActive,
|
|
363
|
+
transientFocusLayoutState,
|
|
364
|
+
]);
|
|
214
365
|
|
|
215
366
|
useShellPaneManagementShortcuts({
|
|
216
367
|
cancelActiveDrag,
|
|
@@ -226,6 +377,7 @@ export function Shell({
|
|
|
226
377
|
overlayOpen,
|
|
227
378
|
popOutFocusedPane,
|
|
228
379
|
startWindowMode,
|
|
380
|
+
toggleFocusedPaneFullscreen,
|
|
229
381
|
toggleFocusedPaneFloating,
|
|
230
382
|
});
|
|
231
383
|
|
|
@@ -333,6 +485,7 @@ export function Shell({
|
|
|
333
485
|
closePaneMenu,
|
|
334
486
|
contentHeight,
|
|
335
487
|
dispatch,
|
|
488
|
+
dockGeometryOptions,
|
|
336
489
|
dockDividerLayouts,
|
|
337
490
|
dockLeafLayouts,
|
|
338
491
|
dragRuntime,
|
|
@@ -349,6 +502,7 @@ export function Shell({
|
|
|
349
502
|
setHoveredMenuItemId,
|
|
350
503
|
setMenuState,
|
|
351
504
|
snapGuides,
|
|
505
|
+
transientFocusActive,
|
|
352
506
|
updateWindowModePreviewLayout,
|
|
353
507
|
visibleFloatingPanes,
|
|
354
508
|
visibleLayout,
|
|
@@ -361,8 +515,13 @@ export function Shell({
|
|
|
361
515
|
|
|
362
516
|
return (
|
|
363
517
|
<Box
|
|
518
|
+
ref={shellRef}
|
|
364
519
|
flexDirection="row"
|
|
365
520
|
flexGrow={1}
|
|
521
|
+
flexShrink={1}
|
|
522
|
+
flexBasis={0}
|
|
523
|
+
minWidth={0}
|
|
524
|
+
minHeight={0}
|
|
366
525
|
height={nativePaneChrome ? undefined : contentHeight}
|
|
367
526
|
position={nativePaneChrome ? "relative" : undefined}
|
|
368
527
|
overflow="hidden"
|
|
@@ -409,6 +568,8 @@ export function Shell({
|
|
|
409
568
|
startNativeDockedDrag={startNativeDockedDrag}
|
|
410
569
|
startNativeFloatingDrag={startNativeFloatingDrag}
|
|
411
570
|
startNativeFloatResize={startNativeFloatResize}
|
|
571
|
+
transientFocusActive={transientFocusActive}
|
|
572
|
+
transientFocusPaneId={transientFocusPaneId}
|
|
412
573
|
visibleFloatingPanes={visibleFloatingPanes}
|
|
413
574
|
width={width}
|
|
414
575
|
windowModeDockResizePathKey={windowModeDockResizePathKey}
|
|
@@ -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 };
|
|
@@ -20,6 +20,7 @@ interface UseShellNativePointerRuntimeOptions {
|
|
|
20
20
|
selectWindowModePane: (paneId: string) => void;
|
|
21
21
|
setHoveredMenuItemId: Dispatch<SetStateAction<string | null>>;
|
|
22
22
|
setMenuState: Dispatch<SetStateAction<ActionMenuState | null>>;
|
|
23
|
+
transientFocusActive: boolean;
|
|
23
24
|
windowMode: WindowEditState | null;
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -35,6 +36,7 @@ export function useShellNativePointerRuntime({
|
|
|
35
36
|
selectWindowModePane,
|
|
36
37
|
setHoveredMenuItemId,
|
|
37
38
|
setMenuState,
|
|
39
|
+
transientFocusActive,
|
|
38
40
|
windowMode,
|
|
39
41
|
}: UseShellNativePointerRuntimeOptions) {
|
|
40
42
|
const {
|
|
@@ -69,6 +71,7 @@ export function useShellNativePointerRuntime({
|
|
|
69
71
|
const startNativeFloatingDrag = useCallback((paneId: string, rect: FloatingRect, event: ShellMouseEvent) => {
|
|
70
72
|
if (!nativePaneChrome) return;
|
|
71
73
|
if (windowMode) return;
|
|
74
|
+
if (transientFocusActive) return;
|
|
72
75
|
if (event.button === 2) return;
|
|
73
76
|
|
|
74
77
|
const pointer = getShellPointer(event);
|
|
@@ -83,11 +86,12 @@ export function useShellNativePointerRuntime({
|
|
|
83
86
|
};
|
|
84
87
|
updateDragFloatingRect({ paneId, rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height } });
|
|
85
88
|
event.preventDefault();
|
|
86
|
-
}, [dragRef, focusNativePane, getShellPointer, nativePaneChrome, updateDragFloatingRect, windowMode]);
|
|
89
|
+
}, [dragRef, focusNativePane, getShellPointer, nativePaneChrome, transientFocusActive, updateDragFloatingRect, windowMode]);
|
|
87
90
|
|
|
88
91
|
const startNativeDockedDrag = useCallback((paneId: string, rect: LayoutBounds, event: ShellMouseEvent) => {
|
|
89
92
|
if (!nativePaneChrome) return;
|
|
90
93
|
if (windowMode) return;
|
|
94
|
+
if (transientFocusActive) return;
|
|
91
95
|
if (event.button === 2) return;
|
|
92
96
|
|
|
93
97
|
const pointer = getShellPointer(event);
|
|
@@ -101,10 +105,11 @@ export function useShellNativePointerRuntime({
|
|
|
101
105
|
origRect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
102
106
|
};
|
|
103
107
|
event.preventDefault();
|
|
104
|
-
}, [dragRef, focusNativePane, getShellPointer, nativePaneChrome, windowMode]);
|
|
108
|
+
}, [dragRef, focusNativePane, getShellPointer, nativePaneChrome, transientFocusActive, windowMode]);
|
|
105
109
|
|
|
106
110
|
const startNativeFloatResize = useCallback((paneId: string, rect: FloatingRect, event: ShellMouseEvent) => {
|
|
107
111
|
if (!nativePaneChrome) return;
|
|
112
|
+
if (transientFocusActive) return;
|
|
108
113
|
|
|
109
114
|
const pointer = getShellPointer(event);
|
|
110
115
|
if (windowMode) {
|
|
@@ -122,10 +127,11 @@ export function useShellNativePointerRuntime({
|
|
|
122
127
|
updateDragFloatingRect({ paneId, rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height } });
|
|
123
128
|
event.stopPropagation();
|
|
124
129
|
event.preventDefault();
|
|
125
|
-
}, [dragRef, focusNativePane, getShellPointer, nativePaneChrome, selectWindowModePane, updateDragFloatingRect, windowMode]);
|
|
130
|
+
}, [dragRef, focusNativePane, getShellPointer, nativePaneChrome, selectWindowModePane, transientFocusActive, updateDragFloatingRect, windowMode]);
|
|
126
131
|
|
|
127
132
|
const startNativeDividerDrag = useCallback((divider: DockDividerLayout, event: ShellMouseEvent) => {
|
|
128
133
|
if (!nativePaneChrome) return;
|
|
134
|
+
if (transientFocusActive) return;
|
|
129
135
|
|
|
130
136
|
const pointer = getShellPointer(event);
|
|
131
137
|
if (menuState) {
|
|
@@ -148,7 +154,7 @@ export function useShellNativePointerRuntime({
|
|
|
148
154
|
});
|
|
149
155
|
event.stopPropagation();
|
|
150
156
|
event.preventDefault();
|
|
151
|
-
}, [dragRef, getShellPointer, menuState, nativePaneChrome, setHoveredMenuItemId, setMenuState, updateDividerPreview]);
|
|
157
|
+
}, [dragRef, getShellPointer, menuState, nativePaneChrome, setHoveredMenuItemId, setMenuState, transientFocusActive, updateDividerPreview]);
|
|
152
158
|
|
|
153
159
|
const handlePaneAction = useCallback((paneId: string, rect: LayoutBounds, event: ShellMouseEvent) => {
|
|
154
160
|
if (windowMode) return;
|
|
@@ -48,6 +48,8 @@ interface ShellPaneLayersProps {
|
|
|
48
48
|
startNativeDockedDrag: (paneId: string, rect: LayoutBounds, event: any) => void;
|
|
49
49
|
startNativeFloatingDrag: (paneId: string, rect: FloatingRect, event: any) => void;
|
|
50
50
|
startNativeFloatResize: (paneId: string, rect: FloatingRect, event: any) => void;
|
|
51
|
+
transientFocusActive: boolean;
|
|
52
|
+
transientFocusPaneId: string | null;
|
|
51
53
|
visibleFloatingPanes: VisibleFloatingPane[];
|
|
52
54
|
width: number;
|
|
53
55
|
windowModeDockResizePathKey: string | null;
|
|
@@ -79,6 +81,8 @@ export function ShellPaneLayers({
|
|
|
79
81
|
startNativeDockedDrag,
|
|
80
82
|
startNativeFloatingDrag,
|
|
81
83
|
startNativeFloatResize,
|
|
84
|
+
transientFocusActive,
|
|
85
|
+
transientFocusPaneId,
|
|
82
86
|
visibleFloatingPanes,
|
|
83
87
|
width,
|
|
84
88
|
windowModeDockResizePathKey,
|
|
@@ -87,8 +91,12 @@ export function ShellPaneLayers({
|
|
|
87
91
|
return (
|
|
88
92
|
<>
|
|
89
93
|
{dockLeafLayouts.map((leaf) => {
|
|
94
|
+
if (transientFocusActive && leaf.instanceId !== transientFocusPaneId) return null;
|
|
90
95
|
const pane = paneMap.get(leaf.instanceId);
|
|
91
96
|
if (!pane) return null;
|
|
97
|
+
const rect = transientFocusActive
|
|
98
|
+
? { x: 0, y: 0, width, height: contentHeight }
|
|
99
|
+
: leaf.rect;
|
|
92
100
|
const focused = focusedPaneId === leaf.instanceId && (!overlayOpen || menuPaneId === leaf.instanceId);
|
|
93
101
|
const windowModeSelected = windowModePaneId === leaf.instanceId;
|
|
94
102
|
const showActions = focused || hoveredPaneId === leaf.instanceId || menuPaneId === leaf.instanceId;
|
|
@@ -96,18 +104,21 @@ export function ShellPaneLayers({
|
|
|
96
104
|
<Box
|
|
97
105
|
key={`dock:${leaf.instanceId}`}
|
|
98
106
|
position="absolute"
|
|
99
|
-
left={
|
|
100
|
-
top={
|
|
101
|
-
width={
|
|
102
|
-
height={
|
|
107
|
+
left={rect.x}
|
|
108
|
+
top={rect.y}
|
|
109
|
+
width={rect.width}
|
|
110
|
+
height={rect.height}
|
|
103
111
|
>
|
|
104
112
|
<PaneFooterProvider>
|
|
105
113
|
{(footer) => {
|
|
106
|
-
const
|
|
114
|
+
const showFooter = hasPaneFooterContent(footer);
|
|
115
|
+
const reserveFooter = shouldReservePaneFooter(nativePaneChrome, showFooter);
|
|
116
|
+
const renderFooter = reserveFooter || showFooter;
|
|
107
117
|
const bodyFrame = resolvePaneBodyFrame({
|
|
108
|
-
width:
|
|
109
|
-
height:
|
|
118
|
+
width: rect.width,
|
|
119
|
+
height: rect.height,
|
|
110
120
|
nativePaneChrome,
|
|
121
|
+
footerVisible: renderFooter,
|
|
111
122
|
reserveFooter,
|
|
112
123
|
});
|
|
113
124
|
return (
|
|
@@ -115,18 +126,18 @@ export function ShellPaneLayers({
|
|
|
115
126
|
paneId={leaf.instanceId}
|
|
116
127
|
title={getPaneTitle(pane)}
|
|
117
128
|
focused={focused}
|
|
118
|
-
width={
|
|
119
|
-
height={
|
|
129
|
+
width={rect.width}
|
|
130
|
+
height={rect.height}
|
|
120
131
|
showActions={showActions}
|
|
121
132
|
windowModeSelected={windowModeSelected}
|
|
122
133
|
footer={footer}
|
|
123
134
|
onMouseDown={nativePaneChrome ? (event) => handleNativePaneMouseDown(leaf.instanceId, event) : undefined}
|
|
124
135
|
onMouseMove={() => setHoveredPaneIfChanged(leaf.instanceId)}
|
|
125
|
-
onHeaderMouseDown={nativePaneChrome ? (event) => startNativeDockedDrag(leaf.instanceId,
|
|
126
|
-
onHeaderMouseDrag={nativePaneChrome ? handleNativeDrag : undefined}
|
|
127
|
-
onHeaderMouseDragEnd={nativePaneChrome ? handleNativeDrag : undefined}
|
|
128
|
-
onHeaderContextMenu={nativePaneChrome && nativeContextMenu === true ? (event) => handleNativePaneContextMenu(leaf.instanceId,
|
|
129
|
-
onActionMouseDown={(event) => handlePaneAction(leaf.instanceId,
|
|
136
|
+
onHeaderMouseDown={nativePaneChrome && !transientFocusActive ? (event) => startNativeDockedDrag(leaf.instanceId, rect, event) : undefined}
|
|
137
|
+
onHeaderMouseDrag={nativePaneChrome && !transientFocusActive ? handleNativeDrag : undefined}
|
|
138
|
+
onHeaderMouseDragEnd={nativePaneChrome && !transientFocusActive ? handleNativeDrag : undefined}
|
|
139
|
+
onHeaderContextMenu={nativePaneChrome && nativeContextMenu === true ? (event) => handleNativePaneContextMenu(leaf.instanceId, rect, event) : undefined}
|
|
140
|
+
onActionMouseDown={(event) => handlePaneAction(leaf.instanceId, rect, event)}
|
|
130
141
|
>
|
|
131
142
|
<PaneContent
|
|
132
143
|
component={pane.def.component}
|
|
@@ -145,7 +156,10 @@ export function ShellPaneLayers({
|
|
|
145
156
|
})}
|
|
146
157
|
|
|
147
158
|
{visibleFloatingPanes.map(({ pane, rect }) => {
|
|
148
|
-
|
|
159
|
+
if (transientFocusActive && pane.instance.instanceId !== transientFocusPaneId) return null;
|
|
160
|
+
const preview = transientFocusActive
|
|
161
|
+
? { x: 0, y: 0, width, height: contentHeight }
|
|
162
|
+
: dragFloatingRect?.paneId === pane.instance.instanceId
|
|
149
163
|
? constrainFloatingRectToBounds(dragFloatingRect.rect, width, contentHeight)
|
|
150
164
|
: rect;
|
|
151
165
|
const focused = focusedPaneId === pane.instance.instanceId && (!overlayOpen || menuPaneId === pane.instance.instanceId);
|
|
@@ -154,11 +168,14 @@ export function ShellPaneLayers({
|
|
|
154
168
|
return (
|
|
155
169
|
<PaneFooterProvider key={`float:${pane.instance.instanceId}`}>
|
|
156
170
|
{(footer) => {
|
|
157
|
-
const
|
|
171
|
+
const showFooter = hasPaneFooterContent(footer);
|
|
172
|
+
const reserveFooter = shouldReservePaneFooter(nativePaneChrome, showFooter);
|
|
173
|
+
const renderFooter = reserveFooter || showFooter;
|
|
158
174
|
const bodyFrame = resolvePaneBodyFrame({
|
|
159
175
|
width: preview.width,
|
|
160
176
|
height: preview.height,
|
|
161
177
|
nativePaneChrome,
|
|
178
|
+
footerVisible: renderFooter,
|
|
162
179
|
reserveFooter,
|
|
163
180
|
});
|
|
164
181
|
return (
|
|
@@ -203,6 +220,7 @@ export function ShellPaneLayers({
|
|
|
203
220
|
})}
|
|
204
221
|
|
|
205
222
|
{dockDividerLayouts.map((divider) => {
|
|
223
|
+
if (transientFocusActive) return null;
|
|
206
224
|
const dividerPathKey = pathKey(divider.path);
|
|
207
225
|
const previewActive = dividerPreview?.pathKey === dividerPathKey;
|
|
208
226
|
const active = previewActive || windowModeDockResizePathKey === dividerPathKey;
|
|
@@ -25,6 +25,7 @@ interface ShellPaneManagementShortcutOptions {
|
|
|
25
25
|
overlayOpen: boolean;
|
|
26
26
|
popOutFocusedPane(): boolean;
|
|
27
27
|
startWindowMode(paneId?: string, mode?: WindowEditMode): void;
|
|
28
|
+
toggleFocusedPaneFullscreen(): boolean;
|
|
28
29
|
toggleFocusedPaneFloating(): boolean;
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -42,6 +43,7 @@ export function useShellPaneManagementShortcuts({
|
|
|
42
43
|
overlayOpen,
|
|
43
44
|
popOutFocusedPane,
|
|
44
45
|
startWindowMode,
|
|
46
|
+
toggleFocusedPaneFullscreen,
|
|
45
47
|
toggleFocusedPaneFloating,
|
|
46
48
|
}: ShellPaneManagementShortcutOptions): void {
|
|
47
49
|
const doubleEscapeCloseRef = useRef(createDoubleEscapeCloseState());
|
|
@@ -53,6 +55,20 @@ export function useShellPaneManagementShortcuts({
|
|
|
53
55
|
}, [overlayOpen]);
|
|
54
56
|
|
|
55
57
|
useShortcut((event) => {
|
|
58
|
+
const shortcut = resolvePaneManagementShortcut(event);
|
|
59
|
+
if (shortcut === "toggle-fullscreen" && !overlayOpen) {
|
|
60
|
+
if (!inputCaptured || inputCaptureAllowsPaneManagementShortcut(shortcut, event)) {
|
|
61
|
+
if (hasActiveDrag()) {
|
|
62
|
+
cancelActiveDrag();
|
|
63
|
+
}
|
|
64
|
+
if (toggleFocusedPaneFullscreen()) {
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
event.stopPropagation();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
56
72
|
const isEscape = event.name === "escape" || event.name === "esc";
|
|
57
73
|
if (isEscape) {
|
|
58
74
|
const doubleEscapeState = doubleEscapeCloseRef.current;
|
|
@@ -91,6 +107,9 @@ export function useShellPaneManagementShortcuts({
|
|
|
91
107
|
case "settings":
|
|
92
108
|
handled = openFocusedPaneSettings();
|
|
93
109
|
break;
|
|
110
|
+
case "toggle-fullscreen":
|
|
111
|
+
handled = toggleFocusedPaneFullscreen();
|
|
112
|
+
break;
|
|
94
113
|
case "toggle-floating":
|
|
95
114
|
handled = toggleFocusedPaneFloating();
|
|
96
115
|
break;
|
|
@@ -2,6 +2,7 @@ import type { KeyEventLike } from "../../../react/input";
|
|
|
2
2
|
|
|
3
3
|
export const PANE_MANAGEMENT_ACCELERATORS = {
|
|
4
4
|
settings: "CmdOrCtrl+,",
|
|
5
|
+
fullscreen: "CmdOrCtrl+Shift+F",
|
|
5
6
|
toggleFloating: "CmdOrCtrl+Shift+D",
|
|
6
7
|
popOut: "CmdOrCtrl+Shift+O",
|
|
7
8
|
copyScreenshot: "CmdOrCtrl+Shift+C",
|
|
@@ -15,6 +16,7 @@ export const PANE_MANAGEMENT_ACCELERATORS = {
|
|
|
15
16
|
|
|
16
17
|
export type PaneManagementShortcut =
|
|
17
18
|
| "settings"
|
|
19
|
+
| "toggle-fullscreen"
|
|
18
20
|
| "toggle-floating"
|
|
19
21
|
| "pop-out"
|
|
20
22
|
| "copy-screenshot"
|
|
@@ -38,6 +40,7 @@ export function resolvePaneManagementShortcut(
|
|
|
38
40
|
if (!shifted || event.alt) return null;
|
|
39
41
|
if (name === "c") return "copy-screenshot";
|
|
40
42
|
if (name === "d") return "toggle-floating";
|
|
43
|
+
if (name === "f") return "toggle-fullscreen";
|
|
41
44
|
if (name === "o") return "pop-out";
|
|
42
45
|
if (name === "l") return "layout-actions";
|
|
43
46
|
if (name === "g") return "gridlock-all";
|
|
@@ -50,6 +53,7 @@ export function inputCaptureAllowsPaneManagementShortcut(
|
|
|
50
53
|
shortcut: PaneManagementShortcut,
|
|
51
54
|
event: Pick<KeyEventLike, "meta" | "super" | "targetEditable">,
|
|
52
55
|
): boolean {
|
|
56
|
+
if (shortcut === "toggle-fullscreen") return event.meta || event.super === true;
|
|
53
57
|
if (shortcut !== "close" && shortcut !== "close-all-floating") return false;
|
|
54
58
|
return event.meta || event.super || event.targetEditable !== true;
|
|
55
59
|
}
|