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,5 +1,5 @@
|
|
|
1
1
|
import { Box, Input, ScrollBox, Text, Textarea, useUiCapabilities } from "../../ui";
|
|
2
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import type { RefObject } from "react";
|
|
4
4
|
import { TextAttributes, type InputRenderable, type ScrollBoxRenderable, type TextareaRenderable } from "../../ui";
|
|
5
5
|
import { useNativeRenderer } from "../../ui";
|
|
@@ -8,7 +8,8 @@ import { Button, NumberField, Spinner, TextField } from "../ui";
|
|
|
8
8
|
import { NativeSelect, openNativeSelect, type NativeSelectElement } from "../ui/native-select";
|
|
9
9
|
import { ToggleList } from "../toggle-list";
|
|
10
10
|
import {
|
|
11
|
-
|
|
11
|
+
applyTheme,
|
|
12
|
+
clearTransientThemePreview,
|
|
12
13
|
commandBarBg,
|
|
13
14
|
commandBarHeadingText,
|
|
14
15
|
commandBarHoverBg,
|
|
@@ -18,9 +19,10 @@ import {
|
|
|
18
19
|
commandBarSelectedText,
|
|
19
20
|
commandBarSubtleText,
|
|
20
21
|
commandBarText,
|
|
22
|
+
getCurrentThemeId,
|
|
23
|
+
previewTheme,
|
|
21
24
|
} from "../../theme/colors";
|
|
22
25
|
import {
|
|
23
|
-
getEffectiveThemeId,
|
|
24
26
|
getFocusedCollectionId,
|
|
25
27
|
useAppDispatch,
|
|
26
28
|
useAppSelector,
|
|
@@ -28,8 +30,9 @@ import {
|
|
|
28
30
|
type AppState,
|
|
29
31
|
} from "../../state/app-context";
|
|
30
32
|
import { fuzzyFilter } from "../../utils/fuzzy-search";
|
|
31
|
-
import { commands,
|
|
32
|
-
import {
|
|
33
|
+
import { commands, matchPrefix, type Command } from "./command-registry";
|
|
34
|
+
import { useThemeColors } from "../../theme/theme-context";
|
|
35
|
+
import { ThemePicker, type ThemePickerHandle } from "./theme-picker";
|
|
33
36
|
import { exportConfig, importConfig, resetAllData, saveConfig } from "../../data/config-store";
|
|
34
37
|
import type { DataProvider } from "../../types/data-provider";
|
|
35
38
|
import type { TickerRepository } from "../../data/ticker-repository";
|
|
@@ -71,12 +74,20 @@ import {
|
|
|
71
74
|
import { notifyGridlockComplete } from "../../plugins/gridlock-notification";
|
|
72
75
|
import { CHART_RENDERER_PREFERENCES } from "../chart/chart-types";
|
|
73
76
|
import {
|
|
74
|
-
buildSections,
|
|
75
77
|
getEmptyState,
|
|
76
78
|
getRowPresentation,
|
|
77
79
|
resolveCommandBarMode,
|
|
78
80
|
truncateText,
|
|
79
81
|
} from "./view-model";
|
|
82
|
+
import {
|
|
83
|
+
buildListRows,
|
|
84
|
+
buildNativeListRows,
|
|
85
|
+
orderListResults,
|
|
86
|
+
type CommandBarListRow,
|
|
87
|
+
type ListScreenState,
|
|
88
|
+
type ResultItem,
|
|
89
|
+
} from "./list-model";
|
|
90
|
+
import { CommandBarConfirmBody } from "./confirm-body";
|
|
80
91
|
import {
|
|
81
92
|
createLocalTickerSearchCandidates,
|
|
82
93
|
normalizeTickerInput,
|
|
@@ -156,55 +167,23 @@ interface CommandBarProps {
|
|
|
156
167
|
onCheckForUpdates?: () => void | Promise<void>;
|
|
157
168
|
}
|
|
158
169
|
|
|
159
|
-
interface ResultItem {
|
|
160
|
-
id: string;
|
|
161
|
-
label: string;
|
|
162
|
-
detail: string;
|
|
163
|
-
category: string;
|
|
164
|
-
kind: "command" | "ticker" | "search" | "theme" | "plugin" | "action" | "info";
|
|
165
|
-
right?: string;
|
|
166
|
-
searchText?: string;
|
|
167
|
-
themeId?: string;
|
|
168
|
-
pluginToggle?: () => void | Promise<void>;
|
|
169
|
-
secondaryAction?: () => void | Promise<void>;
|
|
170
|
-
checked?: boolean;
|
|
171
|
-
current?: boolean;
|
|
172
|
-
disabled?: boolean;
|
|
173
|
-
action: () => void | Promise<void>;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
type ListScreenKind = "root" | "mode" | "picker" | "pane-settings";
|
|
177
|
-
|
|
178
|
-
interface ListScreenState {
|
|
179
|
-
kind: ListScreenKind;
|
|
180
|
-
title: string;
|
|
181
|
-
subtitle?: string;
|
|
182
|
-
query: string;
|
|
183
|
-
selectedIdx: number;
|
|
184
|
-
hoveredIdx: number | null;
|
|
185
|
-
results: ResultItem[];
|
|
186
|
-
searching: boolean;
|
|
187
|
-
emptyLabel: string;
|
|
188
|
-
emptyDetail: string;
|
|
189
|
-
footerLeft: string;
|
|
190
|
-
footerRight: string;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
170
|
type WorkflowStringValues = Record<string, string>;
|
|
194
171
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
172
|
+
const LAYOUT_MOVE_ACTIONS = [
|
|
173
|
+
{ id: "layout-move-left", label: "Move Left", direction: "left", unavailableDetail: "No column to the left" },
|
|
174
|
+
{ id: "layout-move-right", label: "Move Right", direction: "right", unavailableDetail: "No column to the right" },
|
|
175
|
+
{ id: "layout-move-up", label: "Move Up", direction: "above", unavailableDetail: "No pane above" },
|
|
176
|
+
{ id: "layout-move-down", label: "Move Down", direction: "below", unavailableDetail: "No pane below" },
|
|
177
|
+
] as const;
|
|
178
|
+
|
|
179
|
+
type LayoutMoveDirection = (typeof LAYOUT_MOVE_ACTIONS)[number]["direction"];
|
|
202
180
|
|
|
203
181
|
const commandBarLog = debugLog.createLogger("command-bar");
|
|
204
182
|
const COMMAND_BAR_OVERLAY_Z_INDEX = 2_147_483_646;
|
|
205
183
|
const COMMAND_BAR_PANEL_Z_INDEX = 2_147_483_647;
|
|
206
184
|
const NATIVE_COMMAND_BAR_PADDING_X_PX = 14;
|
|
207
185
|
const NATIVE_COMMAND_BAR_PADDING_Y_PX = 14;
|
|
186
|
+
const QUICK_LOOK_TICKER_SEARCH_OPTIONS = { includeOptionContracts: false } as const;
|
|
208
187
|
|
|
209
188
|
function getDefaultConfigBackupPath(): string {
|
|
210
189
|
const home = typeof process !== "undefined" ? process.env.HOME : undefined;
|
|
@@ -286,40 +265,6 @@ function estimateWorkflowBodyRows(route: CommandBarWorkflowRoute): number {
|
|
|
286
265
|
return introRows + fieldRows + statusRows + 1;
|
|
287
266
|
}
|
|
288
267
|
|
|
289
|
-
function orderListResults(results: ResultItem[]): ResultItem[] {
|
|
290
|
-
return buildSections(results).flatMap((section) => section.items);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function buildListRows(listState: ListScreenState): CommandBarListRow[] {
|
|
294
|
-
const rows: CommandBarListRow[] = [];
|
|
295
|
-
const sections = buildSections(listState.results);
|
|
296
|
-
let globalIdx = 0;
|
|
297
|
-
sections.forEach((section, sectionIndex) => {
|
|
298
|
-
if (sectionIndex > 0) {
|
|
299
|
-
rows.push({ kind: "spacer", id: `spacer:${sectionIndex}:${section.category}` });
|
|
300
|
-
}
|
|
301
|
-
rows.push({ kind: "heading", id: `heading:${sectionIndex}:${section.category}`, label: section.category });
|
|
302
|
-
for (const item of section.items) {
|
|
303
|
-
rows.push({ kind: "item", item, globalIdx });
|
|
304
|
-
globalIdx += 1;
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
return rows;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function buildNativeListRows(listState: ListScreenState, rows: CommandBarListRow[]): CommandBarListRow[] {
|
|
311
|
-
if (listState.searching && rows.length === 0) {
|
|
312
|
-
return [{ kind: "spinner", id: "searching", label: "Searching…" }];
|
|
313
|
-
}
|
|
314
|
-
if (rows.length === 0) {
|
|
315
|
-
return [{ kind: "message", id: "empty", label: listState.emptyLabel }];
|
|
316
|
-
}
|
|
317
|
-
if (listState.searching) {
|
|
318
|
-
return [...rows, { kind: "spinner", id: "searching", label: "Searching…" }];
|
|
319
|
-
}
|
|
320
|
-
return rows;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
268
|
function clampListIndex(index: number, length: number): number {
|
|
324
269
|
return Math.max(0, Math.min(index, Math.max(0, length - 1)));
|
|
325
270
|
}
|
|
@@ -356,6 +301,309 @@ function getVisibleMultiSelectPickerOptions(
|
|
|
356
301
|
});
|
|
357
302
|
}
|
|
358
303
|
|
|
304
|
+
type CommandBarListScrollEvent = {
|
|
305
|
+
stopPropagation: () => void;
|
|
306
|
+
preventDefault: () => void;
|
|
307
|
+
scroll?: { direction?: string; delta?: number };
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
interface CommandBarListHeaderProps {
|
|
311
|
+
kind: ListScreenState["kind"];
|
|
312
|
+
query: string;
|
|
313
|
+
queryDisplayWidth: number;
|
|
314
|
+
nativePaneChrome: boolean;
|
|
315
|
+
inputBg: string;
|
|
316
|
+
paletteBg: string;
|
|
317
|
+
paletteText: string;
|
|
318
|
+
paletteSubtleText: string;
|
|
319
|
+
cursorColor: string;
|
|
320
|
+
contentPadding: number;
|
|
321
|
+
rootGhostSuffix: string | null;
|
|
322
|
+
rootQueryLength: number;
|
|
323
|
+
rootShortcutFeedback: string | null;
|
|
324
|
+
onQueryChange: (query: string) => void;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const CommandBarListHeader = memo(function CommandBarListHeader({
|
|
328
|
+
kind,
|
|
329
|
+
query,
|
|
330
|
+
queryDisplayWidth,
|
|
331
|
+
nativePaneChrome,
|
|
332
|
+
inputBg,
|
|
333
|
+
paletteBg,
|
|
334
|
+
paletteText,
|
|
335
|
+
paletteSubtleText,
|
|
336
|
+
cursorColor,
|
|
337
|
+
contentPadding,
|
|
338
|
+
rootGhostSuffix,
|
|
339
|
+
rootQueryLength,
|
|
340
|
+
rootShortcutFeedback,
|
|
341
|
+
onQueryChange,
|
|
342
|
+
}: CommandBarListHeaderProps) {
|
|
343
|
+
return (
|
|
344
|
+
<>
|
|
345
|
+
<Box height={1} paddingX={contentPadding}>
|
|
346
|
+
<Box
|
|
347
|
+
width={queryDisplayWidth}
|
|
348
|
+
height={1}
|
|
349
|
+
position="relative"
|
|
350
|
+
backgroundColor={nativePaneChrome ? undefined : inputBg}
|
|
351
|
+
style={nativePaneChrome ? undefined : {
|
|
352
|
+
overflow: "hidden",
|
|
353
|
+
}}
|
|
354
|
+
>
|
|
355
|
+
<Input
|
|
356
|
+
value={query}
|
|
357
|
+
onInput={onQueryChange}
|
|
358
|
+
onChange={onQueryChange}
|
|
359
|
+
placeholder={kind === "root" ? "Search" : "Filter"}
|
|
360
|
+
focused
|
|
361
|
+
width={nativePaneChrome ? "100%" : queryDisplayWidth}
|
|
362
|
+
backgroundColor={nativePaneChrome ? "transparent" : paletteBg}
|
|
363
|
+
focusedBackgroundColor={nativePaneChrome ? "transparent" : paletteBg}
|
|
364
|
+
textColor={paletteText}
|
|
365
|
+
focusedTextColor={paletteText}
|
|
366
|
+
placeholderColor={paletteSubtleText}
|
|
367
|
+
cursorColor={cursorColor}
|
|
368
|
+
/>
|
|
369
|
+
{kind === "root" && rootGhostSuffix && (
|
|
370
|
+
<Box
|
|
371
|
+
position="absolute"
|
|
372
|
+
top={0}
|
|
373
|
+
left={Math.max(0, Math.min(rootQueryLength, queryDisplayWidth - 1))}
|
|
374
|
+
width={Math.max(0, queryDisplayWidth - Math.min(rootQueryLength, queryDisplayWidth - 1))}
|
|
375
|
+
height={1}
|
|
376
|
+
>
|
|
377
|
+
<Text fg={paletteSubtleText}>
|
|
378
|
+
{truncateText(
|
|
379
|
+
rootGhostSuffix,
|
|
380
|
+
Math.max(0, queryDisplayWidth - Math.min(rootQueryLength, queryDisplayWidth - 1)),
|
|
381
|
+
)}
|
|
382
|
+
</Text>
|
|
383
|
+
</Box>
|
|
384
|
+
)}
|
|
385
|
+
</Box>
|
|
386
|
+
</Box>
|
|
387
|
+
<Box height={1} paddingX={contentPadding}>
|
|
388
|
+
{kind === "root" && rootShortcutFeedback
|
|
389
|
+
? (
|
|
390
|
+
<Text fg={paletteSubtleText}>
|
|
391
|
+
{truncateText(rootShortcutFeedback, queryDisplayWidth)}
|
|
392
|
+
</Text>
|
|
393
|
+
)
|
|
394
|
+
: null}
|
|
395
|
+
</Box>
|
|
396
|
+
</>
|
|
397
|
+
);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
interface CommandBarListItemRowProps {
|
|
401
|
+
item: ResultItem;
|
|
402
|
+
globalIdx: number;
|
|
403
|
+
isSelected: boolean;
|
|
404
|
+
isHovered: boolean;
|
|
405
|
+
contentPadding: number;
|
|
406
|
+
labelWidth: number;
|
|
407
|
+
trailingWidth: number;
|
|
408
|
+
nativePaneChrome: boolean;
|
|
409
|
+
paletteBg: string;
|
|
410
|
+
paletteHoverBg: string;
|
|
411
|
+
paletteSelectedBg: string;
|
|
412
|
+
paletteSelectedText: string;
|
|
413
|
+
paletteSubtleText: string;
|
|
414
|
+
paletteText: string;
|
|
415
|
+
panelBg: string;
|
|
416
|
+
onHoverIndex: (index: number | null) => void;
|
|
417
|
+
onListScroll: (event: CommandBarListScrollEvent) => void;
|
|
418
|
+
onRowMouseDown: (event: any, item: ResultItem, globalIdx: number) => void;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const CommandBarListItemRow = memo(function CommandBarListItemRow({
|
|
422
|
+
item,
|
|
423
|
+
globalIdx,
|
|
424
|
+
isSelected,
|
|
425
|
+
isHovered,
|
|
426
|
+
contentPadding,
|
|
427
|
+
labelWidth,
|
|
428
|
+
trailingWidth,
|
|
429
|
+
nativePaneChrome,
|
|
430
|
+
paletteBg,
|
|
431
|
+
paletteHoverBg,
|
|
432
|
+
paletteSelectedBg,
|
|
433
|
+
paletteSelectedText,
|
|
434
|
+
paletteSubtleText,
|
|
435
|
+
paletteText,
|
|
436
|
+
panelBg,
|
|
437
|
+
onHoverIndex,
|
|
438
|
+
onListScroll,
|
|
439
|
+
onRowMouseDown,
|
|
440
|
+
}: CommandBarListItemRowProps) {
|
|
441
|
+
const presentation = getRowPresentation(item, isSelected, trailingWidth > 0);
|
|
442
|
+
const label = truncateText(presentation.label, labelWidth);
|
|
443
|
+
const trailing = truncateText(presentation.trailing, trailingWidth);
|
|
444
|
+
|
|
445
|
+
return (
|
|
446
|
+
<Box
|
|
447
|
+
key={item.id}
|
|
448
|
+
flexDirection="row"
|
|
449
|
+
height={1}
|
|
450
|
+
paddingX={contentPadding}
|
|
451
|
+
backgroundColor={isSelected
|
|
452
|
+
? paletteSelectedBg
|
|
453
|
+
: isHovered
|
|
454
|
+
? paletteHoverBg
|
|
455
|
+
: (nativePaneChrome ? panelBg : paletteBg)}
|
|
456
|
+
onMouseMove={() => onHoverIndex(globalIdx)}
|
|
457
|
+
onMouseOut={() => onHoverIndex(null)}
|
|
458
|
+
{...(!nativePaneChrome ? { onMouseScroll: onListScroll } : {})}
|
|
459
|
+
onMouseDown={(event: any) => onRowMouseDown(event, item, globalIdx)}
|
|
460
|
+
data-command-bar-row-selected={nativePaneChrome && isSelected ? "true" : undefined}
|
|
461
|
+
style={nativePaneChrome ? { borderRadius: 6 } : undefined}
|
|
462
|
+
>
|
|
463
|
+
<Box width={labelWidth}>
|
|
464
|
+
<Text fg={isSelected ? paletteSelectedText : presentation.primaryMuted ? paletteSubtleText : paletteText}>
|
|
465
|
+
{label}
|
|
466
|
+
</Text>
|
|
467
|
+
</Box>
|
|
468
|
+
<Box width={trailingWidth}>
|
|
469
|
+
<Text fg={isSelected ? paletteSelectedText : paletteSubtleText}>{trailing}</Text>
|
|
470
|
+
</Box>
|
|
471
|
+
</Box>
|
|
472
|
+
);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
interface CommandBarListBodyProps {
|
|
476
|
+
visibleListState: ListScreenState;
|
|
477
|
+
nativeListRows: CommandBarListRow[];
|
|
478
|
+
listBodyHeight: number;
|
|
479
|
+
contentPadding: number;
|
|
480
|
+
labelWidth: number;
|
|
481
|
+
nativePaneChrome: boolean;
|
|
482
|
+
nativeListScrollRef: RefObject<ScrollBoxRenderable | null>;
|
|
483
|
+
paletteBg: string;
|
|
484
|
+
paletteHeadingText: string;
|
|
485
|
+
paletteHoverBg: string;
|
|
486
|
+
paletteSelectedBg: string;
|
|
487
|
+
paletteSelectedText: string;
|
|
488
|
+
paletteSubtleText: string;
|
|
489
|
+
paletteText: string;
|
|
490
|
+
panelBg: string;
|
|
491
|
+
queryDisplayWidth: number;
|
|
492
|
+
trailingWidth: number;
|
|
493
|
+
onHoverIndex: (index: number | null) => void;
|
|
494
|
+
onListScroll: (event: CommandBarListScrollEvent) => void;
|
|
495
|
+
onRowMouseDown: (event: any, item: ResultItem, globalIdx: number) => void;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const CommandBarListBody = memo(function CommandBarListBody({
|
|
499
|
+
visibleListState,
|
|
500
|
+
nativeListRows,
|
|
501
|
+
listBodyHeight,
|
|
502
|
+
contentPadding,
|
|
503
|
+
labelWidth,
|
|
504
|
+
nativePaneChrome,
|
|
505
|
+
nativeListScrollRef,
|
|
506
|
+
paletteBg,
|
|
507
|
+
paletteHeadingText,
|
|
508
|
+
paletteHoverBg,
|
|
509
|
+
paletteSelectedBg,
|
|
510
|
+
paletteSelectedText,
|
|
511
|
+
paletteSubtleText,
|
|
512
|
+
paletteText,
|
|
513
|
+
panelBg,
|
|
514
|
+
queryDisplayWidth,
|
|
515
|
+
trailingWidth,
|
|
516
|
+
onHoverIndex,
|
|
517
|
+
onListScroll,
|
|
518
|
+
onRowMouseDown,
|
|
519
|
+
}: CommandBarListBodyProps) {
|
|
520
|
+
const visibleRows = useMemo(() => {
|
|
521
|
+
const rows = nativeListRows;
|
|
522
|
+
if (nativePaneChrome) return rows;
|
|
523
|
+
const paddedRows = [...rows];
|
|
524
|
+
while (paddedRows.length < listBodyHeight) {
|
|
525
|
+
paddedRows.push({ kind: "filler", id: `filler:${paddedRows.length}` });
|
|
526
|
+
}
|
|
527
|
+
return paddedRows;
|
|
528
|
+
}, [
|
|
529
|
+
listBodyHeight,
|
|
530
|
+
nativeListRows,
|
|
531
|
+
nativePaneChrome,
|
|
532
|
+
]);
|
|
533
|
+
|
|
534
|
+
const renderedRows = (
|
|
535
|
+
<>
|
|
536
|
+
{visibleRows.map((row) => {
|
|
537
|
+
if (row.kind === "filler" || row.kind === "spacer") {
|
|
538
|
+
return <Box key={row.id} height={1} />;
|
|
539
|
+
}
|
|
540
|
+
if (row.kind === "spinner") {
|
|
541
|
+
return (
|
|
542
|
+
<Box key={row.id} height={1} paddingX={contentPadding} {...(!nativePaneChrome ? { onMouseScroll: onListScroll } : {})}>
|
|
543
|
+
<Spinner label={row.label} />
|
|
544
|
+
</Box>
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
if (row.kind === "message") {
|
|
548
|
+
return (
|
|
549
|
+
<Box key={row.id} height={1} paddingX={contentPadding} {...(!nativePaneChrome ? { onMouseScroll: onListScroll } : {})}>
|
|
550
|
+
<Text fg={paletteText}>{truncateText(row.label, queryDisplayWidth)}</Text>
|
|
551
|
+
</Box>
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
if (row.kind === "heading") {
|
|
555
|
+
return (
|
|
556
|
+
<Box key={row.id} height={1} paddingX={contentPadding} {...(!nativePaneChrome ? { onMouseScroll: onListScroll } : {})}>
|
|
557
|
+
<Text attributes={TextAttributes.BOLD} fg={paletteHeadingText}>
|
|
558
|
+
{truncateText(row.label, queryDisplayWidth)}
|
|
559
|
+
</Text>
|
|
560
|
+
</Box>
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const isSelected = row.globalIdx === visibleListState.selectedIdx;
|
|
565
|
+
const isHovered = row.globalIdx === visibleListState.hoveredIdx && !isSelected;
|
|
566
|
+
return (
|
|
567
|
+
<CommandBarListItemRow
|
|
568
|
+
key={row.item.id}
|
|
569
|
+
item={row.item}
|
|
570
|
+
globalIdx={row.globalIdx}
|
|
571
|
+
isSelected={isSelected}
|
|
572
|
+
isHovered={isHovered}
|
|
573
|
+
contentPadding={contentPadding}
|
|
574
|
+
labelWidth={labelWidth}
|
|
575
|
+
trailingWidth={trailingWidth}
|
|
576
|
+
nativePaneChrome={nativePaneChrome}
|
|
577
|
+
paletteBg={paletteBg}
|
|
578
|
+
paletteHoverBg={paletteHoverBg}
|
|
579
|
+
paletteSelectedBg={paletteSelectedBg}
|
|
580
|
+
paletteSelectedText={paletteSelectedText}
|
|
581
|
+
paletteSubtleText={paletteSubtleText}
|
|
582
|
+
paletteText={paletteText}
|
|
583
|
+
panelBg={panelBg}
|
|
584
|
+
onHoverIndex={onHoverIndex}
|
|
585
|
+
onListScroll={onListScroll}
|
|
586
|
+
onRowMouseDown={onRowMouseDown}
|
|
587
|
+
/>
|
|
588
|
+
);
|
|
589
|
+
})}
|
|
590
|
+
</>
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
return (
|
|
594
|
+
<ScrollBox
|
|
595
|
+
ref={nativeListScrollRef}
|
|
596
|
+
flexDirection="column"
|
|
597
|
+
height={listBodyHeight}
|
|
598
|
+
scrollY
|
|
599
|
+
focusable={false}
|
|
600
|
+
{...(!nativePaneChrome ? { onMouseScroll: onListScroll } : {})}
|
|
601
|
+
>
|
|
602
|
+
{renderedRows}
|
|
603
|
+
</ScrollBox>
|
|
604
|
+
);
|
|
605
|
+
});
|
|
606
|
+
|
|
359
607
|
export function CommandBar({
|
|
360
608
|
dataProvider,
|
|
361
609
|
tickerRepository,
|
|
@@ -364,6 +612,7 @@ export function CommandBar({
|
|
|
364
612
|
onCheckForUpdates,
|
|
365
613
|
}: CommandBarProps) {
|
|
366
614
|
const dispatch = useAppDispatch();
|
|
615
|
+
const themeColors = useThemeColors();
|
|
367
616
|
const config = useAppSelector((state) => state.config);
|
|
368
617
|
const paneState = useAppSelector((state) => state.paneState);
|
|
369
618
|
const tickers = useAppSelector((state) => state.tickers);
|
|
@@ -425,6 +674,8 @@ export function CommandBar({
|
|
|
425
674
|
const rootQueryRef = useRef(rootQuery);
|
|
426
675
|
rootQueryRef.current = rootQuery;
|
|
427
676
|
const rootModeInfo = resolveCommandBarMode(rootQuery, availableCommands);
|
|
677
|
+
const rootModeKindRef = useRef(rootModeInfo.kind);
|
|
678
|
+
rootModeKindRef.current = rootModeInfo.kind;
|
|
428
679
|
const [rootSelectedIdx, setRootSelectedIdx] = useState(0);
|
|
429
680
|
const [rootHoveredIdx, setRootHoveredIdx] = useState<number | null>(null);
|
|
430
681
|
const [rootSearching, setRootSearching] = useState(false);
|
|
@@ -449,32 +700,50 @@ export function CommandBar({
|
|
|
449
700
|
const workflowNativeSelectRefs = useRef(new Map<string, NativeSelectElement>());
|
|
450
701
|
const workflowScrollRef = useRef<ScrollBoxRenderable | null>(null);
|
|
451
702
|
const nativeListScrollRef = useRef<ScrollBoxRenderable | null>(null);
|
|
703
|
+
const themePickerRef = useRef<ThemePickerHandle | null>(null);
|
|
452
704
|
const visibleListStateRef = useRef<ListScreenState | null>(null);
|
|
453
705
|
const processedLaunchSequenceRef = useRef<number | null>(null);
|
|
706
|
+
const currentThemePreviewRef = useRef<string | null>(null);
|
|
454
707
|
const currentRoute = routeStack[routeStack.length - 1] ?? null;
|
|
455
708
|
const currentRouteRef = useRef<CommandBarRoute | null>(currentRoute);
|
|
456
709
|
currentRouteRef.current = currentRoute;
|
|
457
710
|
const activeCollectionId = getFocusedCollectionId(state);
|
|
458
711
|
const activePortfolio = state.config.portfolios.find((portfolio) => portfolio.id === activeCollectionId);
|
|
459
712
|
|
|
713
|
+
const applyThemePreview = useCallback((themeId: string | null) => {
|
|
714
|
+
const committedThemeId = stateRef.current.config.theme;
|
|
715
|
+
const preview = themeId && themeId !== committedThemeId ? themeId : null;
|
|
716
|
+
if (preview) {
|
|
717
|
+
previewTheme(preview);
|
|
718
|
+
} else {
|
|
719
|
+
clearTransientThemePreview();
|
|
720
|
+
if (getCurrentThemeId() !== committedThemeId) {
|
|
721
|
+
applyTheme(committedThemeId);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (currentThemePreviewRef.current !== preview) {
|
|
725
|
+
currentThemePreviewRef.current = preview;
|
|
726
|
+
dispatch({ type: "PREVIEW_THEME", theme: preview });
|
|
727
|
+
}
|
|
728
|
+
}, [dispatch]);
|
|
729
|
+
|
|
460
730
|
const clearThemePreview = useCallback((themeId: string | null | undefined) => {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
731
|
+
themePickerRef.current?.cancelPreview();
|
|
732
|
+
const targetThemeId = themeId ?? stateRef.current.config.theme;
|
|
733
|
+
if (!targetThemeId) return;
|
|
734
|
+
clearTransientThemePreview();
|
|
735
|
+
if (getCurrentThemeId() !== targetThemeId) {
|
|
736
|
+
applyTheme(targetThemeId);
|
|
737
|
+
}
|
|
738
|
+
if (currentThemePreviewRef.current !== null) {
|
|
739
|
+
currentThemePreviewRef.current = null;
|
|
740
|
+
dispatch({ type: "PREVIEW_THEME", theme: null });
|
|
464
741
|
}
|
|
465
|
-
dispatch({ type: "PREVIEW_THEME", theme: null });
|
|
466
742
|
}, [dispatch]);
|
|
467
743
|
|
|
468
744
|
const restoreThemePreview = useCallback(() => {
|
|
469
|
-
const themeRoute = [...routeStack].reverse().find((route) => (
|
|
470
|
-
route.kind === "mode" && route.screen === "themes"
|
|
471
|
-
));
|
|
472
|
-
if (themeRoute?.kind === "mode" && themeRoute.themeBaseId) {
|
|
473
|
-
clearThemePreview(themeRoute.themeBaseId);
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
745
|
clearThemePreview(rootThemeBaseIdRef.current);
|
|
477
|
-
}, [clearThemePreview
|
|
746
|
+
}, [clearThemePreview]);
|
|
478
747
|
|
|
479
748
|
const closeAll = useCallback((options?: { revertThemePreview?: boolean }) => {
|
|
480
749
|
if (options?.revertThemePreview !== false) {
|
|
@@ -521,10 +790,6 @@ export function CommandBar({
|
|
|
521
790
|
return;
|
|
522
791
|
}
|
|
523
792
|
|
|
524
|
-
if (currentRoute.kind === "mode" && currentRoute.screen === "themes" && currentRoute.themeBaseId) {
|
|
525
|
-
clearThemePreview(currentRoute.themeBaseId);
|
|
526
|
-
}
|
|
527
|
-
|
|
528
793
|
if (routeStack.length === 1 && currentRoute.restoreMain) {
|
|
529
794
|
setRootQuery(currentRoute.restoreMain.query);
|
|
530
795
|
setRootSelectedIdx(currentRoute.restoreMain.selectedIdx);
|
|
@@ -584,20 +849,22 @@ export function CommandBar({
|
|
|
584
849
|
pluginRegistry.pinTicker(symbol, { floating: true, paneType: "ticker-detail" });
|
|
585
850
|
}, [pluginRegistry]);
|
|
586
851
|
|
|
587
|
-
const
|
|
588
|
-
|
|
852
|
+
const commitTheme = useCallback((themeId: string) => {
|
|
853
|
+
themePickerRef.current?.cancelPreview();
|
|
854
|
+
clearTransientThemePreview();
|
|
589
855
|
if (getCurrentThemeId() !== themeId) {
|
|
590
856
|
applyTheme(themeId);
|
|
591
857
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
dispatch({ type: "PREVIEW_THEME", theme: previewTheme });
|
|
595
|
-
}
|
|
858
|
+
currentThemePreviewRef.current = null;
|
|
859
|
+
dispatch({ type: "SET_THEME", theme: themeId });
|
|
596
860
|
}, [dispatch]);
|
|
597
861
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
862
|
+
useEffect(() => {
|
|
863
|
+
return () => {
|
|
864
|
+
themePickerRef.current?.cancelPreview();
|
|
865
|
+
clearTransientThemePreview();
|
|
866
|
+
};
|
|
867
|
+
}, []);
|
|
601
868
|
|
|
602
869
|
const focusTicker = useCallback((symbol: string, options?: { forceNewPane?: boolean }) => {
|
|
603
870
|
const currentState = stateRef.current;
|
|
@@ -650,7 +917,7 @@ export function CommandBar({
|
|
|
650
917
|
}, [dispatch, persistLayoutChange, pluginRegistry]);
|
|
651
918
|
|
|
652
919
|
const openModeRoute = useCallback((
|
|
653
|
-
screen: "ticker-search" | "
|
|
920
|
+
screen: "ticker-search" | "plugins" | "layout",
|
|
654
921
|
initialQuery = "",
|
|
655
922
|
payload?: Record<string, unknown>,
|
|
656
923
|
) => {
|
|
@@ -663,7 +930,6 @@ export function CommandBar({
|
|
|
663
930
|
query: initialQuery,
|
|
664
931
|
selectedIdx: 0,
|
|
665
932
|
hoveredIdx: null,
|
|
666
|
-
themeBaseId: screen === "themes" ? stateRef.current.config.theme : undefined,
|
|
667
933
|
payload,
|
|
668
934
|
});
|
|
669
935
|
}, [pushRoute]);
|
|
@@ -1220,6 +1486,306 @@ export function CommandBar({
|
|
|
1220
1486
|
});
|
|
1221
1487
|
}, [openConfirmRoute]);
|
|
1222
1488
|
|
|
1489
|
+
const buildPluginItems = useCallback((query: string): ResultItem[] => {
|
|
1490
|
+
const disabledPlugins = state.config.disabledPlugins || [];
|
|
1491
|
+
const toggleable = [...pluginRegistry.allPlugins.values()].filter((plugin) => plugin.toggleable);
|
|
1492
|
+
const filtered = query
|
|
1493
|
+
? toggleable.filter((plugin) => (
|
|
1494
|
+
plugin.name.toLowerCase().includes(query.toLowerCase())
|
|
1495
|
+
|| plugin.id.includes(query.toLowerCase())
|
|
1496
|
+
))
|
|
1497
|
+
: toggleable;
|
|
1498
|
+
return filtered.map((plugin): ResultItem => {
|
|
1499
|
+
const enabled = !disabledPlugins.includes(plugin.id);
|
|
1500
|
+
const toggleAction = () => {
|
|
1501
|
+
dispatch({ type: "TOGGLE_PLUGIN", pluginId: plugin.id });
|
|
1502
|
+
const nextDisabled = enabled
|
|
1503
|
+
? [...disabledPlugins, plugin.id]
|
|
1504
|
+
: disabledPlugins.filter((entry) => entry !== plugin.id);
|
|
1505
|
+
if (enabled) {
|
|
1506
|
+
for (const paneId of pluginRegistry.getPluginPaneIds(plugin.id)) {
|
|
1507
|
+
pluginRegistry.hidePane(paneId);
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
void saveConfig({ ...state.config, disabledPlugins: nextDisabled });
|
|
1511
|
+
};
|
|
1512
|
+
return {
|
|
1513
|
+
id: `plugin:${plugin.id}`,
|
|
1514
|
+
label: plugin.name,
|
|
1515
|
+
detail: plugin.description || "",
|
|
1516
|
+
category: "Plugins",
|
|
1517
|
+
kind: "plugin",
|
|
1518
|
+
checked: enabled,
|
|
1519
|
+
pluginToggle: toggleAction,
|
|
1520
|
+
action: toggleAction,
|
|
1521
|
+
};
|
|
1522
|
+
});
|
|
1523
|
+
}, [dispatch, pluginRegistry, state.config]);
|
|
1524
|
+
|
|
1525
|
+
const buildLayoutItems = useCallback((
|
|
1526
|
+
query: string,
|
|
1527
|
+
options?: { confirmDangerousActions?: boolean },
|
|
1528
|
+
): ResultItem[] => {
|
|
1529
|
+
const currentLayout = state.config.layout;
|
|
1530
|
+
const focusedPane = state.focusedPaneId ? findPaneInstance(currentLayout, state.focusedPaneId) : null;
|
|
1531
|
+
const focusedPaneDef = focusedPane ? pluginRegistry.panes.get(focusedPane.paneId) : null;
|
|
1532
|
+
const dockedPaneIds = getDockedPaneIds(currentLayout);
|
|
1533
|
+
const layoutHistory = state.layoutHistory[state.config.activeLayoutIndex];
|
|
1534
|
+
const layoutSnapshot = JSON.stringify(currentLayout);
|
|
1535
|
+
const canMove = (direction: LayoutMoveDirection) => (
|
|
1536
|
+
!!focusedPane && JSON.stringify(movePaneRelative(currentLayout, focusedPane.instanceId, direction)) !== layoutSnapshot
|
|
1537
|
+
);
|
|
1538
|
+
const layoutItems: ResultItem[] = [];
|
|
1539
|
+
|
|
1540
|
+
if (focusedPane && focusedPaneDef) {
|
|
1541
|
+
const focusedFloating = currentLayout.floating.find((entry) => entry.instanceId === focusedPane.instanceId);
|
|
1542
|
+
layoutItems.push({
|
|
1543
|
+
id: "layout-toggle-mode",
|
|
1544
|
+
label: focusedFloating ? "Dock Pane" : "Float Pane",
|
|
1545
|
+
detail: focusedFloating ? "Return the focused window to the layout" : "Detach the focused pane into a floating window",
|
|
1546
|
+
category: "Focused Pane",
|
|
1547
|
+
kind: "action",
|
|
1548
|
+
action: () => {
|
|
1549
|
+
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
1550
|
+
const nextLayout = focusedFloating
|
|
1551
|
+
? dockPane(currentLayout, focusedPane.instanceId)
|
|
1552
|
+
: floatPane(currentLayout, focusedPane.instanceId, width, height, focusedPaneDef);
|
|
1553
|
+
persistLayoutChange(nextLayout);
|
|
1554
|
+
closeAll({ revertThemePreview: false });
|
|
1555
|
+
},
|
|
1556
|
+
});
|
|
1557
|
+
|
|
1558
|
+
LAYOUT_MOVE_ACTIONS.forEach((entry) => {
|
|
1559
|
+
const available = canMove(entry.direction);
|
|
1560
|
+
layoutItems.push({
|
|
1561
|
+
id: entry.id,
|
|
1562
|
+
label: entry.label,
|
|
1563
|
+
detail: available ? "Reposition the focused pane" : entry.unavailableDetail,
|
|
1564
|
+
category: "Focused Pane",
|
|
1565
|
+
kind: available ? "action" : "info",
|
|
1566
|
+
disabled: !available,
|
|
1567
|
+
action: available
|
|
1568
|
+
? () => {
|
|
1569
|
+
persistLayoutChange(movePaneRelative(currentLayout, focusedPane.instanceId, entry.direction));
|
|
1570
|
+
closeAll({ revertThemePreview: false });
|
|
1571
|
+
}
|
|
1572
|
+
: () => {},
|
|
1573
|
+
});
|
|
1574
|
+
});
|
|
1575
|
+
|
|
1576
|
+
layoutItems.push({
|
|
1577
|
+
id: "layout-swap",
|
|
1578
|
+
label: "Swap With…",
|
|
1579
|
+
detail: dockedPaneIds.length + currentLayout.floating.length > 1
|
|
1580
|
+
? "Choose another pane to swap positions"
|
|
1581
|
+
: "Need at least two panes",
|
|
1582
|
+
category: "Focused Pane",
|
|
1583
|
+
kind: "action",
|
|
1584
|
+
disabled: dockedPaneIds.length + currentLayout.floating.length <= 1,
|
|
1585
|
+
action: () => {
|
|
1586
|
+
const pickerOptions = [
|
|
1587
|
+
...dockedPaneIds,
|
|
1588
|
+
...currentLayout.floating.map((entry) => entry.instanceId),
|
|
1589
|
+
]
|
|
1590
|
+
.filter((paneId) => paneId !== focusedPane.instanceId)
|
|
1591
|
+
.map((paneId) => {
|
|
1592
|
+
const instance = findPaneInstance(currentLayout, paneId)!;
|
|
1593
|
+
const isFloating = currentLayout.floating.some((entry) => entry.instanceId === paneId);
|
|
1594
|
+
return {
|
|
1595
|
+
id: paneId,
|
|
1596
|
+
label: instance.title || pluginRegistry.panes.get(instance.paneId)?.name || instance.paneId,
|
|
1597
|
+
detail: isFloating ? "Floating window" : "Docked pane",
|
|
1598
|
+
description: isFloating ? "Floating window" : "Docked pane",
|
|
1599
|
+
};
|
|
1600
|
+
});
|
|
1601
|
+
if (pickerOptions.length === 0) return;
|
|
1602
|
+
openPickerRoute({
|
|
1603
|
+
kind: "picker",
|
|
1604
|
+
pickerId: "layout-swap",
|
|
1605
|
+
title: "Swap With…",
|
|
1606
|
+
query: "",
|
|
1607
|
+
selectedIdx: 0,
|
|
1608
|
+
hoveredIdx: null,
|
|
1609
|
+
options: pickerOptions,
|
|
1610
|
+
payload: { sourcePaneId: focusedPane.instanceId },
|
|
1611
|
+
});
|
|
1612
|
+
},
|
|
1613
|
+
});
|
|
1614
|
+
|
|
1615
|
+
layoutItems.push({
|
|
1616
|
+
id: "layout-duplicate",
|
|
1617
|
+
label: "Duplicate Pane",
|
|
1618
|
+
detail: "Create another instance next to the focused pane",
|
|
1619
|
+
category: "Focused Pane",
|
|
1620
|
+
kind: "action",
|
|
1621
|
+
action: () => {
|
|
1622
|
+
duplicatePane(focusedPane.instanceId);
|
|
1623
|
+
closeAll({ revertThemePreview: false });
|
|
1624
|
+
},
|
|
1625
|
+
});
|
|
1626
|
+
|
|
1627
|
+
layoutItems.push({
|
|
1628
|
+
id: "layout-close-pane",
|
|
1629
|
+
label: "Close Pane",
|
|
1630
|
+
detail: "Remove the focused pane from the layout",
|
|
1631
|
+
category: "Focused Pane",
|
|
1632
|
+
kind: "action",
|
|
1633
|
+
action: options?.confirmDangerousActions
|
|
1634
|
+
? () => {
|
|
1635
|
+
openInlineConfirm({
|
|
1636
|
+
confirmId: "layout-close-pane",
|
|
1637
|
+
title: "Close Pane",
|
|
1638
|
+
body: [`Close "${focusedPane.title || focusedPaneDef.name || focusedPane.instanceId}"?`],
|
|
1639
|
+
confirmLabel: "Close Pane",
|
|
1640
|
+
cancelLabel: "Back",
|
|
1641
|
+
tone: "danger",
|
|
1642
|
+
onConfirm: () => {
|
|
1643
|
+
persistLayoutChange(removePane(currentLayout, focusedPane.instanceId));
|
|
1644
|
+
},
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
: () => {
|
|
1648
|
+
persistLayoutChange(removePane(currentLayout, focusedPane.instanceId));
|
|
1649
|
+
closeAll({ revertThemePreview: false });
|
|
1650
|
+
},
|
|
1651
|
+
});
|
|
1652
|
+
} else {
|
|
1653
|
+
layoutItems.push({
|
|
1654
|
+
id: "layout-no-focused-pane",
|
|
1655
|
+
label: "No focused pane",
|
|
1656
|
+
detail: "Focus a pane to show pane-specific layout actions",
|
|
1657
|
+
category: "Focused Pane",
|
|
1658
|
+
kind: "info",
|
|
1659
|
+
action: () => {},
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
layoutItems.push({
|
|
1664
|
+
id: "layout-undo",
|
|
1665
|
+
label: "Undo Layout Change",
|
|
1666
|
+
detail: (layoutHistory?.past.length ?? 0) > 0 ? "Restore the previous layout state" : "No previous layout state",
|
|
1667
|
+
category: "Current Layout",
|
|
1668
|
+
kind: "action",
|
|
1669
|
+
disabled: (layoutHistory?.past.length ?? 0) === 0,
|
|
1670
|
+
action: () => {
|
|
1671
|
+
if ((layoutHistory?.past.length ?? 0) === 0) return;
|
|
1672
|
+
dispatch({ type: "UNDO_LAYOUT" });
|
|
1673
|
+
closeAll({ revertThemePreview: false });
|
|
1674
|
+
},
|
|
1675
|
+
});
|
|
1676
|
+
layoutItems.push({
|
|
1677
|
+
id: "layout-redo",
|
|
1678
|
+
label: "Redo Layout Change",
|
|
1679
|
+
detail: (layoutHistory?.future.length ?? 0) > 0 ? "Reapply the next layout state" : "No later layout state",
|
|
1680
|
+
category: "Current Layout",
|
|
1681
|
+
kind: "action",
|
|
1682
|
+
disabled: (layoutHistory?.future.length ?? 0) === 0,
|
|
1683
|
+
action: () => {
|
|
1684
|
+
if ((layoutHistory?.future.length ?? 0) === 0) return;
|
|
1685
|
+
dispatch({ type: "REDO_LAYOUT" });
|
|
1686
|
+
closeAll({ revertThemePreview: false });
|
|
1687
|
+
},
|
|
1688
|
+
});
|
|
1689
|
+
layoutItems.push({
|
|
1690
|
+
id: "layout-reset",
|
|
1691
|
+
label: "Reset Current Layout",
|
|
1692
|
+
detail: "Restore the default two-pane layout",
|
|
1693
|
+
category: "Current Layout",
|
|
1694
|
+
kind: "action",
|
|
1695
|
+
action: options?.confirmDangerousActions
|
|
1696
|
+
? () => {
|
|
1697
|
+
openInlineConfirm({
|
|
1698
|
+
confirmId: "layout-reset",
|
|
1699
|
+
title: "Reset Current Layout",
|
|
1700
|
+
body: ["Reset the current layout to the default two-pane arrangement?"],
|
|
1701
|
+
confirmLabel: "Reset Layout",
|
|
1702
|
+
cancelLabel: "Back",
|
|
1703
|
+
tone: "danger",
|
|
1704
|
+
onConfirm: () => {
|
|
1705
|
+
persistLayoutChange(cloneLayout(DEFAULT_LAYOUT));
|
|
1706
|
+
},
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
: () => {
|
|
1710
|
+
persistLayoutChange(cloneLayout(DEFAULT_LAYOUT));
|
|
1711
|
+
closeAll({ revertThemePreview: false });
|
|
1712
|
+
},
|
|
1713
|
+
});
|
|
1714
|
+
layoutItems.push({
|
|
1715
|
+
id: "layout-gridlock",
|
|
1716
|
+
label: "Gridlock All Windows",
|
|
1717
|
+
detail: currentLayout.floating.length > 0
|
|
1718
|
+
? "Infer a tiled layout from the current window positions"
|
|
1719
|
+
: "Retile all panes from their current arrangement",
|
|
1720
|
+
category: "Current Layout",
|
|
1721
|
+
kind: "action",
|
|
1722
|
+
action: () => {
|
|
1723
|
+
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
1724
|
+
persistLayoutChange(gridlockAllPanes(currentLayout, { x: 0, y: 0, width, height }));
|
|
1725
|
+
notifyGridlockRevert();
|
|
1726
|
+
closeAll({ revertThemePreview: false });
|
|
1727
|
+
},
|
|
1728
|
+
});
|
|
1729
|
+
layoutItems.push({
|
|
1730
|
+
id: "layout-rename",
|
|
1731
|
+
label: "Rename Layout",
|
|
1732
|
+
detail: "Change the current saved layout name",
|
|
1733
|
+
category: "Current Layout",
|
|
1734
|
+
kind: "action",
|
|
1735
|
+
action: () => openBuiltInWorkflow("rename-layout"),
|
|
1736
|
+
});
|
|
1737
|
+
layoutItems.push({
|
|
1738
|
+
id: "layout-duplicate-layout",
|
|
1739
|
+
label: "Duplicate Layout",
|
|
1740
|
+
detail: "Create a copy of the current layout",
|
|
1741
|
+
category: "Current Layout",
|
|
1742
|
+
kind: "action",
|
|
1743
|
+
action: () => {
|
|
1744
|
+
dispatch({ type: "DUPLICATE_LAYOUT", index: state.config.activeLayoutIndex });
|
|
1745
|
+
closeAll({ revertThemePreview: false });
|
|
1746
|
+
},
|
|
1747
|
+
});
|
|
1748
|
+
layoutItems.push({
|
|
1749
|
+
id: "layout-new",
|
|
1750
|
+
label: "New Layout",
|
|
1751
|
+
detail: "Create a fresh saved layout",
|
|
1752
|
+
category: "Current Layout",
|
|
1753
|
+
kind: "action",
|
|
1754
|
+
action: () => openBuiltInWorkflow("new-layout"),
|
|
1755
|
+
});
|
|
1756
|
+
|
|
1757
|
+
state.config.layouts.forEach((savedLayout, index) => {
|
|
1758
|
+
layoutItems.push({
|
|
1759
|
+
id: `layout-switch:${index}`,
|
|
1760
|
+
label: savedLayout.name,
|
|
1761
|
+
detail: index === state.config.activeLayoutIndex ? "Current layout" : "Switch to this saved layout",
|
|
1762
|
+
right: getLayoutPreview(savedLayout.layout),
|
|
1763
|
+
category: "Saved Layouts",
|
|
1764
|
+
kind: "action",
|
|
1765
|
+
current: index === state.config.activeLayoutIndex,
|
|
1766
|
+
action: () => {
|
|
1767
|
+
dispatch({ type: "SWITCH_LAYOUT", index });
|
|
1768
|
+
closeAll({ revertThemePreview: false });
|
|
1769
|
+
},
|
|
1770
|
+
});
|
|
1771
|
+
});
|
|
1772
|
+
|
|
1773
|
+
return query
|
|
1774
|
+
? fuzzyFilter(layoutItems, query, (item) => `${item.label} ${item.detail} ${item.right || ""}`)
|
|
1775
|
+
: layoutItems;
|
|
1776
|
+
}, [
|
|
1777
|
+
closeAll,
|
|
1778
|
+
dispatch,
|
|
1779
|
+
duplicatePane,
|
|
1780
|
+
notifyGridlockRevert,
|
|
1781
|
+
openBuiltInWorkflow,
|
|
1782
|
+
openInlineConfirm,
|
|
1783
|
+
openPickerRoute,
|
|
1784
|
+
persistLayoutChange,
|
|
1785
|
+
pluginRegistry,
|
|
1786
|
+
state,
|
|
1787
|
+
]);
|
|
1788
|
+
|
|
1223
1789
|
const resolvePluginCommandConfirm = useCallback((command: CommandDef) => {
|
|
1224
1790
|
const context = {
|
|
1225
1791
|
config: state.config,
|
|
@@ -1987,10 +2553,15 @@ export function CommandBar({
|
|
|
1987
2553
|
activeCollectionId,
|
|
1988
2554
|
buildSharedWorkflowDeps(),
|
|
1989
2555
|
);
|
|
1990
|
-
|
|
2556
|
+
const createOptions = {
|
|
1991
2557
|
arg: trimmedList,
|
|
1992
2558
|
symbols,
|
|
1993
|
-
}
|
|
2559
|
+
};
|
|
2560
|
+
if (template.canCreate && !template.canCreate(getPaneTemplateContext(), createOptions)) {
|
|
2561
|
+
openPaneTemplateWorkflow(template, { arg: trimmedArg });
|
|
2562
|
+
return;
|
|
2563
|
+
}
|
|
2564
|
+
await openPaneTemplateDirect(template, createOptions);
|
|
1994
2565
|
} catch {
|
|
1995
2566
|
openPaneTemplateWorkflow(template, { arg: trimmedArg });
|
|
1996
2567
|
}
|
|
@@ -2095,20 +2666,24 @@ export function CommandBar({
|
|
|
2095
2666
|
tickerSearchCacheRef.current.clear();
|
|
2096
2667
|
}, [state.tickers]);
|
|
2097
2668
|
|
|
2669
|
+
const createQuickLookLocalTickerCandidates = useCallback((tickers: Iterable<TickerRecord>) => (
|
|
2670
|
+
createLocalTickerSearchCandidates(tickers, new Map(), QUICK_LOOK_TICKER_SEARCH_OPTIONS)
|
|
2671
|
+
), []);
|
|
2672
|
+
|
|
2098
2673
|
const localTickerSearchResultItems = useCallback((query?: string, options?: {
|
|
2099
2674
|
category?: string;
|
|
2100
2675
|
limit?: number;
|
|
2101
2676
|
}): ResultItem[] => {
|
|
2102
2677
|
const items = query
|
|
2103
|
-
? rankTickerSearchItems(
|
|
2104
|
-
:
|
|
2678
|
+
? rankTickerSearchItems(createQuickLookLocalTickerCandidates(state.tickers.values()), query)
|
|
2679
|
+
: createQuickLookLocalTickerCandidates(state.tickers.values());
|
|
2105
2680
|
return items
|
|
2106
2681
|
.slice(0, options?.limit)
|
|
2107
2682
|
.map((candidate) => ({
|
|
2108
2683
|
...mapTickerSearchCandidateToResultItem(candidate),
|
|
2109
2684
|
category: options?.category ?? candidate.category,
|
|
2110
2685
|
}));
|
|
2111
|
-
}, [mapTickerSearchCandidateToResultItem, state.tickers]);
|
|
2686
|
+
}, [createQuickLookLocalTickerCandidates, mapTickerSearchCandidateToResultItem, state.tickers]);
|
|
2112
2687
|
|
|
2113
2688
|
const adaptTickerSearchRouteResult = useCallback((
|
|
2114
2689
|
item: ResultItem,
|
|
@@ -2412,7 +2987,7 @@ export function CommandBar({
|
|
|
2412
2987
|
switch (command.id) {
|
|
2413
2988
|
case "help":
|
|
2414
2989
|
closeAll({ revertThemePreview: false });
|
|
2415
|
-
pluginRegistry.
|
|
2990
|
+
pluginRegistry.showPane("help");
|
|
2416
2991
|
return;
|
|
2417
2992
|
case "pane-settings":
|
|
2418
2993
|
if (state.focusedPaneId) openPaneSettingsRoute(state.focusedPaneId);
|
|
@@ -2528,6 +3103,8 @@ export function CommandBar({
|
|
|
2528
3103
|
const importPath = getDefaultConfigBackupPath();
|
|
2529
3104
|
void importConfig(state.config.dataDir, importPath)
|
|
2530
3105
|
.then((imported) => {
|
|
3106
|
+
themePickerRef.current?.cancelPreview();
|
|
3107
|
+
clearTransientThemePreview();
|
|
2531
3108
|
dispatch({ type: "SET_CONFIG", config: imported });
|
|
2532
3109
|
applyTheme(imported.theme);
|
|
2533
3110
|
dispatch({ type: "SET_THEME", theme: imported.theme });
|
|
@@ -2559,6 +3136,10 @@ export function CommandBar({
|
|
|
2559
3136
|
void onCheckForUpdates?.();
|
|
2560
3137
|
closeAll({ revertThemePreview: false });
|
|
2561
3138
|
return;
|
|
3139
|
+
case "theme":
|
|
3140
|
+
rootThemeBaseIdRef.current = stateRef.current.config.theme;
|
|
3141
|
+
setRootQuery(arg ? `TH ${arg}` : "TH ");
|
|
3142
|
+
return;
|
|
2562
3143
|
case "security-description":
|
|
2563
3144
|
void runSecurityDescriptionShortcut(arg);
|
|
2564
3145
|
return;
|
|
@@ -2595,9 +3176,12 @@ export function CommandBar({
|
|
|
2595
3176
|
quitApp,
|
|
2596
3177
|
runSecurityDescriptionShortcut,
|
|
2597
3178
|
executeCollectionCommand,
|
|
3179
|
+
setRootQuery,
|
|
2598
3180
|
]);
|
|
2599
3181
|
|
|
2600
3182
|
const activeMatch = matchPrefix(rootQuery, availableCommands);
|
|
3183
|
+
const themePickerActive = !currentRoute && activeMatch?.command.id === "theme";
|
|
3184
|
+
const themePickerFilter = themePickerActive ? activeMatch.arg : "";
|
|
2601
3185
|
const rootSecurityDescriptionArg = activeMatch?.command.id === "security-description" && activeMatch.arg.length >= 1
|
|
2602
3186
|
? activeMatch.arg
|
|
2603
3187
|
: null;
|
|
@@ -2619,13 +3203,13 @@ export function CommandBar({
|
|
|
2619
3203
|
rootThemeBaseIdRef.current = state.config.theme;
|
|
2620
3204
|
} else if (rootModeInfo.kind !== "themes" && previousMode === "themes") {
|
|
2621
3205
|
const rootThemeBaseId = rootThemeBaseIdRef.current;
|
|
2622
|
-
if (rootThemeBaseId
|
|
3206
|
+
if (rootThemeBaseId) {
|
|
2623
3207
|
clearThemePreview(rootThemeBaseId);
|
|
2624
3208
|
}
|
|
2625
3209
|
rootThemeBaseIdRef.current = null;
|
|
2626
3210
|
}
|
|
2627
3211
|
previousRootModeRef.current = rootModeInfo.kind;
|
|
2628
|
-
}, [clearThemePreview, currentRoute, rootModeInfo.kind, state.config.theme
|
|
3212
|
+
}, [clearThemePreview, currentRoute, rootModeInfo.kind, state.config.theme]);
|
|
2629
3213
|
|
|
2630
3214
|
const tickerSearchRouteQuery = currentRoute?.kind === "mode" && currentRoute.screen === "ticker-search"
|
|
2631
3215
|
? currentRoute.query
|
|
@@ -2673,6 +3257,7 @@ export function CommandBar({
|
|
|
2673
3257
|
brokerId: activePortfolio?.brokerId,
|
|
2674
3258
|
brokerInstanceId: activePortfolio?.brokerInstanceId,
|
|
2675
3259
|
},
|
|
3260
|
+
...QUICK_LOOK_TICKER_SEARCH_OPTIONS,
|
|
2676
3261
|
});
|
|
2677
3262
|
if (requestId !== searchRequestIdRef.current) return;
|
|
2678
3263
|
writeTickerSearchCache(
|
|
@@ -2959,336 +3544,26 @@ export function CommandBar({
|
|
|
2959
3544
|
) {
|
|
2960
3545
|
items.push(shortcutItem);
|
|
2961
3546
|
} else if (match && match.command.id === "plugins") {
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
const toggleAction = () => {
|
|
2973
|
-
dispatch({ type: "TOGGLE_PLUGIN", pluginId: plugin.id });
|
|
2974
|
-
const nextDisabled = enabled
|
|
2975
|
-
? [...disabledPlugins, plugin.id]
|
|
2976
|
-
: disabledPlugins.filter((entry) => entry !== plugin.id);
|
|
2977
|
-
if (enabled) {
|
|
2978
|
-
for (const paneId of pluginRegistry.getPluginPaneIds(plugin.id)) {
|
|
2979
|
-
pluginRegistry.hideWidget(paneId);
|
|
2980
|
-
}
|
|
2981
|
-
}
|
|
2982
|
-
void saveConfig({ ...state.config, disabledPlugins: nextDisabled });
|
|
2983
|
-
};
|
|
3547
|
+
items.push(...buildPluginItems(match.arg));
|
|
3548
|
+
} else if (match && match.command.id === "layout") {
|
|
3549
|
+
items.push(...buildLayoutItems(match.arg, { confirmDangerousActions: true }));
|
|
3550
|
+
} else if (match && match.command.id === "theme") {
|
|
3551
|
+
initialIdx = 0;
|
|
3552
|
+
} else if (match && match.command.id === "security-description") {
|
|
3553
|
+
if (shortcutItem) {
|
|
3554
|
+
items.push(shortcutItem);
|
|
3555
|
+
}
|
|
3556
|
+
if (!match.arg && !shortcutItem) {
|
|
2984
3557
|
items.push({
|
|
2985
|
-
id:
|
|
2986
|
-
label:
|
|
2987
|
-
detail:
|
|
2988
|
-
category: "
|
|
2989
|
-
kind: "
|
|
2990
|
-
|
|
2991
|
-
pluginToggle: toggleAction,
|
|
2992
|
-
action: toggleAction,
|
|
3558
|
+
id: "search-hint",
|
|
3559
|
+
label: "Type a ticker symbol",
|
|
3560
|
+
detail: "Open security details after resolving a ticker",
|
|
3561
|
+
category: "Search",
|
|
3562
|
+
kind: "info",
|
|
3563
|
+
action: () => {},
|
|
2993
3564
|
});
|
|
2994
|
-
}
|
|
2995
|
-
|
|
2996
|
-
const currentLayout = state.config.layout;
|
|
2997
|
-
const focusedPane = state.focusedPaneId ? findPaneInstance(currentLayout, state.focusedPaneId) : null;
|
|
2998
|
-
const focusedPaneDef = focusedPane ? pluginRegistry.panes.get(focusedPane.paneId) : null;
|
|
2999
|
-
const dockedPaneIds = getDockedPaneIds(currentLayout);
|
|
3000
|
-
const focusedFloating = focusedPane ? currentLayout.floating.find((entry) => entry.instanceId === focusedPane.instanceId) : null;
|
|
3001
|
-
const layoutHistory = state.layoutHistory[state.config.activeLayoutIndex];
|
|
3002
|
-
const layoutSnapshot = JSON.stringify(currentLayout);
|
|
3003
|
-
const canMove = (direction: "left" | "right" | "above" | "below") => (
|
|
3004
|
-
!!focusedPane && JSON.stringify(movePaneRelative(currentLayout, focusedPane.instanceId, direction)) !== layoutSnapshot
|
|
3005
|
-
);
|
|
3006
|
-
const layoutItems: ResultItem[] = [];
|
|
3007
|
-
|
|
3008
|
-
if (focusedPane && focusedPaneDef) {
|
|
3009
|
-
layoutItems.push({
|
|
3010
|
-
id: "layout-toggle-mode",
|
|
3011
|
-
label: focusedFloating ? "Dock Pane" : "Float Pane",
|
|
3012
|
-
detail: focusedFloating ? "Return the focused window to the layout" : "Detach the focused pane into a floating window",
|
|
3013
|
-
category: "Focused Pane",
|
|
3014
|
-
kind: "action",
|
|
3015
|
-
action: () => {
|
|
3016
|
-
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
3017
|
-
const nextLayout = focusedFloating
|
|
3018
|
-
? dockPane(currentLayout, focusedPane.instanceId)
|
|
3019
|
-
: floatPane(currentLayout, focusedPane.instanceId, width, height, focusedPaneDef);
|
|
3020
|
-
persistLayoutChange(nextLayout);
|
|
3021
|
-
closeAll({ revertThemePreview: false });
|
|
3022
|
-
},
|
|
3023
|
-
});
|
|
3024
|
-
|
|
3025
|
-
const moveActions: Array<{
|
|
3026
|
-
id: string;
|
|
3027
|
-
label: string;
|
|
3028
|
-
direction: "left" | "right" | "above" | "below";
|
|
3029
|
-
available: boolean;
|
|
3030
|
-
unavailableDetail: string;
|
|
3031
|
-
}> = [
|
|
3032
|
-
{ id: "layout-move-left", label: "Move Left", direction: "left", available: canMove("left"), unavailableDetail: "No column to the left" },
|
|
3033
|
-
{ id: "layout-move-right", label: "Move Right", direction: "right", available: canMove("right"), unavailableDetail: "No column to the right" },
|
|
3034
|
-
{ id: "layout-move-up", label: "Move Up", direction: "above", available: canMove("above"), unavailableDetail: "No pane above" },
|
|
3035
|
-
{ id: "layout-move-down", label: "Move Down", direction: "below", available: canMove("below"), unavailableDetail: "No pane below" },
|
|
3036
|
-
];
|
|
3037
|
-
|
|
3038
|
-
moveActions.forEach((entry) => {
|
|
3039
|
-
layoutItems.push({
|
|
3040
|
-
id: entry.id,
|
|
3041
|
-
label: entry.label,
|
|
3042
|
-
detail: entry.available ? "Reposition the focused pane" : entry.unavailableDetail,
|
|
3043
|
-
category: "Focused Pane",
|
|
3044
|
-
kind: entry.available ? "action" : "info",
|
|
3045
|
-
disabled: !entry.available,
|
|
3046
|
-
action: entry.available
|
|
3047
|
-
? () => {
|
|
3048
|
-
persistLayoutChange(movePaneRelative(currentLayout, focusedPane.instanceId, entry.direction));
|
|
3049
|
-
closeAll({ revertThemePreview: false });
|
|
3050
|
-
}
|
|
3051
|
-
: () => {},
|
|
3052
|
-
});
|
|
3053
|
-
});
|
|
3054
|
-
|
|
3055
|
-
layoutItems.push({
|
|
3056
|
-
id: "layout-swap",
|
|
3057
|
-
label: "Swap With…",
|
|
3058
|
-
detail: dockedPaneIds.length + currentLayout.floating.length > 1
|
|
3059
|
-
? "Choose another pane to swap positions"
|
|
3060
|
-
: "Need at least two panes",
|
|
3061
|
-
category: "Focused Pane",
|
|
3062
|
-
kind: "action",
|
|
3063
|
-
disabled: dockedPaneIds.length + currentLayout.floating.length <= 1,
|
|
3064
|
-
action: () => {
|
|
3065
|
-
const options = [
|
|
3066
|
-
...dockedPaneIds,
|
|
3067
|
-
...currentLayout.floating.map((entry) => entry.instanceId),
|
|
3068
|
-
]
|
|
3069
|
-
.filter((paneId) => paneId !== focusedPane.instanceId)
|
|
3070
|
-
.map((paneId) => {
|
|
3071
|
-
const instance = findPaneInstance(currentLayout, paneId)!;
|
|
3072
|
-
const isFloating = currentLayout.floating.some((entry) => entry.instanceId === paneId);
|
|
3073
|
-
return {
|
|
3074
|
-
id: paneId,
|
|
3075
|
-
label: instance.title || pluginRegistry.panes.get(instance.paneId)?.name || instance.paneId,
|
|
3076
|
-
detail: isFloating ? "Floating window" : "Docked pane",
|
|
3077
|
-
description: isFloating ? "Floating window" : "Docked pane",
|
|
3078
|
-
};
|
|
3079
|
-
});
|
|
3080
|
-
if (options.length === 0) return;
|
|
3081
|
-
openPickerRoute({
|
|
3082
|
-
kind: "picker",
|
|
3083
|
-
pickerId: "layout-swap",
|
|
3084
|
-
title: "Swap With…",
|
|
3085
|
-
query: "",
|
|
3086
|
-
selectedIdx: 0,
|
|
3087
|
-
hoveredIdx: null,
|
|
3088
|
-
options,
|
|
3089
|
-
payload: { sourcePaneId: focusedPane.instanceId },
|
|
3090
|
-
});
|
|
3091
|
-
},
|
|
3092
|
-
});
|
|
3093
|
-
|
|
3094
|
-
layoutItems.push({
|
|
3095
|
-
id: "layout-duplicate",
|
|
3096
|
-
label: "Duplicate Pane",
|
|
3097
|
-
detail: "Create another instance next to the focused pane",
|
|
3098
|
-
category: "Focused Pane",
|
|
3099
|
-
kind: "action",
|
|
3100
|
-
action: () => {
|
|
3101
|
-
duplicatePane(focusedPane.instanceId);
|
|
3102
|
-
closeAll({ revertThemePreview: false });
|
|
3103
|
-
},
|
|
3104
|
-
});
|
|
3105
|
-
|
|
3106
|
-
layoutItems.push({
|
|
3107
|
-
id: "layout-close-pane",
|
|
3108
|
-
label: "Close Pane",
|
|
3109
|
-
detail: "Remove the focused pane from the layout",
|
|
3110
|
-
category: "Focused Pane",
|
|
3111
|
-
kind: "action",
|
|
3112
|
-
action: () => {
|
|
3113
|
-
openInlineConfirm({
|
|
3114
|
-
confirmId: "layout-close-pane",
|
|
3115
|
-
title: "Close Pane",
|
|
3116
|
-
body: [`Close "${focusedPane.title || focusedPaneDef.name || focusedPane.instanceId}"?`],
|
|
3117
|
-
confirmLabel: "Close Pane",
|
|
3118
|
-
cancelLabel: "Back",
|
|
3119
|
-
tone: "danger",
|
|
3120
|
-
onConfirm: () => {
|
|
3121
|
-
persistLayoutChange(removePane(currentLayout, focusedPane.instanceId));
|
|
3122
|
-
},
|
|
3123
|
-
});
|
|
3124
|
-
},
|
|
3125
|
-
});
|
|
3126
|
-
} else {
|
|
3127
|
-
layoutItems.push({
|
|
3128
|
-
id: "layout-no-focused-pane",
|
|
3129
|
-
label: "No focused pane",
|
|
3130
|
-
detail: "Focus a pane to show pane-specific layout actions",
|
|
3131
|
-
category: "Focused Pane",
|
|
3132
|
-
kind: "info",
|
|
3133
|
-
action: () => {},
|
|
3134
|
-
});
|
|
3135
|
-
}
|
|
3136
|
-
|
|
3137
|
-
layoutItems.push({
|
|
3138
|
-
id: "layout-undo",
|
|
3139
|
-
label: "Undo Layout Change",
|
|
3140
|
-
detail: (layoutHistory?.past.length ?? 0) > 0 ? "Restore the previous layout state" : "No previous layout state",
|
|
3141
|
-
category: "Current Layout",
|
|
3142
|
-
kind: "action",
|
|
3143
|
-
disabled: (layoutHistory?.past.length ?? 0) === 0,
|
|
3144
|
-
action: () => {
|
|
3145
|
-
if ((layoutHistory?.past.length ?? 0) === 0) return;
|
|
3146
|
-
dispatch({ type: "UNDO_LAYOUT" });
|
|
3147
|
-
closeAll({ revertThemePreview: false });
|
|
3148
|
-
},
|
|
3149
|
-
});
|
|
3150
|
-
layoutItems.push({
|
|
3151
|
-
id: "layout-redo",
|
|
3152
|
-
label: "Redo Layout Change",
|
|
3153
|
-
detail: (layoutHistory?.future.length ?? 0) > 0 ? "Reapply the next layout state" : "No later layout state",
|
|
3154
|
-
category: "Current Layout",
|
|
3155
|
-
kind: "action",
|
|
3156
|
-
disabled: (layoutHistory?.future.length ?? 0) === 0,
|
|
3157
|
-
action: () => {
|
|
3158
|
-
if ((layoutHistory?.future.length ?? 0) === 0) return;
|
|
3159
|
-
dispatch({ type: "REDO_LAYOUT" });
|
|
3160
|
-
closeAll({ revertThemePreview: false });
|
|
3161
|
-
},
|
|
3162
|
-
});
|
|
3163
|
-
layoutItems.push({
|
|
3164
|
-
id: "layout-reset",
|
|
3165
|
-
label: "Reset Current Layout",
|
|
3166
|
-
detail: "Restore the default two-pane layout",
|
|
3167
|
-
category: "Current Layout",
|
|
3168
|
-
kind: "action",
|
|
3169
|
-
action: () => {
|
|
3170
|
-
openInlineConfirm({
|
|
3171
|
-
confirmId: "layout-reset",
|
|
3172
|
-
title: "Reset Current Layout",
|
|
3173
|
-
body: ["Reset the current layout to the default two-pane arrangement?"],
|
|
3174
|
-
confirmLabel: "Reset Layout",
|
|
3175
|
-
cancelLabel: "Back",
|
|
3176
|
-
tone: "danger",
|
|
3177
|
-
onConfirm: () => {
|
|
3178
|
-
persistLayoutChange(cloneLayout(DEFAULT_LAYOUT));
|
|
3179
|
-
},
|
|
3180
|
-
});
|
|
3181
|
-
},
|
|
3182
|
-
});
|
|
3183
|
-
layoutItems.push({
|
|
3184
|
-
id: "layout-gridlock",
|
|
3185
|
-
label: "Gridlock All Windows",
|
|
3186
|
-
detail: currentLayout.floating.length > 0
|
|
3187
|
-
? "Infer a tiled layout from the current window positions"
|
|
3188
|
-
: "Retile all panes from their current arrangement",
|
|
3189
|
-
category: "Current Layout",
|
|
3190
|
-
kind: "action",
|
|
3191
|
-
action: () => {
|
|
3192
|
-
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
3193
|
-
persistLayoutChange(gridlockAllPanes(currentLayout, { x: 0, y: 0, width, height }));
|
|
3194
|
-
notifyGridlockRevert();
|
|
3195
|
-
closeAll({ revertThemePreview: false });
|
|
3196
|
-
},
|
|
3197
|
-
});
|
|
3198
|
-
layoutItems.push({
|
|
3199
|
-
id: "layout-rename",
|
|
3200
|
-
label: "Rename Layout",
|
|
3201
|
-
detail: "Change the current saved layout name",
|
|
3202
|
-
category: "Current Layout",
|
|
3203
|
-
kind: "action",
|
|
3204
|
-
action: () => openBuiltInWorkflow("rename-layout"),
|
|
3205
|
-
});
|
|
3206
|
-
layoutItems.push({
|
|
3207
|
-
id: "layout-duplicate-layout",
|
|
3208
|
-
label: "Duplicate Layout",
|
|
3209
|
-
detail: "Create a copy of the current layout",
|
|
3210
|
-
category: "Current Layout",
|
|
3211
|
-
kind: "action",
|
|
3212
|
-
action: () => {
|
|
3213
|
-
dispatch({ type: "DUPLICATE_LAYOUT", index: state.config.activeLayoutIndex });
|
|
3214
|
-
closeAll({ revertThemePreview: false });
|
|
3215
|
-
},
|
|
3216
|
-
});
|
|
3217
|
-
layoutItems.push({
|
|
3218
|
-
id: "layout-new",
|
|
3219
|
-
label: "New Layout",
|
|
3220
|
-
detail: "Create a fresh saved layout",
|
|
3221
|
-
category: "Current Layout",
|
|
3222
|
-
kind: "action",
|
|
3223
|
-
action: () => openBuiltInWorkflow("new-layout"),
|
|
3224
|
-
});
|
|
3225
|
-
|
|
3226
|
-
state.config.layouts.forEach((savedLayout, index) => {
|
|
3227
|
-
layoutItems.push({
|
|
3228
|
-
id: `layout-switch:${index}`,
|
|
3229
|
-
label: savedLayout.name,
|
|
3230
|
-
detail: index === state.config.activeLayoutIndex ? "Current layout" : "Switch to this saved layout",
|
|
3231
|
-
right: getLayoutPreview(savedLayout.layout),
|
|
3232
|
-
category: "Saved Layouts",
|
|
3233
|
-
kind: "action",
|
|
3234
|
-
current: index === state.config.activeLayoutIndex,
|
|
3235
|
-
action: () => {
|
|
3236
|
-
dispatch({ type: "SWITCH_LAYOUT", index });
|
|
3237
|
-
closeAll({ revertThemePreview: false });
|
|
3238
|
-
},
|
|
3239
|
-
});
|
|
3240
|
-
});
|
|
3241
|
-
|
|
3242
|
-
items.push(...(match.arg
|
|
3243
|
-
? fuzzyFilter(layoutItems, match.arg, (item) => `${item.label} ${item.detail} ${item.right || ""}`)
|
|
3244
|
-
: layoutItems));
|
|
3245
|
-
} else if (match && match.command.id === "theme") {
|
|
3246
|
-
const savedThemeId = rootThemeBaseIdRef.current ?? state.config.theme;
|
|
3247
|
-
const themeOptions = getThemeOptions();
|
|
3248
|
-
const filtered = match.arg
|
|
3249
|
-
? themeOptions.filter((theme) => (
|
|
3250
|
-
theme.name.toLowerCase().includes(match.arg.toLowerCase())
|
|
3251
|
-
|| theme.id.includes(match.arg.toLowerCase())
|
|
3252
|
-
))
|
|
3253
|
-
: themeOptions;
|
|
3254
|
-
filtered.forEach((theme, index) => {
|
|
3255
|
-
const isSaved = theme.id === savedThemeId;
|
|
3256
|
-
if (isSaved) initialIdx = index;
|
|
3257
|
-
items.push({
|
|
3258
|
-
id: `theme:${theme.id}`,
|
|
3259
|
-
label: theme.name,
|
|
3260
|
-
detail: theme.description,
|
|
3261
|
-
category: "Themes",
|
|
3262
|
-
kind: "theme",
|
|
3263
|
-
current: isSaved,
|
|
3264
|
-
themeId: theme.id,
|
|
3265
|
-
action: () => {
|
|
3266
|
-
rootThemeBaseIdRef.current = theme.id;
|
|
3267
|
-
const nextConfig = {
|
|
3268
|
-
...state.config,
|
|
3269
|
-
theme: theme.id,
|
|
3270
|
-
};
|
|
3271
|
-
dispatch({ type: "SET_THEME", theme: theme.id });
|
|
3272
|
-
void saveConfig(nextConfig);
|
|
3273
|
-
closeAll({ revertThemePreview: false });
|
|
3274
|
-
},
|
|
3275
|
-
});
|
|
3276
|
-
});
|
|
3277
|
-
} else if (match && match.command.id === "security-description") {
|
|
3278
|
-
if (shortcutItem) {
|
|
3279
|
-
items.push(shortcutItem);
|
|
3280
|
-
}
|
|
3281
|
-
if (!match.arg && !shortcutItem) {
|
|
3282
|
-
items.push({
|
|
3283
|
-
id: "search-hint",
|
|
3284
|
-
label: "Type a ticker symbol",
|
|
3285
|
-
detail: "Open security details after resolving a ticker",
|
|
3286
|
-
category: "Search",
|
|
3287
|
-
kind: "info",
|
|
3288
|
-
action: () => {},
|
|
3289
|
-
});
|
|
3290
|
-
} else if (match.arg) {
|
|
3291
|
-
items.push(...localTickerSearchResultItems(match.arg, { limit: 6 }));
|
|
3565
|
+
} else if (match.arg) {
|
|
3566
|
+
items.push(...localTickerSearchResultItems(match.arg, { limit: 6 }));
|
|
3292
3567
|
}
|
|
3293
3568
|
} else if (match && isCollectionCommand(match.command.id)) {
|
|
3294
3569
|
if (shortcutItem) items.push(shortcutItem);
|
|
@@ -3300,18 +3575,26 @@ export function CommandBar({
|
|
|
3300
3575
|
const recentSymbols = state.recentTickers.slice(0, maxDefaultTickers);
|
|
3301
3576
|
const recentTickers = recentSymbols
|
|
3302
3577
|
.map((symbol) => state.tickers.get(symbol))
|
|
3303
|
-
.filter((ticker): ticker is NonNullable<typeof ticker> =>
|
|
3578
|
+
.filter((ticker): ticker is NonNullable<typeof ticker> => (
|
|
3579
|
+
ticker != null && createQuickLookLocalTickerCandidates([ticker]).length > 0
|
|
3580
|
+
));
|
|
3304
3581
|
if (recentTickers.length < maxDefaultTickers) {
|
|
3305
3582
|
const seen = new Set(recentSymbols);
|
|
3306
3583
|
for (const ticker of state.tickers.values()) {
|
|
3307
3584
|
if (recentTickers.length >= maxDefaultTickers) break;
|
|
3585
|
+
if (createQuickLookLocalTickerCandidates([ticker]).length === 0) continue;
|
|
3308
3586
|
if (!seen.has(ticker.metadata.ticker)) recentTickers.push(ticker);
|
|
3309
3587
|
}
|
|
3310
3588
|
}
|
|
3311
|
-
items.push(...recentTickers.
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3589
|
+
items.push(...recentTickers.flatMap((ticker) => {
|
|
3590
|
+
const candidate = createQuickLookLocalTickerCandidates([ticker])[0];
|
|
3591
|
+
return candidate
|
|
3592
|
+
? [{
|
|
3593
|
+
...mapTickerSearchCandidateToResultItem(candidate),
|
|
3594
|
+
category: "Tickers",
|
|
3595
|
+
}]
|
|
3596
|
+
: [];
|
|
3597
|
+
}));
|
|
3315
3598
|
items.push(...paneShortcutItems());
|
|
3316
3599
|
for (const command of availableCommands) {
|
|
3317
3600
|
const item = commandToItem(command);
|
|
@@ -3341,19 +3624,15 @@ export function CommandBar({
|
|
|
3341
3624
|
activeTickerData,
|
|
3342
3625
|
activeTickerSymbol,
|
|
3343
3626
|
availableCommands,
|
|
3344
|
-
|
|
3627
|
+
buildLayoutItems,
|
|
3628
|
+
buildPluginItems,
|
|
3629
|
+
createQuickLookLocalTickerCandidates,
|
|
3345
3630
|
createPluginCommandItem,
|
|
3346
3631
|
currentRoute,
|
|
3347
|
-
dispatch,
|
|
3348
|
-
duplicatePane,
|
|
3349
3632
|
executeCollectionCommand,
|
|
3350
3633
|
localTickerSearchResultItems,
|
|
3351
3634
|
nonShortcutPaneTemplateItems,
|
|
3352
|
-
notifyGridlockRevert,
|
|
3353
|
-
openBuiltInWorkflow,
|
|
3354
|
-
openPickerRoute,
|
|
3355
3635
|
paneShortcutItems,
|
|
3356
|
-
persistLayoutChange,
|
|
3357
3636
|
pluginCommandItems,
|
|
3358
3637
|
pluginRegistry,
|
|
3359
3638
|
rootModeInfo.kind,
|
|
@@ -3435,6 +3714,7 @@ export function CommandBar({
|
|
|
3435
3714
|
brokerId: activeSearchPortfolio?.brokerId,
|
|
3436
3715
|
brokerInstanceId: activeSearchPortfolio?.brokerInstanceId,
|
|
3437
3716
|
},
|
|
3717
|
+
...QUICK_LOOK_TICKER_SEARCH_OPTIONS,
|
|
3438
3718
|
});
|
|
3439
3719
|
if (requestId !== rootSearchRequestIdRef.current) return;
|
|
3440
3720
|
writeTickerSearchCache(
|
|
@@ -3484,6 +3764,7 @@ export function CommandBar({
|
|
|
3484
3764
|
}
|
|
3485
3765
|
return rootResultModel.items;
|
|
3486
3766
|
}, [rootProviderResults, rootProviderResultsQuery, rootResultModel.items, rootSecurityDescriptionArg]);
|
|
3767
|
+
const orderedRootResults = useMemo(() => orderListResults(rootResults), [rootResults]);
|
|
3487
3768
|
|
|
3488
3769
|
const rootGhostCompletion = !currentRoute && rootShortcutIntent.kind === "inferred-complete"
|
|
3489
3770
|
? rootShortcutIntent.completionQuery
|
|
@@ -3689,12 +3970,15 @@ export function CommandBar({
|
|
|
3689
3970
|
|
|
3690
3971
|
if (match.command.id === "theme") {
|
|
3691
3972
|
return {
|
|
3692
|
-
id: "theme-
|
|
3973
|
+
id: "theme-picker",
|
|
3693
3974
|
label: "Change Theme",
|
|
3694
3975
|
detail: "Preview and apply themes",
|
|
3695
3976
|
category: "Themes",
|
|
3696
3977
|
kind: "command",
|
|
3697
|
-
action: () =>
|
|
3978
|
+
action: () => {
|
|
3979
|
+
rootThemeBaseIdRef.current = stateRef.current.config.theme;
|
|
3980
|
+
setRootQuery(match.arg ? `TH ${match.arg}` : "TH ");
|
|
3981
|
+
},
|
|
3698
3982
|
};
|
|
3699
3983
|
}
|
|
3700
3984
|
|
|
@@ -3760,6 +4044,7 @@ export function CommandBar({
|
|
|
3760
4044
|
openModeRoute,
|
|
3761
4045
|
runDirectCommand,
|
|
3762
4046
|
runSecurityDescriptionShortcut,
|
|
4047
|
+
setRootQuery,
|
|
3763
4048
|
]);
|
|
3764
4049
|
|
|
3765
4050
|
const routeListState = useMemo<ListScreenState | null>(() => {
|
|
@@ -3775,7 +4060,7 @@ export function CommandBar({
|
|
|
3775
4060
|
query: rootQuery,
|
|
3776
4061
|
selectedIdx: rootSelectedIdx,
|
|
3777
4062
|
hoveredIdx: rootHoveredIdx,
|
|
3778
|
-
results:
|
|
4063
|
+
results: orderedRootResults,
|
|
3779
4064
|
searching: rootSearching,
|
|
3780
4065
|
emptyLabel: emptyState.label,
|
|
3781
4066
|
emptyDetail: emptyState.detail,
|
|
@@ -3786,81 +4071,8 @@ export function CommandBar({
|
|
|
3786
4071
|
|
|
3787
4072
|
if (currentRoute.kind === "mode") {
|
|
3788
4073
|
switch (currentRoute.screen) {
|
|
3789
|
-
case "themes": {
|
|
3790
|
-
const themeOptions = getThemeOptions();
|
|
3791
|
-
const filtered = currentRoute.query
|
|
3792
|
-
? themeOptions.filter((theme) => (
|
|
3793
|
-
theme.name.toLowerCase().includes(currentRoute.query.toLowerCase())
|
|
3794
|
-
|| theme.id.includes(currentRoute.query.toLowerCase())
|
|
3795
|
-
))
|
|
3796
|
-
: themeOptions;
|
|
3797
|
-
const results = filtered.map((theme) => ({
|
|
3798
|
-
id: `theme:${theme.id}`,
|
|
3799
|
-
label: theme.name,
|
|
3800
|
-
detail: theme.description,
|
|
3801
|
-
category: "Themes",
|
|
3802
|
-
kind: "theme" as const,
|
|
3803
|
-
current: theme.id === currentRoute.themeBaseId,
|
|
3804
|
-
themeId: theme.id,
|
|
3805
|
-
action: () => {
|
|
3806
|
-
const nextConfig = {
|
|
3807
|
-
...state.config,
|
|
3808
|
-
theme: theme.id,
|
|
3809
|
-
};
|
|
3810
|
-
dispatch({ type: "SET_THEME", theme: theme.id });
|
|
3811
|
-
void saveConfig(nextConfig);
|
|
3812
|
-
closeAll({ revertThemePreview: false });
|
|
3813
|
-
},
|
|
3814
|
-
}));
|
|
3815
|
-
return {
|
|
3816
|
-
kind: "mode",
|
|
3817
|
-
title: "Change Theme",
|
|
3818
|
-
subtitle: "Preview themes with the keyboard, then save.",
|
|
3819
|
-
query: currentRoute.query,
|
|
3820
|
-
selectedIdx: currentRoute.selectedIdx,
|
|
3821
|
-
hoveredIdx: currentRoute.hoveredIdx,
|
|
3822
|
-
results: orderListResults(results),
|
|
3823
|
-
searching: false,
|
|
3824
|
-
emptyLabel: getEmptyState("themes", currentRoute.query).label,
|
|
3825
|
-
emptyDetail: getEmptyState("themes", currentRoute.query).detail,
|
|
3826
|
-
footerLeft: getScreenFooterLeft(currentRoute),
|
|
3827
|
-
footerRight: getScreenFooterRight(currentRoute),
|
|
3828
|
-
};
|
|
3829
|
-
}
|
|
3830
4074
|
case "plugins": {
|
|
3831
|
-
const
|
|
3832
|
-
const toggleable = [...pluginRegistry.allPlugins.values()].filter((plugin) => plugin.toggleable);
|
|
3833
|
-
const filtered = currentRoute.query
|
|
3834
|
-
? toggleable.filter((plugin) => (
|
|
3835
|
-
plugin.name.toLowerCase().includes(currentRoute.query.toLowerCase())
|
|
3836
|
-
|| plugin.id.includes(currentRoute.query.toLowerCase())
|
|
3837
|
-
))
|
|
3838
|
-
: toggleable;
|
|
3839
|
-
const results = filtered.map((plugin) => {
|
|
3840
|
-
const enabled = !disabledPlugins.includes(plugin.id);
|
|
3841
|
-
const toggleAction = () => {
|
|
3842
|
-
dispatch({ type: "TOGGLE_PLUGIN", pluginId: plugin.id });
|
|
3843
|
-
const nextDisabled = enabled
|
|
3844
|
-
? [...disabledPlugins, plugin.id]
|
|
3845
|
-
: disabledPlugins.filter((entry) => entry !== plugin.id);
|
|
3846
|
-
if (enabled) {
|
|
3847
|
-
for (const paneId of pluginRegistry.getPluginPaneIds(plugin.id)) {
|
|
3848
|
-
pluginRegistry.hideWidget(paneId);
|
|
3849
|
-
}
|
|
3850
|
-
}
|
|
3851
|
-
void saveConfig({ ...state.config, disabledPlugins: nextDisabled });
|
|
3852
|
-
};
|
|
3853
|
-
return {
|
|
3854
|
-
id: `plugin:${plugin.id}`,
|
|
3855
|
-
label: plugin.name,
|
|
3856
|
-
detail: plugin.description || "",
|
|
3857
|
-
category: "Plugins",
|
|
3858
|
-
kind: "plugin" as const,
|
|
3859
|
-
checked: enabled,
|
|
3860
|
-
pluginToggle: toggleAction,
|
|
3861
|
-
action: toggleAction,
|
|
3862
|
-
};
|
|
3863
|
-
});
|
|
4075
|
+
const results = buildPluginItems(currentRoute.query);
|
|
3864
4076
|
return {
|
|
3865
4077
|
kind: "mode",
|
|
3866
4078
|
title: "Manage Plugins",
|
|
@@ -3877,237 +4089,7 @@ export function CommandBar({
|
|
|
3877
4089
|
};
|
|
3878
4090
|
}
|
|
3879
4091
|
case "layout": {
|
|
3880
|
-
const
|
|
3881
|
-
const focusedPane = state.focusedPaneId ? findPaneInstance(currentLayout, state.focusedPaneId) : null;
|
|
3882
|
-
const focusedPaneDef = focusedPane ? pluginRegistry.panes.get(focusedPane.paneId) : null;
|
|
3883
|
-
const dockedPaneIds = getDockedPaneIds(currentLayout);
|
|
3884
|
-
const layoutHistory = state.layoutHistory[state.config.activeLayoutIndex];
|
|
3885
|
-
const layoutSnapshot = JSON.stringify(currentLayout);
|
|
3886
|
-
const canMove = (direction: "left" | "right" | "above" | "below") => (
|
|
3887
|
-
!!focusedPane && JSON.stringify(movePaneRelative(currentLayout, focusedPane.instanceId, direction)) !== layoutSnapshot
|
|
3888
|
-
);
|
|
3889
|
-
|
|
3890
|
-
const layoutItems: ResultItem[] = [];
|
|
3891
|
-
if (focusedPane && focusedPaneDef) {
|
|
3892
|
-
const focusedFloating = currentLayout.floating.find((entry) => entry.instanceId === focusedPane.instanceId);
|
|
3893
|
-
layoutItems.push({
|
|
3894
|
-
id: "layout-toggle-mode",
|
|
3895
|
-
label: focusedFloating ? "Dock Pane" : "Float Pane",
|
|
3896
|
-
detail: focusedFloating ? "Return the focused window to the layout" : "Detach the focused pane into a floating window",
|
|
3897
|
-
category: "Focused Pane",
|
|
3898
|
-
kind: "action",
|
|
3899
|
-
action: () => {
|
|
3900
|
-
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
3901
|
-
const nextLayout = focusedFloating
|
|
3902
|
-
? dockPane(currentLayout, focusedPane.instanceId)
|
|
3903
|
-
: floatPane(currentLayout, focusedPane.instanceId, width, height, focusedPaneDef);
|
|
3904
|
-
persistLayoutChange(nextLayout);
|
|
3905
|
-
closeAll({ revertThemePreview: false });
|
|
3906
|
-
},
|
|
3907
|
-
});
|
|
3908
|
-
|
|
3909
|
-
const moveActions: Array<{
|
|
3910
|
-
id: string;
|
|
3911
|
-
label: string;
|
|
3912
|
-
direction: "left" | "right" | "above" | "below";
|
|
3913
|
-
available: boolean;
|
|
3914
|
-
unavailableDetail: string;
|
|
3915
|
-
}> = [
|
|
3916
|
-
{ id: "layout-move-left", label: "Move Left", direction: "left", available: canMove("left"), unavailableDetail: "No column to the left" },
|
|
3917
|
-
{ id: "layout-move-right", label: "Move Right", direction: "right", available: canMove("right"), unavailableDetail: "No column to the right" },
|
|
3918
|
-
{ id: "layout-move-up", label: "Move Up", direction: "above", available: canMove("above"), unavailableDetail: "No pane above" },
|
|
3919
|
-
{ id: "layout-move-down", label: "Move Down", direction: "below", available: canMove("below"), unavailableDetail: "No pane below" },
|
|
3920
|
-
];
|
|
3921
|
-
|
|
3922
|
-
moveActions.forEach((entry) => {
|
|
3923
|
-
layoutItems.push({
|
|
3924
|
-
id: entry.id,
|
|
3925
|
-
label: entry.label,
|
|
3926
|
-
detail: entry.available ? "Reposition the focused pane" : entry.unavailableDetail,
|
|
3927
|
-
category: "Focused Pane",
|
|
3928
|
-
kind: entry.available ? "action" : "info",
|
|
3929
|
-
disabled: !entry.available,
|
|
3930
|
-
action: entry.available
|
|
3931
|
-
? () => {
|
|
3932
|
-
persistLayoutChange(movePaneRelative(currentLayout, focusedPane.instanceId, entry.direction));
|
|
3933
|
-
closeAll({ revertThemePreview: false });
|
|
3934
|
-
}
|
|
3935
|
-
: () => {},
|
|
3936
|
-
});
|
|
3937
|
-
});
|
|
3938
|
-
|
|
3939
|
-
layoutItems.push({
|
|
3940
|
-
id: "layout-swap",
|
|
3941
|
-
label: "Swap With…",
|
|
3942
|
-
detail: dockedPaneIds.length + currentLayout.floating.length > 1
|
|
3943
|
-
? "Choose another pane to swap positions"
|
|
3944
|
-
: "Need at least two panes",
|
|
3945
|
-
category: "Focused Pane",
|
|
3946
|
-
kind: "action",
|
|
3947
|
-
disabled: dockedPaneIds.length + currentLayout.floating.length <= 1,
|
|
3948
|
-
action: () => {
|
|
3949
|
-
const options = [
|
|
3950
|
-
...dockedPaneIds,
|
|
3951
|
-
...currentLayout.floating.map((entry) => entry.instanceId),
|
|
3952
|
-
]
|
|
3953
|
-
.filter((paneId) => paneId !== focusedPane.instanceId)
|
|
3954
|
-
.map((paneId) => {
|
|
3955
|
-
const instance = findPaneInstance(currentLayout, paneId)!;
|
|
3956
|
-
return {
|
|
3957
|
-
id: paneId,
|
|
3958
|
-
label: instance.title || pluginRegistry.panes.get(instance.paneId)?.name || instance.paneId,
|
|
3959
|
-
detail: currentLayout.floating.some((entry) => entry.instanceId === paneId) ? "Floating window" : "Docked pane",
|
|
3960
|
-
description: currentLayout.floating.some((entry) => entry.instanceId === paneId) ? "Floating window" : "Docked pane",
|
|
3961
|
-
};
|
|
3962
|
-
});
|
|
3963
|
-
if (options.length === 0) return;
|
|
3964
|
-
openPickerRoute({
|
|
3965
|
-
kind: "picker",
|
|
3966
|
-
pickerId: "layout-swap",
|
|
3967
|
-
title: "Swap With…",
|
|
3968
|
-
query: "",
|
|
3969
|
-
selectedIdx: 0,
|
|
3970
|
-
hoveredIdx: null,
|
|
3971
|
-
options,
|
|
3972
|
-
payload: { sourcePaneId: focusedPane.instanceId },
|
|
3973
|
-
});
|
|
3974
|
-
},
|
|
3975
|
-
});
|
|
3976
|
-
|
|
3977
|
-
layoutItems.push({
|
|
3978
|
-
id: "layout-duplicate",
|
|
3979
|
-
label: "Duplicate Pane",
|
|
3980
|
-
detail: "Create another instance next to the focused pane",
|
|
3981
|
-
category: "Focused Pane",
|
|
3982
|
-
kind: "action",
|
|
3983
|
-
action: () => {
|
|
3984
|
-
duplicatePane(focusedPane.instanceId);
|
|
3985
|
-
closeAll({ revertThemePreview: false });
|
|
3986
|
-
},
|
|
3987
|
-
});
|
|
3988
|
-
|
|
3989
|
-
layoutItems.push({
|
|
3990
|
-
id: "layout-close-pane",
|
|
3991
|
-
label: "Close Pane",
|
|
3992
|
-
detail: "Remove the focused pane from the layout",
|
|
3993
|
-
category: "Focused Pane",
|
|
3994
|
-
kind: "action",
|
|
3995
|
-
action: () => {
|
|
3996
|
-
persistLayoutChange(removePane(currentLayout, focusedPane.instanceId));
|
|
3997
|
-
closeAll({ revertThemePreview: false });
|
|
3998
|
-
},
|
|
3999
|
-
});
|
|
4000
|
-
} else {
|
|
4001
|
-
layoutItems.push({
|
|
4002
|
-
id: "layout-no-focused-pane",
|
|
4003
|
-
label: "No focused pane",
|
|
4004
|
-
detail: "Focus a pane to show pane-specific layout actions",
|
|
4005
|
-
category: "Focused Pane",
|
|
4006
|
-
kind: "info",
|
|
4007
|
-
action: () => {},
|
|
4008
|
-
});
|
|
4009
|
-
}
|
|
4010
|
-
|
|
4011
|
-
layoutItems.push({
|
|
4012
|
-
id: "layout-undo",
|
|
4013
|
-
label: "Undo Layout Change",
|
|
4014
|
-
detail: (layoutHistory?.past.length ?? 0) > 0 ? "Restore the previous layout state" : "No previous layout state",
|
|
4015
|
-
category: "Current Layout",
|
|
4016
|
-
kind: "action",
|
|
4017
|
-
disabled: (layoutHistory?.past.length ?? 0) === 0,
|
|
4018
|
-
action: () => {
|
|
4019
|
-
if ((layoutHistory?.past.length ?? 0) === 0) return;
|
|
4020
|
-
dispatch({ type: "UNDO_LAYOUT" });
|
|
4021
|
-
closeAll({ revertThemePreview: false });
|
|
4022
|
-
},
|
|
4023
|
-
});
|
|
4024
|
-
layoutItems.push({
|
|
4025
|
-
id: "layout-redo",
|
|
4026
|
-
label: "Redo Layout Change",
|
|
4027
|
-
detail: (layoutHistory?.future.length ?? 0) > 0 ? "Reapply the next layout state" : "No later layout state",
|
|
4028
|
-
category: "Current Layout",
|
|
4029
|
-
kind: "action",
|
|
4030
|
-
disabled: (layoutHistory?.future.length ?? 0) === 0,
|
|
4031
|
-
action: () => {
|
|
4032
|
-
if ((layoutHistory?.future.length ?? 0) === 0) return;
|
|
4033
|
-
dispatch({ type: "REDO_LAYOUT" });
|
|
4034
|
-
closeAll({ revertThemePreview: false });
|
|
4035
|
-
},
|
|
4036
|
-
});
|
|
4037
|
-
layoutItems.push({
|
|
4038
|
-
id: "layout-reset",
|
|
4039
|
-
label: "Reset Current Layout",
|
|
4040
|
-
detail: "Restore the default two-pane layout",
|
|
4041
|
-
category: "Current Layout",
|
|
4042
|
-
kind: "action",
|
|
4043
|
-
action: () => {
|
|
4044
|
-
persistLayoutChange(cloneLayout(DEFAULT_LAYOUT));
|
|
4045
|
-
closeAll({ revertThemePreview: false });
|
|
4046
|
-
},
|
|
4047
|
-
});
|
|
4048
|
-
layoutItems.push({
|
|
4049
|
-
id: "layout-gridlock",
|
|
4050
|
-
label: "Gridlock All Windows",
|
|
4051
|
-
detail: currentLayout.floating.length > 0
|
|
4052
|
-
? "Infer a tiled layout from the current window positions"
|
|
4053
|
-
: "Retile all panes from their current arrangement",
|
|
4054
|
-
category: "Current Layout",
|
|
4055
|
-
kind: "action",
|
|
4056
|
-
action: () => {
|
|
4057
|
-
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
4058
|
-
persistLayoutChange(gridlockAllPanes(currentLayout, { x: 0, y: 0, width, height }));
|
|
4059
|
-
notifyGridlockRevert();
|
|
4060
|
-
closeAll({ revertThemePreview: false });
|
|
4061
|
-
},
|
|
4062
|
-
});
|
|
4063
|
-
layoutItems.push({
|
|
4064
|
-
id: "layout-rename",
|
|
4065
|
-
label: "Rename Layout",
|
|
4066
|
-
detail: "Change the current saved layout name",
|
|
4067
|
-
category: "Current Layout",
|
|
4068
|
-
kind: "action",
|
|
4069
|
-
action: () => openBuiltInWorkflow("rename-layout"),
|
|
4070
|
-
});
|
|
4071
|
-
layoutItems.push({
|
|
4072
|
-
id: "layout-duplicate-layout",
|
|
4073
|
-
label: "Duplicate Layout",
|
|
4074
|
-
detail: "Create a copy of the current layout",
|
|
4075
|
-
category: "Current Layout",
|
|
4076
|
-
kind: "action",
|
|
4077
|
-
action: () => {
|
|
4078
|
-
dispatch({ type: "DUPLICATE_LAYOUT", index: state.config.activeLayoutIndex });
|
|
4079
|
-
closeAll({ revertThemePreview: false });
|
|
4080
|
-
},
|
|
4081
|
-
});
|
|
4082
|
-
layoutItems.push({
|
|
4083
|
-
id: "layout-new",
|
|
4084
|
-
label: "New Layout",
|
|
4085
|
-
detail: "Create a fresh saved layout",
|
|
4086
|
-
category: "Current Layout",
|
|
4087
|
-
kind: "action",
|
|
4088
|
-
action: () => openBuiltInWorkflow("new-layout"),
|
|
4089
|
-
});
|
|
4090
|
-
|
|
4091
|
-
state.config.layouts.forEach((savedLayout, index) => {
|
|
4092
|
-
layoutItems.push({
|
|
4093
|
-
id: `layout-switch:${index}`,
|
|
4094
|
-
label: savedLayout.name,
|
|
4095
|
-
detail: index === state.config.activeLayoutIndex ? "Current layout" : "Switch to this saved layout",
|
|
4096
|
-
right: getLayoutPreview(savedLayout.layout),
|
|
4097
|
-
category: "Saved Layouts",
|
|
4098
|
-
kind: "action",
|
|
4099
|
-
current: index === state.config.activeLayoutIndex,
|
|
4100
|
-
action: () => {
|
|
4101
|
-
dispatch({ type: "SWITCH_LAYOUT", index });
|
|
4102
|
-
closeAll({ revertThemePreview: false });
|
|
4103
|
-
},
|
|
4104
|
-
});
|
|
4105
|
-
});
|
|
4106
|
-
|
|
4107
|
-
const results = currentRoute.query
|
|
4108
|
-
? fuzzyFilter(layoutItems, currentRoute.query, (item) => `${item.label} ${item.detail} ${item.right || ""}`)
|
|
4109
|
-
: layoutItems;
|
|
4110
|
-
|
|
4092
|
+
const results = buildLayoutItems(currentRoute.query);
|
|
4111
4093
|
return {
|
|
4112
4094
|
kind: "mode",
|
|
4113
4095
|
title: "Layout Actions",
|
|
@@ -4218,6 +4200,8 @@ export function CommandBar({
|
|
|
4218
4200
|
activeMatch,
|
|
4219
4201
|
activeTickerData,
|
|
4220
4202
|
activeTickerSymbol,
|
|
4203
|
+
buildLayoutItems,
|
|
4204
|
+
buildPluginItems,
|
|
4221
4205
|
closeAll,
|
|
4222
4206
|
createPaneTemplateItem,
|
|
4223
4207
|
currentRoute,
|
|
@@ -4241,7 +4225,7 @@ export function CommandBar({
|
|
|
4241
4225
|
rootHoveredIdx,
|
|
4242
4226
|
rootModeInfo.kind,
|
|
4243
4227
|
rootQuery,
|
|
4244
|
-
|
|
4228
|
+
orderedRootResults,
|
|
4245
4229
|
rootSearching,
|
|
4246
4230
|
rootSelectedIdx,
|
|
4247
4231
|
rootShortcutIntent,
|
|
@@ -4278,8 +4262,9 @@ export function CommandBar({
|
|
|
4278
4262
|
}, [currentRoute, routeListState, updateTopRoute]);
|
|
4279
4263
|
|
|
4280
4264
|
const setActiveListQuery = useCallback((nextQuery: string) => {
|
|
4281
|
-
|
|
4282
|
-
|
|
4265
|
+
const route = currentRouteRef.current;
|
|
4266
|
+
if (!route) {
|
|
4267
|
+
if (rootModeKindRef.current === "themes" && resolveCommandBarMode(nextQuery, availableCommands).kind !== "themes") {
|
|
4283
4268
|
clearThemePreview(rootThemeBaseIdRef.current ?? stateRef.current.config.theme);
|
|
4284
4269
|
rootThemeBaseIdRef.current = null;
|
|
4285
4270
|
}
|
|
@@ -4287,7 +4272,7 @@ export function CommandBar({
|
|
|
4287
4272
|
return;
|
|
4288
4273
|
}
|
|
4289
4274
|
|
|
4290
|
-
if (
|
|
4275
|
+
if (route.kind === "mode" || route.kind === "picker" || route.kind === "pane-settings") {
|
|
4291
4276
|
updateTopRoute((route) => {
|
|
4292
4277
|
if (route.kind === "mode" || route.kind === "picker" || route.kind === "pane-settings") {
|
|
4293
4278
|
return { ...route, query: nextQuery, selectedIdx: 0, hoveredIdx: null };
|
|
@@ -4295,19 +4280,21 @@ export function CommandBar({
|
|
|
4295
4280
|
return route;
|
|
4296
4281
|
});
|
|
4297
4282
|
}
|
|
4298
|
-
}, [availableCommands, clearThemePreview,
|
|
4283
|
+
}, [availableCommands, clearThemePreview, setRootQuery, updateTopRoute]);
|
|
4299
4284
|
|
|
4300
|
-
const
|
|
4285
|
+
const applyListSelectionDelta = useCallback((delta: number) => {
|
|
4301
4286
|
const listState = visibleListStateRef.current;
|
|
4302
4287
|
if (!listState || listState.results.length === 0 || delta === 0) return;
|
|
4303
4288
|
const nextIndex = clampListIndex(listState.selectedIdx + delta, listState.results.length);
|
|
4289
|
+
const selectionChanged = nextIndex !== listState.selectedIdx;
|
|
4290
|
+
const hoverChanged = listState.hoveredIdx !== null;
|
|
4291
|
+
if (!selectionChanged && !hoverChanged) return;
|
|
4304
4292
|
const nextListState = { ...listState, selectedIdx: nextIndex, hoveredIdx: null };
|
|
4305
4293
|
visibleListStateRef.current = nextListState;
|
|
4306
|
-
previewThemeSelection(nextListState, nextIndex);
|
|
4307
4294
|
|
|
4308
4295
|
if (!currentRouteRef.current) {
|
|
4309
|
-
setRootSelectedIdx(nextIndex);
|
|
4310
|
-
setRootHoveredIdx(null);
|
|
4296
|
+
setRootSelectedIdx((current) => (current === nextIndex ? current : nextIndex));
|
|
4297
|
+
setRootHoveredIdx((current) => (current === null ? current : null));
|
|
4311
4298
|
return;
|
|
4312
4299
|
}
|
|
4313
4300
|
setRouteStack((current) => {
|
|
@@ -4317,24 +4304,34 @@ export function CommandBar({
|
|
|
4317
4304
|
if (!top || (top.kind !== "mode" && top.kind !== "picker" && top.kind !== "pane-settings")) {
|
|
4318
4305
|
return current;
|
|
4319
4306
|
}
|
|
4307
|
+
if (top.selectedIdx === nextIndex && top.hoveredIdx === null) return current;
|
|
4320
4308
|
next[next.length - 1] = { ...top, selectedIdx: nextIndex, hoveredIdx: null };
|
|
4321
4309
|
return next;
|
|
4322
4310
|
});
|
|
4323
|
-
}, [
|
|
4311
|
+
}, []);
|
|
4312
|
+
|
|
4313
|
+
const moveListSelection = useCallback((delta: number) => {
|
|
4314
|
+
applyListSelectionDelta(delta);
|
|
4315
|
+
}, [applyListSelectionDelta]);
|
|
4324
4316
|
|
|
4325
4317
|
const setHoveredIndex = useCallback((index: number | null) => {
|
|
4326
|
-
if (!
|
|
4318
|
+
if (!currentRouteRef.current) {
|
|
4327
4319
|
setRootHoveredIdx((current) => (current === index ? current : index));
|
|
4328
4320
|
return;
|
|
4329
4321
|
}
|
|
4330
|
-
|
|
4322
|
+
setRouteStack((current) => {
|
|
4323
|
+
if (current.length === 0) return current;
|
|
4324
|
+
const next = [...current];
|
|
4325
|
+
const route = next[next.length - 1];
|
|
4326
|
+
if (!route) return current;
|
|
4331
4327
|
if (route.kind === "mode" || route.kind === "picker" || route.kind === "pane-settings") {
|
|
4332
|
-
if (route.hoveredIdx === index) return
|
|
4333
|
-
|
|
4328
|
+
if (route.hoveredIdx === index) return current;
|
|
4329
|
+
next[next.length - 1] = { ...route, hoveredIdx: index };
|
|
4330
|
+
return next;
|
|
4334
4331
|
}
|
|
4335
|
-
return
|
|
4332
|
+
return current;
|
|
4336
4333
|
});
|
|
4337
|
-
}, [
|
|
4334
|
+
}, []);
|
|
4338
4335
|
|
|
4339
4336
|
const handleListScroll = useCallback((event: {
|
|
4340
4337
|
stopPropagation: () => void;
|
|
@@ -4708,6 +4705,29 @@ export function CommandBar({
|
|
|
4708
4705
|
disconnectBrokerInstance,
|
|
4709
4706
|
executeCollectionCommand,
|
|
4710
4707
|
]);
|
|
4708
|
+
const activateListSelectionRef = useRef(activateListSelection);
|
|
4709
|
+
activateListSelectionRef.current = activateListSelection;
|
|
4710
|
+
|
|
4711
|
+
const handleListRowMouseDown = useCallback((event: any, item: ResultItem, globalIdx: number) => {
|
|
4712
|
+
event.stopPropagation?.();
|
|
4713
|
+
event.preventDefault?.();
|
|
4714
|
+
if (!currentRouteRef.current) {
|
|
4715
|
+
setRootSelectedIdx((current) => (current === globalIdx ? current : globalIdx));
|
|
4716
|
+
} else {
|
|
4717
|
+
setRouteStack((current) => {
|
|
4718
|
+
if (current.length === 0) return current;
|
|
4719
|
+
const next = [...current];
|
|
4720
|
+
const route = next[next.length - 1];
|
|
4721
|
+
if (!route || (route.kind !== "mode" && route.kind !== "picker" && route.kind !== "pane-settings")) {
|
|
4722
|
+
return current;
|
|
4723
|
+
}
|
|
4724
|
+
if (route.selectedIdx === globalIdx && route.hoveredIdx === globalIdx) return current;
|
|
4725
|
+
next[next.length - 1] = { ...route, selectedIdx: globalIdx, hoveredIdx: globalIdx };
|
|
4726
|
+
return next;
|
|
4727
|
+
});
|
|
4728
|
+
}
|
|
4729
|
+
activateListSelectionRef.current({ item });
|
|
4730
|
+
}, []);
|
|
4711
4731
|
|
|
4712
4732
|
const confirmCurrentRoute = useCallback(async () => {
|
|
4713
4733
|
if (currentRoute?.kind !== "confirm") return;
|
|
@@ -4845,6 +4865,7 @@ export function CommandBar({
|
|
|
4845
4865
|
preventDefault: () => void;
|
|
4846
4866
|
}) => void) => void;
|
|
4847
4867
|
};
|
|
4868
|
+
if (!keyInput) return undefined;
|
|
4848
4869
|
const handleKeyPress = (event: {
|
|
4849
4870
|
name: string;
|
|
4850
4871
|
sequence?: string;
|
|
@@ -5041,6 +5062,27 @@ export function CommandBar({
|
|
|
5041
5062
|
return;
|
|
5042
5063
|
}
|
|
5043
5064
|
|
|
5065
|
+
if (themePickerActive) {
|
|
5066
|
+
if (event.name === "up" || (event.ctrl && event.name === "p")) {
|
|
5067
|
+
event.stopPropagation();
|
|
5068
|
+
event.preventDefault();
|
|
5069
|
+
themePickerRef.current?.move(-1);
|
|
5070
|
+
return;
|
|
5071
|
+
}
|
|
5072
|
+
if (event.name === "down" || (event.ctrl && event.name === "n")) {
|
|
5073
|
+
event.stopPropagation();
|
|
5074
|
+
event.preventDefault();
|
|
5075
|
+
themePickerRef.current?.move(1);
|
|
5076
|
+
return;
|
|
5077
|
+
}
|
|
5078
|
+
if (event.name === "return" || event.name === "enter") {
|
|
5079
|
+
event.stopPropagation();
|
|
5080
|
+
event.preventDefault();
|
|
5081
|
+
themePickerRef.current?.commit();
|
|
5082
|
+
return;
|
|
5083
|
+
}
|
|
5084
|
+
}
|
|
5085
|
+
|
|
5044
5086
|
const activeListState = visibleListStateRef.current;
|
|
5045
5087
|
if (!activeListState) return;
|
|
5046
5088
|
|
|
@@ -5108,13 +5150,13 @@ export function CommandBar({
|
|
|
5108
5150
|
if (keyInput.onInternal) {
|
|
5109
5151
|
keyInput.onInternal("keypress", handleKeyPress);
|
|
5110
5152
|
} else {
|
|
5111
|
-
|
|
5153
|
+
keyInput.on("keypress", handleKeyPress);
|
|
5112
5154
|
}
|
|
5113
5155
|
return () => {
|
|
5114
5156
|
if (keyInput.offInternal) {
|
|
5115
5157
|
keyInput.offInternal("keypress", handleKeyPress);
|
|
5116
5158
|
} else {
|
|
5117
|
-
|
|
5159
|
+
keyInput.off("keypress", handleKeyPress);
|
|
5118
5160
|
}
|
|
5119
5161
|
};
|
|
5120
5162
|
}, [
|
|
@@ -5134,6 +5176,7 @@ export function CommandBar({
|
|
|
5134
5176
|
renderer,
|
|
5135
5177
|
rootModeInfo.kind,
|
|
5136
5178
|
setActiveListQuery,
|
|
5179
|
+
themePickerActive,
|
|
5137
5180
|
updateWorkflowValue,
|
|
5138
5181
|
]);
|
|
5139
5182
|
|
|
@@ -5155,8 +5198,23 @@ export function CommandBar({
|
|
|
5155
5198
|
? routeListState
|
|
5156
5199
|
: null;
|
|
5157
5200
|
const showCustomMultiSelectPicker = currentRoute?.kind === "picker" && currentRoute.pickerId === "field-multi-select";
|
|
5158
|
-
const listRows =
|
|
5159
|
-
|
|
5201
|
+
const listRows = useMemo(
|
|
5202
|
+
() => (visibleListState ? buildListRows(visibleListState) : []),
|
|
5203
|
+
[visibleListState?.results],
|
|
5204
|
+
);
|
|
5205
|
+
const nativeListRows = useMemo(
|
|
5206
|
+
() => (visibleListState ? buildNativeListRows(visibleListState, listRows) : []),
|
|
5207
|
+
[listRows, visibleListState?.emptyLabel, visibleListState?.searching],
|
|
5208
|
+
);
|
|
5209
|
+
const listRowIndexByGlobalIndex = useMemo(() => {
|
|
5210
|
+
const indexByGlobalIndex = new Map<number, number>();
|
|
5211
|
+
listRows.forEach((row, index) => {
|
|
5212
|
+
if (row.kind === "item") {
|
|
5213
|
+
indexByGlobalIndex.set(row.globalIdx, index);
|
|
5214
|
+
}
|
|
5215
|
+
});
|
|
5216
|
+
return indexByGlobalIndex;
|
|
5217
|
+
}, [listRows]);
|
|
5160
5218
|
const workflowBodyHeight = currentRoute?.kind === "workflow"
|
|
5161
5219
|
? Math.min(
|
|
5162
5220
|
Math.max(9, termHeight - (nativePaneChrome ? 7 : 9)),
|
|
@@ -5185,26 +5243,30 @@ export function CommandBar({
|
|
|
5185
5243
|
const trailingWidth = Math.max(8, Math.min(12, Math.floor(resultsInnerWidth * 0.18)));
|
|
5186
5244
|
const labelWidth = Math.max(10, resultsInnerWidth - trailingWidth);
|
|
5187
5245
|
const queryDisplayWidth = Math.max(8, resultsInnerWidth);
|
|
5188
|
-
const
|
|
5189
|
-
?
|
|
5246
|
+
const selectedListRowIndex = visibleListState
|
|
5247
|
+
? listRowIndexByGlobalIndex.get(visibleListState.selectedIdx) ?? -1
|
|
5190
5248
|
: -1;
|
|
5249
|
+
const selectedScrollRowIndex = selectedListRowIndex;
|
|
5191
5250
|
const bodySlotKey = showCustomMultiSelectPicker
|
|
5192
5251
|
? "picker:field-multi-select"
|
|
5193
|
-
:
|
|
5194
|
-
?
|
|
5195
|
-
: currentRoute?.kind
|
|
5252
|
+
: themePickerActive
|
|
5253
|
+
? "theme-picker"
|
|
5254
|
+
: currentRoute?.kind === "picker"
|
|
5255
|
+
? `picker:${currentRoute.pickerId}`
|
|
5256
|
+
: currentRoute?.kind ?? "root";
|
|
5196
5257
|
|
|
5197
|
-
|
|
5198
|
-
if (!nativePaneChrome || nativeSelectedRowIndex < 0) return;
|
|
5258
|
+
useLayoutEffect(() => {
|
|
5199
5259
|
const scrollBox = nativeListScrollRef.current;
|
|
5200
5260
|
if (!scrollBox) return;
|
|
5261
|
+
if (scrollBox.verticalScrollBar) scrollBox.verticalScrollBar.visible = false;
|
|
5262
|
+
if (selectedScrollRowIndex < 0) return;
|
|
5201
5263
|
const viewportHeight = Math.max(1, scrollBox.viewport?.height ?? listBodyHeight);
|
|
5202
|
-
if (
|
|
5203
|
-
scrollBox.scrollTo(
|
|
5204
|
-
} else if (
|
|
5205
|
-
scrollBox.scrollTo(
|
|
5264
|
+
if (selectedScrollRowIndex < scrollBox.scrollTop) {
|
|
5265
|
+
scrollBox.scrollTo(selectedScrollRowIndex);
|
|
5266
|
+
} else if (selectedScrollRowIndex >= scrollBox.scrollTop + viewportHeight) {
|
|
5267
|
+
scrollBox.scrollTo(selectedScrollRowIndex - viewportHeight + 1);
|
|
5206
5268
|
}
|
|
5207
|
-
}, [listBodyHeight,
|
|
5269
|
+
}, [listBodyHeight, selectedScrollRowIndex, visibleListState?.kind, visibleListState?.query]);
|
|
5208
5270
|
|
|
5209
5271
|
useEffect(() => {
|
|
5210
5272
|
if (!nativePaneChrome || currentRoute?.kind !== "workflow") return;
|
|
@@ -5225,140 +5287,6 @@ export function CommandBar({
|
|
|
5225
5287
|
}
|
|
5226
5288
|
}, [bodyHeight, currentRoute, nativePaneChrome]);
|
|
5227
5289
|
|
|
5228
|
-
const renderListBody = () => {
|
|
5229
|
-
if (!visibleListState) return null;
|
|
5230
|
-
|
|
5231
|
-
const allRows = listRows;
|
|
5232
|
-
let visibleRows: CommandBarListRow[];
|
|
5233
|
-
if (nativePaneChrome) {
|
|
5234
|
-
visibleRows = nativeListRows;
|
|
5235
|
-
} else if (visibleListState.searching && allRows.length === 0) {
|
|
5236
|
-
visibleRows = [{ kind: "spinner", id: "searching", label: "Searching…" }];
|
|
5237
|
-
} else if (allRows.length === 0) {
|
|
5238
|
-
visibleRows = [{ kind: "message", id: "empty", label: visibleListState.emptyLabel }];
|
|
5239
|
-
} else {
|
|
5240
|
-
const selectedRowIdx = allRows.findIndex((row) => row.kind === "item" && row.globalIdx === visibleListState.selectedIdx);
|
|
5241
|
-
const halfWindow = Math.floor(listBodyHeight / 2);
|
|
5242
|
-
let windowStart = Math.max(0, Math.min(selectedRowIdx - halfWindow, allRows.length - listBodyHeight));
|
|
5243
|
-
if (windowStart < 0) windowStart = 0;
|
|
5244
|
-
visibleRows = allRows.slice(windowStart, windowStart + listBodyHeight);
|
|
5245
|
-
if (visibleListState.searching) {
|
|
5246
|
-
if (visibleRows.length >= listBodyHeight) {
|
|
5247
|
-
visibleRows = visibleRows.slice(0, listBodyHeight - 1);
|
|
5248
|
-
}
|
|
5249
|
-
visibleRows.push({ kind: "spinner", id: "searching", label: "Searching…" });
|
|
5250
|
-
}
|
|
5251
|
-
}
|
|
5252
|
-
|
|
5253
|
-
while (!nativePaneChrome && visibleRows.length < listBodyHeight) {
|
|
5254
|
-
visibleRows.push({ kind: "filler", id: `filler:${visibleRows.length}` });
|
|
5255
|
-
}
|
|
5256
|
-
|
|
5257
|
-
const renderedRows = (
|
|
5258
|
-
<>
|
|
5259
|
-
{visibleRows.map((row) => {
|
|
5260
|
-
if (row.kind === "filler" || row.kind === "spacer") {
|
|
5261
|
-
return <Box key={row.id} height={1} />;
|
|
5262
|
-
}
|
|
5263
|
-
if (row.kind === "spinner") {
|
|
5264
|
-
return (
|
|
5265
|
-
<Box key={row.id} height={1} paddingX={contentPadding} {...(!nativePaneChrome ? { onMouseScroll: handleListScroll } : {})}>
|
|
5266
|
-
<Spinner label={row.label} />
|
|
5267
|
-
</Box>
|
|
5268
|
-
);
|
|
5269
|
-
}
|
|
5270
|
-
if (row.kind === "message") {
|
|
5271
|
-
return (
|
|
5272
|
-
<Box key={row.id} height={1} paddingX={contentPadding} {...(!nativePaneChrome ? { onMouseScroll: handleListScroll } : {})}>
|
|
5273
|
-
<Text fg={paletteText}>{truncateText(row.label, queryDisplayWidth)}</Text>
|
|
5274
|
-
</Box>
|
|
5275
|
-
);
|
|
5276
|
-
}
|
|
5277
|
-
if (row.kind === "heading") {
|
|
5278
|
-
return (
|
|
5279
|
-
<Box key={row.id} height={1} paddingX={contentPadding} {...(!nativePaneChrome ? { onMouseScroll: handleListScroll } : {})}>
|
|
5280
|
-
<Text attributes={TextAttributes.BOLD} fg={paletteHeadingText}>
|
|
5281
|
-
{truncateText(row.label, queryDisplayWidth)}
|
|
5282
|
-
</Text>
|
|
5283
|
-
</Box>
|
|
5284
|
-
);
|
|
5285
|
-
}
|
|
5286
|
-
|
|
5287
|
-
const isSelected = row.globalIdx === visibleListState.selectedIdx;
|
|
5288
|
-
const isHovered = row.globalIdx === visibleListState.hoveredIdx && !isSelected;
|
|
5289
|
-
const presentation = getRowPresentation(row.item, isSelected, trailingWidth > 0);
|
|
5290
|
-
const label = truncateText(presentation.label, labelWidth);
|
|
5291
|
-
const trailing = truncateText(presentation.trailing, trailingWidth);
|
|
5292
|
-
return (
|
|
5293
|
-
<Box
|
|
5294
|
-
key={row.item.id}
|
|
5295
|
-
flexDirection="row"
|
|
5296
|
-
height={1}
|
|
5297
|
-
paddingX={contentPadding}
|
|
5298
|
-
backgroundColor={isSelected
|
|
5299
|
-
? paletteSelectedBg
|
|
5300
|
-
: isHovered
|
|
5301
|
-
? paletteHoverBg
|
|
5302
|
-
: (nativePaneChrome ? panelBg : paletteBg)}
|
|
5303
|
-
onMouseMove={() => setHoveredIndex(row.globalIdx)}
|
|
5304
|
-
onMouseOut={() => setHoveredIndex(null)}
|
|
5305
|
-
{...(!nativePaneChrome ? { onMouseScroll: handleListScroll } : {})}
|
|
5306
|
-
onMouseDown={(event: any) => {
|
|
5307
|
-
event.stopPropagation?.();
|
|
5308
|
-
event.preventDefault?.();
|
|
5309
|
-
if (!currentRoute) {
|
|
5310
|
-
setRootSelectedIdx(row.globalIdx);
|
|
5311
|
-
} else {
|
|
5312
|
-
updateTopRoute((route) => {
|
|
5313
|
-
if (route.kind === "mode" || route.kind === "picker" || route.kind === "pane-settings") {
|
|
5314
|
-
return { ...route, selectedIdx: row.globalIdx, hoveredIdx: row.globalIdx };
|
|
5315
|
-
}
|
|
5316
|
-
return route;
|
|
5317
|
-
});
|
|
5318
|
-
}
|
|
5319
|
-
activateListSelection({ item: row.item });
|
|
5320
|
-
}}
|
|
5321
|
-
data-command-bar-row-selected={nativePaneChrome && isSelected ? "true" : undefined}
|
|
5322
|
-
style={nativePaneChrome ? { borderRadius: 6 } : undefined}
|
|
5323
|
-
>
|
|
5324
|
-
<Box width={labelWidth}>
|
|
5325
|
-
<Text fg={isSelected ? paletteSelectedText : presentation.primaryMuted ? paletteSubtleText : paletteText}>
|
|
5326
|
-
{label}
|
|
5327
|
-
</Text>
|
|
5328
|
-
</Box>
|
|
5329
|
-
<Box width={trailingWidth}>
|
|
5330
|
-
<Text fg={isSelected ? paletteSelectedText : paletteSubtleText}>{trailing}</Text>
|
|
5331
|
-
</Box>
|
|
5332
|
-
</Box>
|
|
5333
|
-
);
|
|
5334
|
-
})}
|
|
5335
|
-
</>
|
|
5336
|
-
);
|
|
5337
|
-
|
|
5338
|
-
if (nativePaneChrome) {
|
|
5339
|
-
return (
|
|
5340
|
-
<ScrollBox
|
|
5341
|
-
ref={nativeListScrollRef}
|
|
5342
|
-
flexDirection="column"
|
|
5343
|
-
height={listBodyHeight}
|
|
5344
|
-
scrollY
|
|
5345
|
-
>
|
|
5346
|
-
{renderedRows}
|
|
5347
|
-
</ScrollBox>
|
|
5348
|
-
);
|
|
5349
|
-
}
|
|
5350
|
-
|
|
5351
|
-
return (
|
|
5352
|
-
<Box
|
|
5353
|
-
flexDirection="column"
|
|
5354
|
-
height={listBodyHeight}
|
|
5355
|
-
onMouseScroll={handleListScroll}
|
|
5356
|
-
>
|
|
5357
|
-
{renderedRows}
|
|
5358
|
-
</Box>
|
|
5359
|
-
);
|
|
5360
|
-
};
|
|
5361
|
-
|
|
5362
5290
|
const renderWorkflowBody = () => {
|
|
5363
5291
|
if (currentRoute?.kind !== "workflow") return null;
|
|
5364
5292
|
const visibleFields = getVisibleWorkflowFields(currentRoute.fields, currentRoute.values);
|
|
@@ -5436,7 +5364,7 @@ export function CommandBar({
|
|
|
5436
5364
|
borderColor={active ? paletteSelectedBg : paletteBg}
|
|
5437
5365
|
backgroundColor={nativePaneChrome ? inputBg : fieldBg}
|
|
5438
5366
|
style={nativePaneChrome ? {
|
|
5439
|
-
border: `1px solid ${active ?
|
|
5367
|
+
border: `1px solid ${active ? themeColors.borderFocused : themeColors.border}`,
|
|
5440
5368
|
borderRadius: 6,
|
|
5441
5369
|
overflow: "hidden",
|
|
5442
5370
|
} : undefined}
|
|
@@ -5450,7 +5378,7 @@ export function CommandBar({
|
|
|
5450
5378
|
focused={!currentRoute.pending}
|
|
5451
5379
|
textColor={paletteText}
|
|
5452
5380
|
placeholderColor={paletteSubtleText}
|
|
5453
|
-
backgroundColor={nativePaneChrome ? inputBg :
|
|
5381
|
+
backgroundColor={nativePaneChrome ? inputBg : themeColors.panel}
|
|
5454
5382
|
flexGrow={1}
|
|
5455
5383
|
wrapText
|
|
5456
5384
|
/>
|
|
@@ -5531,7 +5459,7 @@ export function CommandBar({
|
|
|
5531
5459
|
})}
|
|
5532
5460
|
{currentRoute.error && (
|
|
5533
5461
|
<Box height={1}>
|
|
5534
|
-
<Text fg={
|
|
5462
|
+
<Text fg={themeColors.negative}>{truncateText(currentRoute.error, queryDisplayWidth)}</Text>
|
|
5535
5463
|
</Box>
|
|
5536
5464
|
)}
|
|
5537
5465
|
{currentRoute.pendingLabel && currentRoute.pending && (
|
|
@@ -5574,33 +5502,14 @@ export function CommandBar({
|
|
|
5574
5502
|
const renderConfirmBody = () => {
|
|
5575
5503
|
if (currentRoute?.kind !== "confirm") return null;
|
|
5576
5504
|
return (
|
|
5577
|
-
<
|
|
5578
|
-
{currentRoute
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
<Box height={1}>
|
|
5586
|
-
<Text fg={colors.negative}>{truncateText(currentRoute.error, queryDisplayWidth)}</Text>
|
|
5587
|
-
</Box>
|
|
5588
|
-
)}
|
|
5589
|
-
{currentRoute.pending && (
|
|
5590
|
-
<Box height={1}>
|
|
5591
|
-
<Spinner label="Working…" />
|
|
5592
|
-
</Box>
|
|
5593
|
-
)}
|
|
5594
|
-
<Box flexGrow={1} />
|
|
5595
|
-
<Box flexDirection="row" gap={1}>
|
|
5596
|
-
<Button
|
|
5597
|
-
label={currentRoute.confirmLabel}
|
|
5598
|
-
variant={currentRoute.tone === "danger" ? "danger" : "primary"}
|
|
5599
|
-
onPress={() => { void confirmCurrentRoute(); }}
|
|
5600
|
-
disabled={currentRoute.pending}
|
|
5601
|
-
/>
|
|
5602
|
-
</Box>
|
|
5603
|
-
</Box>
|
|
5505
|
+
<CommandBarConfirmBody
|
|
5506
|
+
route={currentRoute}
|
|
5507
|
+
bodyHeight={bodyHeight}
|
|
5508
|
+
contentPadding={contentPadding}
|
|
5509
|
+
paletteText={paletteText}
|
|
5510
|
+
queryDisplayWidth={queryDisplayWidth}
|
|
5511
|
+
onConfirm={() => { void confirmCurrentRoute(); }}
|
|
5512
|
+
/>
|
|
5604
5513
|
);
|
|
5605
5514
|
};
|
|
5606
5515
|
|
|
@@ -5691,10 +5600,9 @@ export function CommandBar({
|
|
|
5691
5600
|
<Box flexGrow={1}>
|
|
5692
5601
|
<Text fg={paletteText} attributes={TextAttributes.BOLD}>
|
|
5693
5602
|
{currentRoute?.kind === "mode"
|
|
5694
|
-
? currentRoute.screen === "
|
|
5695
|
-
: currentRoute.screen === "
|
|
5696
|
-
:
|
|
5697
|
-
: "Security Description"
|
|
5603
|
+
? currentRoute.screen === "plugins" ? "Manage Plugins"
|
|
5604
|
+
: currentRoute.screen === "layout" ? "Layout Actions"
|
|
5605
|
+
: "Security Description"
|
|
5698
5606
|
: currentRoute?.kind === "picker" ? currentRoute.title
|
|
5699
5607
|
: currentRoute?.kind === "pane-settings" ? "Pane Settings"
|
|
5700
5608
|
: currentRoute?.kind === "workflow" ? currentRoute.title
|
|
@@ -5723,61 +5631,80 @@ export function CommandBar({
|
|
|
5723
5631
|
)}
|
|
5724
5632
|
{(visibleListState || currentRoute?.kind === "picker") && visibleListState && (
|
|
5725
5633
|
<>
|
|
5726
|
-
<
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
width={nativePaneChrome ? "100%" : queryDisplayWidth}
|
|
5743
|
-
backgroundColor={nativePaneChrome ? "transparent" : paletteBg}
|
|
5744
|
-
focusedBackgroundColor={nativePaneChrome ? "transparent" : paletteBg}
|
|
5745
|
-
textColor={paletteText}
|
|
5746
|
-
focusedTextColor={paletteText}
|
|
5747
|
-
placeholderColor={paletteSubtleText}
|
|
5748
|
-
cursorColor={colors.textBright}
|
|
5749
|
-
/>
|
|
5750
|
-
{visibleListState.kind === "root" && rootGhostSuffix && (
|
|
5751
|
-
<Box
|
|
5752
|
-
position="absolute"
|
|
5753
|
-
top={0}
|
|
5754
|
-
left={Math.max(0, Math.min(rootQuery.length, queryDisplayWidth - 1))}
|
|
5755
|
-
width={Math.max(0, queryDisplayWidth - Math.min(rootQuery.length, queryDisplayWidth - 1))}
|
|
5756
|
-
height={1}
|
|
5757
|
-
>
|
|
5758
|
-
<Text fg={paletteSubtleText}>
|
|
5759
|
-
{truncateText(
|
|
5760
|
-
rootGhostSuffix,
|
|
5761
|
-
Math.max(0, queryDisplayWidth - Math.min(rootQuery.length, queryDisplayWidth - 1)),
|
|
5762
|
-
)}
|
|
5763
|
-
</Text>
|
|
5764
|
-
</Box>
|
|
5765
|
-
)}
|
|
5766
|
-
</Box>
|
|
5767
|
-
</Box>
|
|
5768
|
-
<Box height={1} paddingX={contentPadding}>
|
|
5769
|
-
{visibleListState.kind === "root" && rootShortcutFeedback
|
|
5770
|
-
? (
|
|
5771
|
-
<Text fg={paletteSubtleText}>
|
|
5772
|
-
{truncateText(rootShortcutFeedback, queryDisplayWidth)}
|
|
5773
|
-
</Text>
|
|
5774
|
-
)
|
|
5775
|
-
: null}
|
|
5776
|
-
</Box>
|
|
5634
|
+
<CommandBarListHeader
|
|
5635
|
+
kind={visibleListState.kind}
|
|
5636
|
+
query={visibleListState.query}
|
|
5637
|
+
queryDisplayWidth={queryDisplayWidth}
|
|
5638
|
+
nativePaneChrome={nativePaneChrome}
|
|
5639
|
+
inputBg={inputBg}
|
|
5640
|
+
paletteBg={paletteBg}
|
|
5641
|
+
paletteText={paletteText}
|
|
5642
|
+
paletteSubtleText={paletteSubtleText}
|
|
5643
|
+
cursorColor={themeColors.textBright}
|
|
5644
|
+
contentPadding={contentPadding}
|
|
5645
|
+
rootGhostSuffix={rootGhostSuffix}
|
|
5646
|
+
rootQueryLength={rootQuery.length}
|
|
5647
|
+
rootShortcutFeedback={rootShortcutFeedback}
|
|
5648
|
+
onQueryChange={setActiveListQuery}
|
|
5649
|
+
/>
|
|
5777
5650
|
</>
|
|
5778
5651
|
)}
|
|
5779
5652
|
|
|
5780
|
-
{
|
|
5653
|
+
{themePickerActive && (
|
|
5654
|
+
<ThemePicker
|
|
5655
|
+
ref={themePickerRef}
|
|
5656
|
+
filter={themePickerFilter}
|
|
5657
|
+
committedThemeId={state.config.theme}
|
|
5658
|
+
height={listBodyHeight}
|
|
5659
|
+
contentPadding={contentPadding}
|
|
5660
|
+
labelWidth={labelWidth}
|
|
5661
|
+
trailingWidth={trailingWidth}
|
|
5662
|
+
queryDisplayWidth={queryDisplayWidth}
|
|
5663
|
+
nativePaneChrome={nativePaneChrome}
|
|
5664
|
+
paletteBg={paletteBg}
|
|
5665
|
+
paletteHoverBg={paletteHoverBg}
|
|
5666
|
+
paletteSelectedBg={paletteSelectedBg}
|
|
5667
|
+
paletteSelectedText={paletteSelectedText}
|
|
5668
|
+
paletteSubtleText={paletteSubtleText}
|
|
5669
|
+
paletteText={paletteText}
|
|
5670
|
+
panelBg={panelBg}
|
|
5671
|
+
onPreview={applyThemePreview}
|
|
5672
|
+
onCommit={(themeId) => {
|
|
5673
|
+
const nextConfig = {
|
|
5674
|
+
...stateRef.current.config,
|
|
5675
|
+
theme: themeId,
|
|
5676
|
+
};
|
|
5677
|
+
commitTheme(themeId);
|
|
5678
|
+
void saveConfig(nextConfig);
|
|
5679
|
+
closeAll({ revertThemePreview: false });
|
|
5680
|
+
}}
|
|
5681
|
+
/>
|
|
5682
|
+
)}
|
|
5683
|
+
|
|
5684
|
+
{visibleListState && !themePickerActive && !showCustomMultiSelectPicker && (
|
|
5685
|
+
<CommandBarListBody
|
|
5686
|
+
visibleListState={visibleListState}
|
|
5687
|
+
nativeListRows={nativeListRows}
|
|
5688
|
+
listBodyHeight={listBodyHeight}
|
|
5689
|
+
contentPadding={contentPadding}
|
|
5690
|
+
labelWidth={labelWidth}
|
|
5691
|
+
nativePaneChrome={nativePaneChrome}
|
|
5692
|
+
nativeListScrollRef={nativeListScrollRef}
|
|
5693
|
+
paletteBg={paletteBg}
|
|
5694
|
+
paletteHeadingText={paletteHeadingText}
|
|
5695
|
+
paletteHoverBg={paletteHoverBg}
|
|
5696
|
+
paletteSelectedBg={paletteSelectedBg}
|
|
5697
|
+
paletteSelectedText={paletteSelectedText}
|
|
5698
|
+
paletteSubtleText={paletteSubtleText}
|
|
5699
|
+
paletteText={paletteText}
|
|
5700
|
+
panelBg={panelBg}
|
|
5701
|
+
queryDisplayWidth={queryDisplayWidth}
|
|
5702
|
+
trailingWidth={trailingWidth}
|
|
5703
|
+
onHoverIndex={setHoveredIndex}
|
|
5704
|
+
onListScroll={handleListScroll}
|
|
5705
|
+
onRowMouseDown={handleListRowMouseDown}
|
|
5706
|
+
/>
|
|
5707
|
+
)}
|
|
5781
5708
|
{currentRoute?.kind === "workflow" && renderWorkflowBody()}
|
|
5782
5709
|
{currentRoute?.kind === "confirm" && renderConfirmBody()}
|
|
5783
5710
|
{showCustomMultiSelectPicker && renderMultiSelectBody()}
|