gloomberb 0.7.2 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -132
- package/package.json +1 -1
- package/src/app.tsx +73 -67
- package/src/components/chart/chart-data.ts +7 -2
- package/src/components/chart/chart-pane-settings.ts +7 -4
- package/src/components/chart/chart-renderer.ts +49 -15
- package/src/components/chart/chart-types.ts +2 -2
- package/src/components/chart/chart.test.ts +27 -8
- package/src/components/chart/comparison-stock-chart.tsx +84 -121
- package/src/components/chart/native/chart-rasterizer.ts +43 -17
- package/src/components/chart/price-axis-labels.tsx +25 -9
- package/src/components/chart/stock-chart-renderer-switch.test.tsx +1 -1
- package/src/components/chart/stock-chart.tsx +59 -41
- package/src/components/command-bar/command-bar.test.tsx +446 -120
- package/src/components/command-bar/command-bar.tsx +528 -383
- package/src/components/command-bar/command-registry.ts +16 -6
- package/src/components/command-bar/root-shortcuts.ts +4 -5
- package/src/components/command-bar/view-model.test.ts +1 -0
- package/src/components/command-bar/view-model.ts +2 -1
- package/src/components/command-bar/workflow-ops.ts +11 -32
- package/src/components/data-table-view.test.tsx +24 -1
- package/src/components/data-table-view.tsx +3 -2
- package/src/components/feed-data-table-stack-view.tsx +3 -2
- package/src/components/layout/detached-pane-shell.tsx +2 -2
- package/src/components/layout/floating-pane.tsx +9 -2
- package/src/components/layout/pane-footer.tsx +1 -1
- package/src/components/layout/pane-header.tsx +4 -2
- package/src/components/layout/pane-sizing.ts +12 -0
- package/src/components/layout/pane.tsx +24 -2
- package/src/components/layout/shell.tsx +24 -10
- package/src/components/layout/status-bar.test.tsx +4 -1
- package/src/components/layout/status-bar.tsx +4 -24
- package/src/components/markdown-text.tsx +53 -8
- package/src/components/onboarding/onboarding-wizard.tsx +7 -6
- package/src/components/pane-settings-dialog.tsx +9 -6
- package/src/components/pane-template-wizard.tsx +3 -2
- package/src/components/price-selector-dialog.tsx +3 -2
- package/src/components/price-sparkline-view.tsx +463 -0
- package/src/components/price-sparkline.ts +46 -0
- package/src/components/table-view-shared.tsx +21 -3
- package/src/components/ticker-list-table-view.tsx +3 -2
- package/src/components/ticker-list-table.tsx +11 -51
- package/src/components/toggle-list.tsx +3 -3
- package/src/components/ui/choice-dialog.tsx +3 -2
- package/src/components/ui/multi-select-dialog.tsx +3 -2
- package/src/components/ui/native-select.tsx +3 -3
- package/src/core/state/app-state.ts +210 -80
- package/src/data/config-store-node.ts +113 -25
- package/src/data/config-store.test.ts +65 -3
- package/src/data/plugin-state-store.ts +39 -13
- package/src/data/sqlite-cache.test.ts +13 -0
- package/src/market-data/coordinator.ts +8 -1
- package/src/news/aggregator.test.ts +41 -0
- package/src/news/aggregator.ts +61 -9
- package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +25 -6
- package/src/plugins/builtin/ai/settings.ts +3 -0
- package/src/plugins/builtin/broker-manager/index.tsx +4 -3
- package/src/plugins/builtin/chat-controller.test.ts +398 -17
- package/src/plugins/builtin/chat-controller.ts +258 -44
- package/src/plugins/builtin/chat.test.tsx +257 -6
- package/src/plugins/builtin/chat.tsx +225 -60
- package/src/plugins/builtin/comparison-chart.test.tsx +117 -3
- package/src/plugins/builtin/correlation/colors.test.ts +39 -0
- package/src/plugins/builtin/correlation/colors.ts +87 -0
- package/src/plugins/builtin/correlation/index.test.tsx +151 -0
- package/src/plugins/builtin/correlation/index.tsx +92 -21
- package/src/plugins/builtin/debug.tsx +4 -3
- package/src/plugins/builtin/econ/index.tsx +3 -2
- package/src/plugins/builtin/help.test.tsx +2 -2
- package/src/plugins/builtin/holders/index.tsx +25 -13
- package/src/plugins/builtin/layout-manager.tsx +1 -7
- package/src/plugins/builtin/news-wire/index.tsx +1 -1
- package/src/plugins/builtin/news-wire/news-detail-view.tsx +3 -2
- package/src/plugins/builtin/news-wire/news-preset-pane.tsx +4 -1
- package/src/plugins/builtin/news-wire/persisted-articles.ts +7 -1
- package/src/plugins/builtin/options.tsx +9 -17
- package/src/plugins/builtin/portfolio-list/cli/commands.ts +6 -10
- package/src/plugins/builtin/portfolio-list/metrics.ts +11 -0
- package/src/plugins/builtin/portfolio-list/mutations.ts +17 -0
- package/src/plugins/builtin/portfolio-list/pane.tsx +117 -18
- package/src/plugins/builtin/portfolio-list/quick-add.tsx +422 -0
- package/src/plugins/builtin/portfolio-list/settings.ts +20 -12
- package/src/plugins/builtin/portfolio-list/table.tsx +3 -3
- package/src/plugins/builtin/portfolio-list.test.tsx +282 -2
- package/src/plugins/builtin/quote-monitor.test.tsx +158 -25
- package/src/plugins/builtin/sectors/index.tsx +252 -41
- package/src/plugins/builtin/sectors/sector-data.ts +41 -1
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +3 -2
- package/src/plugins/builtin/ticker-detail/index.tsx +30 -9
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +25 -15
- package/src/plugins/builtin/ticker-detail/pane.tsx +0 -6
- package/src/plugins/builtin/ticker-detail/quote-monitor.tsx +442 -63
- package/src/plugins/builtin/ticker-detail/settings.ts +84 -3
- package/src/plugins/builtin/ticker-detail.test.tsx +2 -2
- package/src/plugins/ibkr/trading-pane.tsx +3 -2
- package/src/plugins/plugin-runtime.tsx +2 -2
- package/src/plugins/prediction-markets/settings.ts +4 -0
- package/src/plugins/registry.test.ts +55 -0
- package/src/plugins/registry.ts +11 -4
- package/src/plugins/ticker-navigation.test.ts +35 -5
- package/src/plugins/ticker-navigation.ts +16 -22
- package/src/renderers/electrobun/bun/application-menu-click.test.ts +9 -0
- package/src/renderers/electrobun/bun/application-menu-click.ts +7 -4
- package/src/renderers/electrobun/bun/application-menu.ts +6 -1
- package/src/renderers/electrobun/bun/desktop-workspace.ts +47 -29
- package/src/renderers/electrobun/bun/index.ts +52 -13
- package/src/renderers/electrobun/view/app-services.ts +51 -9
- package/src/renderers/electrobun/view/backend-rpc.ts +5 -8
- package/src/renderers/electrobun/view/desktop-controls.tsx +4 -4
- package/src/renderers/electrobun/view/main.tsx +0 -16
- package/src/renderers/electrobun/view/styles.css +23 -21
- package/src/renderers/electrobun/view/ui-host.tsx +6 -4
- package/src/renderers/opentui/host.tsx +2 -1
- package/src/renderers/opentui/start.tsx +1 -1
- package/src/renderers/opentui/test-utils.tsx +6 -5
- package/src/state/app-bootstrap.ts +3 -3
- package/src/state/app-context.test.ts +113 -0
- package/src/state/app-context.tsx +8 -4
- package/src/theme/colors.ts +13 -14
- package/src/types/config.ts +9 -4
- package/src/types/plugin.ts +8 -1
- package/src/ui/context-menu.tsx +4 -4
- package/src/utils/api-client.test.ts +128 -0
- package/src/utils/api-client.ts +112 -7
- package/src/utils/fuzzy-search.ts +12 -1
- package/src/utils/keyboard.ts +31 -0
- package/src/utils/ticker-search.test.ts +2 -2
- package/src/utils/ticker-search.ts +48 -1
- package/src/version.ts +1 -1
|
@@ -2,8 +2,7 @@ import { Box, Input, ScrollBox, Text, Textarea, useUiCapabilities } from "../../
|
|
|
2
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
|
-
import {
|
|
6
|
-
import { useViewport } from "../../react/input";
|
|
5
|
+
import { useShortcut, useViewport } from "../../react/input";
|
|
7
6
|
import { Button, NumberField, Spinner, TextField } from "../ui";
|
|
8
7
|
import { NativeSelect, openNativeSelect, type NativeSelectElement } from "../ui/native-select";
|
|
9
8
|
import { ToggleList } from "../toggle-list";
|
|
@@ -19,21 +18,25 @@ import {
|
|
|
19
18
|
commandBarSelectedText,
|
|
20
19
|
commandBarSubtleText,
|
|
21
20
|
commandBarText,
|
|
21
|
+
blendHex,
|
|
22
|
+
colors,
|
|
22
23
|
getCurrentThemeId,
|
|
23
24
|
previewTheme,
|
|
24
25
|
} from "../../theme/colors";
|
|
25
26
|
import {
|
|
26
27
|
getFocusedCollectionId,
|
|
28
|
+
syncConfigActiveLayoutState,
|
|
27
29
|
useAppDispatch,
|
|
28
30
|
useAppSelector,
|
|
29
31
|
useFocusedTicker,
|
|
30
32
|
type AppState,
|
|
31
33
|
} from "../../state/app-context";
|
|
34
|
+
import { scheduleConfigSave } from "../../state/config-save-scheduler";
|
|
32
35
|
import { fuzzyFilter } from "../../utils/fuzzy-search";
|
|
33
36
|
import { commands, matchPrefix, type Command } from "./command-registry";
|
|
34
37
|
import { useThemeColors } from "../../theme/theme-context";
|
|
35
38
|
import { ThemePicker, type ThemePickerHandle } from "./theme-picker";
|
|
36
|
-
import { exportConfig, importConfig, resetAllData
|
|
39
|
+
import { exportConfig, importConfig, resetAllData } from "../../data/config-store";
|
|
37
40
|
import type { DataProvider } from "../../types/data-provider";
|
|
38
41
|
import type { TickerRepository } from "../../data/ticker-repository";
|
|
39
42
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
@@ -185,6 +188,37 @@ const NATIVE_COMMAND_BAR_PADDING_X_PX = 14;
|
|
|
185
188
|
const NATIVE_COMMAND_BAR_PADDING_Y_PX = 14;
|
|
186
189
|
const QUICK_LOOK_TICKER_SEARCH_OPTIONS = { includeOptionContracts: false } as const;
|
|
187
190
|
|
|
191
|
+
function normalizeCommandTickerSearchText(value: string): string {
|
|
192
|
+
return value.trim().toUpperCase().replace(/[^A-Z0-9]+/g, "");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function isLikelyPlainTickerSearch(query: string): boolean {
|
|
196
|
+
const trimmed = query.trim();
|
|
197
|
+
if (!trimmed || /\s/.test(trimmed)) return false;
|
|
198
|
+
if (!/^[A-Za-z0-9.^=\-/]+$/.test(trimmed)) return false;
|
|
199
|
+
|
|
200
|
+
const compact = normalizeCommandTickerSearchText(trimmed);
|
|
201
|
+
if (compact.length < 2) return false;
|
|
202
|
+
if (compact.length <= 6) return true;
|
|
203
|
+
return /[.^=\-/]/.test(trimmed) && compact.length <= 10;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function isExactTickerResultMatch(item: ResultItem, query: string): boolean {
|
|
207
|
+
if (item.kind !== "ticker" && item.kind !== "search") return false;
|
|
208
|
+
const normalizedQuery = normalizeCommandTickerSearchText(query);
|
|
209
|
+
if (!normalizedQuery) return false;
|
|
210
|
+
return normalizeCommandTickerSearchText(item.label) === normalizedQuery;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function getPaneTemplateArgKind(template: PaneTemplateDef): string | undefined {
|
|
214
|
+
return template.shortcut?.argKind ?? template.shortcut?.argPlaceholder;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function canPromptForPaneTemplateArg(template: PaneTemplateDef): boolean {
|
|
218
|
+
const argKind = getPaneTemplateArgKind(template);
|
|
219
|
+
return argKind === "ticker" || argKind === "ticker-list" || argKind === "tickers";
|
|
220
|
+
}
|
|
221
|
+
|
|
188
222
|
function getDefaultConfigBackupPath(): string {
|
|
189
223
|
const home = typeof process !== "undefined" ? process.env.HOME : undefined;
|
|
190
224
|
return `${home || "~"}/gloomberb-config-backup.json`;
|
|
@@ -563,9 +597,10 @@ const CommandBarListBody = memo(function CommandBarListBody({
|
|
|
563
597
|
|
|
564
598
|
const isSelected = row.globalIdx === visibleListState.selectedIdx;
|
|
565
599
|
const isHovered = row.globalIdx === visibleListState.hoveredIdx && !isSelected;
|
|
600
|
+
const itemRowKey = `item:${row.globalIdx}:${row.item.id}:${row.item.category}:${row.item.label}:${row.item.right || ""}`;
|
|
566
601
|
return (
|
|
567
602
|
<CommandBarListItemRow
|
|
568
|
-
key={
|
|
603
|
+
key={itemRowKey}
|
|
569
604
|
item={row.item}
|
|
570
605
|
globalIdx={row.globalIdx}
|
|
571
606
|
isSelected={isSelected}
|
|
@@ -618,6 +653,7 @@ export function CommandBar({
|
|
|
618
653
|
const tickers = useAppSelector((state) => state.tickers);
|
|
619
654
|
const financials = useAppSelector((state) => state.financials);
|
|
620
655
|
const focusedPaneId = useAppSelector((state) => state.focusedPaneId);
|
|
656
|
+
const activePanel = useAppSelector((state) => state.activePanel);
|
|
621
657
|
const layoutHistory = useAppSelector((state) => state.layoutHistory);
|
|
622
658
|
const recentTickers = useAppSelector((state) => state.recentTickers);
|
|
623
659
|
const commandBarOpen = useAppSelector((state) => state.commandBarOpen);
|
|
@@ -633,6 +669,7 @@ export function CommandBar({
|
|
|
633
669
|
tickers,
|
|
634
670
|
financials,
|
|
635
671
|
focusedPaneId,
|
|
672
|
+
activePanel,
|
|
636
673
|
layoutHistory,
|
|
637
674
|
recentTickers,
|
|
638
675
|
commandBarOpen,
|
|
@@ -643,6 +680,7 @@ export function CommandBar({
|
|
|
643
680
|
updateCheckInProgress,
|
|
644
681
|
updateNotice,
|
|
645
682
|
}) as AppState, [
|
|
683
|
+
activePanel,
|
|
646
684
|
commandBarLaunchRequest,
|
|
647
685
|
commandBarOpen,
|
|
648
686
|
commandBarQuery,
|
|
@@ -660,8 +698,16 @@ export function CommandBar({
|
|
|
660
698
|
]);
|
|
661
699
|
const stateRef = useRef(state);
|
|
662
700
|
stateRef.current = state;
|
|
701
|
+
const persistConfig = useCallback((nextConfig: AppState["config"]) => {
|
|
702
|
+
const currentState = stateRef.current;
|
|
703
|
+
scheduleConfigSave(syncConfigActiveLayoutState(
|
|
704
|
+
nextConfig,
|
|
705
|
+
currentState.paneState,
|
|
706
|
+
currentState.focusedPaneId,
|
|
707
|
+
currentState.activePanel,
|
|
708
|
+
));
|
|
709
|
+
}, []);
|
|
663
710
|
const { symbol: activeTickerSymbol, ticker: activeTickerData, financials: activeFinancials } = useFocusedTicker();
|
|
664
|
-
const renderer = useNativeRenderer();
|
|
665
711
|
const { width: termWidth, height: termHeight } = useViewport();
|
|
666
712
|
const { nativePaneChrome, cellWidthPx = 8, cellHeightPx = 18 } = useUiCapabilities();
|
|
667
713
|
const availableCommands = useMemo(
|
|
@@ -833,20 +879,16 @@ export function CommandBar({
|
|
|
833
879
|
: instance
|
|
834
880
|
)),
|
|
835
881
|
};
|
|
836
|
-
const nextConfig = {
|
|
837
|
-
...currentState.config,
|
|
838
|
-
layout: nextLayout,
|
|
839
|
-
layouts: currentState.config.layouts.map((savedLayout, index) => (
|
|
840
|
-
index === currentState.config.activeLayoutIndex ? { ...savedLayout, layout: nextLayout } : savedLayout
|
|
841
|
-
)),
|
|
842
|
-
};
|
|
843
882
|
dispatch({ type: "UPDATE_LAYOUT", layout: nextLayout });
|
|
844
|
-
void saveConfig(nextConfig);
|
|
845
883
|
dispatch({ type: "FOCUS_PANE", paneId: targetPane.instanceId });
|
|
846
884
|
}, [dispatch]);
|
|
847
885
|
|
|
848
|
-
const openFixedTickerPane = useCallback((symbol: string) => {
|
|
849
|
-
pluginRegistry.pinTicker(symbol, {
|
|
886
|
+
const openFixedTickerPane = useCallback((symbol: string, options?: { forceNewPane?: boolean }) => {
|
|
887
|
+
pluginRegistry.pinTicker(symbol, {
|
|
888
|
+
floating: true,
|
|
889
|
+
paneType: "ticker-detail",
|
|
890
|
+
forceNewPane: options?.forceNewPane,
|
|
891
|
+
});
|
|
850
892
|
}, [pluginRegistry]);
|
|
851
893
|
|
|
852
894
|
const commitTheme = useCallback((themeId: string) => {
|
|
@@ -872,7 +914,7 @@ export function CommandBar({
|
|
|
872
914
|
? findPaneInstance(currentState.config.layout, currentState.focusedPaneId)
|
|
873
915
|
: null;
|
|
874
916
|
if (options?.forceNewPane) {
|
|
875
|
-
openFixedTickerPane(symbol);
|
|
917
|
+
openFixedTickerPane(symbol, { forceNewPane: true });
|
|
876
918
|
return;
|
|
877
919
|
}
|
|
878
920
|
|
|
@@ -1165,9 +1207,9 @@ export function CommandBar({
|
|
|
1165
1207
|
);
|
|
1166
1208
|
dispatch({ type: "SET_CONFIG", config: nextConfig });
|
|
1167
1209
|
setActiveCollection(portfolio.id);
|
|
1168
|
-
|
|
1210
|
+
persistConfig(nextConfig);
|
|
1169
1211
|
notify(`Created portfolio "${portfolio.name}".`, { type: "success" });
|
|
1170
|
-
}, [dispatch, notify, setActiveCollection]);
|
|
1212
|
+
}, [dispatch, notify, persistConfig, setActiveCollection]);
|
|
1171
1213
|
|
|
1172
1214
|
const createWatchlist = useCallback(async (name: string) => {
|
|
1173
1215
|
const currentState = stateRef.current;
|
|
@@ -1184,9 +1226,9 @@ export function CommandBar({
|
|
|
1184
1226
|
};
|
|
1185
1227
|
dispatch({ type: "SET_CONFIG", config: nextConfig });
|
|
1186
1228
|
setActiveCollection(id);
|
|
1187
|
-
|
|
1229
|
+
persistConfig(nextConfig);
|
|
1188
1230
|
notify(`Created watchlist "${trimmedName}".`, { type: "success" });
|
|
1189
|
-
}, [dispatch, notify, setActiveCollection]);
|
|
1231
|
+
}, [dispatch, notify, persistConfig, setActiveCollection]);
|
|
1190
1232
|
|
|
1191
1233
|
const deleteWatchlist = useCallback(async (watchlistId: string) => {
|
|
1192
1234
|
const currentState = stateRef.current;
|
|
@@ -1204,9 +1246,9 @@ export function CommandBar({
|
|
|
1204
1246
|
const fallback = nextConfig.portfolios[0]?.id || nextConfig.watchlists[0]?.id || "";
|
|
1205
1247
|
if (fallback) setActiveCollection(fallback);
|
|
1206
1248
|
}
|
|
1207
|
-
|
|
1249
|
+
persistConfig(nextConfig);
|
|
1208
1250
|
notify(`Deleted "${watchlist.name}".`, { type: "success" });
|
|
1209
|
-
}, [activeCollectionId, dispatch, notify, setActiveCollection]);
|
|
1251
|
+
}, [activeCollectionId, dispatch, notify, persistConfig, setActiveCollection]);
|
|
1210
1252
|
|
|
1211
1253
|
const deletePortfolio = useCallback(async (portfolioId: string) => {
|
|
1212
1254
|
const currentState = stateRef.current;
|
|
@@ -1234,9 +1276,9 @@ export function CommandBar({
|
|
|
1234
1276
|
const fallback = nextConfig.portfolios[0]?.id || nextConfig.watchlists[0]?.id || "";
|
|
1235
1277
|
if (fallback) setActiveCollection(fallback);
|
|
1236
1278
|
}
|
|
1237
|
-
|
|
1279
|
+
persistConfig(nextConfig);
|
|
1238
1280
|
notify(`Deleted "${portfolio.name}".`, { type: "success" });
|
|
1239
|
-
}, [activeCollectionId, dispatch, notify, setActiveCollection, tickerRepository]);
|
|
1281
|
+
}, [activeCollectionId, dispatch, notify, persistConfig, setActiveCollection, tickerRepository]);
|
|
1240
1282
|
|
|
1241
1283
|
const setPortfolioPositionFromWorkflow = useCallback(async (values: Record<string, CommandBarFieldValue>) => {
|
|
1242
1284
|
const currentState = stateRef.current;
|
|
@@ -1499,6 +1541,7 @@ export function CommandBar({
|
|
|
1499
1541
|
const enabled = !disabledPlugins.includes(plugin.id);
|
|
1500
1542
|
const toggleAction = () => {
|
|
1501
1543
|
dispatch({ type: "TOGGLE_PLUGIN", pluginId: plugin.id });
|
|
1544
|
+
const currentState = stateRef.current;
|
|
1502
1545
|
const nextDisabled = enabled
|
|
1503
1546
|
? [...disabledPlugins, plugin.id]
|
|
1504
1547
|
: disabledPlugins.filter((entry) => entry !== plugin.id);
|
|
@@ -1507,7 +1550,7 @@ export function CommandBar({
|
|
|
1507
1550
|
pluginRegistry.hidePane(paneId);
|
|
1508
1551
|
}
|
|
1509
1552
|
}
|
|
1510
|
-
|
|
1553
|
+
persistConfig({ ...currentState.config, disabledPlugins: nextDisabled });
|
|
1511
1554
|
};
|
|
1512
1555
|
return {
|
|
1513
1556
|
id: `plugin:${plugin.id}`,
|
|
@@ -1520,7 +1563,7 @@ export function CommandBar({
|
|
|
1520
1563
|
action: toggleAction,
|
|
1521
1564
|
};
|
|
1522
1565
|
});
|
|
1523
|
-
}, [dispatch, pluginRegistry, state.config]);
|
|
1566
|
+
}, [dispatch, persistConfig, pluginRegistry, state.config]);
|
|
1524
1567
|
|
|
1525
1568
|
const buildLayoutItems = useCallback((
|
|
1526
1569
|
query: string,
|
|
@@ -2164,6 +2207,94 @@ export function CommandBar({
|
|
|
2164
2207
|
}
|
|
2165
2208
|
}, []);
|
|
2166
2209
|
|
|
2210
|
+
const activatePaneSettingField = useCallback((
|
|
2211
|
+
paneId: string,
|
|
2212
|
+
field: PaneSettingField,
|
|
2213
|
+
currentValue: unknown,
|
|
2214
|
+
options?: { keepRouteOpen?: boolean },
|
|
2215
|
+
) => {
|
|
2216
|
+
const normalized = normalizePaneSettingField(paneId, field, currentValue);
|
|
2217
|
+
if (normalized.mode === "toggle") {
|
|
2218
|
+
if (options?.keepRouteOpen) {
|
|
2219
|
+
updateTopRoute((route) => route.kind === "pane-settings"
|
|
2220
|
+
? { ...route, pendingFieldKey: field.key, error: null }
|
|
2221
|
+
: route);
|
|
2222
|
+
}
|
|
2223
|
+
void pluginRegistry.applyPaneSettingValueFn(paneId, field, !normalized.value)
|
|
2224
|
+
.then(() => {
|
|
2225
|
+
if (options?.keepRouteOpen) {
|
|
2226
|
+
updateTopRoute((route) => route.kind === "pane-settings"
|
|
2227
|
+
? { ...route, pendingFieldKey: null, error: null }
|
|
2228
|
+
: route);
|
|
2229
|
+
} else {
|
|
2230
|
+
closeAll({ revertThemePreview: false });
|
|
2231
|
+
}
|
|
2232
|
+
})
|
|
2233
|
+
.catch((error) => {
|
|
2234
|
+
if (options?.keepRouteOpen) {
|
|
2235
|
+
updateTopRoute((route) => route.kind === "pane-settings"
|
|
2236
|
+
? {
|
|
2237
|
+
...route,
|
|
2238
|
+
pendingFieldKey: null,
|
|
2239
|
+
error: error instanceof Error ? error.message : "Could not apply that setting.",
|
|
2240
|
+
}
|
|
2241
|
+
: route);
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
notify(error instanceof Error ? error.message : "Could not apply that setting.", { type: "error" });
|
|
2245
|
+
});
|
|
2246
|
+
return;
|
|
2247
|
+
}
|
|
2248
|
+
if (normalized.mode === "workflow" && normalized.route) {
|
|
2249
|
+
openWorkflowRoute(normalized.route);
|
|
2250
|
+
return;
|
|
2251
|
+
}
|
|
2252
|
+
if (normalized.mode === "picker") {
|
|
2253
|
+
openPickerRoute(normalized.route);
|
|
2254
|
+
}
|
|
2255
|
+
}, [
|
|
2256
|
+
closeAll,
|
|
2257
|
+
normalizePaneSettingField,
|
|
2258
|
+
notify,
|
|
2259
|
+
openPickerRoute,
|
|
2260
|
+
openWorkflowRoute,
|
|
2261
|
+
pluginRegistry,
|
|
2262
|
+
updateTopRoute,
|
|
2263
|
+
]);
|
|
2264
|
+
|
|
2265
|
+
const buildPaneSettingItems = useCallback((
|
|
2266
|
+
paneId: string | null,
|
|
2267
|
+
query: string,
|
|
2268
|
+
options?: { keepRouteOpen?: boolean },
|
|
2269
|
+
): ResultItem[] => {
|
|
2270
|
+
if (!paneId) return [];
|
|
2271
|
+
const descriptor = pluginRegistry.resolvePaneSettings(paneId);
|
|
2272
|
+
if (!descriptor) return [];
|
|
2273
|
+
|
|
2274
|
+
const category = descriptor.settingsDef.title || "Pane Settings";
|
|
2275
|
+
const paneLabel = descriptor.pane.title || descriptor.paneDef.name || descriptor.pane.paneId;
|
|
2276
|
+
const items = descriptor.settingsDef.fields.map((field): ResultItem => {
|
|
2277
|
+
const currentValue = descriptor.context.settings[field.key];
|
|
2278
|
+
return {
|
|
2279
|
+
id: `pane-setting:${field.key}`,
|
|
2280
|
+
label: field.label,
|
|
2281
|
+
detail: summarizePaneSettingValue(field, currentValue),
|
|
2282
|
+
category,
|
|
2283
|
+
kind: "action",
|
|
2284
|
+
right: field.type,
|
|
2285
|
+
searchText: `${category} ${paneLabel} ${field.label} ${field.description || ""} ${field.type}`,
|
|
2286
|
+
action: () => activatePaneSettingField(descriptor.paneId, field, currentValue, options),
|
|
2287
|
+
};
|
|
2288
|
+
});
|
|
2289
|
+
|
|
2290
|
+
return query
|
|
2291
|
+
? fuzzyFilter(items, query, (item) => `${item.label} ${item.detail} ${item.right || ""} ${item.searchText || ""}`)
|
|
2292
|
+
: items;
|
|
2293
|
+
}, [
|
|
2294
|
+
activatePaneSettingField,
|
|
2295
|
+
pluginRegistry,
|
|
2296
|
+
]);
|
|
2297
|
+
|
|
2167
2298
|
function readWorkflowTextareaValue(fieldId: string, fallback = ""): string {
|
|
2168
2299
|
const ref = getInputRef(workflowInputRefs.current, fieldId).current;
|
|
2169
2300
|
const nextValue = (ref as TextareaRenderable | null)?.editBuffer?.getText?.();
|
|
@@ -2446,7 +2577,7 @@ export function CommandBar({
|
|
|
2446
2577
|
const argPlaceholder = template.shortcut?.argPlaceholder;
|
|
2447
2578
|
return template.wizard.some((step) => step.type === "textarea" || step.key !== argPlaceholder);
|
|
2448
2579
|
}
|
|
2449
|
-
if (template
|
|
2580
|
+
if (canPromptForPaneTemplateArg(template)) {
|
|
2450
2581
|
return !arg?.trim();
|
|
2451
2582
|
}
|
|
2452
2583
|
return false;
|
|
@@ -2632,6 +2763,57 @@ export function CommandBar({
|
|
|
2632
2763
|
}]
|
|
2633
2764
|
), [mapTickerSearchCandidateToResultItem]);
|
|
2634
2765
|
|
|
2766
|
+
const mergeTickerSearchResultItems = useCallback((
|
|
2767
|
+
query: string,
|
|
2768
|
+
preferredItems: ResultItem[],
|
|
2769
|
+
fallbackItems: ResultItem[],
|
|
2770
|
+
): ResultItem[] => {
|
|
2771
|
+
const merged: ResultItem[] = [];
|
|
2772
|
+
const seen = new Set<string>();
|
|
2773
|
+
const addItem = (item: ResultItem) => {
|
|
2774
|
+
if (item.kind === "info") return;
|
|
2775
|
+
const key = `${item.label.trim().toUpperCase()}:${(item.right || "").trim().toUpperCase()}`;
|
|
2776
|
+
if (seen.has(key)) return;
|
|
2777
|
+
seen.add(key);
|
|
2778
|
+
merged.push(item);
|
|
2779
|
+
};
|
|
2780
|
+
preferredItems.forEach(addItem);
|
|
2781
|
+
fallbackItems.forEach(addItem);
|
|
2782
|
+
if (merged.length === 0) return fallbackItems;
|
|
2783
|
+
|
|
2784
|
+
return rankTickerSearchItems(merged, query)
|
|
2785
|
+
.map((item) => isExactTickerResultMatch(item, query) && item.category !== "Saved"
|
|
2786
|
+
? { ...item, category: "Exact Match" }
|
|
2787
|
+
: item);
|
|
2788
|
+
}, []);
|
|
2789
|
+
|
|
2790
|
+
const mergePlainRootTickerResults = useCallback((
|
|
2791
|
+
query: string,
|
|
2792
|
+
providerItems: ResultItem[],
|
|
2793
|
+
rootItems: ResultItem[],
|
|
2794
|
+
): ResultItem[] => {
|
|
2795
|
+
const merged: ResultItem[] = [];
|
|
2796
|
+
const seen = new Set<string>();
|
|
2797
|
+
const addItem = (item: ResultItem, options?: { skipInfo?: boolean }) => {
|
|
2798
|
+
if (options?.skipInfo && item.kind === "info") return;
|
|
2799
|
+
const key = (item.kind === "ticker" || item.kind === "search")
|
|
2800
|
+
? `${item.kind}:${item.label.trim().toUpperCase()}:${(item.right || "").trim().toUpperCase()}`
|
|
2801
|
+
: `${item.kind}:${item.id}`;
|
|
2802
|
+
if (seen.has(key)) return;
|
|
2803
|
+
seen.add(key);
|
|
2804
|
+
merged.push(item);
|
|
2805
|
+
};
|
|
2806
|
+
|
|
2807
|
+
providerItems
|
|
2808
|
+
.filter((item) => item.category === "Exact Match" || isExactTickerResultMatch(item, query))
|
|
2809
|
+
.forEach((item) => addItem(item, { skipInfo: true }));
|
|
2810
|
+
rootItems.forEach((item) => addItem(item));
|
|
2811
|
+
providerItems
|
|
2812
|
+
.filter((item) => item.category !== "Exact Match" && !isExactTickerResultMatch(item, query))
|
|
2813
|
+
.forEach((item) => addItem(item, { skipInfo: true }));
|
|
2814
|
+
return merged.length > 0 ? merged : rootItems;
|
|
2815
|
+
}, []);
|
|
2816
|
+
|
|
2635
2817
|
const buildTickerSearchCacheKey = useCallback((
|
|
2636
2818
|
query: string,
|
|
2637
2819
|
brokerId?: string | null,
|
|
@@ -2718,7 +2900,10 @@ export function CommandBar({
|
|
|
2718
2900
|
activeCollectionId,
|
|
2719
2901
|
}), [activeCollectionId, activeTickerSymbol, state.config, state.focusedPaneId]);
|
|
2720
2902
|
|
|
2721
|
-
const getAvailablePaneTemplates = useCallback((
|
|
2903
|
+
const getAvailablePaneTemplates = useCallback((
|
|
2904
|
+
options?: PaneTemplateCreateOptions,
|
|
2905
|
+
availability?: { includePromptableTickerTemplates?: boolean },
|
|
2906
|
+
): PaneTemplateDef[] => {
|
|
2722
2907
|
const disabledPlugins = new Set(state.config.disabledPlugins || []);
|
|
2723
2908
|
const context = getPaneTemplateContext();
|
|
2724
2909
|
return [...pluginRegistry.paneTemplates.values()]
|
|
@@ -2727,7 +2912,12 @@ export function CommandBar({
|
|
|
2727
2912
|
if (pluginId && disabledPlugins.has(pluginId)) return false;
|
|
2728
2913
|
if (!template.canCreate) return true;
|
|
2729
2914
|
try {
|
|
2730
|
-
|
|
2915
|
+
const canCreate = template.canCreate(context, options);
|
|
2916
|
+
if (canCreate) return true;
|
|
2917
|
+
return !!availability?.includePromptableTickerTemplates
|
|
2918
|
+
&& !options?.arg
|
|
2919
|
+
&& !context.activeTicker
|
|
2920
|
+
&& canPromptForPaneTemplateArg(template);
|
|
2731
2921
|
} catch (error) {
|
|
2732
2922
|
commandBarLog.error("Pane template canCreate failed", {
|
|
2733
2923
|
templateId: template.id,
|
|
@@ -2751,13 +2941,13 @@ export function CommandBar({
|
|
|
2751
2941
|
const prefix = template.shortcut?.prefix?.toUpperCase();
|
|
2752
2942
|
if (!prefix) return false;
|
|
2753
2943
|
const arg = trimmed.slice(prefix.length).trim();
|
|
2754
|
-
const argKind = template
|
|
2944
|
+
const argKind = getPaneTemplateArgKind(template);
|
|
2755
2945
|
if (upper !== prefix && (!argKind || !upper.startsWith(`${prefix} `))) return false;
|
|
2756
2946
|
if (!template.canCreate) return true;
|
|
2757
2947
|
try {
|
|
2758
2948
|
const canCreate = template.canCreate(context, arg ? { arg } : undefined);
|
|
2759
2949
|
if (canCreate) return true;
|
|
2760
|
-
if (!arg && !context.activeTicker && (
|
|
2950
|
+
if (!arg && !context.activeTicker && canPromptForPaneTemplateArg(template)) {
|
|
2761
2951
|
return true;
|
|
2762
2952
|
}
|
|
2763
2953
|
return false;
|
|
@@ -2837,8 +3027,11 @@ export function CommandBar({
|
|
|
2837
3027
|
const paneShortcutItems = useCallback((options?: {
|
|
2838
3028
|
filterQuery?: string;
|
|
2839
3029
|
createOptions?: PaneTemplateCreateOptions;
|
|
3030
|
+
includePromptableTickerTemplates?: boolean;
|
|
2840
3031
|
}): ResultItem[] => {
|
|
2841
|
-
const items = getAvailablePaneTemplates(options?.createOptions
|
|
3032
|
+
const items = getAvailablePaneTemplates(options?.createOptions, {
|
|
3033
|
+
includePromptableTickerTemplates: options?.includePromptableTickerTemplates,
|
|
3034
|
+
})
|
|
2842
3035
|
.filter((template) => template.shortcut)
|
|
2843
3036
|
.map((template) => createPaneTemplateItem(template, {
|
|
2844
3037
|
category: "Panes",
|
|
@@ -3128,7 +3321,7 @@ export function CommandBar({
|
|
|
3128
3321
|
},
|
|
3129
3322
|
};
|
|
3130
3323
|
dispatch({ type: "SET_CONFIG", config: nextConfig });
|
|
3131
|
-
|
|
3324
|
+
persistConfig(nextConfig);
|
|
3132
3325
|
closeAll({ revertThemePreview: false });
|
|
3133
3326
|
return;
|
|
3134
3327
|
}
|
|
@@ -3217,6 +3410,7 @@ export function CommandBar({
|
|
|
3217
3410
|
|
|
3218
3411
|
useEffect(() => {
|
|
3219
3412
|
if (tickerSearchRouteQuery == null) {
|
|
3413
|
+
searchRequestIdRef.current += 1;
|
|
3220
3414
|
setTickerSearchPending(false);
|
|
3221
3415
|
setTickerSearchResults([]);
|
|
3222
3416
|
if (searchTimerRef.current) clearTimeout(searchTimerRef.current);
|
|
@@ -3225,6 +3419,7 @@ export function CommandBar({
|
|
|
3225
3419
|
|
|
3226
3420
|
const searchQuery = tickerSearchRouteQuery.trim();
|
|
3227
3421
|
if (!searchQuery) {
|
|
3422
|
+
searchRequestIdRef.current += 1;
|
|
3228
3423
|
setTickerSearchPending(false);
|
|
3229
3424
|
setTickerSearchResults([]);
|
|
3230
3425
|
if (searchTimerRef.current) clearTimeout(searchTimerRef.current);
|
|
@@ -3239,7 +3434,7 @@ export function CommandBar({
|
|
|
3239
3434
|
activePortfolio?.brokerInstanceId,
|
|
3240
3435
|
);
|
|
3241
3436
|
setTickerSearchResults(cachedCandidates
|
|
3242
|
-
? buildTickerSearchResultItems(cachedCandidates, searchQuery)
|
|
3437
|
+
? mergeTickerSearchResultItems(searchQuery, localItems, buildTickerSearchResultItems(cachedCandidates, searchQuery))
|
|
3243
3438
|
: localItems);
|
|
3244
3439
|
const requestId = ++searchRequestIdRef.current;
|
|
3245
3440
|
const searchDelay = skipTickerSearchDebounceRef.current ? 0 : 200;
|
|
@@ -3266,7 +3461,11 @@ export function CommandBar({
|
|
|
3266
3461
|
activePortfolio?.brokerId,
|
|
3267
3462
|
activePortfolio?.brokerInstanceId,
|
|
3268
3463
|
);
|
|
3269
|
-
setTickerSearchResults(
|
|
3464
|
+
setTickerSearchResults(mergeTickerSearchResultItems(
|
|
3465
|
+
searchQuery,
|
|
3466
|
+
localTickerSearchResultItems(searchQuery, { limit: 6 }),
|
|
3467
|
+
buildTickerSearchResultItems(combined, searchQuery),
|
|
3468
|
+
));
|
|
3270
3469
|
} catch {
|
|
3271
3470
|
if (requestId !== searchRequestIdRef.current) return;
|
|
3272
3471
|
const nextItems: ResultItem[] = [{
|
|
@@ -3295,6 +3494,7 @@ export function CommandBar({
|
|
|
3295
3494
|
buildTickerSearchResultItems,
|
|
3296
3495
|
localTickerSearchResultItems,
|
|
3297
3496
|
mapTickerSearchCandidateToResultItem,
|
|
3497
|
+
mergeTickerSearchResultItems,
|
|
3298
3498
|
readTickerSearchCache,
|
|
3299
3499
|
tickerSearchRouteQuery,
|
|
3300
3500
|
writeTickerSearchCache,
|
|
@@ -3559,8 +3759,8 @@ export function CommandBar({
|
|
|
3559
3759
|
label: "Type a ticker symbol",
|
|
3560
3760
|
detail: "Open security details after resolving a ticker",
|
|
3561
3761
|
category: "Search",
|
|
3562
|
-
kind: "
|
|
3563
|
-
action: () =>
|
|
3762
|
+
kind: "command",
|
|
3763
|
+
action: () => openModeRoute("ticker-search", ""),
|
|
3564
3764
|
});
|
|
3565
3765
|
} else if (match.arg) {
|
|
3566
3766
|
items.push(...localTickerSearchResultItems(match.arg, { limit: 6 }));
|
|
@@ -3610,7 +3810,9 @@ export function CommandBar({
|
|
|
3610
3810
|
const allItems = [
|
|
3611
3811
|
...tickerItems,
|
|
3612
3812
|
...commandItems,
|
|
3613
|
-
...
|
|
3813
|
+
...buildLayoutItems("", { confirmDangerousActions: true }),
|
|
3814
|
+
...buildPaneSettingItems(state.focusedPaneId, rootQuery),
|
|
3815
|
+
...paneShortcutItems({ includePromptableTickerTemplates: true }),
|
|
3614
3816
|
...nonShortcutPaneTemplateItems(),
|
|
3615
3817
|
...tickerActionItems(),
|
|
3616
3818
|
...pluginCommandItems(),
|
|
@@ -3625,6 +3827,7 @@ export function CommandBar({
|
|
|
3625
3827
|
activeTickerSymbol,
|
|
3626
3828
|
availableCommands,
|
|
3627
3829
|
buildLayoutItems,
|
|
3830
|
+
buildPaneSettingItems,
|
|
3628
3831
|
buildPluginItems,
|
|
3629
3832
|
createQuickLookLocalTickerCandidates,
|
|
3630
3833
|
createPluginCommandItem,
|
|
@@ -3644,6 +3847,18 @@ export function CommandBar({
|
|
|
3644
3847
|
tickerActionItems,
|
|
3645
3848
|
]);
|
|
3646
3849
|
|
|
3850
|
+
const rootPlainTickerSearchArg = useMemo(() => {
|
|
3851
|
+
if (activeMatch || rootShortcutIntent.kind !== "none" || !isLikelyPlainTickerSearch(rootQuery)) return null;
|
|
3852
|
+
const normalizedQuery = normalizeCommandTickerSearchText(rootQuery);
|
|
3853
|
+
const hasExactNonTickerResult = rootResultModel.items.some((item) => (
|
|
3854
|
+
item.kind !== "ticker"
|
|
3855
|
+
&& item.kind !== "search"
|
|
3856
|
+
&& normalizeCommandTickerSearchText(item.label) === normalizedQuery
|
|
3857
|
+
));
|
|
3858
|
+
return hasExactNonTickerResult ? null : rootQuery.trim();
|
|
3859
|
+
}, [activeMatch, rootQuery, rootResultModel.items, rootShortcutIntent.kind]);
|
|
3860
|
+
const rootTickerSearchArg = rootSecurityDescriptionArg ?? rootPlainTickerSearchArg;
|
|
3861
|
+
|
|
3647
3862
|
useEffect(() => {
|
|
3648
3863
|
if (currentRoute) return;
|
|
3649
3864
|
|
|
@@ -3668,6 +3883,7 @@ export function CommandBar({
|
|
|
3668
3883
|
|
|
3669
3884
|
useEffect(() => {
|
|
3670
3885
|
if (currentRoute) {
|
|
3886
|
+
rootSearchRequestIdRef.current += 1;
|
|
3671
3887
|
setRootSearching(false);
|
|
3672
3888
|
setRootProviderResults(null);
|
|
3673
3889
|
setRootProviderResultsQuery(null);
|
|
@@ -3676,7 +3892,8 @@ export function CommandBar({
|
|
|
3676
3892
|
return;
|
|
3677
3893
|
}
|
|
3678
3894
|
|
|
3679
|
-
if (!
|
|
3895
|
+
if (!rootTickerSearchArg) {
|
|
3896
|
+
rootSearchRequestIdRef.current += 1;
|
|
3680
3897
|
setRootSearching(false);
|
|
3681
3898
|
setRootProviderResults(null);
|
|
3682
3899
|
setRootProviderResultsQuery(null);
|
|
@@ -3685,7 +3902,7 @@ export function CommandBar({
|
|
|
3685
3902
|
return;
|
|
3686
3903
|
}
|
|
3687
3904
|
|
|
3688
|
-
const searchQuery =
|
|
3905
|
+
const searchQuery = rootTickerSearchArg;
|
|
3689
3906
|
if (rootSearchTimerRef.current) clearTimeout(rootSearchTimerRef.current);
|
|
3690
3907
|
if (rootLastSearchedQueryRef.current === searchQuery) {
|
|
3691
3908
|
return;
|
|
@@ -3699,7 +3916,10 @@ export function CommandBar({
|
|
|
3699
3916
|
activeSearchPortfolio?.brokerId,
|
|
3700
3917
|
activeSearchPortfolio?.brokerInstanceId,
|
|
3701
3918
|
);
|
|
3702
|
-
|
|
3919
|
+
const localItems = localTickerSearchResultItems(searchQuery, { limit: 6 });
|
|
3920
|
+
setRootProviderResults(cachedCandidates
|
|
3921
|
+
? mergeTickerSearchResultItems(searchQuery, localItems, buildTickerSearchResultItems(cachedCandidates, searchQuery))
|
|
3922
|
+
: null);
|
|
3703
3923
|
setRootProviderResultsQuery(cachedCandidates ? searchQuery : null);
|
|
3704
3924
|
|
|
3705
3925
|
const requestId = ++rootSearchRequestIdRef.current;
|
|
@@ -3723,7 +3943,11 @@ export function CommandBar({
|
|
|
3723
3943
|
activeSearchPortfolio?.brokerId,
|
|
3724
3944
|
activeSearchPortfolio?.brokerInstanceId,
|
|
3725
3945
|
);
|
|
3726
|
-
setRootProviderResults(
|
|
3946
|
+
setRootProviderResults(mergeTickerSearchResultItems(
|
|
3947
|
+
searchQuery,
|
|
3948
|
+
localTickerSearchResultItems(searchQuery, { limit: 6 }),
|
|
3949
|
+
buildTickerSearchResultItems(combined, searchQuery),
|
|
3950
|
+
));
|
|
3727
3951
|
setRootProviderResultsQuery(searchQuery);
|
|
3728
3952
|
} catch {
|
|
3729
3953
|
if (requestId !== rootSearchRequestIdRef.current) return;
|
|
@@ -3751,20 +3975,46 @@ export function CommandBar({
|
|
|
3751
3975
|
buildTickerSearchResultItems,
|
|
3752
3976
|
currentRoute,
|
|
3753
3977
|
dataProvider,
|
|
3978
|
+
localTickerSearchResultItems,
|
|
3979
|
+
mergeTickerSearchResultItems,
|
|
3754
3980
|
readTickerSearchCache,
|
|
3755
|
-
|
|
3981
|
+
rootTickerSearchArg,
|
|
3756
3982
|
state.config.portfolios,
|
|
3757
3983
|
state.tickers,
|
|
3758
3984
|
writeTickerSearchCache,
|
|
3759
3985
|
]);
|
|
3760
3986
|
|
|
3761
3987
|
const rootResults = useMemo(() => {
|
|
3762
|
-
if (
|
|
3988
|
+
if (rootTickerSearchArg && rootProviderResultsQuery === rootTickerSearchArg && rootProviderResults) {
|
|
3989
|
+
if (rootPlainTickerSearchArg) {
|
|
3990
|
+
return mergePlainRootTickerResults(rootPlainTickerSearchArg, rootProviderResults, rootResultModel.items);
|
|
3991
|
+
}
|
|
3763
3992
|
return rootProviderResults;
|
|
3764
3993
|
}
|
|
3765
3994
|
return rootResultModel.items;
|
|
3766
|
-
}, [
|
|
3995
|
+
}, [
|
|
3996
|
+
mergePlainRootTickerResults,
|
|
3997
|
+
rootPlainTickerSearchArg,
|
|
3998
|
+
rootProviderResults,
|
|
3999
|
+
rootProviderResultsQuery,
|
|
4000
|
+
rootResultModel.items,
|
|
4001
|
+
rootTickerSearchArg,
|
|
4002
|
+
]);
|
|
3767
4003
|
const orderedRootResults = useMemo(() => orderListResults(rootResults), [rootResults]);
|
|
4004
|
+
const activeRootProviderResultsKey = useMemo(() => {
|
|
4005
|
+
if (!rootTickerSearchArg || rootProviderResultsQuery !== rootTickerSearchArg || !rootProviderResults) return null;
|
|
4006
|
+
return [
|
|
4007
|
+
rootTickerSearchArg,
|
|
4008
|
+
...rootProviderResults.map((item) => `${item.id}:${item.category}:${item.label}:${item.right || ""}`),
|
|
4009
|
+
].join("\n");
|
|
4010
|
+
}, [rootProviderResults, rootProviderResultsQuery, rootTickerSearchArg]);
|
|
4011
|
+
|
|
4012
|
+
useEffect(() => {
|
|
4013
|
+
if (!activeRootProviderResultsKey) return;
|
|
4014
|
+
setRootSelectedIdx(0);
|
|
4015
|
+
setRootHoveredIdx(null);
|
|
4016
|
+
nativeListScrollRef.current?.scrollTo(0);
|
|
4017
|
+
}, [activeRootProviderResultsKey]);
|
|
3768
4018
|
|
|
3769
4019
|
const rootGhostCompletion = !currentRoute && rootShortcutIntent.kind === "inferred-complete"
|
|
3770
4020
|
? rootShortcutIntent.completionQuery
|
|
@@ -3776,7 +4026,7 @@ export function CommandBar({
|
|
|
3776
4026
|
if (currentRoute || rootShortcutIntent.kind === "none") return null;
|
|
3777
4027
|
|
|
3778
4028
|
if (rootShortcutIntent.source === "pane-template") {
|
|
3779
|
-
const argKind = rootShortcutIntent.template
|
|
4029
|
+
const argKind = getPaneTemplateArgKind(rootShortcutIntent.template);
|
|
3780
4030
|
if (argKind === "ticker") {
|
|
3781
4031
|
const symbol = normalizeTickerInput(activeTickerSymbol, rootShortcutIntent.argText);
|
|
3782
4032
|
if (symbol) {
|
|
@@ -3792,9 +4042,9 @@ export function CommandBar({
|
|
|
3792
4042
|
}
|
|
3793
4043
|
const inferred = normalizeTickerInput(activeTickerSymbol, undefined);
|
|
3794
4044
|
if (inferred) {
|
|
3795
|
-
return `Shortcut: ${rootShortcutIntent.label}
|
|
4045
|
+
return `Shortcut: ${rootShortcutIntent.label} for ${inferred} · Tab to accept`;
|
|
3796
4046
|
}
|
|
3797
|
-
return `Shortcut: ${rootShortcutIntent.label} · Enter tickers
|
|
4047
|
+
return `Shortcut: ${rootShortcutIntent.label} · Enter tickers`;
|
|
3798
4048
|
}
|
|
3799
4049
|
return `Shortcut: ${rootShortcutIntent.label}`;
|
|
3800
4050
|
}
|
|
@@ -3809,8 +4059,8 @@ export function CommandBar({
|
|
|
3809
4059
|
const symbol = normalizeTickerInput(activeTickerSymbol, rootShortcutIntent.argText);
|
|
3810
4060
|
if (symbol) {
|
|
3811
4061
|
return rootShortcutIntent.kind === "inferred-complete"
|
|
3812
|
-
? `Shortcut:
|
|
3813
|
-
: `Shortcut:
|
|
4062
|
+
? `Shortcut: ${rootShortcutIntent.prefix} ${symbol} · Tab to accept`
|
|
4063
|
+
: `Shortcut: ${rootShortcutIntent.prefix} ${symbol}`;
|
|
3814
4064
|
}
|
|
3815
4065
|
return "Shortcut: Description";
|
|
3816
4066
|
}
|
|
@@ -3865,7 +4115,7 @@ export function CommandBar({
|
|
|
3865
4115
|
return true;
|
|
3866
4116
|
}
|
|
3867
4117
|
if (intent.source === "pane-template") {
|
|
3868
|
-
const argKind = intent.template
|
|
4118
|
+
const argKind = getPaneTemplateArgKind(intent.template);
|
|
3869
4119
|
if (argKind === "ticker") {
|
|
3870
4120
|
openModeRoute("ticker-search", intent.argText, {
|
|
3871
4121
|
action: "pane-template",
|
|
@@ -3959,7 +4209,7 @@ export function CommandBar({
|
|
|
3959
4209
|
}
|
|
3960
4210
|
return {
|
|
3961
4211
|
id: `security-description:${match.arg}`,
|
|
3962
|
-
label:
|
|
4212
|
+
label: `${match.prefix} ${match.arg.toUpperCase()}`,
|
|
3963
4213
|
detail: "Open security details or resolve the ticker",
|
|
3964
4214
|
category: "Search",
|
|
3965
4215
|
kind: "command",
|
|
@@ -4162,21 +4412,7 @@ export function CommandBar({
|
|
|
4162
4412
|
if (currentRoute.kind === "pane-settings") {
|
|
4163
4413
|
const descriptor = pluginRegistry.resolvePaneSettings(currentRoute.paneId);
|
|
4164
4414
|
if (!descriptor) return null;
|
|
4165
|
-
const
|
|
4166
|
-
const currentValue = descriptor.context.settings[field.key];
|
|
4167
|
-
return {
|
|
4168
|
-
id: `pane-setting:${field.key}`,
|
|
4169
|
-
label: field.label,
|
|
4170
|
-
detail: summarizePaneSettingValue(field, currentValue),
|
|
4171
|
-
category: descriptor.settingsDef.title || "Pane Settings",
|
|
4172
|
-
kind: "action" as const,
|
|
4173
|
-
right: field.type,
|
|
4174
|
-
action: () => {},
|
|
4175
|
-
};
|
|
4176
|
-
});
|
|
4177
|
-
const filtered = currentRoute.query
|
|
4178
|
-
? fuzzyFilter(items, currentRoute.query, (item) => `${item.label} ${item.detail} ${item.right || ""}`)
|
|
4179
|
-
: items;
|
|
4415
|
+
const filtered = buildPaneSettingItems(currentRoute.paneId, currentRoute.query, { keepRouteOpen: true });
|
|
4180
4416
|
return {
|
|
4181
4417
|
kind: "pane-settings",
|
|
4182
4418
|
title: descriptor.settingsDef.title || "Pane Settings",
|
|
@@ -4201,6 +4437,7 @@ export function CommandBar({
|
|
|
4201
4437
|
activeTickerData,
|
|
4202
4438
|
activeTickerSymbol,
|
|
4203
4439
|
buildLayoutItems,
|
|
4440
|
+
buildPaneSettingItems,
|
|
4204
4441
|
buildPluginItems,
|
|
4205
4442
|
closeAll,
|
|
4206
4443
|
createPaneTemplateItem,
|
|
@@ -4648,40 +4885,7 @@ export function CommandBar({
|
|
|
4648
4885
|
}
|
|
4649
4886
|
|
|
4650
4887
|
if (currentRoute?.kind === "pane-settings") {
|
|
4651
|
-
|
|
4652
|
-
if (!descriptor) return;
|
|
4653
|
-
const selectedField = descriptor.settingsDef.fields.find((field) => `pane-setting:${field.key}` === selected.id);
|
|
4654
|
-
if (!selectedField) return;
|
|
4655
|
-
const currentValue = descriptor.context.settings[selectedField.key];
|
|
4656
|
-
const normalized = normalizePaneSettingField(currentRoute.paneId, selectedField, currentValue);
|
|
4657
|
-
if (normalized.mode === "toggle") {
|
|
4658
|
-
updateTopRoute((route) => route.kind === "pane-settings"
|
|
4659
|
-
? { ...route, pendingFieldKey: selectedField.key, error: null }
|
|
4660
|
-
: route);
|
|
4661
|
-
void pluginRegistry.applyPaneSettingValueFn(currentRoute.paneId, selectedField, !normalized.value)
|
|
4662
|
-
.then(() => {
|
|
4663
|
-
updateTopRoute((route) => route.kind === "pane-settings"
|
|
4664
|
-
? { ...route, pendingFieldKey: null, error: null }
|
|
4665
|
-
: route);
|
|
4666
|
-
})
|
|
4667
|
-
.catch((error) => {
|
|
4668
|
-
updateTopRoute((route) => route.kind === "pane-settings"
|
|
4669
|
-
? {
|
|
4670
|
-
...route,
|
|
4671
|
-
pendingFieldKey: null,
|
|
4672
|
-
error: error instanceof Error ? error.message : "Could not apply that setting.",
|
|
4673
|
-
}
|
|
4674
|
-
: route);
|
|
4675
|
-
});
|
|
4676
|
-
return;
|
|
4677
|
-
}
|
|
4678
|
-
if (normalized.mode === "workflow" && normalized.route) {
|
|
4679
|
-
openWorkflowRoute(normalized.route);
|
|
4680
|
-
return;
|
|
4681
|
-
}
|
|
4682
|
-
if (normalized.mode === "picker") {
|
|
4683
|
-
openPickerRoute(normalized.route);
|
|
4684
|
-
}
|
|
4888
|
+
void selected.action();
|
|
4685
4889
|
return;
|
|
4686
4890
|
}
|
|
4687
4891
|
|
|
@@ -4689,10 +4893,7 @@ export function CommandBar({
|
|
|
4689
4893
|
}, [
|
|
4690
4894
|
closeAll,
|
|
4691
4895
|
currentRoute,
|
|
4692
|
-
normalizePaneSettingField,
|
|
4693
4896
|
openInlineConfirm,
|
|
4694
|
-
openPickerRoute,
|
|
4695
|
-
openWorkflowRoute,
|
|
4696
4897
|
persistLayoutChange,
|
|
4697
4898
|
pluginRegistry,
|
|
4698
4899
|
resolveImmediateRootSelection,
|
|
@@ -4842,343 +5043,277 @@ export function CommandBar({
|
|
|
4842
5043
|
}
|
|
4843
5044
|
}, [currentRoute, pluginRegistry, updateTopRoute, updateWorkflowValue]);
|
|
4844
5045
|
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
preventDefault: () => void;
|
|
4856
|
-
}) => void) => void;
|
|
4857
|
-
offInternal?: (event: "keypress", handler: (event: {
|
|
4858
|
-
name: string;
|
|
4859
|
-
sequence?: string;
|
|
4860
|
-
ctrl?: boolean;
|
|
4861
|
-
option?: boolean;
|
|
4862
|
-
meta?: boolean;
|
|
4863
|
-
shift?: boolean;
|
|
4864
|
-
stopPropagation: () => void;
|
|
4865
|
-
preventDefault: () => void;
|
|
4866
|
-
}) => void) => void;
|
|
4867
|
-
};
|
|
4868
|
-
if (!keyInput) return undefined;
|
|
4869
|
-
const handleKeyPress = (event: {
|
|
4870
|
-
name: string;
|
|
4871
|
-
sequence?: string;
|
|
4872
|
-
ctrl?: boolean;
|
|
4873
|
-
option?: boolean;
|
|
4874
|
-
meta?: boolean;
|
|
4875
|
-
shift?: boolean;
|
|
4876
|
-
stopPropagation: () => void;
|
|
4877
|
-
preventDefault: () => void;
|
|
4878
|
-
}) => {
|
|
4879
|
-
if (event.name === "escape" || event.name === "`") {
|
|
5046
|
+
useShortcut((event) => {
|
|
5047
|
+
if (event.name === "escape" || event.name === "`") {
|
|
5048
|
+
event.stopPropagation();
|
|
5049
|
+
event.preventDefault();
|
|
5050
|
+
dismissCommandBar();
|
|
5051
|
+
return;
|
|
5052
|
+
}
|
|
5053
|
+
|
|
5054
|
+
if (currentRoute?.kind === "confirm") {
|
|
5055
|
+
if (isPlainBackspace(event)) {
|
|
4880
5056
|
event.stopPropagation();
|
|
4881
5057
|
event.preventDefault();
|
|
4882
|
-
|
|
5058
|
+
popRoute();
|
|
4883
5059
|
return;
|
|
4884
5060
|
}
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
event.preventDefault();
|
|
4890
|
-
popRoute();
|
|
4891
|
-
return;
|
|
4892
|
-
}
|
|
4893
|
-
if (event.name === "return" || event.name === "enter" || event.name === "y") {
|
|
4894
|
-
event.stopPropagation();
|
|
4895
|
-
event.preventDefault();
|
|
4896
|
-
void confirmCurrentRoute();
|
|
4897
|
-
return;
|
|
4898
|
-
}
|
|
4899
|
-
if (event.name === "n") {
|
|
4900
|
-
event.stopPropagation();
|
|
4901
|
-
event.preventDefault();
|
|
4902
|
-
popRoute();
|
|
4903
|
-
}
|
|
5061
|
+
if (event.name === "return" || event.name === "enter" || event.name === "y") {
|
|
5062
|
+
event.stopPropagation();
|
|
5063
|
+
event.preventDefault();
|
|
5064
|
+
void confirmCurrentRoute();
|
|
4904
5065
|
return;
|
|
4905
5066
|
}
|
|
4906
|
-
|
|
4907
|
-
if (
|
|
4908
|
-
currentRoute
|
|
4909
|
-
&& (currentRoute.kind === "mode"
|
|
4910
|
-
|| currentRoute.kind === "picker"
|
|
4911
|
-
|| currentRoute.kind === "pane-settings")
|
|
4912
|
-
&& isPlainBackspace(event)
|
|
4913
|
-
&& currentRoute.query.length === 0
|
|
4914
|
-
) {
|
|
5067
|
+
if (event.name === "n") {
|
|
4915
5068
|
event.stopPropagation();
|
|
4916
5069
|
event.preventDefault();
|
|
4917
5070
|
popRoute();
|
|
4918
|
-
return;
|
|
4919
5071
|
}
|
|
5072
|
+
return;
|
|
5073
|
+
}
|
|
4920
5074
|
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
return;
|
|
4935
|
-
}
|
|
4936
|
-
}
|
|
4937
|
-
|
|
4938
|
-
if (event.name === "tab") {
|
|
4939
|
-
event.stopPropagation();
|
|
4940
|
-
event.preventDefault();
|
|
4941
|
-
moveWorkflowFocus(event.shift ? -1 : 1);
|
|
4942
|
-
return;
|
|
4943
|
-
}
|
|
4944
|
-
|
|
4945
|
-
if (activeTextarea && event.ctrl && event.name === "s") {
|
|
4946
|
-
event.stopPropagation();
|
|
4947
|
-
event.preventDefault();
|
|
4948
|
-
void submitWorkflowRoute(currentRoute);
|
|
4949
|
-
return;
|
|
4950
|
-
}
|
|
5075
|
+
if (
|
|
5076
|
+
currentRoute
|
|
5077
|
+
&& (currentRoute.kind === "mode"
|
|
5078
|
+
|| currentRoute.kind === "picker"
|
|
5079
|
+
|| currentRoute.kind === "pane-settings")
|
|
5080
|
+
&& isPlainBackspace(event)
|
|
5081
|
+
&& currentRoute.query.length === 0
|
|
5082
|
+
) {
|
|
5083
|
+
event.stopPropagation();
|
|
5084
|
+
event.preventDefault();
|
|
5085
|
+
popRoute();
|
|
5086
|
+
return;
|
|
5087
|
+
}
|
|
4951
5088
|
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
5089
|
+
if (currentRoute?.kind === "workflow") {
|
|
5090
|
+
const visibleFields = getVisibleWorkflowFields(currentRoute.fields, currentRoute.values);
|
|
5091
|
+
const activeField = visibleFields.find((field) => field.id === currentRoute.activeFieldId) ?? visibleFields[0];
|
|
5092
|
+
const activeTextarea = activeField?.type === "textarea";
|
|
4955
5093
|
|
|
4956
|
-
|
|
5094
|
+
if (isPlainBackspace(event)) {
|
|
5095
|
+
const activeValue = activeField
|
|
5096
|
+
? getWorkflowFieldStringValue(activeField, currentRoute.values[activeField.id])
|
|
5097
|
+
: "";
|
|
5098
|
+
if (!activeField || !isWorkflowTextField(activeField) || activeValue.length === 0) {
|
|
4957
5099
|
event.stopPropagation();
|
|
4958
5100
|
event.preventDefault();
|
|
4959
|
-
|
|
5101
|
+
popRoute();
|
|
4960
5102
|
return;
|
|
4961
5103
|
}
|
|
5104
|
+
}
|
|
4962
5105
|
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
5106
|
+
if (event.name === "tab") {
|
|
5107
|
+
event.stopPropagation();
|
|
5108
|
+
event.preventDefault();
|
|
5109
|
+
moveWorkflowFocus(event.shift ? -1 : 1);
|
|
5110
|
+
return;
|
|
5111
|
+
}
|
|
4969
5112
|
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
5113
|
+
if (activeTextarea && event.ctrl && event.name === "s") {
|
|
5114
|
+
event.stopPropagation();
|
|
5115
|
+
event.preventDefault();
|
|
5116
|
+
void submitWorkflowRoute(currentRoute);
|
|
5117
|
+
return;
|
|
5118
|
+
}
|
|
4976
5119
|
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|| (nativePaneChrome && event.name === "space" && activeField?.type === "select")
|
|
4981
|
-
) {
|
|
4982
|
-
if (!activeField) return;
|
|
4983
|
-
if (activeField.type === "select" || activeField.type === "multi-select" || activeField.type === "ordered-multi-select" || activeField.type === "toggle") {
|
|
4984
|
-
event.stopPropagation();
|
|
4985
|
-
event.preventDefault();
|
|
4986
|
-
if (nativePaneChrome && activeField.type === "select") {
|
|
4987
|
-
openNativeSelect(workflowNativeSelectRefs.current.get(activeField.id));
|
|
4988
|
-
return;
|
|
4989
|
-
}
|
|
4990
|
-
openWorkflowFieldPicker(currentRoute, activeField);
|
|
4991
|
-
return;
|
|
4992
|
-
}
|
|
4993
|
-
}
|
|
5120
|
+
if (activeTextarea && (event.name === "up" || event.name === "down" || (event.ctrl && (event.name === "p" || event.name === "n")))) {
|
|
5121
|
+
return;
|
|
5122
|
+
}
|
|
4994
5123
|
|
|
5124
|
+
if (event.name === "up" || (event.ctrl && event.name === "p")) {
|
|
5125
|
+
event.stopPropagation();
|
|
5126
|
+
event.preventDefault();
|
|
5127
|
+
moveWorkflowFocus(-1);
|
|
4995
5128
|
return;
|
|
4996
5129
|
}
|
|
4997
5130
|
|
|
4998
|
-
if (
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
moveListSelection(-1);
|
|
5003
|
-
return;
|
|
5004
|
-
}
|
|
5005
|
-
if (event.name === "down" || (event.ctrl && event.name === "n")) {
|
|
5006
|
-
event.stopPropagation();
|
|
5007
|
-
event.preventDefault();
|
|
5008
|
-
moveListSelection(1);
|
|
5009
|
-
return;
|
|
5010
|
-
}
|
|
5011
|
-
if (currentRoute.pickerId === "field-multi-select" && (event.name === "space" || event.sequence === " ")) {
|
|
5012
|
-
event.stopPropagation();
|
|
5013
|
-
event.preventDefault();
|
|
5014
|
-
const listState = visibleListStateRef.current;
|
|
5015
|
-
const selected = listState?.results[listState.selectedIdx];
|
|
5016
|
-
if (!selected) return;
|
|
5017
|
-
handleMultiSelectToggle(selected.id);
|
|
5018
|
-
return;
|
|
5019
|
-
}
|
|
5020
|
-
if (currentRoute.pickerId === "field-multi-select" && event.name === "[") {
|
|
5021
|
-
event.stopPropagation();
|
|
5022
|
-
event.preventDefault();
|
|
5023
|
-
handleMultiSelectMove("up");
|
|
5024
|
-
return;
|
|
5025
|
-
}
|
|
5026
|
-
if (currentRoute.pickerId === "field-multi-select" && event.name === "]") {
|
|
5027
|
-
event.stopPropagation();
|
|
5028
|
-
event.preventDefault();
|
|
5029
|
-
handleMultiSelectMove("down");
|
|
5030
|
-
return;
|
|
5031
|
-
}
|
|
5032
|
-
if (event.name === "return" || event.name === "enter") {
|
|
5033
|
-
event.stopPropagation();
|
|
5034
|
-
event.preventDefault();
|
|
5035
|
-
if (currentRoute.pickerId === "field-multi-select") {
|
|
5036
|
-
commitMultiSelectPicker();
|
|
5037
|
-
return;
|
|
5038
|
-
}
|
|
5039
|
-
activateListSelection();
|
|
5040
|
-
}
|
|
5131
|
+
if (event.name === "down" || (event.ctrl && event.name === "n")) {
|
|
5132
|
+
event.stopPropagation();
|
|
5133
|
+
event.preventDefault();
|
|
5134
|
+
moveWorkflowFocus(1);
|
|
5041
5135
|
return;
|
|
5042
5136
|
}
|
|
5043
5137
|
|
|
5044
|
-
if (
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
moveListSelection(-1);
|
|
5049
|
-
return;
|
|
5050
|
-
}
|
|
5051
|
-
if (event.name === "down" || (event.ctrl && event.name === "n")) {
|
|
5052
|
-
event.stopPropagation();
|
|
5053
|
-
event.preventDefault();
|
|
5054
|
-
moveListSelection(1);
|
|
5055
|
-
return;
|
|
5056
|
-
}
|
|
5057
|
-
if (event.name === "return" || event.name === "enter" || event.name === "space") {
|
|
5058
|
-
event.stopPropagation();
|
|
5059
|
-
event.preventDefault();
|
|
5060
|
-
activateListSelection();
|
|
5061
|
-
}
|
|
5138
|
+
if (event.name === "space" && activeField?.type === "toggle") {
|
|
5139
|
+
event.stopPropagation();
|
|
5140
|
+
event.preventDefault();
|
|
5141
|
+
updateWorkflowValue(activeField.id, !coerceFieldBoolean(currentRoute.values[activeField.id]));
|
|
5062
5142
|
return;
|
|
5063
5143
|
}
|
|
5064
5144
|
|
|
5065
|
-
if (
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
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") {
|
|
5145
|
+
if (
|
|
5146
|
+
event.name === "return"
|
|
5147
|
+
|| event.name === "enter"
|
|
5148
|
+
|| (nativePaneChrome && event.name === "space" && activeField?.type === "select")
|
|
5149
|
+
) {
|
|
5150
|
+
if (!activeField) return;
|
|
5151
|
+
if (activeField.type === "select" || activeField.type === "multi-select" || activeField.type === "ordered-multi-select" || activeField.type === "toggle") {
|
|
5079
5152
|
event.stopPropagation();
|
|
5080
5153
|
event.preventDefault();
|
|
5081
|
-
|
|
5154
|
+
if (nativePaneChrome && activeField.type === "select") {
|
|
5155
|
+
openNativeSelect(workflowNativeSelectRefs.current.get(activeField.id));
|
|
5156
|
+
return;
|
|
5157
|
+
}
|
|
5158
|
+
openWorkflowFieldPicker(currentRoute, activeField);
|
|
5082
5159
|
return;
|
|
5083
5160
|
}
|
|
5084
5161
|
}
|
|
5085
5162
|
|
|
5086
|
-
|
|
5087
|
-
|
|
5163
|
+
return;
|
|
5164
|
+
}
|
|
5088
5165
|
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5166
|
+
if (currentRoute?.kind === "picker") {
|
|
5167
|
+
if (event.name === "up" || (event.ctrl && event.name === "p")) {
|
|
5168
|
+
event.stopPropagation();
|
|
5169
|
+
event.preventDefault();
|
|
5170
|
+
moveListSelection(-1);
|
|
5171
|
+
return;
|
|
5095
5172
|
}
|
|
5096
|
-
|
|
5097
5173
|
if (event.name === "down" || (event.ctrl && event.name === "n")) {
|
|
5098
5174
|
event.stopPropagation();
|
|
5099
5175
|
event.preventDefault();
|
|
5100
5176
|
moveListSelection(1);
|
|
5101
5177
|
return;
|
|
5102
5178
|
}
|
|
5179
|
+
if (currentRoute.pickerId === "field-multi-select" && (event.name === "space" || event.sequence === " ")) {
|
|
5180
|
+
event.stopPropagation();
|
|
5181
|
+
event.preventDefault();
|
|
5182
|
+
const listState = visibleListStateRef.current;
|
|
5183
|
+
const selected = listState?.results[listState.selectedIdx];
|
|
5184
|
+
if (!selected) return;
|
|
5185
|
+
handleMultiSelectToggle(selected.id);
|
|
5186
|
+
return;
|
|
5187
|
+
}
|
|
5188
|
+
if (currentRoute.pickerId === "field-multi-select" && event.name === "[") {
|
|
5189
|
+
event.stopPropagation();
|
|
5190
|
+
event.preventDefault();
|
|
5191
|
+
handleMultiSelectMove("up");
|
|
5192
|
+
return;
|
|
5193
|
+
}
|
|
5194
|
+
if (currentRoute.pickerId === "field-multi-select" && event.name === "]") {
|
|
5195
|
+
event.stopPropagation();
|
|
5196
|
+
event.preventDefault();
|
|
5197
|
+
handleMultiSelectMove("down");
|
|
5198
|
+
return;
|
|
5199
|
+
}
|
|
5200
|
+
if (event.name === "return" || event.name === "enter") {
|
|
5201
|
+
event.stopPropagation();
|
|
5202
|
+
event.preventDefault();
|
|
5203
|
+
if (currentRoute.pickerId === "field-multi-select") {
|
|
5204
|
+
commitMultiSelectPicker();
|
|
5205
|
+
return;
|
|
5206
|
+
}
|
|
5207
|
+
activateListSelection();
|
|
5208
|
+
}
|
|
5209
|
+
return;
|
|
5210
|
+
}
|
|
5103
5211
|
|
|
5212
|
+
if (currentRoute?.kind === "pane-settings") {
|
|
5104
5213
|
if (event.name === "up" || (event.ctrl && event.name === "p")) {
|
|
5105
5214
|
event.stopPropagation();
|
|
5106
5215
|
event.preventDefault();
|
|
5107
5216
|
moveListSelection(-1);
|
|
5108
5217
|
return;
|
|
5109
5218
|
}
|
|
5110
|
-
|
|
5111
|
-
if ((event.meta && (event.name === "backspace" || event.name === "delete")) || (event.ctrl && event.name === "u")) {
|
|
5219
|
+
if (event.name === "down" || (event.ctrl && event.name === "n")) {
|
|
5112
5220
|
event.stopPropagation();
|
|
5113
5221
|
event.preventDefault();
|
|
5114
|
-
|
|
5222
|
+
moveListSelection(1);
|
|
5115
5223
|
return;
|
|
5116
5224
|
}
|
|
5225
|
+
if (event.name === "return" || event.name === "enter" || event.name === "space") {
|
|
5226
|
+
event.stopPropagation();
|
|
5227
|
+
event.preventDefault();
|
|
5228
|
+
activateListSelection();
|
|
5229
|
+
}
|
|
5230
|
+
return;
|
|
5231
|
+
}
|
|
5117
5232
|
|
|
5118
|
-
|
|
5233
|
+
if (themePickerActive) {
|
|
5234
|
+
if (event.name === "up" || (event.ctrl && event.name === "p")) {
|
|
5119
5235
|
event.stopPropagation();
|
|
5120
5236
|
event.preventDefault();
|
|
5121
|
-
|
|
5122
|
-
const nextQuery = trimmed.replace(/[^\s]+$/, "").replace(/\s+$/, "");
|
|
5123
|
-
setActiveListQuery(nextQuery);
|
|
5237
|
+
themePickerRef.current?.move(-1);
|
|
5124
5238
|
return;
|
|
5125
5239
|
}
|
|
5126
|
-
|
|
5127
|
-
const pluginToggleMode = (currentRoute?.kind === "mode" && currentRoute.screen === "plugins")
|
|
5128
|
-
|| (!currentRoute && rootModeInfo.kind === "plugins");
|
|
5129
|
-
if (pluginToggleMode && event.name === "space") {
|
|
5240
|
+
if (event.name === "down" || (event.ctrl && event.name === "n")) {
|
|
5130
5241
|
event.stopPropagation();
|
|
5131
5242
|
event.preventDefault();
|
|
5132
|
-
|
|
5133
|
-
if (selected?.pluginToggle) {
|
|
5134
|
-
void selected.pluginToggle();
|
|
5135
|
-
}
|
|
5243
|
+
themePickerRef.current?.move(1);
|
|
5136
5244
|
return;
|
|
5137
5245
|
}
|
|
5138
|
-
|
|
5139
5246
|
if (event.name === "return" || event.name === "enter") {
|
|
5140
5247
|
event.stopPropagation();
|
|
5141
5248
|
event.preventDefault();
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
return;
|
|
5145
|
-
}
|
|
5146
|
-
activateListSelection();
|
|
5249
|
+
themePickerRef.current?.commit();
|
|
5250
|
+
return;
|
|
5147
5251
|
}
|
|
5148
|
-
}
|
|
5252
|
+
}
|
|
5149
5253
|
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5254
|
+
const activeListState = visibleListStateRef.current;
|
|
5255
|
+
if (!activeListState) return;
|
|
5256
|
+
|
|
5257
|
+
if (!currentRoute && event.name === "tab") {
|
|
5258
|
+
if (acceptRootShortcutTab()) {
|
|
5259
|
+
event.stopPropagation();
|
|
5260
|
+
event.preventDefault();
|
|
5261
|
+
return;
|
|
5262
|
+
}
|
|
5154
5263
|
}
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5264
|
+
|
|
5265
|
+
if (event.name === "down" || (event.ctrl && event.name === "n")) {
|
|
5266
|
+
event.stopPropagation();
|
|
5267
|
+
event.preventDefault();
|
|
5268
|
+
moveListSelection(1);
|
|
5269
|
+
return;
|
|
5270
|
+
}
|
|
5271
|
+
|
|
5272
|
+
if (event.name === "up" || (event.ctrl && event.name === "p")) {
|
|
5273
|
+
event.stopPropagation();
|
|
5274
|
+
event.preventDefault();
|
|
5275
|
+
moveListSelection(-1);
|
|
5276
|
+
return;
|
|
5277
|
+
}
|
|
5278
|
+
|
|
5279
|
+
if ((event.meta && (event.name === "backspace" || event.name === "delete")) || (event.ctrl && event.name === "u")) {
|
|
5280
|
+
event.stopPropagation();
|
|
5281
|
+
event.preventDefault();
|
|
5282
|
+
setActiveListQuery("");
|
|
5283
|
+
return;
|
|
5284
|
+
}
|
|
5285
|
+
|
|
5286
|
+
if ((event.ctrl && event.name === "w") || (event.meta && (event.name === "h" || event.name === "u"))) {
|
|
5287
|
+
event.stopPropagation();
|
|
5288
|
+
event.preventDefault();
|
|
5289
|
+
const trimmed = activeListState.query.replace(/\s+$/, "");
|
|
5290
|
+
const nextQuery = trimmed.replace(/[^\s]+$/, "").replace(/\s+$/, "");
|
|
5291
|
+
setActiveListQuery(nextQuery);
|
|
5292
|
+
return;
|
|
5293
|
+
}
|
|
5294
|
+
|
|
5295
|
+
const pluginToggleMode = (currentRoute?.kind === "mode" && currentRoute.screen === "plugins")
|
|
5296
|
+
|| (!currentRoute && rootModeInfo.kind === "plugins");
|
|
5297
|
+
if (pluginToggleMode && event.name === "space") {
|
|
5298
|
+
event.stopPropagation();
|
|
5299
|
+
event.preventDefault();
|
|
5300
|
+
const selected = activeListState.results[activeListState.selectedIdx];
|
|
5301
|
+
if (selected?.pluginToggle) {
|
|
5302
|
+
void selected.pluginToggle();
|
|
5160
5303
|
}
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
openWorkflowFieldPicker,
|
|
5175
|
-
popRoute,
|
|
5176
|
-
renderer,
|
|
5177
|
-
rootModeInfo.kind,
|
|
5178
|
-
setActiveListQuery,
|
|
5179
|
-
themePickerActive,
|
|
5180
|
-
updateWorkflowValue,
|
|
5181
|
-
]);
|
|
5304
|
+
return;
|
|
5305
|
+
}
|
|
5306
|
+
|
|
5307
|
+
if (event.name === "return" || event.name === "enter") {
|
|
5308
|
+
event.stopPropagation();
|
|
5309
|
+
event.preventDefault();
|
|
5310
|
+
if (event.shift) {
|
|
5311
|
+
activateListSelection({ secondary: true });
|
|
5312
|
+
return;
|
|
5313
|
+
}
|
|
5314
|
+
activateListSelection();
|
|
5315
|
+
}
|
|
5316
|
+
}, { phase: "before" });
|
|
5182
5317
|
|
|
5183
5318
|
const barWidth = nativePaneChrome
|
|
5184
5319
|
? Math.max(46, Math.min(78, termWidth - 10, Math.floor(termWidth * 0.64)))
|
|
@@ -5221,8 +5356,18 @@ export function CommandBar({
|
|
|
5221
5356
|
Math.max(7, estimateWorkflowBodyRows(currentRoute)),
|
|
5222
5357
|
)
|
|
5223
5358
|
: baseBodyHeight;
|
|
5224
|
-
const
|
|
5225
|
-
|
|
5359
|
+
const shouldUseCompactListHeight = nativePaneChrome
|
|
5360
|
+
&& visibleListState != null
|
|
5361
|
+
&& !themePickerActive
|
|
5362
|
+
&& !showCustomMultiSelectPicker;
|
|
5363
|
+
const listBodyHeight = shouldUseCompactListHeight
|
|
5364
|
+
? Math.min(baseBodyHeight, Math.max(1, nativeListRows.length))
|
|
5365
|
+
: baseBodyHeight;
|
|
5366
|
+
const bodyHeight = currentRoute?.kind === "workflow"
|
|
5367
|
+
? workflowBodyHeight
|
|
5368
|
+
: shouldUseCompactListHeight
|
|
5369
|
+
? listBodyHeight
|
|
5370
|
+
: baseBodyHeight;
|
|
5226
5371
|
const nativePanelPaddingColumns = nativePaneChrome
|
|
5227
5372
|
? Math.ceil((NATIVE_COMMAND_BAR_PADDING_X_PX * 2) / Math.max(1, cellWidthPx))
|
|
5228
5373
|
: 0;
|
|
@@ -5233,7 +5378,7 @@ export function CommandBar({
|
|
|
5233
5378
|
|| currentRoute?.kind === "confirm"
|
|
5234
5379
|
|| showCustomMultiSelectPicker)
|
|
5235
5380
|
? 1
|
|
5236
|
-
: 3;
|
|
5381
|
+
: currentRoute ? 3 : 2;
|
|
5237
5382
|
const barHeight = nativePaneChrome
|
|
5238
5383
|
? bodyHeight + nativeBodyChromeRows + nativePanelPaddingRows
|
|
5239
5384
|
: bodyHeight + 7;
|
|
@@ -5578,7 +5723,7 @@ export function CommandBar({
|
|
|
5578
5723
|
data-gloom-role="command-bar-panel"
|
|
5579
5724
|
style={nativePaneChrome ? {
|
|
5580
5725
|
borderRadius: 8,
|
|
5581
|
-
boxShadow:
|
|
5726
|
+
boxShadow: `0 14px 32px ${blendHex(commandBarBg(), colors.bg, 0.18)}`,
|
|
5582
5727
|
overflow: "hidden",
|
|
5583
5728
|
padding: `${NATIVE_COMMAND_BAR_PADDING_Y_PX}px ${NATIVE_COMMAND_BAR_PADDING_X_PX}px`,
|
|
5584
5729
|
} : undefined}
|
|
@@ -5669,15 +5814,15 @@ export function CommandBar({
|
|
|
5669
5814
|
paletteText={paletteText}
|
|
5670
5815
|
panelBg={panelBg}
|
|
5671
5816
|
onPreview={applyThemePreview}
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5817
|
+
onCommit={(themeId) => {
|
|
5818
|
+
const nextConfig = {
|
|
5819
|
+
...stateRef.current.config,
|
|
5820
|
+
theme: themeId,
|
|
5821
|
+
};
|
|
5822
|
+
commitTheme(themeId);
|
|
5823
|
+
persistConfig(nextConfig);
|
|
5824
|
+
closeAll({ revertThemePreview: false });
|
|
5825
|
+
}}
|
|
5681
5826
|
/>
|
|
5682
5827
|
)}
|
|
5683
5828
|
|