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
|
@@ -1,50 +1,23 @@
|
|
|
1
1
|
import { useShortcut } from "../react/input";
|
|
2
|
-
import { type
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
preventDefault?: () => void;
|
|
10
|
-
stopPropagation?: () => void;
|
|
11
|
-
}
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
import {
|
|
4
|
+
DataTableView,
|
|
5
|
+
type DataTableKeyEvent,
|
|
6
|
+
type DataTableViewProps,
|
|
7
|
+
} from "./data-table-view";
|
|
8
|
+
import { PageStackView, type DataTableColumn } from "./ui";
|
|
12
9
|
|
|
13
10
|
export interface DataTableStackViewProps<
|
|
14
11
|
T,
|
|
15
12
|
C extends DataTableColumn = DataTableColumn,
|
|
16
|
-
> extends Omit<
|
|
17
|
-
DataTableProps<T, C>,
|
|
18
|
-
| "headerScrollRef"
|
|
19
|
-
| "scrollRef"
|
|
20
|
-
| "syncHeaderScroll"
|
|
21
|
-
| "onBodyScrollActivity"
|
|
22
|
-
| "hoveredIdx"
|
|
23
|
-
| "setHoveredIdx"
|
|
24
|
-
> {
|
|
13
|
+
> extends Omit<DataTableViewProps<T, C>, "focused" | "selectedIndex"> {
|
|
25
14
|
focused: boolean;
|
|
26
15
|
detailOpen: boolean;
|
|
27
16
|
onBack: () => void;
|
|
28
17
|
detailContent: ReactNode;
|
|
29
18
|
detailTitle?: string;
|
|
30
19
|
selectedIndex: number;
|
|
31
|
-
|
|
32
|
-
onActivateIndex?: (index: number, item: T) => void;
|
|
33
|
-
isNavigable?: (item: T, index: number) => boolean;
|
|
34
|
-
rootBefore?: ReactNode;
|
|
35
|
-
rootAfter?: ReactNode;
|
|
36
|
-
rootWidth?: number;
|
|
37
|
-
rootHeight?: number;
|
|
38
|
-
rootBackgroundColor?: string;
|
|
39
|
-
headerScrollRef?: RefObject<ScrollBoxRenderable | null>;
|
|
40
|
-
scrollRef?: RefObject<ScrollBoxRenderable | null>;
|
|
41
|
-
syncHeaderScroll?: () => void;
|
|
42
|
-
onBodyScrollActivity?: () => void;
|
|
43
|
-
hoveredIdx?: number | null;
|
|
44
|
-
setHoveredIdx?: (index: number | null) => void;
|
|
45
|
-
keyboardNavigation?: boolean;
|
|
46
|
-
onRootKeyDown?: (event: KeyboardEventLike) => boolean | void;
|
|
47
|
-
onDetailKeyDown?: (event: KeyboardEventLike) => boolean | void;
|
|
20
|
+
onDetailKeyDown?: (event: DataTableKeyEvent) => boolean | void;
|
|
48
21
|
}
|
|
49
22
|
|
|
50
23
|
export function DataTableStackView<
|
|
@@ -56,42 +29,9 @@ export function DataTableStackView<
|
|
|
56
29
|
onBack,
|
|
57
30
|
detailContent,
|
|
58
31
|
detailTitle,
|
|
59
|
-
selectedIndex,
|
|
60
|
-
onSelectIndex,
|
|
61
|
-
onActivateIndex,
|
|
62
|
-
isNavigable,
|
|
63
|
-
rootBefore,
|
|
64
|
-
rootAfter,
|
|
65
|
-
rootWidth,
|
|
66
|
-
rootHeight,
|
|
67
|
-
rootBackgroundColor,
|
|
68
|
-
headerScrollRef,
|
|
69
|
-
scrollRef,
|
|
70
|
-
syncHeaderScroll,
|
|
71
|
-
onBodyScrollActivity,
|
|
72
|
-
hoveredIdx,
|
|
73
|
-
setHoveredIdx,
|
|
74
32
|
keyboardNavigation = true,
|
|
75
|
-
onRootKeyDown,
|
|
76
33
|
onDetailKeyDown,
|
|
77
|
-
|
|
78
|
-
items,
|
|
79
|
-
sortColumnId,
|
|
80
|
-
sortDirection,
|
|
81
|
-
onHeaderClick,
|
|
82
|
-
getItemKey,
|
|
83
|
-
isSelected,
|
|
84
|
-
onSelect,
|
|
85
|
-
onActivate,
|
|
86
|
-
renderCell,
|
|
87
|
-
renderSectionHeader,
|
|
88
|
-
getRowBackgroundColor,
|
|
89
|
-
emptyContent,
|
|
90
|
-
emptyStateTitle,
|
|
91
|
-
emptyStateHint,
|
|
92
|
-
virtualize = true,
|
|
93
|
-
overscan,
|
|
94
|
-
showHorizontalScrollbar,
|
|
34
|
+
...tableProps
|
|
95
35
|
}: DataTableStackViewProps<T, C>) {
|
|
96
36
|
useShortcut((event) => {
|
|
97
37
|
if (!focused || !detailOpen || !keyboardNavigation) return;
|
|
@@ -100,42 +40,9 @@ export function DataTableStackView<
|
|
|
100
40
|
|
|
101
41
|
const rootContent = (
|
|
102
42
|
<DataTableView<T, C>
|
|
43
|
+
{...tableProps}
|
|
103
44
|
focused={focused && !detailOpen}
|
|
104
|
-
selectedIndex={selectedIndex}
|
|
105
|
-
onSelectIndex={onSelectIndex}
|
|
106
|
-
onActivateIndex={onActivateIndex}
|
|
107
|
-
isNavigable={isNavigable}
|
|
108
|
-
rootBefore={rootBefore}
|
|
109
|
-
rootAfter={rootAfter}
|
|
110
|
-
rootWidth={rootWidth}
|
|
111
|
-
rootHeight={rootHeight}
|
|
112
|
-
rootBackgroundColor={rootBackgroundColor}
|
|
113
|
-
headerScrollRef={headerScrollRef}
|
|
114
|
-
scrollRef={scrollRef}
|
|
115
|
-
syncHeaderScroll={syncHeaderScroll}
|
|
116
|
-
onBodyScrollActivity={onBodyScrollActivity}
|
|
117
|
-
hoveredIdx={hoveredIdx}
|
|
118
|
-
setHoveredIdx={setHoveredIdx}
|
|
119
45
|
keyboardNavigation={keyboardNavigation}
|
|
120
|
-
onRootKeyDown={onRootKeyDown}
|
|
121
|
-
columns={columns}
|
|
122
|
-
items={items}
|
|
123
|
-
sortColumnId={sortColumnId}
|
|
124
|
-
sortDirection={sortDirection}
|
|
125
|
-
onHeaderClick={onHeaderClick}
|
|
126
|
-
getItemKey={getItemKey}
|
|
127
|
-
isSelected={isSelected}
|
|
128
|
-
onSelect={onSelect}
|
|
129
|
-
onActivate={onActivate}
|
|
130
|
-
renderCell={renderCell}
|
|
131
|
-
renderSectionHeader={renderSectionHeader}
|
|
132
|
-
getRowBackgroundColor={getRowBackgroundColor}
|
|
133
|
-
emptyContent={emptyContent}
|
|
134
|
-
emptyStateTitle={emptyStateTitle}
|
|
135
|
-
emptyStateHint={emptyStateHint}
|
|
136
|
-
virtualize={virtualize}
|
|
137
|
-
overscan={overscan}
|
|
138
|
-
showHorizontalScrollbar={showHorizontalScrollbar}
|
|
139
46
|
/>
|
|
140
47
|
);
|
|
141
48
|
|
|
@@ -2,24 +2,26 @@ import {
|
|
|
2
2
|
useCallback,
|
|
3
3
|
useEffect,
|
|
4
4
|
useMemo,
|
|
5
|
-
useRef,
|
|
6
5
|
useState,
|
|
7
6
|
type ReactNode,
|
|
8
7
|
type RefObject,
|
|
9
8
|
} from "react";
|
|
10
|
-
import {
|
|
9
|
+
import type { ScrollBoxRenderable } from "../ui";
|
|
11
10
|
import { useShortcut } from "../react/input";
|
|
12
11
|
import { DataTable, type DataTableColumn, type DataTableProps } from "./ui";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
12
|
+
import {
|
|
13
|
+
isNextTableRowKey,
|
|
14
|
+
isPreviousTableRowKey,
|
|
15
|
+
isTableActivationKey,
|
|
16
|
+
stopTableKey,
|
|
17
|
+
TableViewFrame,
|
|
18
|
+
type TableViewKeyEvent,
|
|
19
|
+
useResetTableScroll,
|
|
20
|
+
useTableBodyScrollActivity,
|
|
21
|
+
useTableViewState,
|
|
22
|
+
} from "./table-view-shared";
|
|
23
|
+
|
|
24
|
+
export type DataTableKeyEvent = TableViewKeyEvent;
|
|
23
25
|
|
|
24
26
|
export interface DataTableViewProps<
|
|
25
27
|
T,
|
|
@@ -54,23 +56,6 @@ export interface DataTableViewProps<
|
|
|
54
56
|
resetScrollKey?: unknown;
|
|
55
57
|
}
|
|
56
58
|
|
|
57
|
-
function isActivationKey(name: string | undefined): boolean {
|
|
58
|
-
return name === "enter" || name === "return";
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function isNextRowKey(name: string | undefined): boolean {
|
|
62
|
-
return name === "j" || name === "down";
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function isPreviousRowKey(name: string | undefined): boolean {
|
|
66
|
-
return name === "k" || name === "up";
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function stopTableKey(event: DataTableKeyEvent) {
|
|
70
|
-
event.stopPropagation?.();
|
|
71
|
-
event.preventDefault?.();
|
|
72
|
-
}
|
|
73
|
-
|
|
74
59
|
export function DataTableView<
|
|
75
60
|
T,
|
|
76
61
|
C extends DataTableColumn = DataTableColumn,
|
|
@@ -97,17 +82,19 @@ export function DataTableView<
|
|
|
97
82
|
scrollToIndex,
|
|
98
83
|
...tableProps
|
|
99
84
|
}: DataTableViewProps<T, C>) {
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
85
|
+
const {
|
|
86
|
+
effectiveHeaderScrollRef,
|
|
87
|
+
effectiveScrollRef,
|
|
88
|
+
effectiveHoveredIdx,
|
|
89
|
+
effectiveSetHoveredIdx,
|
|
90
|
+
effectiveSyncHeaderScroll,
|
|
91
|
+
} = useTableViewState({
|
|
92
|
+
headerScrollRef,
|
|
93
|
+
scrollRef,
|
|
94
|
+
syncHeaderScroll,
|
|
95
|
+
hoveredIdx,
|
|
96
|
+
setHoveredIdx,
|
|
97
|
+
});
|
|
111
98
|
const [fallbackSelectedIndex, setFallbackSelectedIndex] = useState<
|
|
112
99
|
number | null
|
|
113
100
|
>(null);
|
|
@@ -148,36 +135,16 @@ export function DataTableView<
|
|
|
148
135
|
setFallbackSelectedIndex(nextIndex);
|
|
149
136
|
}, [fallbackSelectedIndex, navigableIndices, usesFallbackSelection]);
|
|
150
137
|
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (header.scrollLeft !== body.scrollLeft) {
|
|
156
|
-
header.scrollLeft = body.scrollLeft;
|
|
157
|
-
}
|
|
158
|
-
}, [effectiveHeaderScrollRef, effectiveScrollRef]);
|
|
159
|
-
const effectiveSyncHeaderScroll = syncHeaderScroll ?? defaultSyncHeaderScroll;
|
|
160
|
-
|
|
161
|
-
const handleBodyScrollActivity = useCallback(() => {
|
|
162
|
-
if (onBodyScrollActivity) {
|
|
163
|
-
onBodyScrollActivity();
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
queueMicrotask(effectiveSyncHeaderScroll);
|
|
167
|
-
}, [effectiveSyncHeaderScroll, onBodyScrollActivity]);
|
|
138
|
+
const handleBodyScrollActivity = useTableBodyScrollActivity({
|
|
139
|
+
onBodyScrollActivity,
|
|
140
|
+
syncHeaderScroll: effectiveSyncHeaderScroll,
|
|
141
|
+
});
|
|
168
142
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
body.scrollLeft = 0;
|
|
175
|
-
}
|
|
176
|
-
const header = effectiveHeaderScrollRef.current;
|
|
177
|
-
if (header) {
|
|
178
|
-
header.scrollLeft = 0;
|
|
179
|
-
}
|
|
180
|
-
}, [effectiveHeaderScrollRef, effectiveScrollRef, resetScrollKey]);
|
|
143
|
+
useResetTableScroll({
|
|
144
|
+
headerScrollRef: effectiveHeaderScrollRef,
|
|
145
|
+
scrollRef: effectiveScrollRef,
|
|
146
|
+
resetScrollKey,
|
|
147
|
+
});
|
|
181
148
|
|
|
182
149
|
const selectIndex = useCallback((index: number) => {
|
|
183
150
|
if (index < 0 || index >= tableProps.items.length) return;
|
|
@@ -226,34 +193,32 @@ export function DataTableView<
|
|
|
226
193
|
if (onRootKeyDown?.(event)) return;
|
|
227
194
|
if (tableProps.items.length === 0) return;
|
|
228
195
|
|
|
229
|
-
if (
|
|
196
|
+
if (isNextTableRowKey(event.name)) {
|
|
230
197
|
stopTableKey(event);
|
|
231
198
|
selectByOffset(1);
|
|
232
199
|
return;
|
|
233
200
|
}
|
|
234
201
|
|
|
235
|
-
if (
|
|
202
|
+
if (isPreviousTableRowKey(event.name)) {
|
|
236
203
|
stopTableKey(event);
|
|
237
204
|
selectByOffset(-1);
|
|
238
205
|
return;
|
|
239
206
|
}
|
|
240
207
|
|
|
241
|
-
if (
|
|
208
|
+
if (isTableActivationKey(event.name)) {
|
|
242
209
|
stopTableKey(event);
|
|
243
210
|
activateSelection();
|
|
244
211
|
}
|
|
245
212
|
});
|
|
246
213
|
|
|
247
214
|
return (
|
|
248
|
-
<
|
|
249
|
-
flexDirection="column"
|
|
250
|
-
flexGrow={1}
|
|
215
|
+
<TableViewFrame
|
|
251
216
|
width={rootWidth}
|
|
252
217
|
height={rootHeight}
|
|
253
218
|
backgroundColor={rootBackgroundColor}
|
|
254
|
-
|
|
219
|
+
before={rootBefore}
|
|
220
|
+
after={rootAfter}
|
|
255
221
|
>
|
|
256
|
-
{rootBefore}
|
|
257
222
|
<DataTable<T, C>
|
|
258
223
|
{...tableProps}
|
|
259
224
|
headerScrollRef={effectiveHeaderScrollRef}
|
|
@@ -273,7 +238,6 @@ export function DataTableView<
|
|
|
273
238
|
)
|
|
274
239
|
)}
|
|
275
240
|
/>
|
|
276
|
-
|
|
277
|
-
</Box>
|
|
241
|
+
</TableViewFrame>
|
|
278
242
|
);
|
|
279
243
|
}
|
|
@@ -5,6 +5,12 @@ import { colors } from "../theme/colors";
|
|
|
5
5
|
import { formatTimeAgo } from "../utils/format";
|
|
6
6
|
import { toTimestampMillis } from "../utils/timestamp";
|
|
7
7
|
import { DataTableStackView } from "./data-table-stack-view";
|
|
8
|
+
import {
|
|
9
|
+
activeStackIndex,
|
|
10
|
+
sortIndexedStackRows,
|
|
11
|
+
type IndexedStackRow,
|
|
12
|
+
type StackSortPreference,
|
|
13
|
+
} from "./feed-stack-controller";
|
|
8
14
|
import { ExternalLink, type DataTableCell, type DataTableColumn } from "./ui";
|
|
9
15
|
import { wrapTextLines } from "../utils/text-wrap";
|
|
10
16
|
|
|
@@ -23,15 +29,9 @@ export interface FeedDataTableItem {
|
|
|
23
29
|
type DetailColumnId = "time" | "source" | "title";
|
|
24
30
|
type DetailColumn = DataTableColumn & { id: DetailColumnId };
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
item: FeedDataTableItem;
|
|
28
|
-
itemIndex: number;
|
|
29
|
-
}
|
|
32
|
+
type DetailRow = IndexedStackRow<FeedDataTableItem>;
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
columnId: DetailColumnId;
|
|
33
|
-
direction: "asc" | "desc";
|
|
34
|
-
}
|
|
34
|
+
type SortPreference = StackSortPreference<DetailColumnId>;
|
|
35
35
|
|
|
36
36
|
interface FeedDataTableStackViewProps {
|
|
37
37
|
width: number;
|
|
@@ -151,19 +151,12 @@ export function FeedDataTableStackView({
|
|
|
151
151
|
[items, sourceLabel, titleLabel, width],
|
|
152
152
|
);
|
|
153
153
|
const sortedRows = useMemo(() => {
|
|
154
|
-
|
|
155
|
-
return items
|
|
156
|
-
.map((item, itemIndex) => ({ item, itemIndex }))
|
|
157
|
-
.sort((a, b) => {
|
|
158
|
-
const primary = compareRows(a, b, sortPreference.columnId) * direction;
|
|
159
|
-
return primary !== 0 ? primary : a.itemIndex - b.itemIndex;
|
|
160
|
-
});
|
|
154
|
+
return sortIndexedStackRows(items, sortPreference, compareRows);
|
|
161
155
|
}, [items, sortPreference]);
|
|
162
156
|
const selectedRowIndex = sortedRows.findIndex(
|
|
163
157
|
(row) => row.itemIndex === selectedIdx,
|
|
164
158
|
);
|
|
165
|
-
const activeRowIndex =
|
|
166
|
-
selectedRowIndex >= 0 ? selectedRowIndex : sortedRows.length > 0 ? 0 : -1;
|
|
159
|
+
const activeRowIndex = activeStackIndex(sortedRows.length, selectedRowIndex);
|
|
167
160
|
const openItem = useMemo(
|
|
168
161
|
() =>
|
|
169
162
|
openItemId
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface StackSortPreference<C extends string> {
|
|
2
|
+
columnId: C;
|
|
3
|
+
direction: "asc" | "desc";
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface IndexedStackRow<T> {
|
|
7
|
+
item: T;
|
|
8
|
+
itemIndex: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function sortStackItems<T, C extends string>(
|
|
12
|
+
items: T[],
|
|
13
|
+
preference: StackSortPreference<C>,
|
|
14
|
+
compare: (a: T, b: T, columnId: C) => number,
|
|
15
|
+
tieBreak: (a: T, b: T) => number = () => 0,
|
|
16
|
+
): T[] {
|
|
17
|
+
const direction = preference.direction === "asc" ? 1 : -1;
|
|
18
|
+
return [...items].sort((a, b) => {
|
|
19
|
+
const primary = compare(a, b, preference.columnId) * direction;
|
|
20
|
+
return primary !== 0 ? primary : tieBreak(a, b);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function sortIndexedStackRows<T, C extends string>(
|
|
25
|
+
items: T[],
|
|
26
|
+
preference: StackSortPreference<C>,
|
|
27
|
+
compare: (a: IndexedStackRow<T>, b: IndexedStackRow<T>, columnId: C) => number,
|
|
28
|
+
): Array<IndexedStackRow<T>> {
|
|
29
|
+
return sortStackItems(
|
|
30
|
+
items.map((item, itemIndex) => ({ item, itemIndex })),
|
|
31
|
+
preference,
|
|
32
|
+
compare,
|
|
33
|
+
(a, b) => a.itemIndex - b.itemIndex,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function activeStackIndex(length: number, selectedIndex: number): number {
|
|
38
|
+
return selectedIndex >= 0 ? selectedIndex : length > 0 ? 0 : -1;
|
|
39
|
+
}
|
package/src/components/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
export { PriceSelectorDialog } from "./price-selector-dialog";
|
|
3
3
|
export { StaticChartSurface } from "./chart/static-chart-surface";
|
|
4
|
+
export { SpeedometerGauge } from "./speedometer-gauge";
|
|
5
|
+
export type { SpeedometerSegment } from "./speedometer-gauge";
|
|
4
6
|
export type { QuoteFlashDirection } from "./ticker-list-table";
|
|
5
7
|
export { TickerListTableView } from "./ticker-list-table-view";
|
|
6
8
|
export type { TickerListVisibleRange } from "./ticker-list-table-view";
|
|
@@ -10,14 +12,16 @@ export type { DataTableKeyEvent } from "./data-table-view";
|
|
|
10
12
|
export { DataTableStackView } from "./data-table-stack-view";
|
|
11
13
|
export { FeedDataTableStackView } from "./feed-data-table-stack-view";
|
|
12
14
|
export type { FeedDataTableItem } from "./feed-data-table-stack-view";
|
|
15
|
+
export { activeStackIndex, sortStackItems } from "./feed-stack-controller";
|
|
16
|
+
export type { StackSortPreference } from "./feed-stack-controller";
|
|
13
17
|
export { PaneFooterScope, usePaneFooter } from "./layout/pane-footer";
|
|
14
18
|
export type { PaneFooterSegment, PaneHint } from "./layout/pane-footer";
|
|
15
19
|
export { useExternalLinkFooter } from "./use-external-link-footer";
|
|
16
20
|
export { Button } from "./ui/button";
|
|
21
|
+
export { ConfirmDialog } from "./ui/confirm-dialog";
|
|
17
22
|
export { ChoiceDialog } from "./ui/choice-dialog";
|
|
18
23
|
export type { ChoiceDialogChoice, ChoiceDialogProps } from "./ui/choice-dialog";
|
|
19
24
|
export type { DataTableCell, DataTableColumn } from "./ui/data-table";
|
|
20
|
-
export { DialogFrame } from "./ui/frame";
|
|
21
25
|
export { EmptyState } from "./ui/status";
|
|
22
26
|
export { getMessageComposerBlockHeight, MessageComposer } from "./ui/message-composer";
|
|
23
27
|
export { NumberField, TextField } from "./ui/fields";
|
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
import { Box, Text, useRendererHost, useUiCapabilities } from "../../ui";
|
|
2
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { useShortcut, useViewport } from "../../react/input";
|
|
4
4
|
import { useAppDispatch, useAppSelector } from "../../state/app-context";
|
|
5
5
|
import type { DesktopWindowBridge } from "../../types/desktop-window";
|
|
6
6
|
import { findPaneInstance } from "../../types/config";
|
|
7
7
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
8
8
|
import { colors, floatingPaneBg, floatingPaneTitleBg, paneTitleText } from "../../theme/colors";
|
|
9
|
+
import { useThemeColors } from "../../theme/theme-context";
|
|
9
10
|
import { hasPaneFooterContent, PaneFooterBar, PaneFooterProvider } from "./pane-footer";
|
|
10
11
|
import { PaneContent } from "./pane-content";
|
|
11
|
-
import { getPaneBodyWidth } from "./pane-sizing";
|
|
12
|
+
import { getPaneBodyWidth, NATIVE_PANE_BODY_LAYOUT_PROPS } from "./pane-sizing";
|
|
12
13
|
import { getPaneDisplayTitle } from "./pane-title";
|
|
13
14
|
import { TITLEBAR_OVERLAY_HEIGHT_PX, TITLEBAR_TRAFFIC_LIGHT_WIDTH } from "./titlebar-overlay";
|
|
15
|
+
import {
|
|
16
|
+
createDoubleEscapeCloseState,
|
|
17
|
+
recordDoubleEscapeClose,
|
|
18
|
+
resetDoubleEscapeClose,
|
|
19
|
+
} from "../../utils/double-escape-close";
|
|
14
20
|
|
|
15
21
|
interface DetachedPaneShellProps {
|
|
16
22
|
pluginRegistry: PluginRegistry;
|
|
@@ -23,12 +29,13 @@ function stopMouse(event?: { stopPropagation?: () => void; preventDefault?: () =
|
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
export function DetachedPaneShell({ pluginRegistry, desktopWindowBridge }: DetachedPaneShellProps) {
|
|
32
|
+
useThemeColors();
|
|
26
33
|
const dispatch = useAppDispatch();
|
|
27
34
|
const rendererHost = useRendererHost();
|
|
28
35
|
const config = useAppSelector((state) => state.config);
|
|
29
36
|
const paneState = useAppSelector((state) => state.paneState);
|
|
30
|
-
const focusedPaneId = useAppSelector((state) => state.focusedPaneId);
|
|
31
37
|
const inputCaptured = useAppSelector((state) => state.inputCaptured);
|
|
38
|
+
const doubleEscapeCloseRef = useRef(createDoubleEscapeCloseState());
|
|
32
39
|
const [windowFocused, setWindowFocused] = useState(() => (
|
|
33
40
|
typeof document === "undefined" ? true : document.hasFocus()
|
|
34
41
|
));
|
|
@@ -52,12 +59,6 @@ export function DetachedPaneShell({ pluginRegistry, desktopWindowBridge }: Detac
|
|
|
52
59
|
dispatch({ type: "FOCUS_PANE", paneId: desktopWindowBridge.paneId });
|
|
53
60
|
}, [desktopWindowBridge.paneId, dispatch]);
|
|
54
61
|
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
if (windowFocused && focusedPaneId !== desktopWindowBridge.paneId) {
|
|
57
|
-
focusPane();
|
|
58
|
-
}
|
|
59
|
-
}, [desktopWindowBridge.paneId, focusPane, focusedPaneId, windowFocused]);
|
|
60
|
-
|
|
61
62
|
useEffect(() => {
|
|
62
63
|
if (typeof window === "undefined" || typeof document === "undefined") return;
|
|
63
64
|
|
|
@@ -81,6 +82,21 @@ export function DetachedPaneShell({ pluginRegistry, desktopWindowBridge }: Detac
|
|
|
81
82
|
};
|
|
82
83
|
}, [focusPane]);
|
|
83
84
|
|
|
85
|
+
useShortcut((event) => {
|
|
86
|
+
const doubleEscapeState = doubleEscapeCloseRef.current;
|
|
87
|
+
const isEscape = event.name === "escape" || event.name === "esc";
|
|
88
|
+
if (isEscape) {
|
|
89
|
+
if (recordDoubleEscapeClose(doubleEscapeState, desktopWindowBridge.paneId, Date.now())) {
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
event.stopPropagation();
|
|
92
|
+
void desktopWindowBridge.closeDetachedPane?.(desktopWindowBridge.paneId);
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
resetDoubleEscapeClose(doubleEscapeState);
|
|
98
|
+
}, { phase: "before" });
|
|
99
|
+
|
|
84
100
|
useShortcut((event) => {
|
|
85
101
|
if (event.name !== "w" || (!event.ctrl && !event.meta && !event.super)) return;
|
|
86
102
|
if (inputCaptured && event.ctrl && !event.meta && !event.super) return;
|
|
@@ -116,6 +132,9 @@ export function DetachedPaneShell({ pluginRegistry, desktopWindowBridge }: Detac
|
|
|
116
132
|
const bodyHeight = Math.max(1, height - headerHeightRows - footerHeightRows);
|
|
117
133
|
const background = floatingPaneBg(focused);
|
|
118
134
|
const titleBackground = floatingPaneTitleBg(focused);
|
|
135
|
+
const bodyLayoutProps = nativePaneChrome
|
|
136
|
+
? NATIVE_PANE_BODY_LAYOUT_PROPS
|
|
137
|
+
: { height: bodyHeight };
|
|
119
138
|
|
|
120
139
|
return (
|
|
121
140
|
<Box
|
|
@@ -166,7 +185,7 @@ export function DetachedPaneShell({ pluginRegistry, desktopWindowBridge }: Detac
|
|
|
166
185
|
)}
|
|
167
186
|
</Box>
|
|
168
187
|
</Box>
|
|
169
|
-
<Box
|
|
188
|
+
<Box {...bodyLayoutProps} overflow="hidden" backgroundColor={background}>
|
|
170
189
|
<PaneContent
|
|
171
190
|
component={paneDef.component}
|
|
172
191
|
paneId={instance.instanceId}
|
|
@@ -3,7 +3,7 @@ import type { ReactNode } from "react";
|
|
|
3
3
|
import { colors, floatingPaneBg } 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 FloatingPaneWrapperProps {
|
|
9
9
|
paneId?: string;
|
|
@@ -30,6 +30,24 @@ interface FloatingPaneWrapperProps {
|
|
|
30
30
|
children: ReactNode;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
function TerminalFloatingPaneBorder({ width, height }: { width: number; height: number }) {
|
|
34
|
+
const borderWidth = Math.max(0, Math.floor(width));
|
|
35
|
+
const borderHeight = Math.max(0, Math.floor(height));
|
|
36
|
+
const bodyHeight = Math.max(0, borderHeight - 2);
|
|
37
|
+
if (borderWidth < 2 || bodyHeight <= 0) return null;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
<Box position="absolute" top={1} left={0} width={1} height={bodyHeight}>
|
|
42
|
+
<Text fg={colors.border} selectable={false}>{"│".repeat(bodyHeight)}</Text>
|
|
43
|
+
</Box>
|
|
44
|
+
<Box position="absolute" top={1} left={borderWidth - 1} width={1} height={bodyHeight}>
|
|
45
|
+
<Text fg={colors.border} selectable={false}>{"│".repeat(bodyHeight)}</Text>
|
|
46
|
+
</Box>
|
|
47
|
+
</>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
33
51
|
/** Pure visual wrapper; Shell owns the interaction state and supplies handlers. */
|
|
34
52
|
export function FloatingPaneWrapper({
|
|
35
53
|
paneId,
|
|
@@ -60,6 +78,9 @@ export function FloatingPaneWrapper({
|
|
|
60
78
|
const showFooter = hasPaneFooterContent(footer);
|
|
61
79
|
const reserveFooter = shouldReservePaneFooter(nativePaneChrome, showFooter);
|
|
62
80
|
const bodyHeight = getPaneBodyHeight(height, reserveFooter);
|
|
81
|
+
const bodyLayoutProps = nativePaneChrome
|
|
82
|
+
? NATIVE_PANE_BODY_LAYOUT_PROPS
|
|
83
|
+
: { height: bodyHeight };
|
|
63
84
|
|
|
64
85
|
return (
|
|
65
86
|
<Box
|
|
@@ -97,11 +118,21 @@ export function FloatingPaneWrapper({
|
|
|
97
118
|
/>
|
|
98
119
|
|
|
99
120
|
{/* Content */}
|
|
100
|
-
<Box
|
|
121
|
+
<Box {...bodyLayoutProps} overflow="hidden" backgroundColor={bg}>
|
|
101
122
|
{children}
|
|
102
123
|
</Box>
|
|
103
124
|
|
|
104
|
-
{reserveFooter &&
|
|
125
|
+
{reserveFooter && (
|
|
126
|
+
<PaneFooterBar
|
|
127
|
+
footer={footer}
|
|
128
|
+
focused={focused}
|
|
129
|
+
width={width}
|
|
130
|
+
reserveRight={2}
|
|
131
|
+
showBorder={!nativePaneChrome && !focused}
|
|
132
|
+
/>
|
|
133
|
+
)}
|
|
134
|
+
|
|
135
|
+
{!nativePaneChrome && !focused && <TerminalFloatingPaneBorder width={width} height={height} />}
|
|
105
136
|
|
|
106
137
|
{nativePaneChrome ? (
|
|
107
138
|
<Box
|
|
@@ -117,7 +148,7 @@ export function FloatingPaneWrapper({
|
|
|
117
148
|
/>
|
|
118
149
|
) : (
|
|
119
150
|
<Box position="absolute" bottom={0} right={0} width={2} height={1}>
|
|
120
|
-
<Text fg={focused ? colors.borderFocused : colors.
|
|
151
|
+
<Text fg={focused ? colors.borderFocused : colors.border} selectable={false}>{"─◢"}</Text>
|
|
121
152
|
</Box>
|
|
122
153
|
)}
|
|
123
154
|
</Box>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Box, SpinnerMark, Text, TextAttributes, useUiCapabilities } from "../../ui";
|
|
2
2
|
import { useEffect, type ReactNode } from "react";
|
|
3
|
-
import { colors, priceColor } from "../../theme/colors";
|
|
3
|
+
import { blendHex, colors, priceColor } from "../../theme/colors";
|
|
4
|
+
import { useThemeColors } from "../../theme/theme-context";
|
|
4
5
|
import { useAppActive } from "../../state/app-activity";
|
|
5
6
|
import { useAppDispatch, useAppSelector } from "../../state/app-context";
|
|
6
7
|
import {
|
|
@@ -23,8 +24,8 @@ const UPDATE_NOTICE_DURATION_MS = 5_000;
|
|
|
23
24
|
|
|
24
25
|
function DesktopHeaderPill({
|
|
25
26
|
children,
|
|
26
|
-
backgroundColor =
|
|
27
|
-
borderColor =
|
|
27
|
+
backgroundColor = blendHex(colors.header, colors.bg, 0.28),
|
|
28
|
+
borderColor = blendHex(colors.border, colors.headerText, 0.22),
|
|
28
29
|
}: {
|
|
29
30
|
children: ReactNode;
|
|
30
31
|
backgroundColor?: string;
|
|
@@ -121,6 +122,7 @@ function UpdateStatus() {
|
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
export function Header() {
|
|
125
|
+
useThemeColors();
|
|
124
126
|
const baseCurrency = useAppSelector(selectBaseCurrency);
|
|
125
127
|
const appActive = useAppActive();
|
|
126
128
|
const { titleBarOverlay } = useUiCapabilities();
|
|
@@ -173,8 +175,8 @@ export function Header() {
|
|
|
173
175
|
Gloomberb
|
|
174
176
|
</Text>
|
|
175
177
|
<DesktopHeaderPill
|
|
176
|
-
backgroundColor=
|
|
177
|
-
borderColor=
|
|
178
|
+
backgroundColor={blendHex(colors.header, colors.headerText, 0.1)}
|
|
179
|
+
borderColor={blendHex(colors.border, colors.headerText, 0.28)}
|
|
178
180
|
>
|
|
179
181
|
<Text fg={colors.headerText} style={{ fontSize: 11 }}>v{VERSION}</Text>
|
|
180
182
|
</DesktopHeaderPill>
|
|
@@ -184,8 +186,11 @@ export function Header() {
|
|
|
184
186
|
</Box>
|
|
185
187
|
{mktLabel ? (
|
|
186
188
|
<Box paddingRight={1}>
|
|
187
|
-
<DesktopHeaderPill
|
|
188
|
-
|
|
189
|
+
<DesktopHeaderPill
|
|
190
|
+
backgroundColor={blendHex(colors.header, mktColor, 0.12)}
|
|
191
|
+
borderColor={blendHex(colors.border, mktColor, 0.3)}
|
|
192
|
+
>
|
|
193
|
+
<Text fg={mktColor} style={{ fontSize: 11, fontWeight: 700 }}>{mktLabel}</Text>
|
|
189
194
|
</DesktopHeaderPill>
|
|
190
195
|
</Box>
|
|
191
196
|
) : null}
|