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
|
@@ -87,7 +87,11 @@ export function buildCurrentLayoutItems({
|
|
|
87
87
|
kind: "action",
|
|
88
88
|
action: () => {
|
|
89
89
|
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
90
|
-
persistLayoutChange(gridlockAllPanes(
|
|
90
|
+
persistLayoutChange(gridlockAllPanes(
|
|
91
|
+
currentLayout,
|
|
92
|
+
{ x: 0, y: 0, width, height },
|
|
93
|
+
pluginRegistry.panes,
|
|
94
|
+
));
|
|
91
95
|
notifyGridlockRevert();
|
|
92
96
|
closeAll({ revertThemePreview: false });
|
|
93
97
|
},
|
|
@@ -165,7 +165,7 @@ const CommandBarListItemRow = memo(function CommandBarListItemRow({
|
|
|
165
165
|
: isHovered
|
|
166
166
|
? paletteHoverBg
|
|
167
167
|
: (nativePaneChrome ? panelBg : paletteBg)}
|
|
168
|
-
|
|
168
|
+
onMouseOver={() => onHoverIndex(globalIdx)}
|
|
169
169
|
onMouseOut={() => onHoverIndex(null)}
|
|
170
170
|
{...(!nativePaneChrome ? { onMouseScroll: onListScroll } : {})}
|
|
171
171
|
onMouseDown={(event: any) => onRowMouseDown(event, item, globalIdx)}
|
|
@@ -219,7 +219,7 @@ export const ThemePicker = memo(forwardRef<ThemePickerHandle, ThemePickerProps>(
|
|
|
219
219
|
: hovered
|
|
220
220
|
? paletteHoverBg
|
|
221
221
|
: (nativePaneChrome ? panelBg : paletteBg)}
|
|
222
|
-
|
|
222
|
+
onMouseOver={() => setHoveredIndex(index)}
|
|
223
223
|
onMouseOut={() => setHoveredIndex(null)}
|
|
224
224
|
onMouseDown={(event: any) => {
|
|
225
225
|
event.stopPropagation?.();
|
|
@@ -73,8 +73,6 @@ export interface DataTableViewProps<
|
|
|
73
73
|
| "scrollRef"
|
|
74
74
|
| "syncHeaderScroll"
|
|
75
75
|
| "onBodyScrollActivity"
|
|
76
|
-
| "hoveredIdx"
|
|
77
|
-
| "setHoveredIdx"
|
|
78
76
|
| "isSelected"
|
|
79
77
|
| "onSelect"
|
|
80
78
|
| "onActivate"
|
|
@@ -97,8 +95,6 @@ export interface DataTableViewProps<
|
|
|
97
95
|
scrollRef?: RefObject<ScrollBoxRenderable | null>;
|
|
98
96
|
syncHeaderScroll?: () => void;
|
|
99
97
|
onBodyScrollActivity?: () => void;
|
|
100
|
-
hoveredIdx?: number | null;
|
|
101
|
-
setHoveredIdx?: (index: number | null) => void;
|
|
102
98
|
keyboardNavigation?: boolean;
|
|
103
99
|
onRootKeyDown?: (
|
|
104
100
|
event: DataTableKeyEvent,
|
|
@@ -125,8 +121,6 @@ export function DataTableView<
|
|
|
125
121
|
scrollRef,
|
|
126
122
|
syncHeaderScroll,
|
|
127
123
|
onBodyScrollActivity,
|
|
128
|
-
hoveredIdx,
|
|
129
|
-
setHoveredIdx,
|
|
130
124
|
keyboardNavigation = true,
|
|
131
125
|
onRootKeyDown,
|
|
132
126
|
resetScrollKey,
|
|
@@ -137,15 +131,11 @@ export function DataTableView<
|
|
|
137
131
|
const {
|
|
138
132
|
effectiveHeaderScrollRef,
|
|
139
133
|
effectiveScrollRef,
|
|
140
|
-
effectiveHoveredIdx,
|
|
141
|
-
effectiveSetHoveredIdx,
|
|
142
134
|
effectiveSyncHeaderScroll,
|
|
143
135
|
} = useTableViewState({
|
|
144
136
|
headerScrollRef,
|
|
145
137
|
scrollRef,
|
|
146
138
|
syncHeaderScroll,
|
|
147
|
-
hoveredIdx,
|
|
148
|
-
setHoveredIdx,
|
|
149
139
|
});
|
|
150
140
|
const [cursorIndex, setCursorIndex] = useState<number | null>(null);
|
|
151
141
|
const pendingCommitRef = useRef(false);
|
|
@@ -532,8 +522,6 @@ export function DataTableView<
|
|
|
532
522
|
scrollRef={effectiveScrollRef}
|
|
533
523
|
syncHeaderScroll={effectiveSyncHeaderScroll}
|
|
534
524
|
onBodyScrollActivity={handleBodyScrollActivity}
|
|
535
|
-
hoveredIdx={effectiveHoveredIdx}
|
|
536
|
-
setHoveredIdx={effectiveSetHoveredIdx}
|
|
537
525
|
scrollToIndex={effectiveScrollToIndex}
|
|
538
526
|
scrollToIndexVersion={scrollToIndexVersion + selectionScrollVersion}
|
|
539
527
|
isSelected={isItemSelected}
|
|
@@ -10,7 +10,7 @@ import { useThemeColors } from "../../theme/theme-context";
|
|
|
10
10
|
import { hasPaneFooterContent, PaneFooterBar, PaneFooterProvider } from "./pane/footer";
|
|
11
11
|
import { PaneBodyFrame, getPaneWindowAttributes } from "./pane/frame";
|
|
12
12
|
import { PaneContent } from "./pane/content";
|
|
13
|
-
import { resolvePaneBodyFrame } from "./pane/sizing";
|
|
13
|
+
import { resolvePaneBodyFrame, shouldReservePaneFooter } from "./pane/sizing";
|
|
14
14
|
import { getPaneDisplayTitle } from "./pane/title";
|
|
15
15
|
import { TITLEBAR_OVERLAY_HEIGHT_PX, getTitlebarLeadingInset } from "./titlebar-overlay";
|
|
16
16
|
import { WindowControls, WINDOWS_CONTROL_GROUP_WIDTH_PX } from "./window-controls";
|
|
@@ -130,6 +130,8 @@ export function DetachedPaneShell({ pluginRegistry, desktopWindowBridge }: Detac
|
|
|
130
130
|
<PaneFooterProvider>
|
|
131
131
|
{(footer) => {
|
|
132
132
|
const showFooter = hasPaneFooterContent(footer);
|
|
133
|
+
const reserveFooter = shouldReservePaneFooter(nativePaneChrome, showFooter);
|
|
134
|
+
const renderFooter = reserveFooter || showFooter;
|
|
133
135
|
const headerHeightRows = titleBarOverlay ? TITLEBAR_OVERLAY_HEIGHT_PX / cellHeightPx : 1;
|
|
134
136
|
const background = floatingPaneBg(focused);
|
|
135
137
|
const titleBackground = floatingPaneTitleBg(focused);
|
|
@@ -137,7 +139,8 @@ export function DetachedPaneShell({ pluginRegistry, desktopWindowBridge }: Detac
|
|
|
137
139
|
width,
|
|
138
140
|
height,
|
|
139
141
|
nativePaneChrome,
|
|
140
|
-
|
|
142
|
+
footerVisible: renderFooter,
|
|
143
|
+
reserveFooter,
|
|
141
144
|
headerRows: headerHeightRows,
|
|
142
145
|
});
|
|
143
146
|
const bodyWidth = bodyFrame.width ?? 1;
|
|
@@ -208,7 +211,7 @@ export function DetachedPaneShell({ pluginRegistry, desktopWindowBridge }: Detac
|
|
|
208
211
|
height={bodyHeight}
|
|
209
212
|
/>
|
|
210
213
|
</PaneBodyFrame>
|
|
211
|
-
{
|
|
214
|
+
{renderFooter && <PaneFooterBar footer={footer} focused={focused} width={width} />}
|
|
212
215
|
</Box>
|
|
213
216
|
);
|
|
214
217
|
}}
|
|
@@ -18,7 +18,7 @@ interface FloatingPaneWrapperProps {
|
|
|
18
18
|
windowModeSelected?: boolean;
|
|
19
19
|
showActions?: boolean;
|
|
20
20
|
onMouseDown?: (event: any) => void;
|
|
21
|
-
|
|
21
|
+
onHeaderMouseMove?: (event: any) => void;
|
|
22
22
|
onHeaderMouseDown?: (event: any) => void;
|
|
23
23
|
onHeaderMouseDrag?: (event: any) => void;
|
|
24
24
|
onHeaderMouseDragEnd?: (event: any) => void;
|
|
@@ -63,7 +63,7 @@ export function FloatingPaneWrapper({
|
|
|
63
63
|
windowModeSelected = false,
|
|
64
64
|
showActions = false,
|
|
65
65
|
onMouseDown,
|
|
66
|
-
|
|
66
|
+
onHeaderMouseMove,
|
|
67
67
|
onHeaderMouseDown,
|
|
68
68
|
onHeaderMouseDrag,
|
|
69
69
|
onHeaderMouseDragEnd,
|
|
@@ -80,7 +80,8 @@ export function FloatingPaneWrapper({
|
|
|
80
80
|
const bg = floatingPaneBg(focused);
|
|
81
81
|
const showFooter = hasPaneFooterContent(footer);
|
|
82
82
|
const reserveFooter = shouldReservePaneFooter(nativePaneChrome, showFooter);
|
|
83
|
-
const
|
|
83
|
+
const renderFooter = reserveFooter || showFooter;
|
|
84
|
+
const bodyFrame = resolvePaneBodyFrame({ height, nativePaneChrome, footerVisible: renderFooter, reserveFooter });
|
|
84
85
|
|
|
85
86
|
return (
|
|
86
87
|
<Box
|
|
@@ -103,7 +104,6 @@ export function FloatingPaneWrapper({
|
|
|
103
104
|
showBorderColor: true,
|
|
104
105
|
})}
|
|
105
106
|
onMouseDown={onMouseDown}
|
|
106
|
-
onMouseMove={onMouseMove}
|
|
107
107
|
>
|
|
108
108
|
<PaneHeader
|
|
109
109
|
title={title}
|
|
@@ -112,6 +112,7 @@ export function FloatingPaneWrapper({
|
|
|
112
112
|
windowModeSelected={windowModeSelected}
|
|
113
113
|
floating
|
|
114
114
|
showActions={showActions}
|
|
115
|
+
onHeaderMouseMove={onHeaderMouseMove}
|
|
115
116
|
onHeaderMouseDown={onHeaderMouseDown}
|
|
116
117
|
onHeaderMouseDrag={onHeaderMouseDrag}
|
|
117
118
|
onHeaderMouseDragEnd={onHeaderMouseDragEnd}
|
|
@@ -124,7 +125,7 @@ export function FloatingPaneWrapper({
|
|
|
124
125
|
{children}
|
|
125
126
|
</PaneBodyFrame>
|
|
126
127
|
|
|
127
|
-
{
|
|
128
|
+
{renderFooter && (
|
|
128
129
|
<PaneFooterBar
|
|
129
130
|
footer={footer}
|
|
130
131
|
focused={focused}
|
|
@@ -3,6 +3,7 @@ import { PaneInstanceProvider } from "../../../state/app/context";
|
|
|
3
3
|
import { PaneKeyboardScrollController } from "../../../state/pane-scroll-registry";
|
|
4
4
|
import { useThemeColors } from "../../../theme/theme-context";
|
|
5
5
|
import type { PaneDef } from "../../../types/plugin";
|
|
6
|
+
import { Box } from "../../../ui";
|
|
6
7
|
|
|
7
8
|
interface PaneContentProps {
|
|
8
9
|
component: PaneDef["component"];
|
|
@@ -31,14 +32,25 @@ export const PaneContent = memo(function PaneContent({
|
|
|
31
32
|
return (
|
|
32
33
|
<PaneInstanceProvider paneId={paneId}>
|
|
33
34
|
<PaneKeyboardScrollController paneId={paneId} focused={focused} />
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
<Box
|
|
36
|
+
flexDirection="column"
|
|
37
|
+
flexGrow={1}
|
|
38
|
+
flexShrink={1}
|
|
39
|
+
flexBasis={0}
|
|
40
|
+
minWidth={0}
|
|
41
|
+
minHeight={0}
|
|
42
|
+
overflow="hidden"
|
|
43
|
+
data-gloom-role="pane-content"
|
|
44
|
+
>
|
|
45
|
+
<Component
|
|
46
|
+
paneId={paneId}
|
|
47
|
+
paneType={paneType}
|
|
48
|
+
focused={focused}
|
|
49
|
+
width={width}
|
|
50
|
+
height={height}
|
|
51
|
+
close={onClose ? close : undefined}
|
|
52
|
+
/>
|
|
53
|
+
</Box>
|
|
42
54
|
</PaneInstanceProvider>
|
|
43
55
|
);
|
|
44
56
|
});
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
useCallback,
|
|
4
4
|
useContext,
|
|
5
5
|
useEffect,
|
|
6
|
+
useLayoutEffect,
|
|
6
7
|
useMemo,
|
|
7
8
|
useRef,
|
|
8
9
|
useState,
|
|
@@ -17,6 +18,9 @@ import {
|
|
|
17
18
|
type PaneHint,
|
|
18
19
|
} from "./model";
|
|
19
20
|
|
|
21
|
+
const usePaneFooterRegistrationEffect =
|
|
22
|
+
typeof document === "undefined" ? useEffect : useLayoutEffect;
|
|
23
|
+
|
|
20
24
|
interface PaneFooterContextValue {
|
|
21
25
|
register(registrationId: string, registration: PaneFooterRegistration | null): void;
|
|
22
26
|
unregister(registrationId: string): void;
|
|
@@ -85,14 +89,14 @@ export function usePaneFooter(
|
|
|
85
89
|
const context = useContext(PaneFooterContext);
|
|
86
90
|
const previousRegistrationRef = useRef<PaneFooterRegistration | null>(null);
|
|
87
91
|
|
|
88
|
-
|
|
92
|
+
usePaneFooterRegistrationEffect(() => {
|
|
89
93
|
return () => {
|
|
90
94
|
previousRegistrationRef.current = null;
|
|
91
95
|
context?.unregister(registrationId);
|
|
92
96
|
};
|
|
93
97
|
}, [context, registrationId]);
|
|
94
98
|
|
|
95
|
-
|
|
99
|
+
usePaneFooterRegistrationEffect(() => {
|
|
96
100
|
if (!context) return;
|
|
97
101
|
const nextRegistration = factory() ?? null;
|
|
98
102
|
if (samePaneFooterRegistration(previousRegistrationRef.current, nextRegistration)) return;
|
|
@@ -19,13 +19,13 @@ export function getPaneWindowAttributes({
|
|
|
19
19
|
windowModeSelected?: boolean;
|
|
20
20
|
showBorderColor?: boolean;
|
|
21
21
|
}): Record<string, unknown> {
|
|
22
|
-
if (!enabled) return {};
|
|
23
|
-
|
|
24
22
|
const attributes: Record<string, unknown> = {
|
|
25
23
|
"data-gloom-role": role,
|
|
26
|
-
"data-focused": focused ? "true" : "false",
|
|
27
24
|
};
|
|
28
25
|
if (paneId) attributes["data-gloom-pane-id"] = paneId;
|
|
26
|
+
if (!enabled) return attributes;
|
|
27
|
+
|
|
28
|
+
attributes["data-focused"] = focused ? "true" : "false";
|
|
29
29
|
if (floating != null) attributes["data-floating"] = floating ? "true" : "false";
|
|
30
30
|
if (windowModeSelected != null) {
|
|
31
31
|
attributes["data-window-mode-selected"] = windowModeSelected ? "true" : "false";
|
|
@@ -48,7 +48,7 @@ export function PaneBodyFrame({
|
|
|
48
48
|
children: ReactNode;
|
|
49
49
|
}) {
|
|
50
50
|
return (
|
|
51
|
-
<Box {...layoutProps} overflow="hidden" backgroundColor={backgroundColor}>
|
|
51
|
+
<Box {...layoutProps} overflow="hidden" backgroundColor={backgroundColor} data-gloom-role="pane-body">
|
|
52
52
|
{children}
|
|
53
53
|
</Box>
|
|
54
54
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Box, Span, Text, useUiCapabilities } from "../../../ui";
|
|
2
|
-
import type
|
|
1
|
+
import { Box, Span, Text, useNativeRenderer, useUiCapabilities } from "../../../ui";
|
|
2
|
+
import { useCallback, useRef, type ReactNode } from "react";
|
|
3
3
|
import { blendHex, colors, floatingPaneTitleBg, paneTitleBg, paneTitleText } from "../../../theme/colors";
|
|
4
4
|
|
|
5
5
|
const PANE_HEADER_HEIGHT = 1;
|
|
@@ -14,6 +14,7 @@ interface PaneHeaderProps {
|
|
|
14
14
|
windowModeSelected?: boolean;
|
|
15
15
|
floating?: boolean;
|
|
16
16
|
showActions?: boolean;
|
|
17
|
+
onHeaderMouseMove?: (event: any) => void;
|
|
17
18
|
onHeaderMouseDown?: (event: any) => void;
|
|
18
19
|
onHeaderMouseDrag?: (event: any) => void;
|
|
19
20
|
onHeaderMouseDragEnd?: (event: any) => void;
|
|
@@ -29,6 +30,18 @@ function truncateTitle(title: string, maxWidth: number): string {
|
|
|
29
30
|
return `${title.slice(0, maxWidth - 2)}..`;
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
function captureTerminalPointerDrag(renderer: unknown, renderable: unknown): void {
|
|
34
|
+
if (!renderable) return;
|
|
35
|
+
const hostCapture = (renderer as { captureMouseRenderable?: (target: unknown) => void }).captureMouseRenderable;
|
|
36
|
+
if (typeof hostCapture === "function") {
|
|
37
|
+
hostCapture.call(renderer, renderable);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const capture = (renderer as { setCapturedRenderable?: (target: unknown) => void }).setCapturedRenderable;
|
|
41
|
+
if (typeof capture !== "function") return;
|
|
42
|
+
capture.call(renderer, renderable);
|
|
43
|
+
}
|
|
44
|
+
|
|
32
45
|
function DesktopPaneButton({
|
|
33
46
|
icon,
|
|
34
47
|
onMouseDown,
|
|
@@ -99,6 +112,7 @@ export function PaneHeader({
|
|
|
99
112
|
windowModeSelected = false,
|
|
100
113
|
floating = false,
|
|
101
114
|
showActions = false,
|
|
115
|
+
onHeaderMouseMove,
|
|
102
116
|
onHeaderMouseDown,
|
|
103
117
|
onHeaderMouseDrag,
|
|
104
118
|
onHeaderMouseDragEnd,
|
|
@@ -107,11 +121,17 @@ export function PaneHeader({
|
|
|
107
121
|
onCloseMouseDown,
|
|
108
122
|
}: PaneHeaderProps) {
|
|
109
123
|
const { nativePaneChrome } = useUiCapabilities();
|
|
124
|
+
const nativeRenderer = useNativeRenderer();
|
|
125
|
+
const terminalHeaderRef = useRef<unknown>(null);
|
|
110
126
|
const visuallyFocused = focused || windowModeSelected;
|
|
111
127
|
const backgroundColor = floating ? floatingPaneTitleBg(visuallyFocused) : paneTitleBg(visuallyFocused);
|
|
112
128
|
const actionText = showActions ? PANE_HEADER_ACTION : " ";
|
|
113
129
|
const closeText = floating ? PANE_HEADER_CLOSE : "";
|
|
114
130
|
const textColor = paneTitleText(visuallyFocused, floating);
|
|
131
|
+
const handleTerminalHeaderMouseDown = useCallback((event: any) => {
|
|
132
|
+
captureTerminalPointerDrag(nativeRenderer, terminalHeaderRef.current);
|
|
133
|
+
onHeaderMouseDown?.(event);
|
|
134
|
+
}, [nativeRenderer, onHeaderMouseDown]);
|
|
115
135
|
|
|
116
136
|
if (nativePaneChrome) {
|
|
117
137
|
return (
|
|
@@ -125,6 +145,7 @@ export function PaneHeader({
|
|
|
125
145
|
data-focused={focused ? "true" : "false"}
|
|
126
146
|
data-window-mode-selected={windowModeSelected ? "true" : "false"}
|
|
127
147
|
onMouseDown={onHeaderMouseDown}
|
|
148
|
+
onMouseMove={onHeaderMouseMove}
|
|
128
149
|
onMouseDrag={onHeaderMouseDrag}
|
|
129
150
|
onMouseDragEnd={onHeaderMouseDragEnd}
|
|
130
151
|
onContextMenu={onHeaderContextMenu}
|
|
@@ -201,7 +222,17 @@ export function PaneHeader({
|
|
|
201
222
|
const fill = "─".repeat(fillLen);
|
|
202
223
|
|
|
203
224
|
return (
|
|
204
|
-
<Box
|
|
225
|
+
<Box
|
|
226
|
+
ref={terminalHeaderRef}
|
|
227
|
+
height={PANE_HEADER_HEIGHT}
|
|
228
|
+
width={width}
|
|
229
|
+
backgroundColor={backgroundColor}
|
|
230
|
+
flexDirection="row"
|
|
231
|
+
onMouseDown={handleTerminalHeaderMouseDown}
|
|
232
|
+
onMouseMove={onHeaderMouseMove}
|
|
233
|
+
onMouseDrag={onHeaderMouseDrag}
|
|
234
|
+
onMouseDragEnd={onHeaderMouseDragEnd}
|
|
235
|
+
>
|
|
205
236
|
<Text fg={borderColor} selectable={false}>{"┌─"}</Text>
|
|
206
237
|
<Text fg={textColor} selectable={false}>{`${PANE_HEADER_GRIP}${clippedTitle}`}</Text>
|
|
207
238
|
<Text fg={borderColor} selectable={false}>{fill}</Text>
|
|
@@ -229,7 +260,17 @@ export function PaneHeader({
|
|
|
229
260
|
const padding = " ".repeat(Math.max(0, titleWidth - clippedTitle.length));
|
|
230
261
|
|
|
231
262
|
return (
|
|
232
|
-
<Box
|
|
263
|
+
<Box
|
|
264
|
+
ref={terminalHeaderRef}
|
|
265
|
+
height={PANE_HEADER_HEIGHT}
|
|
266
|
+
width={width}
|
|
267
|
+
backgroundColor={backgroundColor}
|
|
268
|
+
flexDirection="row"
|
|
269
|
+
onMouseDown={handleTerminalHeaderMouseDown}
|
|
270
|
+
onMouseMove={onHeaderMouseMove}
|
|
271
|
+
onMouseDrag={onHeaderMouseDrag}
|
|
272
|
+
onMouseDragEnd={onHeaderMouseDragEnd}
|
|
273
|
+
>
|
|
233
274
|
<Text fg={textColor} selectable={false}>
|
|
234
275
|
{`${PANE_HEADER_GRIP}${clippedTitle}${padding}`}
|
|
235
276
|
</Text>
|
|
@@ -16,7 +16,7 @@ interface PaneWrapperProps {
|
|
|
16
16
|
flexGrow?: number;
|
|
17
17
|
showActions?: boolean;
|
|
18
18
|
onMouseDown?: (event: any) => void;
|
|
19
|
-
|
|
19
|
+
onHeaderMouseMove?: (event: any) => void;
|
|
20
20
|
onHeaderMouseDown?: (event: any) => void;
|
|
21
21
|
onHeaderMouseDrag?: (event: any) => void;
|
|
22
22
|
onHeaderMouseDragEnd?: (event: any) => void;
|
|
@@ -36,7 +36,7 @@ export function PaneWrapper({
|
|
|
36
36
|
flexGrow,
|
|
37
37
|
showActions = false,
|
|
38
38
|
onMouseDown,
|
|
39
|
-
|
|
39
|
+
onHeaderMouseMove,
|
|
40
40
|
onHeaderMouseDown,
|
|
41
41
|
onHeaderMouseDrag,
|
|
42
42
|
onHeaderMouseDragEnd,
|
|
@@ -49,9 +49,11 @@ export function PaneWrapper({
|
|
|
49
49
|
const bg = paneBg(focused);
|
|
50
50
|
const showFooter = hasPaneFooterContent(footer);
|
|
51
51
|
const reserveFooter = !!title && shouldReservePaneFooter(nativePaneChrome, showFooter);
|
|
52
|
+
const renderFooter = !!title && (reserveFooter || showFooter);
|
|
52
53
|
const bodyFrame = resolvePaneBodyFrame({
|
|
53
54
|
height: typeof height === "number" ? height : undefined,
|
|
54
55
|
nativePaneChrome,
|
|
56
|
+
footerVisible: renderFooter,
|
|
55
57
|
reserveFooter,
|
|
56
58
|
headerRows: title ? 1 : 0,
|
|
57
59
|
});
|
|
@@ -74,7 +76,6 @@ export function PaneWrapper({
|
|
|
74
76
|
showBorderColor: true,
|
|
75
77
|
})}
|
|
76
78
|
onMouseDown={onMouseDown}
|
|
77
|
-
onMouseMove={onMouseMove}
|
|
78
79
|
>
|
|
79
80
|
{title && (
|
|
80
81
|
<PaneHeader
|
|
@@ -83,6 +84,7 @@ export function PaneWrapper({
|
|
|
83
84
|
focused={focused}
|
|
84
85
|
windowModeSelected={windowModeSelected}
|
|
85
86
|
showActions={showActions}
|
|
87
|
+
onHeaderMouseMove={onHeaderMouseMove}
|
|
86
88
|
onHeaderMouseDown={onHeaderMouseDown}
|
|
87
89
|
onHeaderMouseDrag={onHeaderMouseDrag}
|
|
88
90
|
onHeaderMouseDragEnd={onHeaderMouseDragEnd}
|
|
@@ -93,7 +95,7 @@ export function PaneWrapper({
|
|
|
93
95
|
<PaneBodyFrame layoutProps={bodyFrame.layoutProps} backgroundColor={bg}>
|
|
94
96
|
{children}
|
|
95
97
|
</PaneBodyFrame>
|
|
96
|
-
{
|
|
98
|
+
{renderFooter && (
|
|
97
99
|
<PaneFooterBar
|
|
98
100
|
footer={footer}
|
|
99
101
|
focused={focused}
|
|
@@ -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,
|
|
@@ -52,7 +52,7 @@ export function ShellActionMenuOverlay({
|
|
|
52
52
|
height={1}
|
|
53
53
|
width={innerWidth}
|
|
54
54
|
backgroundColor={hovered ? colors.selected : colors.panel}
|
|
55
|
-
|
|
55
|
+
onMouseOver={() => onHoverItem(item.id)}
|
|
56
56
|
onMouseDown={(mouseEvent: any) => {
|
|
57
57
|
mouseEvent.stopPropagation();
|
|
58
58
|
mouseEvent.preventDefault();
|