gloomberb 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/patches/@opentui%2Fcore@0.1.90.patch +48 -24
- package/src/app.tsx +158 -57
- package/src/capabilities/factories.ts +3 -0
- package/src/cli/registry.test.ts +0 -1
- package/src/components/chart/chart-controller.ts +0 -1
- package/src/components/chart/chart-data.ts +1 -31
- package/src/components/chart/chart-renderer.ts +94 -12
- package/src/components/chart/chart-resolution.test.ts +0 -36
- package/src/components/chart/chart-resolution.ts +0 -27
- package/src/components/chart/chart.test.ts +60 -45
- package/src/components/chart/comparison-chart-data.test.ts +1 -5
- package/src/components/chart/comparison-chart-data.ts +1 -4
- package/src/components/chart/comparison-chart-renderer.test.ts +26 -0
- package/src/components/chart/comparison-chart-renderer.ts +13 -6
- package/src/components/chart/comparison-stock-chart.tsx +63 -39
- package/src/components/chart/price-axis-labels.tsx +114 -0
- package/src/components/chart/static-chart-surface.tsx +30 -4
- package/src/components/chart/stock-chart.test.ts +0 -16
- package/src/components/chart/stock-chart.tsx +69 -41
- package/src/components/chart/time-axis-label.tsx +120 -0
- package/src/components/command-bar/__snapshots__/command-bar.test.tsx.snap +0 -28
- package/src/components/command-bar/command-bar.test.tsx +182 -36
- package/src/components/command-bar/command-bar.tsx +943 -1016
- package/src/components/command-bar/command-registry.ts +0 -10
- package/src/components/command-bar/confirm-body.tsx +51 -0
- package/src/components/command-bar/helpers.ts +2 -5
- package/src/components/command-bar/list-model.ts +76 -0
- package/src/components/command-bar/theme-picker.tsx +254 -0
- package/src/components/command-bar/view-model.test.ts +4 -4
- package/src/components/command-bar/view-model.ts +1 -20
- package/src/components/command-bar/workflow-ops.ts +4 -0
- package/src/components/command-bar/workflow-types.ts +1 -2
- package/src/components/data-table-stack-view.test.tsx +39 -4
- package/src/components/data-table-stack-view.tsx +11 -104
- package/src/components/data-table-view.tsx +43 -79
- package/src/components/feed-data-table-stack-view.tsx +10 -17
- package/src/components/feed-stack-controller.ts +39 -0
- package/src/components/index.ts +5 -1
- package/src/components/layout/detached-pane-shell.tsx +29 -10
- package/src/components/layout/floating-pane.tsx +35 -4
- package/src/components/layout/header.tsx +12 -7
- package/src/components/layout/pane-content.test.tsx +64 -0
- package/src/components/layout/pane-content.tsx +2 -0
- package/src/components/layout/pane-cycle-order.test.ts +30 -0
- package/src/components/layout/pane-cycle-order.ts +32 -0
- package/src/components/layout/pane-footer.tsx +11 -4
- package/src/components/layout/pane-header.tsx +5 -5
- package/src/components/layout/pane-sizing.ts +9 -2
- package/src/components/layout/pane-title.ts +4 -1
- package/src/components/layout/pane.tsx +11 -5
- package/src/components/layout/shell.test.tsx +264 -22
- package/src/components/layout/shell.tsx +46 -8
- package/src/components/layout/status-bar.tsx +2 -0
- package/src/components/onboarding/onboarding-wizard.tsx +1 -2
- package/src/components/pane-settings-dialog.tsx +12 -4
- package/src/components/pane-template-wizard.test.tsx +3 -4
- package/src/components/speedometer-gauge.test.tsx +45 -0
- package/src/components/speedometer-gauge.tsx +562 -0
- package/src/components/table-view-shared.tsx +218 -0
- package/src/components/ticker-list-table-view.tsx +57 -73
- package/src/components/ticker-list-table.test.tsx +82 -1
- package/src/components/ticker-list-table.tsx +22 -9
- package/src/components/ui/confirm-dialog.tsx +66 -0
- package/src/components/ui/data-table.tsx +30 -19
- package/src/components/ui/frame.tsx +2 -0
- package/src/components/ui/index.ts +0 -1
- package/src/components/ui/page-stack-view.tsx +39 -10
- package/src/components/ui/table-layout.test.ts +42 -0
- package/src/components/ui/table-layout.ts +49 -4
- package/src/components/ui/tabs.tsx +34 -1
- package/src/components/ui/ui.test.tsx +168 -21
- package/src/core/state/app-state.ts +10 -17
- package/src/market-data/coordinator.test.ts +76 -1
- package/src/market-data/coordinator.ts +218 -49
- package/src/news/aggregator.test.ts +49 -1
- package/src/news/aggregator.ts +48 -3
- package/src/news/hooks.ts +6 -2
- package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +46 -16
- package/src/plugins/builtin/ai/providers.ts +0 -17
- package/src/plugins/builtin/ai/screener-pane.test.tsx +3 -4
- package/src/plugins/builtin/ai/screener-pane.tsx +3 -5
- package/src/plugins/builtin/analytics/index.tsx +1 -18
- package/src/plugins/builtin/ask-ai.test.tsx +89 -2
- package/src/plugins/builtin/broker-manager/index.test.tsx +2 -2
- package/src/plugins/builtin/broker-manager/index.tsx +19 -36
- package/src/plugins/builtin/chat-controller.test.ts +188 -1
- package/src/plugins/builtin/chat-controller.ts +537 -220
- package/src/plugins/builtin/chat.test.tsx +431 -13
- package/src/plugins/builtin/chat.tsx +1134 -227
- package/src/plugins/builtin/cloud-tweets.tsx +1 -1
- package/src/plugins/builtin/comparison-chart.test.tsx +53 -2
- package/src/plugins/builtin/comparison-chart.tsx +11 -5
- package/src/plugins/builtin/correlation/index.tsx +1 -6
- package/src/plugins/builtin/debug.tsx +1 -3
- package/src/plugins/builtin/econ/index.tsx +1 -2
- package/src/plugins/builtin/fear-greed/fear-greed-data.test.ts +72 -0
- package/src/plugins/builtin/fear-greed/fear-greed-data.ts +366 -0
- package/src/plugins/builtin/fear-greed/index.tsx +587 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
- package/src/plugins/builtin/help.test.tsx +2 -2
- package/src/plugins/builtin/help.tsx +2 -2
- package/src/plugins/builtin/holders/index.test.ts +13 -1
- package/src/plugins/builtin/holders/index.tsx +64 -29
- package/src/plugins/builtin/insider/index.tsx +22 -89
- package/src/plugins/builtin/market-movers/index.tsx +13 -19
- package/src/plugins/builtin/market-movers/screener.test.ts +39 -1
- package/src/plugins/builtin/market-movers/screener.ts +70 -17
- package/src/plugins/builtin/news-wire/breaking-pane.tsx +3 -2
- package/src/plugins/builtin/news-wire/index.tsx +41 -2
- package/src/plugins/builtin/news-wire/industry-pane.tsx +4 -18
- package/src/plugins/builtin/news-wire/news-detail-view.tsx +46 -4
- package/src/plugins/builtin/news-wire/news-preset-pane.tsx +3 -2
- package/src/plugins/builtin/news-wire/news-table.tsx +11 -11
- package/src/plugins/builtin/news.tsx +4 -16
- package/src/plugins/builtin/notes.tsx +19 -55
- package/src/plugins/builtin/options.test.tsx +58 -2
- package/src/plugins/builtin/options.tsx +110 -65
- package/src/plugins/builtin/plugin-groups.ts +2 -0
- package/src/plugins/builtin/portfolio-list/cli/commands.ts +1 -1
- package/src/plugins/builtin/portfolio-list/pane.tsx +16 -39
- package/src/plugins/builtin/research/index.test.ts +94 -0
- package/src/plugins/builtin/research/index.tsx +164 -33
- package/src/plugins/builtin/sec-filing-display.tsx +84 -0
- package/src/plugins/builtin/sec.tsx +13 -66
- package/src/plugins/builtin/sectors/index.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +0 -1
- package/src/plugins/builtin/ticker-detail/index.tsx +0 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +0 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +28 -22
- package/src/plugins/builtin/ticker-detail-chart-tab-switch.test.tsx +3 -4
- package/src/plugins/builtin/ticker-detail.test.tsx +38 -0
- package/src/plugins/builtin/world-indices/index.tsx +1 -1
- package/src/plugins/builtin/yield-curve/index.tsx +1 -1
- package/src/plugins/ibkr/broker-adapter.ts +1 -3
- package/src/plugins/ibkr/gateway-service-native.ts +1 -5
- package/src/plugins/ibkr/gateway-service.ts +1 -1
- package/src/plugins/ibkr/index.test.tsx +0 -2
- package/src/plugins/ibkr/trade-tab.test.tsx +3 -4
- package/src/plugins/pane-manager.ts +0 -13
- package/src/plugins/plugin-runtime.test.tsx +40 -6
- package/src/plugins/plugin-runtime.tsx +20 -10
- package/src/plugins/prediction-markets/cli.ts +0 -1
- package/src/plugins/prediction-markets/controller-data.ts +0 -2
- package/src/plugins/prediction-markets/controller.ts +0 -15
- package/src/plugins/prediction-markets/detail/pane.tsx +1 -0
- package/src/plugins/prediction-markets/pane.test.tsx +3 -11
- package/src/plugins/prediction-markets/test-helpers.tsx +1 -1
- package/src/plugins/registry.ts +13 -42
- package/src/plugins/ticker-navigation.test.ts +66 -0
- package/src/plugins/ticker-navigation.ts +44 -0
- package/src/react/input.ts +2 -1
- package/src/renderers/electrobun/bun/index.ts +32 -11
- package/src/renderers/electrobun/bun/window-focus.ts +37 -0
- package/src/renderers/electrobun/shared/protocol.ts +7 -1
- package/src/renderers/electrobun/view/app-services.ts +88 -31
- package/src/renderers/electrobun/view/backend-rpc.ts +15 -0
- package/src/renderers/electrobun/view/data-table.tsx +138 -101
- package/src/renderers/electrobun/view/desktop-controls.tsx +42 -20
- package/src/renderers/electrobun/view/desktop-window-bridge.ts +12 -1
- package/src/renderers/electrobun/view/dialog-host.tsx +13 -1
- package/src/renderers/electrobun/view/input-host.tsx +74 -10
- package/src/renderers/electrobun/view/key-event.ts +1 -1
- package/src/renderers/electrobun/view/main.tsx +18 -1
- package/src/renderers/electrobun/view/styles.css +329 -0
- package/src/renderers/electrobun/view/toast-host.tsx +29 -3
- package/src/renderers/electrobun/view/ui-host.tsx +42 -0
- package/src/renderers/opentui/chart-surface.tsx +179 -0
- package/src/renderers/opentui/input-host.tsx +1 -1
- package/src/renderers/opentui/start.tsx +48 -30
- package/src/renderers/opentui/test-utils.tsx +17 -0
- package/src/renderers/opentui/ui-host.tsx +5 -2
- package/src/sources/gloomberb-cloud.test.ts +86 -1
- package/src/sources/gloomberb-cloud.ts +117 -21
- package/src/sources/provider-router.test.ts +122 -0
- package/src/sources/provider-router.ts +198 -15
- package/src/sources/yahoo-finance.test.ts +44 -0
- package/src/sources/yahoo-finance.ts +6 -0
- package/src/state/app-bootstrap.ts +20 -7
- package/src/state/app-context-selector.test.tsx +189 -4
- package/src/state/app-context.test.ts +65 -0
- package/src/state/app-context.tsx +48 -16
- package/src/state/use-inline-tickers.test.tsx +73 -0
- package/src/state/use-inline-tickers.ts +19 -10
- package/src/test-support/plugin-runtime.ts +3 -2
- package/src/theme/colors.test.ts +75 -0
- package/src/theme/colors.ts +76 -6
- package/src/theme/theme-context.tsx +26 -0
- package/src/theme/themes.test.ts +23 -1
- package/src/theme/themes.ts +115 -0
- package/src/types/broker.ts +1 -1
- package/src/types/capability-route-source.ts +1 -0
- package/src/types/data-provider.ts +15 -2
- package/src/types/desktop-window.ts +6 -0
- package/src/types/financials.ts +2 -0
- package/src/types/plugin.ts +1 -3
- package/src/ui/host.tsx +7 -0
- package/src/utils/api-client.test.ts +32 -0
- package/src/utils/api-client.ts +119 -18
- package/src/utils/back-navigation.ts +24 -1
- package/src/utils/double-escape-close.test.ts +33 -0
- package/src/utils/double-escape-close.ts +40 -0
- package/src/utils/throttled-fetch.test.ts +22 -0
- package/src/utils/throttled-fetch.ts +3 -1
- package/src/utils/ticker-search.ts +53 -9
- package/src/version.ts +1 -1
- package/src/plugins/builtin/news-wire/feed-pane.tsx +0 -21
- package/src/plugins/builtin/news-wire/top-pane.tsx +0 -21
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { act, type Dispatch } from "react";
|
|
3
|
+
import { testRender } from "../../renderers/opentui/test-utils";
|
|
4
|
+
import { AppProvider, useAppDispatch, type AppAction } from "../../state/app-context";
|
|
5
|
+
import { applyTheme, colors } from "../../theme/colors";
|
|
6
|
+
import { getTheme } from "../../theme/themes";
|
|
7
|
+
import { createDefaultConfig } from "../../types/config";
|
|
8
|
+
import { PaneContent } from "./pane-content";
|
|
9
|
+
|
|
10
|
+
let testSetup: Awaited<ReturnType<typeof testRender>> | undefined;
|
|
11
|
+
let capturedDispatch: Dispatch<AppAction> | null = null;
|
|
12
|
+
|
|
13
|
+
function DispatchCapture() {
|
|
14
|
+
capturedDispatch = useAppDispatch();
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function ThemeColorProbe() {
|
|
19
|
+
return <text>{colors.textBright}</text>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("PaneContent", () => {
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
testSetup?.renderer.destroy();
|
|
25
|
+
testSetup = undefined;
|
|
26
|
+
capturedDispatch = null;
|
|
27
|
+
applyTheme("amber");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("rerenders memoized pane bodies when the theme preview changes", async () => {
|
|
31
|
+
const config = {
|
|
32
|
+
...createDefaultConfig("/tmp/gloomberb-test"),
|
|
33
|
+
theme: "amber",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
testSetup = await testRender(
|
|
37
|
+
<AppProvider config={config}>
|
|
38
|
+
<DispatchCapture />
|
|
39
|
+
<PaneContent
|
|
40
|
+
component={ThemeColorProbe}
|
|
41
|
+
paneId="theme-preview:test"
|
|
42
|
+
paneType="test"
|
|
43
|
+
focused
|
|
44
|
+
width={24}
|
|
45
|
+
height={4}
|
|
46
|
+
/>
|
|
47
|
+
</AppProvider>,
|
|
48
|
+
{ width: 32, height: 6 },
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
await testSetup.renderOnce();
|
|
52
|
+
expect(testSetup.captureCharFrame()).toContain(getTheme("amber").textBright);
|
|
53
|
+
|
|
54
|
+
await act(async () => {
|
|
55
|
+
capturedDispatch?.({ type: "PREVIEW_THEME", theme: "green" });
|
|
56
|
+
await testSetup!.renderOnce();
|
|
57
|
+
});
|
|
58
|
+
await testSetup.renderOnce();
|
|
59
|
+
|
|
60
|
+
const frame = testSetup.captureCharFrame();
|
|
61
|
+
expect(frame).toContain(getTheme("green").textBright);
|
|
62
|
+
expect(frame).not.toContain(getTheme("amber").textBright);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { memo, useCallback } from "react";
|
|
2
2
|
import { PaneInstanceProvider } from "../../state/app-context";
|
|
3
3
|
import { PaneKeyboardScrollController } from "../../state/pane-scroll-registry";
|
|
4
|
+
import { useThemeColors } from "../../theme/theme-context";
|
|
4
5
|
import type { PaneDef } from "../../types/plugin";
|
|
5
6
|
|
|
6
7
|
interface PaneContentProps {
|
|
@@ -22,6 +23,7 @@ export const PaneContent = memo(function PaneContent({
|
|
|
22
23
|
height,
|
|
23
24
|
onClose,
|
|
24
25
|
}: PaneContentProps) {
|
|
26
|
+
useThemeColors();
|
|
25
27
|
const close = useCallback(() => {
|
|
26
28
|
onClose?.(paneId);
|
|
27
29
|
}, [onClose, paneId]);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import type { PluginRegistry } from "../../plugins/registry";
|
|
3
|
+
import { createDefaultConfig } from "../../types/config";
|
|
4
|
+
import { getVisiblePaneCycleOrder } from "./pane-cycle-order";
|
|
5
|
+
|
|
6
|
+
function createRegistry(options: {
|
|
7
|
+
paneIds: readonly string[];
|
|
8
|
+
disabledPaneIds?: Record<string, readonly string[]>;
|
|
9
|
+
}): PluginRegistry {
|
|
10
|
+
return {
|
|
11
|
+
panes: new Map(options.paneIds.map((paneId) => [paneId, {}])),
|
|
12
|
+
getPluginPaneIds: (pluginId: string) => [...(options.disabledPaneIds?.[pluginId] ?? [])],
|
|
13
|
+
} as unknown as PluginRegistry;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe("getVisiblePaneCycleOrder", () => {
|
|
17
|
+
test("skips disabled and unregistered panes when cycling focus", () => {
|
|
18
|
+
const config = createDefaultConfig("/tmp/gloomberb-pane-cycle-order");
|
|
19
|
+
const registry = createRegistry({
|
|
20
|
+
paneIds: ["portfolio-list", "chat", "ticker-detail"],
|
|
21
|
+
disabledPaneIds: { chat: ["chat"] },
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
expect(getVisiblePaneCycleOrder(config.layout, registry, ["chat"])).toEqual([
|
|
25
|
+
"portfolio-list:main",
|
|
26
|
+
"ticker-detail:main",
|
|
27
|
+
"ticker-detail:nvda",
|
|
28
|
+
]);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { getDockedPaneIds } from "../../plugins/pane-manager";
|
|
2
|
+
import type { PluginRegistry } from "../../plugins/registry";
|
|
3
|
+
import { findPaneInstance, type LayoutConfig } from "../../types/config";
|
|
4
|
+
|
|
5
|
+
function disabledPaneTypes(pluginRegistry: PluginRegistry, disabledPlugins: readonly string[]): Set<string> {
|
|
6
|
+
const paneTypes = new Set<string>();
|
|
7
|
+
for (const pluginId of disabledPlugins) {
|
|
8
|
+
for (const paneId of pluginRegistry.getPluginPaneIds(pluginId)) {
|
|
9
|
+
paneTypes.add(paneId);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return paneTypes;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getVisiblePaneCycleOrder(
|
|
16
|
+
layout: LayoutConfig,
|
|
17
|
+
pluginRegistry: PluginRegistry,
|
|
18
|
+
disabledPlugins: readonly string[],
|
|
19
|
+
): string[] {
|
|
20
|
+
const disabledTypes = disabledPaneTypes(pluginRegistry, disabledPlugins);
|
|
21
|
+
const isVisiblePane = (instanceId: string) => {
|
|
22
|
+
const instance = findPaneInstance(layout, instanceId);
|
|
23
|
+
return !!instance
|
|
24
|
+
&& !disabledTypes.has(instance.paneId)
|
|
25
|
+
&& pluginRegistry.panes.has(instance.paneId);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return [
|
|
29
|
+
...getDockedPaneIds(layout),
|
|
30
|
+
...layout.floating.map((entry) => entry.instanceId),
|
|
31
|
+
].filter(isVisiblePane);
|
|
32
|
+
}
|
|
@@ -357,17 +357,24 @@ export function PaneFooterBar({
|
|
|
357
357
|
focused,
|
|
358
358
|
width = 0,
|
|
359
359
|
reserveRight = 0,
|
|
360
|
+
showBorder = false,
|
|
360
361
|
}: {
|
|
361
362
|
footer?: CombinedPaneFooter | null;
|
|
362
363
|
focused: boolean;
|
|
363
364
|
width?: number;
|
|
364
365
|
reserveRight?: number;
|
|
366
|
+
showBorder?: boolean;
|
|
365
367
|
}) {
|
|
366
368
|
const { nativePaneChrome } = useUiCapabilities();
|
|
367
369
|
const resolvedFooter = footer ?? EMPTY_FOOTER;
|
|
368
370
|
const empty = !hasPaneFooterContent(resolvedFooter);
|
|
369
371
|
const borderColor = focused ? colors.borderFocused : colors.border;
|
|
370
372
|
const topBorderColor = colors.border;
|
|
373
|
+
const nativeBackgroundColor = empty
|
|
374
|
+
? "transparent"
|
|
375
|
+
: focused
|
|
376
|
+
? blendHex(colors.bg, colors.borderFocused, 0.06)
|
|
377
|
+
: blendHex(colors.panel, colors.border, 0.12);
|
|
371
378
|
const reservedRight = Math.max(0, reserveRight);
|
|
372
379
|
const rightPadding = reservedRight > 0 ? reservedRight : 1;
|
|
373
380
|
|
|
@@ -385,7 +392,7 @@ export function PaneFooterBar({
|
|
|
385
392
|
style={{
|
|
386
393
|
"--pane-footer-border-color": empty ? "transparent" : topBorderColor,
|
|
387
394
|
borderTop: `1px solid ${empty ? "transparent" : topBorderColor}`,
|
|
388
|
-
backgroundColor:
|
|
395
|
+
backgroundColor: nativeBackgroundColor,
|
|
389
396
|
boxShadow: empty ? "none" : "inset 0 1px 0 rgba(255,255,255,0.03)",
|
|
390
397
|
}}
|
|
391
398
|
>
|
|
@@ -399,15 +406,15 @@ export function PaneFooterBar({
|
|
|
399
406
|
);
|
|
400
407
|
}
|
|
401
408
|
|
|
402
|
-
if (focused) {
|
|
409
|
+
if (focused || showBorder) {
|
|
403
410
|
const contentWidth = Math.max(0, Math.floor(width) - 1 - reservedRight - (reservedRight > 0 ? 0 : 1));
|
|
404
411
|
return (
|
|
405
|
-
<Box height={1} width={width} flexDirection="row" data-gloom-role="pane-footer" data-focused="true" data-empty={empty ? "true" : "false"}>
|
|
412
|
+
<Box height={1} width={width} flexDirection="row" data-gloom-role="pane-footer" data-focused={focused ? "true" : "false"} data-empty={empty ? "true" : "false"}>
|
|
406
413
|
<Text fg={borderColor} selectable={false}>└</Text>
|
|
407
414
|
<Box width={contentWidth} height={1} overflow="hidden">
|
|
408
415
|
{empty
|
|
409
416
|
? <Text fg={borderColor} selectable={false}>{"─".repeat(contentWidth)}</Text>
|
|
410
|
-
: <FooterContent footer={resolvedFooter} focused width={contentWidth} />}
|
|
417
|
+
: <FooterContent footer={resolvedFooter} focused={focused} width={contentWidth} />}
|
|
411
418
|
</Box>
|
|
412
419
|
{reservedRight === 0 && <Text fg={borderColor} selectable={false}>┘</Text>}
|
|
413
420
|
</Box>
|
|
@@ -108,7 +108,6 @@ export function PaneHeader({
|
|
|
108
108
|
const backgroundColor = floating ? floatingPaneTitleBg(focused) : paneTitleBg(focused);
|
|
109
109
|
const actionText = showActions ? PANE_HEADER_ACTION : " ";
|
|
110
110
|
const closeText = floating ? PANE_HEADER_CLOSE : "";
|
|
111
|
-
const bc = colors.borderFocused;
|
|
112
111
|
const textColor = paneTitleText(focused, floating);
|
|
113
112
|
|
|
114
113
|
if (nativePaneChrome) {
|
|
@@ -184,9 +183,10 @@ export function PaneHeader({
|
|
|
184
183
|
);
|
|
185
184
|
}
|
|
186
185
|
|
|
187
|
-
if (focused) {
|
|
186
|
+
if (focused || floating) {
|
|
188
187
|
// Build: ┌─:: Title ─────────── ... x─┐
|
|
189
188
|
// Reserve 2 for corners, 1 for ─ after ┌, 1 for ─ before ┐
|
|
189
|
+
const borderColor = focused ? colors.borderFocused : colors.border;
|
|
190
190
|
const innerWidth = Math.max(0, width - 4);
|
|
191
191
|
const contentWidth = PANE_HEADER_GRIP.length + closeText.length + actionText.length;
|
|
192
192
|
const titleWidth = Math.max(0, innerWidth - contentWidth);
|
|
@@ -196,9 +196,9 @@ export function PaneHeader({
|
|
|
196
196
|
|
|
197
197
|
return (
|
|
198
198
|
<Box height={PANE_HEADER_HEIGHT} width={width} backgroundColor={backgroundColor} flexDirection="row">
|
|
199
|
-
<Text fg={
|
|
199
|
+
<Text fg={borderColor} selectable={false}>{"┌─"}</Text>
|
|
200
200
|
<Text fg={textColor} selectable={false}>{`${PANE_HEADER_GRIP}${clippedTitle}`}</Text>
|
|
201
|
-
<Text fg={
|
|
201
|
+
<Text fg={borderColor} selectable={false}>{fill}</Text>
|
|
202
202
|
<TerminalPaneButton
|
|
203
203
|
text={actionText}
|
|
204
204
|
fg={textColor}
|
|
@@ -213,7 +213,7 @@ export function PaneHeader({
|
|
|
213
213
|
onMouseDown={onCloseMouseDown}
|
|
214
214
|
/>
|
|
215
215
|
)}
|
|
216
|
-
<Text fg={
|
|
216
|
+
<Text fg={borderColor} selectable={false}>{"─┐"}</Text>
|
|
217
217
|
</Box>
|
|
218
218
|
);
|
|
219
219
|
}
|
|
@@ -2,14 +2,21 @@
|
|
|
2
2
|
const PANE_HEADER_ROWS = 1;
|
|
3
3
|
const PANE_FOOTER_ROWS = 1;
|
|
4
4
|
|
|
5
|
+
export const NATIVE_PANE_BODY_LAYOUT_PROPS = {
|
|
6
|
+
flexGrow: 1,
|
|
7
|
+
flexBasis: 0,
|
|
8
|
+
minHeight: 0,
|
|
9
|
+
} as const;
|
|
10
|
+
|
|
5
11
|
export function shouldReservePaneFooter(nativePaneChrome: boolean | undefined, showFooter: boolean): boolean {
|
|
6
12
|
return !nativePaneChrome || showFooter;
|
|
7
13
|
}
|
|
8
14
|
|
|
9
15
|
export function getPaneBodyHeight(height: number, reserveFooter = true): number {
|
|
10
|
-
|
|
16
|
+
const cellHeight = Math.max(1, Math.floor(height));
|
|
17
|
+
return Math.max(1, cellHeight - PANE_HEADER_ROWS - (reserveFooter ? PANE_FOOTER_ROWS : 0));
|
|
11
18
|
}
|
|
12
19
|
|
|
13
20
|
export function getPaneBodyWidth(width: number): number {
|
|
14
|
-
return Math.max(1, width);
|
|
21
|
+
return Math.max(1, Math.floor(width));
|
|
15
22
|
}
|
|
@@ -8,7 +8,10 @@ export function getPaneDisplayTitle(
|
|
|
8
8
|
paneDef: PaneDef,
|
|
9
9
|
): string {
|
|
10
10
|
if (instance.paneId === "chat") {
|
|
11
|
-
|
|
11
|
+
const channelId = typeof instance.settings?.channelId === "string" && instance.settings.channelId.trim()
|
|
12
|
+
? instance.settings.channelId.trim()
|
|
13
|
+
: "everyone";
|
|
14
|
+
return instance.title ?? `#${channelId}`;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
if (instance.paneId === "ticker-detail") {
|
|
@@ -3,7 +3,7 @@ import type { ReactNode } from "react";
|
|
|
3
3
|
import { colors, paneBg } from "../../theme/colors";
|
|
4
4
|
import { PaneHeader } from "./pane-header";
|
|
5
5
|
import { hasPaneFooterContent, PaneFooterBar, type CombinedPaneFooter } from "./pane-footer";
|
|
6
|
-
import { getPaneBodyHeight, shouldReservePaneFooter } from "./pane-sizing";
|
|
6
|
+
import { getPaneBodyHeight, NATIVE_PANE_BODY_LAYOUT_PROPS, shouldReservePaneFooter } from "./pane-sizing";
|
|
7
7
|
|
|
8
8
|
interface PaneWrapperProps {
|
|
9
9
|
paneId?: string;
|
|
@@ -47,8 +47,15 @@ export function PaneWrapper({
|
|
|
47
47
|
const showFooter = hasPaneFooterContent(footer);
|
|
48
48
|
const reserveFooter = shouldReservePaneFooter(nativePaneChrome, showFooter);
|
|
49
49
|
const bodyHeight = typeof height === "number"
|
|
50
|
-
? title ? getPaneBodyHeight(height, reserveFooter) : height
|
|
50
|
+
? title ? getPaneBodyHeight(height, reserveFooter) : Math.max(1, Math.floor(height))
|
|
51
51
|
: undefined;
|
|
52
|
+
const bodyLayoutProps = nativePaneChrome
|
|
53
|
+
? NATIVE_PANE_BODY_LAYOUT_PROPS
|
|
54
|
+
: {
|
|
55
|
+
height: bodyHeight,
|
|
56
|
+
flexGrow: bodyHeight == null ? 1 : 0,
|
|
57
|
+
flexBasis: bodyHeight == null ? 0 : undefined,
|
|
58
|
+
};
|
|
52
59
|
|
|
53
60
|
return (
|
|
54
61
|
<Box
|
|
@@ -82,10 +89,9 @@ export function PaneWrapper({
|
|
|
82
89
|
/>
|
|
83
90
|
)}
|
|
84
91
|
<Box
|
|
85
|
-
|
|
86
|
-
flexGrow={bodyHeight == null ? 1 : 0}
|
|
87
|
-
flexBasis={bodyHeight == null ? 0 : undefined}
|
|
92
|
+
{...bodyLayoutProps}
|
|
88
93
|
overflow="hidden"
|
|
94
|
+
backgroundColor={bg}
|
|
89
95
|
>
|
|
90
96
|
{children}
|
|
91
97
|
</Box>
|