gloomberb 0.3.7 → 0.4.0
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 +19 -8
- package/package.json +1 -1
- package/src/app.tsx +29 -65
- package/src/cli/base-converter.ts +31 -0
- package/src/cli/commands/search.ts +11 -3
- package/src/cli/commands/ticker.ts +22 -11
- package/src/cli/context.ts +27 -32
- package/src/cli/helpers.ts +1 -4
- package/src/cli/index.ts +140 -123
- package/src/cli/registry.test.ts +226 -0
- package/src/cli/registry.ts +201 -0
- package/src/cli.test.ts +219 -0
- package/src/components/chart/chart-controller.ts +309 -0
- package/src/components/chart/chart-data.ts +5 -6
- package/src/components/chart/chart-pane-settings.ts +27 -0
- package/src/components/chart/chart-resolution.test.ts +56 -0
- package/src/components/chart/chart-resolution.ts +362 -0
- package/src/components/chart/chart-types.ts +12 -4
- package/src/components/chart/chart-viewport.test.ts +25 -0
- package/src/components/chart/chart-viewport.ts +28 -0
- package/src/components/chart/comparison-chart-data.ts +20 -27
- package/src/components/chart/comparison-stock-chart.tsx +374 -99
- package/src/components/chart/stock-chart-renderer-switch.test.tsx +124 -0
- package/src/components/chart/stock-chart.tsx +455 -106
- package/src/components/command-bar/__snapshots__/command-bar.test.tsx.snap +4 -4
- package/src/components/command-bar/command-bar.test.tsx +650 -5
- package/src/components/command-bar/command-bar.tsx +359 -63
- package/src/components/command-bar/command-registry.ts +8 -0
- package/src/components/command-bar/helpers.test.ts +23 -1
- package/src/components/command-bar/helpers.ts +14 -5
- package/src/components/command-bar/pane-template-display.ts +12 -0
- package/src/components/command-bar/root-shortcuts.ts +2 -1
- package/src/components/command-bar/workflow-ops.ts +4 -2
- package/src/components/detail-feed-view.test.tsx +124 -49
- package/src/components/detail-feed-view.tsx +396 -160
- package/src/components/layout/floating-pane.tsx +1 -1
- package/src/components/layout/pane-header.tsx +26 -2
- package/src/components/layout/shell.tsx +62 -50
- package/src/components/layout/status-bar.test.tsx +66 -0
- package/src/components/layout/status-bar.tsx +26 -5
- package/src/components/onboarding/onboarding-wizard.test.tsx +65 -0
- package/src/components/onboarding/onboarding-wizard.tsx +5 -6
- package/src/components/ticker-list-table.test.tsx +83 -0
- package/src/components/ticker-list-table.tsx +174 -97
- package/src/components/ui/data-table.tsx +27 -2
- package/src/components/ui/index.ts +1 -0
- package/src/components/ui/page-stack-view.tsx +60 -0
- package/src/components/use-double-click-activation.ts +31 -0
- package/src/index.tsx +10 -10
- package/src/market-data/coordinator.test.ts +92 -4
- package/src/market-data/coordinator.ts +32 -5
- package/src/market-data/hooks.test.tsx +120 -0
- package/src/market-data/hooks.tsx +53 -16
- package/src/market-data/request-types.ts +4 -2
- package/src/market-data/selectors.ts +24 -58
- package/src/plugins/builtin/chat-controller.test.ts +370 -3
- package/src/plugins/builtin/chat-controller.ts +310 -35
- package/src/plugins/builtin/chat.test.tsx +118 -6
- package/src/plugins/builtin/chat.tsx +175 -116
- package/src/plugins/builtin/comparison-chart.test.tsx +66 -5
- package/src/plugins/builtin/comparison-chart.tsx +21 -1
- package/src/plugins/builtin/news.tsx +1 -11
- package/src/plugins/builtin/portfolio-list/cli/commands.ts +656 -0
- package/src/plugins/builtin/portfolio-list/command-bar.ts +122 -0
- package/src/plugins/builtin/portfolio-list/header.tsx +16 -34
- package/src/plugins/builtin/portfolio-list/index.tsx +5 -0
- package/src/plugins/builtin/portfolio-list/metrics.test.ts +61 -0
- package/src/plugins/builtin/portfolio-list/metrics.ts +2 -2
- package/src/plugins/builtin/portfolio-list/mutations.test.ts +187 -0
- package/src/plugins/builtin/portfolio-list/mutations.ts +226 -0
- package/src/plugins/builtin/portfolio-list/pane.tsx +39 -17
- package/src/plugins/builtin/portfolio-list/table.tsx +11 -2
- package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.test.tsx +163 -0
- package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.ts +81 -0
- package/src/plugins/builtin/sec.tsx +1 -11
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +13 -2
- package/src/plugins/builtin/ticker-detail/financials-tab.tsx +21 -0
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +45 -10
- package/src/plugins/builtin/ticker-detail/pane.tsx +35 -17
- package/src/plugins/builtin/ticker-detail/settings.ts +19 -1
- package/src/plugins/builtin/ticker-detail.test.tsx +47 -0
- package/src/plugins/catalog.ts +65 -0
- package/src/plugins/ibkr/broker-adapter.ts +28 -0
- package/src/plugins/ibkr/gateway-service.test.ts +45 -1
- package/src/plugins/ibkr/gateway-service.ts +141 -7
- package/src/plugins/pane-manager.test.ts +1 -1
- package/src/plugins/plugin-runtime.tsx +27 -7
- package/src/plugins/prediction-markets/cli.ts +28 -0
- package/src/plugins/prediction-markets/controller-data.ts +11 -3
- package/src/plugins/prediction-markets/controller.ts +141 -121
- package/src/plugins/prediction-markets/index.tsx +2 -0
- package/src/plugins/prediction-markets/launch.ts +51 -16
- package/src/plugins/prediction-markets/metrics.ts +9 -5
- package/src/plugins/prediction-markets/pane.test.tsx +119 -57
- package/src/plugins/prediction-markets/pane.tsx +74 -153
- package/src/plugins/prediction-markets/table.tsx +6 -3
- package/src/plugins/prediction-markets/types.ts +0 -1
- package/src/sources/gloomberb-cloud.test.ts +29 -0
- package/src/sources/gloomberb-cloud.ts +89 -1
- package/src/sources/provider-router.test.ts +265 -3
- package/src/sources/provider-router.ts +239 -11
- package/src/sources/yahoo-finance.test.ts +26 -0
- package/src/sources/yahoo-finance.ts +54 -3
- package/src/state/app-context-selector.test.tsx +99 -0
- package/src/state/app-context.test.ts +18 -0
- package/src/state/app-context.tsx +170 -65
- package/src/state/selectors.ts +25 -0
- package/src/theme/colors.ts +8 -8
- package/src/theme/themes.test.ts +2 -0
- package/src/theme/themes.ts +22 -10
- package/src/types/broker.ts +21 -0
- package/src/types/config.ts +17 -1
- package/src/types/data-provider.ts +18 -0
- package/src/types/financials.ts +31 -1
- package/src/types/plugin.ts +70 -0
- package/src/utils/api-client.test.ts +57 -0
- package/src/utils/api-client.ts +35 -12
- package/src/utils/back-navigation.ts +27 -0
- package/src/utils/format.test.ts +14 -0
- package/src/utils/format.ts +3 -3
- package/src/utils/market-status.ts +46 -1
- package/src/utils/quote-freshness.ts +127 -0
- package/src/utils/quote-resolution.test.ts +277 -0
- package/src/utils/quote-resolution.ts +641 -0
- package/src/utils/slugify.ts +3 -0
- package/src/utils/timestamp.ts +25 -0
- package/src/version.ts +1 -1
- package/src/cli/commands/portfolio.ts +0 -210
- package/src/cli/commands/watchlist.ts +0 -210
- package/src/plugins/builtin/data-providers.ts +0 -16
- package/src/sources/exchange-rates.ts +0 -18
- package/src/utils/ascii-chart.ts +0 -172
- package/src/utils/ticker-tokenizer.test.ts +0 -48
|
@@ -18,11 +18,12 @@ import {
|
|
|
18
18
|
import { getFocusedCollectionId, useAppState, useFocusedTicker } from "../../state/app-context";
|
|
19
19
|
import { fuzzyFilter } from "../../utils/fuzzy-search";
|
|
20
20
|
import { commands, getThemeOptions, matchPrefix, type Command } from "./command-registry";
|
|
21
|
-
import { applyTheme
|
|
21
|
+
import { applyTheme } from "../../theme/colors";
|
|
22
22
|
import { exportConfig, importConfig, resetAllData, saveConfig } from "../../data/config-store";
|
|
23
23
|
import type { DataProvider } from "../../types/data-provider";
|
|
24
24
|
import type { TickerRepository } from "../../data/ticker-repository";
|
|
25
25
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
26
|
+
import type { TickerRecord } from "../../types/ticker";
|
|
26
27
|
import type {
|
|
27
28
|
CommandDef,
|
|
28
29
|
PaneSettingField,
|
|
@@ -68,6 +69,7 @@ import {
|
|
|
68
69
|
type TickerSearchCandidate,
|
|
69
70
|
} from "../../utils/ticker-search";
|
|
70
71
|
import { debugLog } from "../../utils/debug-log";
|
|
72
|
+
import { isPlainBackspace } from "../../utils/back-navigation";
|
|
71
73
|
import type {
|
|
72
74
|
CommandBarFieldOption,
|
|
73
75
|
CommandBarFieldValue,
|
|
@@ -78,11 +80,13 @@ import type {
|
|
|
78
80
|
CommandBarWorkflowRoute,
|
|
79
81
|
} from "./workflow-types";
|
|
80
82
|
import { parseRootShortcutIntent } from "./root-shortcuts";
|
|
83
|
+
import { getPaneTemplateDisplayLabel } from "./pane-template-display";
|
|
81
84
|
import {
|
|
82
85
|
applyCollectionMembershipChange,
|
|
83
86
|
getCollectionTargetOptions,
|
|
84
87
|
resolvePreferredCollectionTarget,
|
|
85
88
|
resolveTickerInput,
|
|
89
|
+
resolveTickerInputOrThrow,
|
|
86
90
|
resolveTickerListInput,
|
|
87
91
|
} from "./workflow-ops";
|
|
88
92
|
import {
|
|
@@ -112,6 +116,19 @@ import {
|
|
|
112
116
|
summarizeWorkflowFieldValue,
|
|
113
117
|
toggleSelectedValue,
|
|
114
118
|
} from "./helpers";
|
|
119
|
+
import {
|
|
120
|
+
buildAddToPortfolioWorkflow,
|
|
121
|
+
buildSetPortfolioPositionWorkflow,
|
|
122
|
+
} from "../../plugins/builtin/portfolio-list/command-bar";
|
|
123
|
+
import {
|
|
124
|
+
addTickerToPortfolio,
|
|
125
|
+
createManualPortfolio as createManualPortfolioConfig,
|
|
126
|
+
deleteManualPortfolio,
|
|
127
|
+
isManualPortfolio,
|
|
128
|
+
removeTickerFromPortfolio,
|
|
129
|
+
resolveManualPositionCurrency,
|
|
130
|
+
setManualPortfolioPosition,
|
|
131
|
+
} from "../../plugins/builtin/portfolio-list/mutations";
|
|
115
132
|
|
|
116
133
|
interface CommandBarProps {
|
|
117
134
|
dataProvider: DataProvider;
|
|
@@ -433,7 +450,7 @@ export function CommandBar({
|
|
|
433
450
|
query: initialQuery,
|
|
434
451
|
selectedIdx: 0,
|
|
435
452
|
hoveredIdx: null,
|
|
436
|
-
themeBaseId: screen === "themes" ?
|
|
453
|
+
themeBaseId: screen === "themes" ? stateRef.current.config.theme : undefined,
|
|
437
454
|
payload,
|
|
438
455
|
});
|
|
439
456
|
}, [pushRoute]);
|
|
@@ -590,6 +607,39 @@ export function CommandBar({
|
|
|
590
607
|
return next;
|
|
591
608
|
}, []);
|
|
592
609
|
|
|
610
|
+
const openAddToPortfolioWorkflow = useCallback((
|
|
611
|
+
ticker: TickerRecord,
|
|
612
|
+
preferredPortfolioId?: string | null,
|
|
613
|
+
) => {
|
|
614
|
+
const defaultAvgCost = stateRef.current.financials.get(ticker.metadata.ticker)?.quote?.price ?? null;
|
|
615
|
+
const workflow = buildAddToPortfolioWorkflow(stateRef.current.config, {
|
|
616
|
+
preferredPortfolioId,
|
|
617
|
+
ticker,
|
|
618
|
+
defaultAvgCost,
|
|
619
|
+
});
|
|
620
|
+
if (!workflow) {
|
|
621
|
+
pluginRegistry.showToastFn("Create a manual portfolio first.", { type: "info" });
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
openWorkflowRoute({
|
|
626
|
+
kind: "workflow",
|
|
627
|
+
workflowId: "builtin:add-portfolio",
|
|
628
|
+
title: `Add ${ticker.metadata.ticker} to Portfolio`,
|
|
629
|
+
subtitle: "Choose a portfolio and optionally record the manual position now.",
|
|
630
|
+
fields: workflow.fields,
|
|
631
|
+
values: workflow.values,
|
|
632
|
+
activeFieldId: getFirstVisibleFieldId(workflow.fields, workflow.values),
|
|
633
|
+
submitLabel: "Add to Portfolio",
|
|
634
|
+
cancelLabel: "Back",
|
|
635
|
+
pendingLabel: workflow.pendingLabel,
|
|
636
|
+
pending: false,
|
|
637
|
+
error: null,
|
|
638
|
+
successBehavior: "close",
|
|
639
|
+
payload: { kind: "builtin", actionId: "add-portfolio" },
|
|
640
|
+
});
|
|
641
|
+
}, [openWorkflowRoute, pluginRegistry]);
|
|
642
|
+
|
|
593
643
|
const connectBrokerProfile = useCallback(async (
|
|
594
644
|
brokerId: string,
|
|
595
645
|
values: WorkflowStringValues,
|
|
@@ -625,25 +675,15 @@ export function CommandBar({
|
|
|
625
675
|
|
|
626
676
|
const createManualPortfolio = useCallback(async (name: string) => {
|
|
627
677
|
const currentState = stateRef.current;
|
|
628
|
-
const
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
const id = slugifyName(trimmedName, "portfolio");
|
|
634
|
-
const newPortfolio = {
|
|
635
|
-
id,
|
|
636
|
-
name: trimmedName,
|
|
637
|
-
currency: currentState.config.baseCurrency || "USD",
|
|
638
|
-
};
|
|
639
|
-
const nextConfig = {
|
|
640
|
-
...currentState.config,
|
|
641
|
-
portfolios: [...currentState.config.portfolios, newPortfolio],
|
|
642
|
-
};
|
|
678
|
+
const { config: nextConfig, portfolio } = createManualPortfolioConfig(
|
|
679
|
+
currentState.config,
|
|
680
|
+
name,
|
|
681
|
+
currentState.config.baseCurrency,
|
|
682
|
+
);
|
|
643
683
|
dispatch({ type: "SET_CONFIG", config: nextConfig });
|
|
644
|
-
setActiveCollection(id);
|
|
684
|
+
setActiveCollection(portfolio.id);
|
|
645
685
|
await saveConfig(nextConfig);
|
|
646
|
-
pluginRegistry.showToastFn(`Created portfolio "${
|
|
686
|
+
pluginRegistry.showToastFn(`Created portfolio "${portfolio.name}".`, { type: "success" });
|
|
647
687
|
}, [dispatch, pluginRegistry, setActiveCollection]);
|
|
648
688
|
|
|
649
689
|
const createWatchlist = useCallback(async (name: string) => {
|
|
@@ -691,11 +731,21 @@ export function CommandBar({
|
|
|
691
731
|
if (!portfolio) {
|
|
692
732
|
throw new Error("Portfolio not found.");
|
|
693
733
|
}
|
|
734
|
+
if (!isManualPortfolio(portfolio)) {
|
|
735
|
+
throw new Error("Broker-managed portfolios cannot be deleted here.");
|
|
736
|
+
}
|
|
694
737
|
|
|
695
|
-
const
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
738
|
+
const result = deleteManualPortfolio(
|
|
739
|
+
currentState.config,
|
|
740
|
+
[...currentState.tickers.values()],
|
|
741
|
+
portfolioId,
|
|
742
|
+
);
|
|
743
|
+
for (const ticker of result.tickers) {
|
|
744
|
+
await tickerRepository.saveTicker(ticker);
|
|
745
|
+
dispatch({ type: "UPDATE_TICKER", ticker });
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
const nextConfig = result.config;
|
|
699
749
|
dispatch({ type: "SET_CONFIG", config: nextConfig });
|
|
700
750
|
if (activeCollectionId === portfolioId) {
|
|
701
751
|
const fallback = nextConfig.portfolios[0]?.id || nextConfig.watchlists[0]?.id || "";
|
|
@@ -703,7 +753,87 @@ export function CommandBar({
|
|
|
703
753
|
}
|
|
704
754
|
await saveConfig(nextConfig);
|
|
705
755
|
pluginRegistry.showToastFn(`Deleted "${portfolio.name}".`, { type: "success" });
|
|
706
|
-
}, [activeCollectionId, dispatch, pluginRegistry, setActiveCollection]);
|
|
756
|
+
}, [activeCollectionId, dispatch, pluginRegistry, setActiveCollection, tickerRepository]);
|
|
757
|
+
|
|
758
|
+
const setPortfolioPositionFromWorkflow = useCallback(async (values: Record<string, CommandBarFieldValue>) => {
|
|
759
|
+
const currentState = stateRef.current;
|
|
760
|
+
const portfolioId = coerceFieldString(values.portfolioId).trim();
|
|
761
|
+
const portfolio = currentState.config.portfolios.find((entry) => entry.id === portfolioId);
|
|
762
|
+
if (!portfolio || !isManualPortfolio(portfolio)) {
|
|
763
|
+
throw new Error("Choose a manual portfolio.");
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
const shares = Number(coerceFieldString(values.shares));
|
|
767
|
+
if (!Number.isFinite(shares) || shares <= 0) {
|
|
768
|
+
throw new Error("Shares must be greater than 0.");
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
const avgCost = Number(coerceFieldString(values.avgCost));
|
|
772
|
+
if (!Number.isFinite(avgCost)) {
|
|
773
|
+
throw new Error("Avg Cost must be a valid number.");
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
const resolvedTicker = await resolveTickerInputOrThrow(
|
|
777
|
+
coerceFieldString(values.ticker),
|
|
778
|
+
activeTickerSymbol,
|
|
779
|
+
activeCollectionId,
|
|
780
|
+
{
|
|
781
|
+
dataProvider,
|
|
782
|
+
tickerRepository,
|
|
783
|
+
pluginRegistry,
|
|
784
|
+
dispatch,
|
|
785
|
+
getState: () => stateRef.current,
|
|
786
|
+
},
|
|
787
|
+
);
|
|
788
|
+
|
|
789
|
+
const currency = resolveManualPositionCurrency(
|
|
790
|
+
coerceFieldString(values.currency),
|
|
791
|
+
resolvedTicker.ticker,
|
|
792
|
+
portfolio,
|
|
793
|
+
currentState.config.baseCurrency,
|
|
794
|
+
);
|
|
795
|
+
|
|
796
|
+
const result = setManualPortfolioPosition(resolvedTicker.ticker, portfolio.id, {
|
|
797
|
+
shares,
|
|
798
|
+
avgCost,
|
|
799
|
+
currency,
|
|
800
|
+
});
|
|
801
|
+
await tickerRepository.saveTicker(result.ticker);
|
|
802
|
+
dispatch({ type: "UPDATE_TICKER", ticker: result.ticker });
|
|
803
|
+
pluginRegistry.showToastFn(`Set position for ${result.ticker.metadata.ticker} in "${portfolio.name}".`, { type: "success" });
|
|
804
|
+
}, [activeCollectionId, activeTickerSymbol, dataProvider, dispatch, pluginRegistry, tickerRepository]);
|
|
805
|
+
|
|
806
|
+
const addTickerMembershipFromWorkflow = useCallback(async (values: Record<string, CommandBarFieldValue>) => {
|
|
807
|
+
const currentState = stateRef.current;
|
|
808
|
+
const portfolioId = coerceFieldString(values.portfolioId).trim();
|
|
809
|
+
const portfolio = currentState.config.portfolios.find((entry) => entry.id === portfolioId);
|
|
810
|
+
if (!portfolio || !isManualPortfolio(portfolio)) {
|
|
811
|
+
throw new Error("Choose a manual portfolio.");
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
const resolvedTicker = await resolveTickerInputOrThrow(
|
|
815
|
+
coerceFieldString(values.ticker),
|
|
816
|
+
activeTickerSymbol,
|
|
817
|
+
activeCollectionId,
|
|
818
|
+
{
|
|
819
|
+
dataProvider,
|
|
820
|
+
tickerRepository,
|
|
821
|
+
pluginRegistry,
|
|
822
|
+
dispatch,
|
|
823
|
+
getState: () => stateRef.current,
|
|
824
|
+
},
|
|
825
|
+
);
|
|
826
|
+
|
|
827
|
+
const result = addTickerToPortfolio(resolvedTicker.ticker, portfolio.id);
|
|
828
|
+
if (result.changed) {
|
|
829
|
+
await tickerRepository.saveTicker(result.ticker);
|
|
830
|
+
dispatch({ type: "UPDATE_TICKER", ticker: result.ticker });
|
|
831
|
+
pluginRegistry.showToastFn(`Added ${result.ticker.metadata.ticker} to "${portfolio.name}".`, { type: "success" });
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
pluginRegistry.showToastFn(`${result.ticker.metadata.ticker} is already in "${portfolio.name}".`, { type: "info" });
|
|
836
|
+
}, [activeCollectionId, activeTickerSymbol, dataProvider, dispatch, pluginRegistry, tickerRepository]);
|
|
707
837
|
|
|
708
838
|
const disconnectBrokerInstance = useCallback(async (instanceId: string) => {
|
|
709
839
|
const instance = stateRef.current.config.brokerInstances.find((entry) => entry.id === instanceId);
|
|
@@ -783,6 +913,33 @@ export function CommandBar({
|
|
|
783
913
|
openWorkflowRoute(workflow);
|
|
784
914
|
return;
|
|
785
915
|
}
|
|
916
|
+
case "set-portfolio-position": {
|
|
917
|
+
const workflow = buildSetPortfolioPositionWorkflow(state.config, {
|
|
918
|
+
activeCollectionId,
|
|
919
|
+
activeTicker: activeTickerData,
|
|
920
|
+
});
|
|
921
|
+
if (!workflow) {
|
|
922
|
+
pluginRegistry.showToastFn("Create a manual portfolio first.", { type: "info" });
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
openWorkflowRoute({
|
|
926
|
+
kind: "workflow",
|
|
927
|
+
workflowId: "builtin:set-portfolio-position",
|
|
928
|
+
title: "Set Portfolio Position",
|
|
929
|
+
subtitle: "Create or update a manual position without leaving the command bar.",
|
|
930
|
+
fields: workflow.fields,
|
|
931
|
+
values: workflow.values,
|
|
932
|
+
activeFieldId: getFirstVisibleFieldId(workflow.fields, workflow.values),
|
|
933
|
+
submitLabel: "Save Position",
|
|
934
|
+
cancelLabel: "Back",
|
|
935
|
+
pendingLabel: workflow.pendingLabel,
|
|
936
|
+
pending: false,
|
|
937
|
+
error: null,
|
|
938
|
+
successBehavior: "close",
|
|
939
|
+
payload: { kind: "builtin", actionId },
|
|
940
|
+
});
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
786
943
|
case "add-broker-account": {
|
|
787
944
|
const workflow = buildBrokerWorkflow(
|
|
788
945
|
"brokerType",
|
|
@@ -801,7 +958,7 @@ export function CommandBar({
|
|
|
801
958
|
default:
|
|
802
959
|
return;
|
|
803
960
|
}
|
|
804
|
-
}, [buildBrokerWorkflow, openWorkflowRoute, pluginRegistry, state.config
|
|
961
|
+
}, [activeCollectionId, activeTickerData, buildBrokerWorkflow, openWorkflowRoute, pluginRegistry, state.config]);
|
|
805
962
|
|
|
806
963
|
const openPickerRoute = useCallback((
|
|
807
964
|
route: CommandBarRoute,
|
|
@@ -912,6 +1069,55 @@ export function CommandBar({
|
|
|
912
1069
|
return;
|
|
913
1070
|
}
|
|
914
1071
|
|
|
1072
|
+
if (kind === "portfolio" && action === "add") {
|
|
1073
|
+
const manualPortfolios = stateForCommand.config.portfolios.filter(isManualPortfolio);
|
|
1074
|
+
if (manualPortfolios.length === 0) {
|
|
1075
|
+
pluginRegistry.showToastFn("Create a manual portfolio first.", { type: "info" });
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
const preferredTargetId = explicitTargetId
|
|
1080
|
+
?? (activeCollectionId && manualPortfolios.some((portfolio) => portfolio.id === activeCollectionId)
|
|
1081
|
+
? activeCollectionId
|
|
1082
|
+
: manualPortfolios.length === 1
|
|
1083
|
+
? manualPortfolios[0]!.id
|
|
1084
|
+
: null);
|
|
1085
|
+
|
|
1086
|
+
if (!preferredTargetId) {
|
|
1087
|
+
const options = manualPortfolios.map((portfolio) => {
|
|
1088
|
+
const isMember = resolvedTicker.ticker.metadata.portfolios.includes(portfolio.id);
|
|
1089
|
+
const description = isMember
|
|
1090
|
+
? `Update position in "${portfolio.name}"`
|
|
1091
|
+
: `Add to "${portfolio.name}"`;
|
|
1092
|
+
return {
|
|
1093
|
+
id: portfolio.id,
|
|
1094
|
+
label: portfolio.name,
|
|
1095
|
+
detail: description,
|
|
1096
|
+
description,
|
|
1097
|
+
};
|
|
1098
|
+
});
|
|
1099
|
+
openPickerRoute({
|
|
1100
|
+
kind: "picker",
|
|
1101
|
+
pickerId: "collection-target",
|
|
1102
|
+
title: `Add ${resolvedTicker.symbol} to Portfolio`,
|
|
1103
|
+
query: "",
|
|
1104
|
+
selectedIdx: 0,
|
|
1105
|
+
hoveredIdx: null,
|
|
1106
|
+
options,
|
|
1107
|
+
payload: {
|
|
1108
|
+
commandId,
|
|
1109
|
+
kind,
|
|
1110
|
+
action,
|
|
1111
|
+
symbol: resolvedTicker.symbol,
|
|
1112
|
+
},
|
|
1113
|
+
});
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
openAddToPortfolioWorkflow(resolvedTicker.ticker, preferredTargetId);
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
915
1121
|
const targetId = explicitTargetId
|
|
916
1122
|
?? resolvePreferredCollectionTarget(
|
|
917
1123
|
stateForCommand,
|
|
@@ -956,13 +1162,32 @@ export function CommandBar({
|
|
|
956
1162
|
return;
|
|
957
1163
|
}
|
|
958
1164
|
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1165
|
+
let changed = false;
|
|
1166
|
+
if (kind === "watchlist") {
|
|
1167
|
+
({ changed } = await applyCollectionMembershipChange(
|
|
1168
|
+
resolvedTicker.ticker,
|
|
1169
|
+
kind,
|
|
1170
|
+
action,
|
|
1171
|
+
targetId,
|
|
1172
|
+
deps,
|
|
1173
|
+
));
|
|
1174
|
+
} else {
|
|
1175
|
+
const portfolio = stateForCommand.config.portfolios.find((entry) => entry.id === targetId);
|
|
1176
|
+
if (!portfolio || !isManualPortfolio(portfolio)) {
|
|
1177
|
+
pluginRegistry.showToastFn("Choose a manual portfolio.", { type: "error" });
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
const result = action === "add"
|
|
1182
|
+
? addTickerToPortfolio(resolvedTicker.ticker, targetId)
|
|
1183
|
+
: removeTickerFromPortfolio(resolvedTicker.ticker, targetId);
|
|
1184
|
+
|
|
1185
|
+
changed = result.changed;
|
|
1186
|
+
if (result.changed) {
|
|
1187
|
+
await deps.tickerRepository.saveTicker(result.ticker);
|
|
1188
|
+
deps.dispatch({ type: "UPDATE_TICKER", ticker: result.ticker });
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
966
1191
|
const targetName = (kind === "watchlist"
|
|
967
1192
|
? stateForCommand.config.watchlists.find((entry) => entry.id === targetId)?.name
|
|
968
1193
|
: stateForCommand.config.portfolios.find((entry) => entry.id === targetId)?.name) || targetId;
|
|
@@ -985,6 +1210,7 @@ export function CommandBar({
|
|
|
985
1210
|
activeTickerSymbol,
|
|
986
1211
|
buildSharedWorkflowDeps,
|
|
987
1212
|
closeAll,
|
|
1213
|
+
openAddToPortfolioWorkflow,
|
|
988
1214
|
openModeRoute,
|
|
989
1215
|
openPickerRoute,
|
|
990
1216
|
pluginRegistry,
|
|
@@ -1234,6 +1460,18 @@ export function CommandBar({
|
|
|
1234
1460
|
await connectBrokerProfile(brokerId, values);
|
|
1235
1461
|
break;
|
|
1236
1462
|
}
|
|
1463
|
+
case "add-portfolio": {
|
|
1464
|
+
const shares = coerceFieldString(route.values.shares).trim();
|
|
1465
|
+
if (!shares) {
|
|
1466
|
+
await addTickerMembershipFromWorkflow(route.values);
|
|
1467
|
+
} else {
|
|
1468
|
+
await setPortfolioPositionFromWorkflow(route.values);
|
|
1469
|
+
}
|
|
1470
|
+
break;
|
|
1471
|
+
}
|
|
1472
|
+
case "set-portfolio-position":
|
|
1473
|
+
await setPortfolioPositionFromWorkflow(route.values);
|
|
1474
|
+
break;
|
|
1237
1475
|
default:
|
|
1238
1476
|
break;
|
|
1239
1477
|
}
|
|
@@ -1329,6 +1567,8 @@ export function CommandBar({
|
|
|
1329
1567
|
extractBrokerWorkflowValues,
|
|
1330
1568
|
getWorkflowFieldStringValue,
|
|
1331
1569
|
pluginRegistry,
|
|
1570
|
+
addTickerMembershipFromWorkflow,
|
|
1571
|
+
setPortfolioPositionFromWorkflow,
|
|
1332
1572
|
state.config.activeLayoutIndex,
|
|
1333
1573
|
syncActiveWorkflowTextarea,
|
|
1334
1574
|
updateTopRoute,
|
|
@@ -1375,6 +1615,7 @@ export function CommandBar({
|
|
|
1375
1615
|
}, []);
|
|
1376
1616
|
|
|
1377
1617
|
const openPaneTemplateWorkflow = useCallback((template: PaneTemplateDef, options?: { arg?: string }) => {
|
|
1618
|
+
const displayLabel = getPaneTemplateDisplayLabel(template);
|
|
1378
1619
|
const normalized = template.wizard && template.wizard.length > 0
|
|
1379
1620
|
? normalizeWizardFields(template.wizard)
|
|
1380
1621
|
: { fields: [] as CommandBarWorkflowField[], description: [] as string[], initialValues: {} as Record<string, CommandBarFieldValue> };
|
|
@@ -1395,7 +1636,7 @@ export function CommandBar({
|
|
|
1395
1636
|
openWorkflowRoute({
|
|
1396
1637
|
kind: "workflow",
|
|
1397
1638
|
workflowId: `pane-template:${template.id}`,
|
|
1398
|
-
title:
|
|
1639
|
+
title: displayLabel,
|
|
1399
1640
|
subtitle: template.description,
|
|
1400
1641
|
description: normalized.description,
|
|
1401
1642
|
fields,
|
|
@@ -1403,7 +1644,7 @@ export function CommandBar({
|
|
|
1403
1644
|
activeFieldId: getFirstVisibleFieldId(fields, values),
|
|
1404
1645
|
submitLabel: "Create Pane",
|
|
1405
1646
|
cancelLabel: "Back",
|
|
1406
|
-
pendingLabel: normalized.pendingLabel ?? `Creating ${
|
|
1647
|
+
pendingLabel: normalized.pendingLabel ?? `Creating ${displayLabel.toLowerCase()}…`,
|
|
1407
1648
|
successLabel: normalized.successLabel,
|
|
1408
1649
|
pending: false,
|
|
1409
1650
|
error: null,
|
|
@@ -1426,8 +1667,9 @@ export function CommandBar({
|
|
|
1426
1667
|
await pluginRegistry.createPaneFromTemplateAsyncFn(template.id, createOptions);
|
|
1427
1668
|
closeAll({ revertThemePreview: false });
|
|
1428
1669
|
} catch (error) {
|
|
1670
|
+
const displayLabel = getPaneTemplateDisplayLabel(template);
|
|
1429
1671
|
pluginRegistry.showToastFn(
|
|
1430
|
-
error instanceof Error ? error.message : `Could not create ${
|
|
1672
|
+
error instanceof Error ? error.message : `Could not create ${displayLabel.toLowerCase()}.`,
|
|
1431
1673
|
{ type: "error" },
|
|
1432
1674
|
);
|
|
1433
1675
|
}
|
|
@@ -1701,13 +1943,12 @@ export function CommandBar({
|
|
|
1701
1943
|
): ResultItem => {
|
|
1702
1944
|
const pluginId = pluginRegistry.getPaneTemplatePluginId(template.id);
|
|
1703
1945
|
const pluginName = pluginId ? pluginRegistry.allPlugins.get(pluginId)?.name : null;
|
|
1704
|
-
const
|
|
1946
|
+
const displayLabel = getPaneTemplateDisplayLabel(template);
|
|
1705
1947
|
const shortcutLabel = template.shortcut
|
|
1706
1948
|
? [template.shortcut.prefix, template.shortcut.argPlaceholder && `<${template.shortcut.argPlaceholder}>`]
|
|
1707
1949
|
.filter(Boolean)
|
|
1708
1950
|
.join(" ")
|
|
1709
1951
|
: null;
|
|
1710
|
-
const placementLabel = paneDef?.defaultMode === "floating" ? "float" : "dock";
|
|
1711
1952
|
const arg = options?.createOptions?.arg;
|
|
1712
1953
|
|
|
1713
1954
|
const action = () => {
|
|
@@ -1724,12 +1965,12 @@ export function CommandBar({
|
|
|
1724
1965
|
|
|
1725
1966
|
return {
|
|
1726
1967
|
id: `pane-template:${template.id}:${arg || ""}`,
|
|
1727
|
-
label:
|
|
1968
|
+
label: displayLabel,
|
|
1728
1969
|
detail: shortcutLabel ? `${template.description} · ${shortcutLabel}` : template.description,
|
|
1729
1970
|
category: options?.category ?? (pluginName ? `${pluginName} Panes` : "New Panes"),
|
|
1730
1971
|
kind: "action",
|
|
1731
|
-
right: options?.showShortcut ?
|
|
1732
|
-
searchText: `${template.paneId} ${template.keywords?.join(" ") || ""} ${shortcutLabel || ""} ${pluginName || ""}`,
|
|
1972
|
+
right: options?.showShortcut ? template.shortcut?.prefix : undefined,
|
|
1973
|
+
searchText: `${displayLabel} ${template.label} ${template.paneId} ${template.keywords?.join(" ") || ""} ${shortcutLabel || ""} ${pluginName || ""}`,
|
|
1733
1974
|
action,
|
|
1734
1975
|
};
|
|
1735
1976
|
}, [openPaneTemplateDirect, openPaneTemplateWorkflow, pluginRegistry, runPaneTemplateShortcut, shouldOpenTemplateConfig]);
|
|
@@ -1863,11 +2104,17 @@ export function CommandBar({
|
|
|
1863
2104
|
case "add-broker-account":
|
|
1864
2105
|
case "new-portfolio":
|
|
1865
2106
|
case "new-watchlist":
|
|
2107
|
+
case "set-portfolio-position":
|
|
1866
2108
|
case "disconnect-broker-account":
|
|
1867
2109
|
case "delete-watchlist":
|
|
1868
2110
|
case "delete-portfolio":
|
|
1869
2111
|
case "reset-all-data":
|
|
1870
|
-
if (
|
|
2112
|
+
if (
|
|
2113
|
+
command.id === "add-broker-account"
|
|
2114
|
+
|| command.id === "new-portfolio"
|
|
2115
|
+
|| command.id === "new-watchlist"
|
|
2116
|
+
|| command.id === "set-portfolio-position"
|
|
2117
|
+
) {
|
|
1871
2118
|
openBuiltInWorkflow(command.id);
|
|
1872
2119
|
return;
|
|
1873
2120
|
}
|
|
@@ -1914,7 +2161,7 @@ export function CommandBar({
|
|
|
1914
2161
|
return;
|
|
1915
2162
|
}
|
|
1916
2163
|
if (command.id === "delete-portfolio") {
|
|
1917
|
-
const deletable = state.config.portfolios.filter(
|
|
2164
|
+
const deletable = state.config.portfolios.filter(isManualPortfolio);
|
|
1918
2165
|
const options = deletable.map((portfolio) => ({
|
|
1919
2166
|
id: portfolio.id,
|
|
1920
2167
|
label: portfolio.name,
|
|
@@ -2160,6 +2407,7 @@ export function CommandBar({
|
|
|
2160
2407
|
|
|
2161
2408
|
const isWatchlistTab = state.config.watchlists.some((entry) => entry.id === activeCollectionId);
|
|
2162
2409
|
const isPortfolioTab = state.config.portfolios.some((entry) => entry.id === activeCollectionId);
|
|
2410
|
+
const manualPortfolios = state.config.portfolios.filter(isManualPortfolio);
|
|
2163
2411
|
const tickerData = activeTickerData;
|
|
2164
2412
|
const focusedPaneHasSettings = !!state.focusedPaneId && pluginRegistry.hasPaneSettings(state.focusedPaneId);
|
|
2165
2413
|
|
|
@@ -2167,8 +2415,8 @@ export function CommandBar({
|
|
|
2167
2415
|
? activeCollectionId
|
|
2168
2416
|
: state.config.watchlists[0]?.id ?? null;
|
|
2169
2417
|
const targetPortfolioId = isPortfolioTab
|
|
2170
|
-
? activeCollectionId
|
|
2171
|
-
:
|
|
2418
|
+
? manualPortfolios.find((entry) => entry.id === activeCollectionId)?.id ?? null
|
|
2419
|
+
: manualPortfolios[0]?.id ?? null;
|
|
2172
2420
|
|
|
2173
2421
|
function shouldShow(command: Command): boolean {
|
|
2174
2422
|
switch (command.id) {
|
|
@@ -2177,16 +2425,18 @@ export function CommandBar({
|
|
|
2177
2425
|
case "remove-watchlist":
|
|
2178
2426
|
return !!tickerData && tickerData.metadata.watchlists.length > 0;
|
|
2179
2427
|
case "add-portfolio":
|
|
2180
|
-
return !!tickerData &&
|
|
2428
|
+
return !!tickerData && manualPortfolios.length > 0;
|
|
2181
2429
|
case "remove-portfolio":
|
|
2182
2430
|
return !!tickerData && tickerData.metadata.portfolios.some((id) =>
|
|
2183
|
-
state.config.portfolios.some((entry) => entry.id === id &&
|
|
2431
|
+
state.config.portfolios.some((entry) => entry.id === id && isManualPortfolio(entry)));
|
|
2432
|
+
case "set-portfolio-position":
|
|
2433
|
+
return manualPortfolios.length > 0;
|
|
2184
2434
|
case "disconnect-broker-account":
|
|
2185
2435
|
return state.config.brokerInstances.length > 0;
|
|
2186
2436
|
case "delete-watchlist":
|
|
2187
2437
|
return state.config.watchlists.length > 0;
|
|
2188
2438
|
case "delete-portfolio":
|
|
2189
|
-
return
|
|
2439
|
+
return manualPortfolios.length > 0;
|
|
2190
2440
|
case "pane-settings":
|
|
2191
2441
|
return focusedPaneHasSettings;
|
|
2192
2442
|
default:
|
|
@@ -2204,6 +2454,8 @@ export function CommandBar({
|
|
|
2204
2454
|
return activeTickerSymbol ? `Add ${activeTickerSymbol} to Portfolio` : command.label;
|
|
2205
2455
|
case "remove-portfolio":
|
|
2206
2456
|
return activeTickerSymbol ? `Remove ${activeTickerSymbol} from Portfolio` : command.label;
|
|
2457
|
+
case "set-portfolio-position":
|
|
2458
|
+
return activeTickerSymbol ? `Set Position for ${activeTickerSymbol}` : command.label;
|
|
2207
2459
|
default:
|
|
2208
2460
|
return command.label;
|
|
2209
2461
|
}
|
|
@@ -2227,10 +2479,14 @@ export function CommandBar({
|
|
|
2227
2479
|
}
|
|
2228
2480
|
case "remove-portfolio": {
|
|
2229
2481
|
const names = tickerData?.metadata.portfolios
|
|
2230
|
-
.map((id) => state.config.portfolios.find((entry) => entry.id === id &&
|
|
2482
|
+
.map((id) => state.config.portfolios.find((entry) => entry.id === id && isManualPortfolio(entry))?.name)
|
|
2231
2483
|
.filter(Boolean);
|
|
2232
2484
|
return names?.length ? `from "${names.join(", ")}"` : command.description;
|
|
2233
2485
|
}
|
|
2486
|
+
case "set-portfolio-position": {
|
|
2487
|
+
const name = state.config.portfolios.find((entry) => entry.id === targetPortfolioId)?.name;
|
|
2488
|
+
return name ? `in "${name}"` : command.description;
|
|
2489
|
+
}
|
|
2234
2490
|
case "check-for-updates":
|
|
2235
2491
|
if (state.updateCheckInProgress) return "Checking GitHub releases now";
|
|
2236
2492
|
if (state.updateAvailable) return `Latest available: v${state.updateAvailable.version}`;
|
|
@@ -2241,6 +2497,15 @@ export function CommandBar({
|
|
|
2241
2497
|
}
|
|
2242
2498
|
}
|
|
2243
2499
|
|
|
2500
|
+
function smartSearchText(command: Command): string {
|
|
2501
|
+
switch (command.id) {
|
|
2502
|
+
case "set-portfolio-position":
|
|
2503
|
+
return "edit position update position modify position manual position portfolio position";
|
|
2504
|
+
default:
|
|
2505
|
+
return "";
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2244
2509
|
function commandToItem(command: Command): ResultItem | null {
|
|
2245
2510
|
if (!shouldShow(command)) return null;
|
|
2246
2511
|
return {
|
|
@@ -2250,6 +2515,7 @@ export function CommandBar({
|
|
|
2250
2515
|
category: command.category,
|
|
2251
2516
|
kind: "command",
|
|
2252
2517
|
right: command.prefix || undefined,
|
|
2518
|
+
searchText: smartSearchText(command),
|
|
2253
2519
|
disabled: command.id === "check-for-updates" && (state.updateCheckInProgress || !!state.updateProgress),
|
|
2254
2520
|
action: () => runDirectCommand(command, ""),
|
|
2255
2521
|
};
|
|
@@ -2291,13 +2557,19 @@ export function CommandBar({
|
|
|
2291
2557
|
const displayTicker = normalizeTickerInput(activeTickerSymbol, rootShortcutIntent.argText);
|
|
2292
2558
|
const displayName = kind === "watchlist" ? "Watchlist" : "Portfolio";
|
|
2293
2559
|
const localTicker = displayTicker ? state.tickers.get(displayTicker) ?? null : null;
|
|
2294
|
-
const preferredTargetId =
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2560
|
+
const preferredTargetId = commandId === "add-portfolio"
|
|
2561
|
+
? (activeCollectionId && manualPortfolios.some((portfolio) => portfolio.id === activeCollectionId)
|
|
2562
|
+
? activeCollectionId
|
|
2563
|
+
: manualPortfolios.length === 1
|
|
2564
|
+
? manualPortfolios[0]!.id
|
|
2565
|
+
: null)
|
|
2566
|
+
: resolvePreferredCollectionTarget(
|
|
2567
|
+
state,
|
|
2568
|
+
kind,
|
|
2569
|
+
activeCollectionId,
|
|
2570
|
+
action,
|
|
2571
|
+
localTicker,
|
|
2572
|
+
);
|
|
2301
2573
|
const preferredTargetName = preferredTargetId
|
|
2302
2574
|
? (kind === "watchlist"
|
|
2303
2575
|
? state.config.watchlists.find((entry) => entry.id === preferredTargetId)?.name
|
|
@@ -2872,22 +3144,22 @@ export function CommandBar({
|
|
|
2872
3144
|
const symbol = normalizeTickerInput(activeTickerSymbol, rootShortcutIntent.argText);
|
|
2873
3145
|
if (symbol) {
|
|
2874
3146
|
return rootShortcutIntent.kind === "inferred-complete"
|
|
2875
|
-
? `Shortcut: ${rootShortcutIntent.
|
|
2876
|
-
: `Shortcut: ${rootShortcutIntent.
|
|
3147
|
+
? `Shortcut: ${rootShortcutIntent.label} for ${symbol} · Tab to accept`
|
|
3148
|
+
: `Shortcut: ${rootShortcutIntent.label} for ${symbol}`;
|
|
2877
3149
|
}
|
|
2878
|
-
return `Shortcut: ${rootShortcutIntent.
|
|
3150
|
+
return `Shortcut: ${rootShortcutIntent.label} · Enter to choose ticker`;
|
|
2879
3151
|
}
|
|
2880
3152
|
if (argKind === "ticker-list") {
|
|
2881
3153
|
if (rootShortcutIntent.argText) {
|
|
2882
|
-
return `Shortcut: ${rootShortcutIntent.
|
|
3154
|
+
return `Shortcut: ${rootShortcutIntent.label} · ${rootShortcutIntent.argText}`;
|
|
2883
3155
|
}
|
|
2884
3156
|
const inferred = normalizeTickerInput(activeTickerSymbol, undefined);
|
|
2885
3157
|
if (inferred) {
|
|
2886
|
-
return `Shortcut: ${rootShortcutIntent.
|
|
3158
|
+
return `Shortcut: ${rootShortcutIntent.label} · inferred ${inferred} · Tab to accept`;
|
|
2887
3159
|
}
|
|
2888
|
-
return `Shortcut: ${rootShortcutIntent.
|
|
3160
|
+
return `Shortcut: ${rootShortcutIntent.label} · Enter tickers to compare`;
|
|
2889
3161
|
}
|
|
2890
|
-
return `Shortcut: ${rootShortcutIntent.
|
|
3162
|
+
return `Shortcut: ${rootShortcutIntent.label}`;
|
|
2891
3163
|
}
|
|
2892
3164
|
|
|
2893
3165
|
if (rootShortcutIntent.command.id === "search-ticker") {
|
|
@@ -3617,6 +3889,7 @@ export function CommandBar({
|
|
|
3617
3889
|
visibleListStateRef.current = routeListState;
|
|
3618
3890
|
|
|
3619
3891
|
useEffect(() => {
|
|
3892
|
+
if (!state.commandBarOpen) return;
|
|
3620
3893
|
if (!routeListState) return;
|
|
3621
3894
|
const selected = currentRoute?.kind === "mode" && currentRoute.screen === "themes"
|
|
3622
3895
|
? routeListState.results[currentRoute.selectedIdx]
|
|
@@ -3628,7 +3901,7 @@ export function CommandBar({
|
|
|
3628
3901
|
applyTheme(selected.themeId);
|
|
3629
3902
|
dispatch({ type: "SET_THEME", theme: selected.themeId });
|
|
3630
3903
|
}
|
|
3631
|
-
}, [currentRoute, dispatch, rootModeInfo.kind, rootSelectedIdx, routeListState, state.config.theme]);
|
|
3904
|
+
}, [currentRoute, dispatch, rootModeInfo.kind, rootSelectedIdx, routeListState, state.commandBarOpen, state.config.theme]);
|
|
3632
3905
|
|
|
3633
3906
|
useEffect(() => {
|
|
3634
3907
|
if (currentRoute?.kind !== "workflow") return;
|
|
@@ -4080,6 +4353,7 @@ export function CommandBar({
|
|
|
4080
4353
|
name: string;
|
|
4081
4354
|
sequence?: string;
|
|
4082
4355
|
ctrl?: boolean;
|
|
4356
|
+
option?: boolean;
|
|
4083
4357
|
meta?: boolean;
|
|
4084
4358
|
shift?: boolean;
|
|
4085
4359
|
stopPropagation: () => void;
|
|
@@ -4089,6 +4363,7 @@ export function CommandBar({
|
|
|
4089
4363
|
name: string;
|
|
4090
4364
|
sequence?: string;
|
|
4091
4365
|
ctrl?: boolean;
|
|
4366
|
+
option?: boolean;
|
|
4092
4367
|
meta?: boolean;
|
|
4093
4368
|
shift?: boolean;
|
|
4094
4369
|
stopPropagation: () => void;
|
|
@@ -4099,6 +4374,7 @@ export function CommandBar({
|
|
|
4099
4374
|
name: string;
|
|
4100
4375
|
sequence?: string;
|
|
4101
4376
|
ctrl?: boolean;
|
|
4377
|
+
option?: boolean;
|
|
4102
4378
|
meta?: boolean;
|
|
4103
4379
|
shift?: boolean;
|
|
4104
4380
|
stopPropagation: () => void;
|
|
@@ -4112,6 +4388,12 @@ export function CommandBar({
|
|
|
4112
4388
|
}
|
|
4113
4389
|
|
|
4114
4390
|
if (currentRoute?.kind === "confirm") {
|
|
4391
|
+
if (isPlainBackspace(event)) {
|
|
4392
|
+
event.stopPropagation();
|
|
4393
|
+
event.preventDefault();
|
|
4394
|
+
popRoute();
|
|
4395
|
+
return;
|
|
4396
|
+
}
|
|
4115
4397
|
if (event.name === "return" || event.name === "enter" || event.name === "y") {
|
|
4116
4398
|
event.stopPropagation();
|
|
4117
4399
|
event.preventDefault();
|
|
@@ -4126,6 +4408,20 @@ export function CommandBar({
|
|
|
4126
4408
|
return;
|
|
4127
4409
|
}
|
|
4128
4410
|
|
|
4411
|
+
if (
|
|
4412
|
+
currentRoute
|
|
4413
|
+
&& (currentRoute.kind === "mode"
|
|
4414
|
+
|| currentRoute.kind === "picker"
|
|
4415
|
+
|| currentRoute.kind === "pane-settings")
|
|
4416
|
+
&& isPlainBackspace(event)
|
|
4417
|
+
&& currentRoute.query.length === 0
|
|
4418
|
+
) {
|
|
4419
|
+
event.stopPropagation();
|
|
4420
|
+
event.preventDefault();
|
|
4421
|
+
popRoute();
|
|
4422
|
+
return;
|
|
4423
|
+
}
|
|
4424
|
+
|
|
4129
4425
|
if (currentRoute?.kind === "workflow") {
|
|
4130
4426
|
const visibleFields = getVisibleWorkflowFields(currentRoute.fields, currentRoute.values);
|
|
4131
4427
|
const activeField = visibleFields.find((field) => field.id === currentRoute.activeFieldId) ?? visibleFields[0];
|