gloomberb 0.4.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/app.tsx +168 -65
- package/src/cli/commands/ticker.ts +45 -15
- package/src/cli/helpers.ts +4 -2
- package/src/cli/registry.test.ts +42 -35
- package/src/cli.test.ts +4 -2
- package/src/components/chart/chart-control-hint.tsx +39 -0
- package/src/components/chart/chart-controller.test.ts +110 -0
- package/src/components/chart/chart-controller.ts +364 -2
- package/src/components/chart/chart-data.ts +177 -29
- package/src/components/chart/chart-indicator-selector.tsx +53 -0
- package/src/components/chart/chart-pane-settings.ts +20 -0
- package/src/components/chart/chart-renderer.ts +220 -30
- package/src/components/chart/chart-resolution.test.ts +67 -0
- package/src/components/chart/chart-resolution.ts +74 -4
- package/src/components/chart/chart-scroll.test.ts +69 -0
- package/src/components/chart/chart-scroll.ts +104 -0
- package/src/components/chart/chart-types.ts +12 -0
- package/src/components/chart/chart-viewport.test.ts +5 -1
- package/src/components/chart/chart-viewport.ts +1 -1
- package/src/components/chart/chart.test.ts +249 -10
- package/src/components/chart/comparison-chart-renderer.test.ts +71 -1
- package/src/components/chart/comparison-chart-renderer.ts +40 -3
- package/src/components/chart/comparison-stock-chart.tsx +218 -123
- package/src/components/chart/indicators/bands.test.ts +50 -0
- package/src/components/chart/indicators/bands.ts +29 -0
- package/src/components/chart/indicators/moving-averages.test.ts +54 -0
- package/src/components/chart/indicators/moving-averages.ts +42 -0
- package/src/components/chart/indicators/options.test.ts +32 -0
- package/src/components/chart/indicators/options.ts +107 -0
- package/src/components/chart/indicators/oscillators.test.ts +79 -0
- package/src/components/chart/indicators/oscillators.ts +92 -0
- package/src/components/chart/indicators/types.ts +36 -0
- package/src/components/chart/native/chart-rasterizer.ts +51 -0
- package/src/components/chart/native/kitty.test.ts +68 -0
- package/src/components/chart/stock-chart-renderer-switch.test.tsx +740 -9
- package/src/components/chart/stock-chart.test.ts +239 -0
- package/src/components/chart/stock-chart.tsx +2257 -372
- package/src/components/command-bar/command-bar.test.tsx +337 -5
- package/src/components/command-bar/command-bar.tsx +292 -120
- package/src/components/command-bar/helpers.ts +1 -1
- package/src/components/command-bar/root-shortcuts.ts +33 -4
- package/src/components/command-bar/view-model.test.ts +3 -3
- package/src/components/command-bar/view-model.ts +4 -4
- package/src/components/command-bar/workflow-ops.ts +10 -0
- package/src/components/layout/floating-pane.tsx +3 -2
- package/src/components/layout/header.tsx +2 -1
- package/src/components/layout/pane-sizing.ts +10 -0
- package/src/components/layout/pane.tsx +9 -2
- package/src/components/layout/shell.test.tsx +282 -28
- package/src/components/layout/shell.tsx +10 -7
- package/src/components/layout/status-bar.test.tsx +16 -7
- package/src/components/layout/status-bar.tsx +4 -1
- package/src/components/markdown-editor.tsx +1 -0
- package/src/components/pane-settings-dialog.tsx +13 -152
- package/src/components/pane-template-wizard.tsx +1 -0
- package/src/components/price-selector-dialog.tsx +12 -7
- package/src/components/ticker-badge-text.test.tsx +50 -0
- package/src/components/ticker-badge-text.tsx +17 -2
- package/src/components/ticker-badge.tsx +2 -2
- package/src/components/ticker-list-table.tsx +54 -1
- package/src/components/toggle-list.tsx +15 -2
- package/src/components/ui/data-table.tsx +60 -3
- package/src/components/ui/external-link.tsx +48 -11
- package/src/components/ui/fields.tsx +2 -1
- package/src/components/ui/index.ts +8 -1
- package/src/components/ui/multi-select-chips.tsx +69 -0
- package/src/components/ui/multi-select-dialog.tsx +267 -0
- package/src/components/ui/multi-select.ts +78 -0
- package/src/components/ui/ui.test.tsx +343 -1
- package/src/data/plugin-state-store.ts +30 -19
- package/src/data/resource-store.ts +133 -103
- package/src/data/session-store.ts +19 -12
- package/src/data/sqlite-cache.test.ts +37 -2
- package/src/data/sqlite-database.ts +8 -2
- package/src/data/sqlite-retry.test.ts +63 -0
- package/src/data/sqlite-retry.ts +66 -0
- package/src/data/ticker-store.ts +29 -18
- package/src/index.tsx +6 -1
- package/src/market-data/coordinator.test.ts +114 -5
- package/src/market-data/coordinator.ts +70 -1
- package/src/market-data/hooks.test.tsx +73 -2
- package/src/market-data/hooks.tsx +16 -4
- package/src/notifications/app-notifier.test.ts +177 -0
- package/src/notifications/app-notifier.ts +241 -0
- package/src/pane-settings.test.ts +2 -0
- package/src/plugins/builtin/ai/index.test.tsx +1 -1
- package/src/plugins/builtin/ai/screener-pane.test.tsx +2 -0
- package/src/plugins/builtin/ai/screener-pane.tsx +1 -0
- package/src/plugins/builtin/ai/ticker-context.ts +3 -2
- package/src/plugins/builtin/alerts/alert-engine.test.ts +90 -0
- package/src/plugins/builtin/alerts/alert-engine.ts +55 -0
- package/src/plugins/builtin/alerts/index.test.tsx +357 -0
- package/src/plugins/builtin/alerts/index.tsx +519 -0
- package/src/plugins/builtin/alerts/types.ts +14 -0
- package/src/plugins/builtin/analytics/index.test.tsx +300 -0
- package/src/plugins/builtin/analytics/index.tsx +775 -0
- package/src/plugins/builtin/analytics/metrics.test.ts +125 -0
- package/src/plugins/builtin/analytics/metrics.ts +155 -0
- package/src/plugins/builtin/ask-ai.test.tsx +2 -0
- package/src/plugins/builtin/chat-controller.test.ts +83 -18
- package/src/plugins/builtin/chat-controller.ts +26 -14
- package/src/plugins/builtin/chat.test.tsx +715 -4
- package/src/plugins/builtin/chat.tsx +424 -120
- package/src/plugins/builtin/comparison-chart.test.tsx +97 -57
- package/src/plugins/builtin/comparison-chart.tsx +1 -2
- package/src/plugins/builtin/correlation/compute.test.ts +147 -0
- package/src/plugins/builtin/correlation/compute.ts +115 -0
- package/src/plugins/builtin/correlation/index.tsx +385 -0
- package/src/plugins/builtin/correlation/plugin.test.ts +44 -0
- package/src/plugins/builtin/correlation/settings.test.ts +34 -0
- package/src/plugins/builtin/correlation/settings.ts +95 -0
- package/src/plugins/builtin/debug.tsx +36 -50
- package/src/plugins/builtin/earnings/earnings-cache.test.ts +181 -0
- package/src/plugins/builtin/earnings/earnings-cache.ts +136 -0
- package/src/plugins/builtin/earnings/earnings-data.test.ts +59 -0
- package/src/plugins/builtin/earnings/earnings-data.ts +50 -0
- package/src/plugins/builtin/earnings/index.tsx +337 -0
- package/src/plugins/builtin/econ/calendar-source.test.ts +219 -0
- package/src/plugins/builtin/econ/calendar-source.ts +121 -0
- package/src/plugins/builtin/econ/fred-client.test.ts +63 -0
- package/src/plugins/builtin/econ/fred-client.ts +79 -0
- package/src/plugins/builtin/econ/fred-series-map.ts +88 -0
- package/src/plugins/builtin/econ/index.tsx +999 -0
- package/src/plugins/builtin/econ/types.ts +13 -0
- package/src/plugins/builtin/fred-settings.ts +10 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +178 -0
- package/src/plugins/builtin/fx-matrix/pairs.ts +30 -0
- package/src/plugins/builtin/help.test.tsx +1 -5
- package/src/plugins/builtin/help.tsx +1 -16
- package/src/plugins/builtin/insider/index.tsx +417 -0
- package/src/plugins/builtin/insider/insider-data.test.ts +55 -0
- package/src/plugins/builtin/insider/insider-data.ts +49 -0
- package/src/plugins/builtin/layout-manager.tsx +24 -16
- package/src/plugins/builtin/market-movers/index.tsx +573 -0
- package/src/plugins/builtin/market-movers/screener.test.ts +143 -0
- package/src/plugins/builtin/market-movers/screener.ts +105 -0
- package/src/plugins/builtin/options.tsx +4 -3
- package/src/plugins/builtin/portfolio-list/cli/commands.ts +11 -6
- package/src/plugins/builtin/portfolio-list/command-bar.ts +6 -3
- package/src/plugins/builtin/portfolio-list/header.tsx +3 -2
- package/src/plugins/builtin/portfolio-list/metrics.ts +22 -11
- package/src/plugins/builtin/portfolio-list/pane.tsx +3 -9
- package/src/plugins/builtin/portfolio-list/settings.ts +9 -0
- package/src/plugins/builtin/portfolio-list/table.tsx +3 -0
- package/src/plugins/builtin/portfolio-list.test.tsx +3 -1
- package/src/plugins/builtin/portfolio-metrics.test.ts +13 -1
- package/src/plugins/builtin/sec.tsx +114 -7
- package/src/plugins/builtin/sectors/index.tsx +359 -0
- package/src/plugins/builtin/sectors/sector-data.ts +18 -0
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +75 -13
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +316 -80
- package/src/plugins/builtin/ticker-detail/quote-monitor.tsx +4 -6
- package/src/plugins/builtin/ticker-detail-chart-tab-switch.test.tsx +17 -12
- package/src/plugins/builtin/ticker-detail.test.tsx +3 -3
- package/src/plugins/builtin/world-indices/index.tsx +410 -0
- package/src/plugins/builtin/world-indices/indices.ts +52 -0
- package/src/plugins/builtin/yield-curve/index.tsx +242 -0
- package/src/plugins/builtin/yield-curve/treasury-data.test.ts +212 -0
- package/src/plugins/builtin/yield-curve/treasury-data.ts +107 -0
- package/src/plugins/catalog.ts +22 -0
- package/src/plugins/gridlock-notification.ts +15 -0
- package/src/plugins/ibkr/__snapshots__/trade-tab.test.tsx.snap +5 -5
- package/src/plugins/ibkr/index.test.tsx +1 -1
- package/src/plugins/ibkr/index.tsx +1 -1
- package/src/plugins/ibkr/trade-tab.tsx +23 -5
- package/src/plugins/ibkr/trade-utils.test.ts +1 -1
- package/src/plugins/ibkr/trade-utils.ts +7 -6
- package/src/plugins/ibkr/trading-pane.tsx +8 -7
- package/src/plugins/plugin-runtime.test.tsx +2 -0
- package/src/plugins/plugin-runtime.tsx +10 -0
- package/src/plugins/prediction-markets/controller-data.ts +8 -0
- package/src/plugins/prediction-markets/detail/shared.tsx +7 -6
- package/src/plugins/prediction-markets/plugin.test.tsx +48 -1
- package/src/plugins/prediction-markets/services/fetch.ts +13 -7
- package/src/plugins/prediction-markets/services/polymarket-adapter.ts +29 -11
- package/src/plugins/prediction-markets/services/polymarket-ws.ts +20 -0
- package/src/plugins/prediction-markets/test-helpers.tsx +2 -0
- package/src/plugins/registry.ts +22 -13
- package/src/sources/provider-router.test.ts +3 -2
- package/src/sources/sec-edgar.ts +13 -1
- package/src/sources/yahoo-finance.ts +75 -1
- package/src/state/app-context-selector.test.tsx +17 -1
- package/src/state/app-context.test.tsx +24 -0
- package/src/state/app-context.tsx +29 -6
- package/src/theme/colors.ts +5 -0
- package/src/theme/themes.ts +25 -0
- package/src/types/config.test.ts +107 -0
- package/src/types/config.ts +30 -1
- package/src/types/data-provider.ts +13 -0
- package/src/types/plugin.ts +35 -1
- package/src/utils/api-client.test.ts +90 -0
- package/src/utils/api-client.ts +56 -16
- package/src/utils/format.test.ts +11 -1
- package/src/utils/format.ts +86 -8
- package/src/utils/inline-content-tokenizer.test.ts +22 -0
- package/src/utils/inline-content-tokenizer.ts +76 -0
- package/src/utils/link-tokenizer.test.ts +27 -0
- package/src/utils/link-tokenizer.ts +93 -0
- package/src/utils/market-format.test.ts +109 -0
- package/src/utils/market-format.ts +299 -0
- package/src/utils/market-status.ts +3 -2
- package/src/utils/selection-clipboard.test.ts +43 -0
- package/src/utils/selection-clipboard.ts +88 -0
- package/src/utils/terminal-input-reset.test.ts +50 -0
- package/src/utils/terminal-input-reset.ts +24 -0
- package/src/utils/throttled-fetch.test.ts +145 -0
- package/src/utils/throttled-fetch.ts +212 -0
- package/src/utils/ticker-tokenizer.test.ts +31 -0
- package/src/utils/ticker-tokenizer.ts +7 -42
- package/src/version.ts +1 -1
|
@@ -35,6 +35,8 @@ import {
|
|
|
35
35
|
cloneLayout,
|
|
36
36
|
createPaneInstance,
|
|
37
37
|
findPaneInstance,
|
|
38
|
+
findPrimaryPaneInstance,
|
|
39
|
+
resolveFollowBindingInstance,
|
|
38
40
|
type LayoutConfig,
|
|
39
41
|
} from "../../types/config";
|
|
40
42
|
import { resolveBrokerConfigFields, type BrokerConfigField } from "../../types/broker";
|
|
@@ -52,6 +54,7 @@ import {
|
|
|
52
54
|
removePane,
|
|
53
55
|
swapPanes,
|
|
54
56
|
} from "../../plugins/pane-manager";
|
|
57
|
+
import { notifyGridlockComplete } from "../../plugins/gridlock-notification";
|
|
55
58
|
import { CHART_RENDERER_PREFERENCES } from "../chart/chart-types";
|
|
56
59
|
import {
|
|
57
60
|
buildSections,
|
|
@@ -85,6 +88,7 @@ import {
|
|
|
85
88
|
applyCollectionMembershipChange,
|
|
86
89
|
getCollectionTargetOptions,
|
|
87
90
|
resolvePreferredCollectionTarget,
|
|
91
|
+
resolveSoleCollectionTarget,
|
|
88
92
|
resolveTickerInput,
|
|
89
93
|
resolveTickerInputOrThrow,
|
|
90
94
|
resolveTickerListInput,
|
|
@@ -260,6 +264,7 @@ export function CommandBar({
|
|
|
260
264
|
const rootThemeBaseIdRef = useRef<string | null>(null);
|
|
261
265
|
const workflowInputRefs = useRef<Record<string, RefObject<InputRenderable | TextareaRenderable | null>>>({});
|
|
262
266
|
const visibleListStateRef = useRef<ListScreenState | null>(null);
|
|
267
|
+
const processedLaunchSequenceRef = useRef<number | null>(null);
|
|
263
268
|
const currentRoute = routeStack[routeStack.length - 1] ?? null;
|
|
264
269
|
const currentRouteRef = useRef<CommandBarRoute | null>(currentRoute);
|
|
265
270
|
currentRouteRef.current = currentRoute;
|
|
@@ -348,16 +353,12 @@ export function CommandBar({
|
|
|
348
353
|
|
|
349
354
|
const setActiveCollection = useCallback((collectionId: string) => {
|
|
350
355
|
const currentState = stateRef.current;
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
return null;
|
|
358
|
-
};
|
|
359
|
-
const targetPaneId = resolvePortfolioPane(currentState.focusedPaneId)
|
|
360
|
-
?? currentState.config.layout.instances.find((instance) => instance.paneId === "portfolio-list")?.instanceId
|
|
356
|
+
const targetPaneId = resolveFollowBindingInstance(
|
|
357
|
+
currentState.config.layout,
|
|
358
|
+
currentState.focusedPaneId,
|
|
359
|
+
(instance) => instance.paneId === "portfolio-list",
|
|
360
|
+
)?.instanceId
|
|
361
|
+
?? findPrimaryPaneInstance(currentState.config.layout, "portfolio-list")?.instanceId
|
|
361
362
|
?? null;
|
|
362
363
|
if (!targetPaneId) return;
|
|
363
364
|
dispatch({ type: "UPDATE_PANE_STATE", paneId: targetPaneId, patch: { collectionId } });
|
|
@@ -414,6 +415,12 @@ export function CommandBar({
|
|
|
414
415
|
pluginRegistry.updateLayoutFn(nextLayout);
|
|
415
416
|
}, [pluginRegistry]);
|
|
416
417
|
|
|
418
|
+
const notifyGridlockRevert = useCallback(() => {
|
|
419
|
+
notifyGridlockComplete(pluginRegistry.notify.bind(pluginRegistry), () => {
|
|
420
|
+
dispatch({ type: "UNDO_LAYOUT" });
|
|
421
|
+
});
|
|
422
|
+
}, [dispatch, pluginRegistry]);
|
|
423
|
+
|
|
417
424
|
const duplicatePane = useCallback((paneId: string) => {
|
|
418
425
|
const currentState = stateRef.current;
|
|
419
426
|
const pane = findPaneInstance(currentState.config.layout, paneId);
|
|
@@ -606,6 +613,9 @@ export function CommandBar({
|
|
|
606
613
|
next[selectorKey] = brokerId;
|
|
607
614
|
return next;
|
|
608
615
|
}, []);
|
|
616
|
+
const notify = useCallback((body: string, options?: { type?: "info" | "success" | "error" }) => {
|
|
617
|
+
pluginRegistry.notify({ body, ...options });
|
|
618
|
+
}, [pluginRegistry]);
|
|
609
619
|
|
|
610
620
|
const openAddToPortfolioWorkflow = useCallback((
|
|
611
621
|
ticker: TickerRecord,
|
|
@@ -618,7 +628,7 @@ export function CommandBar({
|
|
|
618
628
|
defaultAvgCost,
|
|
619
629
|
});
|
|
620
630
|
if (!workflow) {
|
|
621
|
-
|
|
631
|
+
notify("Create a manual portfolio first.", { type: "info" });
|
|
622
632
|
return;
|
|
623
633
|
}
|
|
624
634
|
|
|
@@ -638,7 +648,7 @@ export function CommandBar({
|
|
|
638
648
|
successBehavior: "close",
|
|
639
649
|
payload: { kind: "builtin", actionId: "add-portfolio" },
|
|
640
650
|
});
|
|
641
|
-
}, [
|
|
651
|
+
}, [notify, openWorkflowRoute]);
|
|
642
652
|
|
|
643
653
|
const connectBrokerProfile = useCallback(async (
|
|
644
654
|
brokerId: string,
|
|
@@ -670,8 +680,8 @@ export function CommandBar({
|
|
|
670
680
|
dispatch({ type: "SET_CONFIG", config: freshConfig });
|
|
671
681
|
const brokerTab = freshConfig.portfolios.find((portfolio) => portfolio.brokerInstanceId === instance.id);
|
|
672
682
|
if (brokerTab) setActiveCollection(brokerTab.id);
|
|
673
|
-
|
|
674
|
-
}, [dispatch, pluginRegistry, setActiveCollection]);
|
|
683
|
+
notify("Connected! Positions will sync automatically.", { type: "success" });
|
|
684
|
+
}, [dispatch, notify, pluginRegistry, setActiveCollection]);
|
|
675
685
|
|
|
676
686
|
const createManualPortfolio = useCallback(async (name: string) => {
|
|
677
687
|
const currentState = stateRef.current;
|
|
@@ -683,8 +693,8 @@ export function CommandBar({
|
|
|
683
693
|
dispatch({ type: "SET_CONFIG", config: nextConfig });
|
|
684
694
|
setActiveCollection(portfolio.id);
|
|
685
695
|
await saveConfig(nextConfig);
|
|
686
|
-
|
|
687
|
-
}, [dispatch,
|
|
696
|
+
notify(`Created portfolio "${portfolio.name}".`, { type: "success" });
|
|
697
|
+
}, [dispatch, notify, setActiveCollection]);
|
|
688
698
|
|
|
689
699
|
const createWatchlist = useCallback(async (name: string) => {
|
|
690
700
|
const currentState = stateRef.current;
|
|
@@ -702,8 +712,8 @@ export function CommandBar({
|
|
|
702
712
|
dispatch({ type: "SET_CONFIG", config: nextConfig });
|
|
703
713
|
setActiveCollection(id);
|
|
704
714
|
await saveConfig(nextConfig);
|
|
705
|
-
|
|
706
|
-
}, [dispatch,
|
|
715
|
+
notify(`Created watchlist "${trimmedName}".`, { type: "success" });
|
|
716
|
+
}, [dispatch, notify, setActiveCollection]);
|
|
707
717
|
|
|
708
718
|
const deleteWatchlist = useCallback(async (watchlistId: string) => {
|
|
709
719
|
const currentState = stateRef.current;
|
|
@@ -722,8 +732,8 @@ export function CommandBar({
|
|
|
722
732
|
if (fallback) setActiveCollection(fallback);
|
|
723
733
|
}
|
|
724
734
|
await saveConfig(nextConfig);
|
|
725
|
-
|
|
726
|
-
}, [activeCollectionId, dispatch,
|
|
735
|
+
notify(`Deleted "${watchlist.name}".`, { type: "success" });
|
|
736
|
+
}, [activeCollectionId, dispatch, notify, setActiveCollection]);
|
|
727
737
|
|
|
728
738
|
const deletePortfolio = useCallback(async (portfolioId: string) => {
|
|
729
739
|
const currentState = stateRef.current;
|
|
@@ -752,8 +762,8 @@ export function CommandBar({
|
|
|
752
762
|
if (fallback) setActiveCollection(fallback);
|
|
753
763
|
}
|
|
754
764
|
await saveConfig(nextConfig);
|
|
755
|
-
|
|
756
|
-
}, [activeCollectionId, dispatch,
|
|
765
|
+
notify(`Deleted "${portfolio.name}".`, { type: "success" });
|
|
766
|
+
}, [activeCollectionId, dispatch, notify, setActiveCollection, tickerRepository]);
|
|
757
767
|
|
|
758
768
|
const setPortfolioPositionFromWorkflow = useCallback(async (values: Record<string, CommandBarFieldValue>) => {
|
|
759
769
|
const currentState = stateRef.current;
|
|
@@ -800,8 +810,8 @@ export function CommandBar({
|
|
|
800
810
|
});
|
|
801
811
|
await tickerRepository.saveTicker(result.ticker);
|
|
802
812
|
dispatch({ type: "UPDATE_TICKER", ticker: result.ticker });
|
|
803
|
-
|
|
804
|
-
}, [activeCollectionId, activeTickerSymbol, dataProvider, dispatch, pluginRegistry, tickerRepository]);
|
|
813
|
+
notify(`Set position for ${result.ticker.metadata.ticker} in "${portfolio.name}".`, { type: "success" });
|
|
814
|
+
}, [activeCollectionId, activeTickerSymbol, dataProvider, dispatch, notify, pluginRegistry, tickerRepository]);
|
|
805
815
|
|
|
806
816
|
const addTickerMembershipFromWorkflow = useCallback(async (values: Record<string, CommandBarFieldValue>) => {
|
|
807
817
|
const currentState = stateRef.current;
|
|
@@ -828,12 +838,12 @@ export function CommandBar({
|
|
|
828
838
|
if (result.changed) {
|
|
829
839
|
await tickerRepository.saveTicker(result.ticker);
|
|
830
840
|
dispatch({ type: "UPDATE_TICKER", ticker: result.ticker });
|
|
831
|
-
|
|
841
|
+
notify(`Added ${result.ticker.metadata.ticker} to "${portfolio.name}".`, { type: "success" });
|
|
832
842
|
return;
|
|
833
843
|
}
|
|
834
844
|
|
|
835
|
-
|
|
836
|
-
}, [activeCollectionId, activeTickerSymbol, dataProvider, dispatch, pluginRegistry, tickerRepository]);
|
|
845
|
+
notify(`${result.ticker.metadata.ticker} is already in "${portfolio.name}".`, { type: "info" });
|
|
846
|
+
}, [activeCollectionId, activeTickerSymbol, dataProvider, dispatch, notify, pluginRegistry, tickerRepository]);
|
|
837
847
|
|
|
838
848
|
const disconnectBrokerInstance = useCallback(async (instanceId: string) => {
|
|
839
849
|
const instance = stateRef.current.config.brokerInstances.find((entry) => entry.id === instanceId);
|
|
@@ -843,8 +853,8 @@ export function CommandBar({
|
|
|
843
853
|
await pluginRegistry.removeBrokerInstanceFn(instanceId);
|
|
844
854
|
const freshConfig = pluginRegistry.getConfigFn();
|
|
845
855
|
dispatch({ type: "SET_CONFIG", config: freshConfig });
|
|
846
|
-
|
|
847
|
-
}, [dispatch, pluginRegistry]);
|
|
856
|
+
notify(`Removed ${instance.label}.`, { type: "success" });
|
|
857
|
+
}, [dispatch, notify, pluginRegistry]);
|
|
848
858
|
|
|
849
859
|
const openBuiltInWorkflow = useCallback((actionId: string) => {
|
|
850
860
|
switch (actionId) {
|
|
@@ -907,7 +917,7 @@ export function CommandBar({
|
|
|
907
917
|
true,
|
|
908
918
|
);
|
|
909
919
|
if (!workflow) {
|
|
910
|
-
|
|
920
|
+
notify("No connectable brokers are installed.", { type: "info" });
|
|
911
921
|
return;
|
|
912
922
|
}
|
|
913
923
|
openWorkflowRoute(workflow);
|
|
@@ -919,7 +929,7 @@ export function CommandBar({
|
|
|
919
929
|
activeTicker: activeTickerData,
|
|
920
930
|
});
|
|
921
931
|
if (!workflow) {
|
|
922
|
-
|
|
932
|
+
notify("Create a manual portfolio first.", { type: "info" });
|
|
923
933
|
return;
|
|
924
934
|
}
|
|
925
935
|
openWorkflowRoute({
|
|
@@ -949,7 +959,7 @@ export function CommandBar({
|
|
|
949
959
|
false,
|
|
950
960
|
);
|
|
951
961
|
if (!workflow) {
|
|
952
|
-
|
|
962
|
+
notify("No connectable brokers are installed.", { type: "info" });
|
|
953
963
|
return;
|
|
954
964
|
}
|
|
955
965
|
openWorkflowRoute(workflow);
|
|
@@ -957,8 +967,8 @@ export function CommandBar({
|
|
|
957
967
|
}
|
|
958
968
|
default:
|
|
959
969
|
return;
|
|
960
|
-
|
|
961
|
-
}, [activeCollectionId, activeTickerData, buildBrokerWorkflow, openWorkflowRoute, pluginRegistry, state.config]);
|
|
970
|
+
}
|
|
971
|
+
}, [activeCollectionId, activeTickerData, buildBrokerWorkflow, notify, openWorkflowRoute, pluginRegistry, state.config]);
|
|
962
972
|
|
|
963
973
|
const openPickerRoute = useCallback((
|
|
964
974
|
route: CommandBarRoute,
|
|
@@ -1031,7 +1041,7 @@ export function CommandBar({
|
|
|
1031
1041
|
const openPaneSettingsRoute = useCallback((paneId: string) => {
|
|
1032
1042
|
const descriptor = pluginRegistry.resolvePaneSettings(paneId);
|
|
1033
1043
|
if (!descriptor) {
|
|
1034
|
-
|
|
1044
|
+
notify("The focused pane has no settings.", { type: "info" });
|
|
1035
1045
|
return;
|
|
1036
1046
|
}
|
|
1037
1047
|
pushRoute({
|
|
@@ -1043,7 +1053,7 @@ export function CommandBar({
|
|
|
1043
1053
|
error: null,
|
|
1044
1054
|
pendingFieldKey: null,
|
|
1045
1055
|
});
|
|
1046
|
-
}, [pluginRegistry, pushRoute]);
|
|
1056
|
+
}, [notify, pluginRegistry, pushRoute]);
|
|
1047
1057
|
|
|
1048
1058
|
const executeCollectionCommand = useCallback(async (
|
|
1049
1059
|
commandId: "add-watchlist" | "add-portfolio" | "remove-watchlist" | "remove-portfolio",
|
|
@@ -1072,7 +1082,7 @@ export function CommandBar({
|
|
|
1072
1082
|
if (kind === "portfolio" && action === "add") {
|
|
1073
1083
|
const manualPortfolios = stateForCommand.config.portfolios.filter(isManualPortfolio);
|
|
1074
1084
|
if (manualPortfolios.length === 0) {
|
|
1075
|
-
|
|
1085
|
+
notify("Create a manual portfolio first.", { type: "info" });
|
|
1076
1086
|
return;
|
|
1077
1087
|
}
|
|
1078
1088
|
|
|
@@ -1119,12 +1129,27 @@ export function CommandBar({
|
|
|
1119
1129
|
}
|
|
1120
1130
|
|
|
1121
1131
|
const targetId = explicitTargetId
|
|
1122
|
-
??
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1132
|
+
?? (
|
|
1133
|
+
kind === "watchlist" && action === "add"
|
|
1134
|
+
? resolvePreferredCollectionTarget(
|
|
1135
|
+
stateForCommand,
|
|
1136
|
+
kind,
|
|
1137
|
+
activeCollectionId,
|
|
1138
|
+
action,
|
|
1139
|
+
resolvedTicker.ticker,
|
|
1140
|
+
) ?? resolveSoleCollectionTarget(
|
|
1141
|
+
stateForCommand,
|
|
1142
|
+
kind,
|
|
1143
|
+
action,
|
|
1144
|
+
resolvedTicker.ticker,
|
|
1145
|
+
)
|
|
1146
|
+
: resolvePreferredCollectionTarget(
|
|
1147
|
+
stateForCommand,
|
|
1148
|
+
kind,
|
|
1149
|
+
activeCollectionId,
|
|
1150
|
+
action,
|
|
1151
|
+
resolvedTicker.ticker,
|
|
1152
|
+
)
|
|
1128
1153
|
);
|
|
1129
1154
|
|
|
1130
1155
|
if (!targetId) {
|
|
@@ -1136,7 +1161,7 @@ export function CommandBar({
|
|
|
1136
1161
|
description: option.description,
|
|
1137
1162
|
}));
|
|
1138
1163
|
if (options.length === 0) {
|
|
1139
|
-
|
|
1164
|
+
notify(
|
|
1140
1165
|
action === "add"
|
|
1141
1166
|
? `No ${kind}s are available for ${resolvedTicker.symbol}.`
|
|
1142
1167
|
: `${resolvedTicker.symbol} is not in any ${kind}.`,
|
|
@@ -1174,7 +1199,7 @@ export function CommandBar({
|
|
|
1174
1199
|
} else {
|
|
1175
1200
|
const portfolio = stateForCommand.config.portfolios.find((entry) => entry.id === targetId);
|
|
1176
1201
|
if (!portfolio || !isManualPortfolio(portfolio)) {
|
|
1177
|
-
|
|
1202
|
+
notify("Choose a manual portfolio.", { type: "error" });
|
|
1178
1203
|
return;
|
|
1179
1204
|
}
|
|
1180
1205
|
|
|
@@ -1192,12 +1217,12 @@ export function CommandBar({
|
|
|
1192
1217
|
? stateForCommand.config.watchlists.find((entry) => entry.id === targetId)?.name
|
|
1193
1218
|
: stateForCommand.config.portfolios.find((entry) => entry.id === targetId)?.name) || targetId;
|
|
1194
1219
|
if (changed) {
|
|
1195
|
-
|
|
1220
|
+
notify(
|
|
1196
1221
|
`${action === "add" ? "Added" : "Removed"} ${resolvedTicker.symbol} ${action === "add" ? "to" : "from"} "${targetName}".`,
|
|
1197
1222
|
{ type: "success" },
|
|
1198
1223
|
);
|
|
1199
1224
|
} else {
|
|
1200
|
-
|
|
1225
|
+
notify(
|
|
1201
1226
|
action === "add"
|
|
1202
1227
|
? `${resolvedTicker.symbol} is already in "${targetName}".`
|
|
1203
1228
|
: `${resolvedTicker.symbol} is not in "${targetName}".`,
|
|
@@ -1210,6 +1235,7 @@ export function CommandBar({
|
|
|
1210
1235
|
activeTickerSymbol,
|
|
1211
1236
|
buildSharedWorkflowDeps,
|
|
1212
1237
|
closeAll,
|
|
1238
|
+
notify,
|
|
1213
1239
|
openAddToPortfolioWorkflow,
|
|
1214
1240
|
openModeRoute,
|
|
1215
1241
|
openPickerRoute,
|
|
@@ -1433,14 +1459,14 @@ export function CommandBar({
|
|
|
1433
1459
|
const name = coerceFieldString(route.values.name).trim();
|
|
1434
1460
|
if (!name) throw new Error("Layout name is required.");
|
|
1435
1461
|
dispatch({ type: "NEW_LAYOUT", name });
|
|
1436
|
-
|
|
1462
|
+
notify(`Created layout "${name}".`, { type: "success" });
|
|
1437
1463
|
break;
|
|
1438
1464
|
}
|
|
1439
1465
|
case "rename-layout": {
|
|
1440
1466
|
const name = coerceFieldString(route.values.name).trim();
|
|
1441
1467
|
if (!name) throw new Error("Layout name is required.");
|
|
1442
1468
|
dispatch({ type: "RENAME_LAYOUT", index: state.config.activeLayoutIndex, name });
|
|
1443
|
-
|
|
1469
|
+
notify(`Renamed layout to "${name}".`, { type: "success" });
|
|
1444
1470
|
break;
|
|
1445
1471
|
}
|
|
1446
1472
|
case "new-portfolio": {
|
|
@@ -1492,7 +1518,7 @@ export function CommandBar({
|
|
|
1492
1518
|
}
|
|
1493
1519
|
await command.execute(values);
|
|
1494
1520
|
if (route.successLabel) {
|
|
1495
|
-
|
|
1521
|
+
notify(route.successLabel, { type: "success" });
|
|
1496
1522
|
}
|
|
1497
1523
|
break;
|
|
1498
1524
|
}
|
|
@@ -1516,7 +1542,7 @@ export function CommandBar({
|
|
|
1516
1542
|
};
|
|
1517
1543
|
await pluginRegistry.createPaneFromTemplateAsyncFn(template.id, createOptions);
|
|
1518
1544
|
if (route.successLabel) {
|
|
1519
|
-
|
|
1545
|
+
notify(route.successLabel, { type: "success" });
|
|
1520
1546
|
}
|
|
1521
1547
|
break;
|
|
1522
1548
|
}
|
|
@@ -1566,6 +1592,7 @@ export function CommandBar({
|
|
|
1566
1592
|
dispatch,
|
|
1567
1593
|
extractBrokerWorkflowValues,
|
|
1568
1594
|
getWorkflowFieldStringValue,
|
|
1595
|
+
notify,
|
|
1569
1596
|
pluginRegistry,
|
|
1570
1597
|
addTickerMembershipFromWorkflow,
|
|
1571
1598
|
setPortfolioPositionFromWorkflow,
|
|
@@ -1574,9 +1601,16 @@ export function CommandBar({
|
|
|
1574
1601
|
updateTopRoute,
|
|
1575
1602
|
]);
|
|
1576
1603
|
|
|
1577
|
-
const openPluginCommandWorkflow = useCallback((
|
|
1604
|
+
const openPluginCommandWorkflow = useCallback((
|
|
1605
|
+
command: CommandDef,
|
|
1606
|
+
options?: { values?: Record<string, string> },
|
|
1607
|
+
) => {
|
|
1578
1608
|
if (!command.wizard || command.wizard.length === 0) return;
|
|
1579
1609
|
const normalized = normalizeWizardFields(command.wizard);
|
|
1610
|
+
const values = {
|
|
1611
|
+
...normalized.initialValues,
|
|
1612
|
+
...(options?.values ?? {}),
|
|
1613
|
+
};
|
|
1580
1614
|
openWorkflowRoute({
|
|
1581
1615
|
kind: "workflow",
|
|
1582
1616
|
workflowId: `plugin-command:${command.id}`,
|
|
@@ -1584,8 +1618,8 @@ export function CommandBar({
|
|
|
1584
1618
|
subtitle: command.description,
|
|
1585
1619
|
description: normalized.description,
|
|
1586
1620
|
fields: normalized.fields,
|
|
1587
|
-
values
|
|
1588
|
-
activeFieldId: getFirstVisibleFieldId(normalized.fields,
|
|
1621
|
+
values,
|
|
1622
|
+
activeFieldId: getFirstVisibleFieldId(normalized.fields, values),
|
|
1589
1623
|
submitLabel: command.label,
|
|
1590
1624
|
cancelLabel: "Back",
|
|
1591
1625
|
pendingLabel: normalized.pendingLabel,
|
|
@@ -1600,6 +1634,28 @@ export function CommandBar({
|
|
|
1600
1634
|
});
|
|
1601
1635
|
}, [openWorkflowRoute]);
|
|
1602
1636
|
|
|
1637
|
+
useEffect(() => {
|
|
1638
|
+
const launch = state.commandBarLaunchRequest;
|
|
1639
|
+
if (!launch) {
|
|
1640
|
+
processedLaunchSequenceRef.current = null;
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
if (!state.commandBarOpen) return;
|
|
1644
|
+
if (processedLaunchSequenceRef.current === launch.sequence) return;
|
|
1645
|
+
processedLaunchSequenceRef.current = launch.sequence;
|
|
1646
|
+
|
|
1647
|
+
if (launch.kind === "plugin-command") {
|
|
1648
|
+
const command = pluginRegistry.commands.get(launch.commandId);
|
|
1649
|
+
if (!command?.wizard || command.wizard.length === 0) return;
|
|
1650
|
+
openPluginCommandWorkflow(command);
|
|
1651
|
+
}
|
|
1652
|
+
}, [
|
|
1653
|
+
openPluginCommandWorkflow,
|
|
1654
|
+
pluginRegistry,
|
|
1655
|
+
state.commandBarLaunchRequest,
|
|
1656
|
+
state.commandBarOpen,
|
|
1657
|
+
]);
|
|
1658
|
+
|
|
1603
1659
|
const shouldOpenTemplateConfig = useCallback((template: PaneTemplateDef, arg?: string): boolean => {
|
|
1604
1660
|
if (template.wizard && template.wizard.length > 0) {
|
|
1605
1661
|
if (!arg?.trim()) {
|
|
@@ -1668,12 +1724,12 @@ export function CommandBar({
|
|
|
1668
1724
|
closeAll({ revertThemePreview: false });
|
|
1669
1725
|
} catch (error) {
|
|
1670
1726
|
const displayLabel = getPaneTemplateDisplayLabel(template);
|
|
1671
|
-
|
|
1727
|
+
notify(
|
|
1672
1728
|
error instanceof Error ? error.message : `Could not create ${displayLabel.toLowerCase()}.`,
|
|
1673
1729
|
{ type: "error" },
|
|
1674
1730
|
);
|
|
1675
1731
|
}
|
|
1676
|
-
}, [closeAll, pluginRegistry]);
|
|
1732
|
+
}, [closeAll, notify, pluginRegistry]);
|
|
1677
1733
|
|
|
1678
1734
|
async function runPaneTemplateShortcut(
|
|
1679
1735
|
template: PaneTemplateDef,
|
|
@@ -1925,12 +1981,24 @@ export function CommandBar({
|
|
|
1925
1981
|
});
|
|
1926
1982
|
}, [getPaneTemplateContext, pluginRegistry, state.config.disabledPlugins]);
|
|
1927
1983
|
|
|
1984
|
+
const getAvailablePluginCommands = useCallback((): CommandDef[] => {
|
|
1985
|
+
const disabledPlugins = new Set(state.config.disabledPlugins || []);
|
|
1986
|
+
return [...pluginRegistry.commands.values()]
|
|
1987
|
+
.filter((command) => {
|
|
1988
|
+
if (command.hidden?.()) return false;
|
|
1989
|
+
const pluginId = pluginRegistry.getCommandPluginId(command.id);
|
|
1990
|
+
if (pluginId && disabledPlugins.has(pluginId)) return false;
|
|
1991
|
+
return true;
|
|
1992
|
+
});
|
|
1993
|
+
}, [pluginRegistry, state.config.disabledPlugins]);
|
|
1994
|
+
|
|
1928
1995
|
const rootShortcutIntent = useMemo(() => parseRootShortcutIntent({
|
|
1929
1996
|
query: rootQuery,
|
|
1930
1997
|
commands,
|
|
1998
|
+
pluginCommands: getAvailablePluginCommands(),
|
|
1931
1999
|
paneTemplates: getAvailablePaneShortcutTemplates(rootQuery),
|
|
1932
2000
|
activeTicker: activeTickerSymbol,
|
|
1933
|
-
}), [activeTickerSymbol, getAvailablePaneShortcutTemplates, rootQuery]);
|
|
2001
|
+
}), [activeTickerSymbol, getAvailablePaneShortcutTemplates, getAvailablePluginCommands, rootQuery]);
|
|
1934
2002
|
|
|
1935
2003
|
const createPaneTemplateItem = useCallback((
|
|
1936
2004
|
template: PaneTemplateDef,
|
|
@@ -2009,69 +2077,108 @@ export function CommandBar({
|
|
|
2009
2077
|
: items;
|
|
2010
2078
|
}, [createPaneTemplateItem, getAvailablePaneTemplates]);
|
|
2011
2079
|
|
|
2012
|
-
const runPluginCommandDirect = useCallback(async (
|
|
2080
|
+
const runPluginCommandDirect = useCallback(async (
|
|
2081
|
+
command: CommandDef,
|
|
2082
|
+
values?: Record<string, string>,
|
|
2083
|
+
) => {
|
|
2013
2084
|
try {
|
|
2014
|
-
await command.execute();
|
|
2085
|
+
await command.execute(values);
|
|
2015
2086
|
closeAll({ revertThemePreview: false });
|
|
2016
2087
|
} catch (error) {
|
|
2017
|
-
|
|
2088
|
+
notify(
|
|
2018
2089
|
error instanceof Error ? error.message : `Could not run ${command.label.toLowerCase()}.`,
|
|
2019
2090
|
{ type: "error" },
|
|
2020
2091
|
);
|
|
2021
2092
|
}
|
|
2022
|
-
}, [closeAll, pluginRegistry]);
|
|
2093
|
+
}, [closeAll, notify, pluginRegistry]);
|
|
2094
|
+
|
|
2095
|
+
const parsePluginCommandShortcutArg = useCallback((
|
|
2096
|
+
command: CommandDef,
|
|
2097
|
+
shortcutArg: string,
|
|
2098
|
+
): Record<string, string> => {
|
|
2099
|
+
if (!shortcutArg.trim()) return {};
|
|
2100
|
+
return command.shortcutArg?.parse?.(shortcutArg, {
|
|
2101
|
+
activeTicker: activeTickerSymbol,
|
|
2102
|
+
}) ?? { shortcut: shortcutArg };
|
|
2103
|
+
}, [activeTickerSymbol]);
|
|
2023
2104
|
|
|
2024
|
-
const
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
.
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
}
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
}
|
|
2065
|
-
|
|
2105
|
+
const createPluginCommandItem = useCallback((
|
|
2106
|
+
command: CommandDef,
|
|
2107
|
+
options?: { shortcutArg?: string },
|
|
2108
|
+
): ResultItem => {
|
|
2109
|
+
const pluginId = pluginRegistry.getCommandPluginId(command.id);
|
|
2110
|
+
const pluginName = pluginId ? pluginRegistry.allPlugins.get(pluginId)?.name : null;
|
|
2111
|
+
const shortcut = command.shortcut?.trim() || undefined;
|
|
2112
|
+
const shortcutArg = options?.shortcutArg?.trim() || "";
|
|
2113
|
+
return {
|
|
2114
|
+
id: command.id,
|
|
2115
|
+
label: command.label,
|
|
2116
|
+
detail: shortcutArg || command.description || "",
|
|
2117
|
+
category: pluginName || "Plugin Commands",
|
|
2118
|
+
kind: "command",
|
|
2119
|
+
right: shortcut,
|
|
2120
|
+
searchText: `${command.label} ${command.description || ""} ${(command.keywords ?? []).join(" ")} ${shortcut || ""}`,
|
|
2121
|
+
action: () => {
|
|
2122
|
+
if (shortcutArg && command.wizard && command.wizard.length > 0) {
|
|
2123
|
+
try {
|
|
2124
|
+
openPluginCommandWorkflow(command, {
|
|
2125
|
+
values: parsePluginCommandShortcutArg(command, shortcutArg),
|
|
2126
|
+
});
|
|
2127
|
+
} catch (error) {
|
|
2128
|
+
notify(
|
|
2129
|
+
error instanceof Error ? error.message : `Could not parse ${command.label.toLowerCase()}.`,
|
|
2130
|
+
{ type: "error" },
|
|
2131
|
+
);
|
|
2132
|
+
}
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
if (shortcutArg) {
|
|
2136
|
+
try {
|
|
2137
|
+
void runPluginCommandDirect(command, parsePluginCommandShortcutArg(command, shortcutArg));
|
|
2138
|
+
} catch (error) {
|
|
2139
|
+
notify(
|
|
2140
|
+
error instanceof Error ? error.message : `Could not parse ${command.label.toLowerCase()}.`,
|
|
2141
|
+
{ type: "error" },
|
|
2142
|
+
);
|
|
2143
|
+
}
|
|
2144
|
+
return;
|
|
2145
|
+
}
|
|
2146
|
+
if (command.wizard && command.wizard.length > 0) {
|
|
2147
|
+
openPluginCommandWorkflow(command);
|
|
2148
|
+
return;
|
|
2149
|
+
}
|
|
2150
|
+
const confirm = resolvePluginCommandConfirm(command);
|
|
2151
|
+
if (confirm) {
|
|
2152
|
+
openInlineConfirm({
|
|
2153
|
+
confirmId: `plugin-command:${command.id}`,
|
|
2154
|
+
title: confirm.title,
|
|
2155
|
+
body: confirm.body,
|
|
2156
|
+
confirmLabel: confirm.confirmLabel || command.label,
|
|
2157
|
+
cancelLabel: confirm.cancelLabel || "Back",
|
|
2158
|
+
tone: confirm.tone || "danger",
|
|
2159
|
+
onConfirm: async () => {
|
|
2160
|
+
await command.execute();
|
|
2161
|
+
},
|
|
2162
|
+
});
|
|
2163
|
+
return;
|
|
2164
|
+
}
|
|
2165
|
+
void runPluginCommandDirect(command);
|
|
2166
|
+
},
|
|
2167
|
+
};
|
|
2066
2168
|
}, [
|
|
2067
2169
|
openInlineConfirm,
|
|
2068
2170
|
openPluginCommandWorkflow,
|
|
2171
|
+
parsePluginCommandShortcutArg,
|
|
2172
|
+
notify,
|
|
2069
2173
|
pluginRegistry,
|
|
2070
2174
|
resolvePluginCommandConfirm,
|
|
2071
2175
|
runPluginCommandDirect,
|
|
2072
|
-
state.config.disabledPlugins,
|
|
2073
2176
|
]);
|
|
2074
2177
|
|
|
2178
|
+
const pluginCommandItems = useCallback((): ResultItem[] => {
|
|
2179
|
+
return getAvailablePluginCommands().map((command) => createPluginCommandItem(command));
|
|
2180
|
+
}, [createPluginCommandItem, getAvailablePluginCommands]);
|
|
2181
|
+
|
|
2075
2182
|
const tickerActionItems = useCallback((): ResultItem[] => {
|
|
2076
2183
|
const ticker = activeTickerData;
|
|
2077
2184
|
const financials = activeFinancials;
|
|
@@ -2200,11 +2307,11 @@ export function CommandBar({
|
|
|
2200
2307
|
const exportPath = `${process.env.HOME || "~"}/gloomberb-config-backup.json`;
|
|
2201
2308
|
void exportConfig(state.config, exportPath)
|
|
2202
2309
|
.then(() => {
|
|
2203
|
-
|
|
2310
|
+
notify(`Config exported to ${exportPath}`, { type: "success" });
|
|
2204
2311
|
closeAll({ revertThemePreview: false });
|
|
2205
2312
|
})
|
|
2206
2313
|
.catch((error) => {
|
|
2207
|
-
|
|
2314
|
+
notify(error instanceof Error ? error.message : "Export failed.", { type: "error" });
|
|
2208
2315
|
});
|
|
2209
2316
|
return;
|
|
2210
2317
|
}
|
|
@@ -2215,11 +2322,11 @@ export function CommandBar({
|
|
|
2215
2322
|
dispatch({ type: "SET_CONFIG", config: imported });
|
|
2216
2323
|
applyTheme(imported.theme);
|
|
2217
2324
|
dispatch({ type: "SET_THEME", theme: imported.theme });
|
|
2218
|
-
|
|
2325
|
+
notify(`Imported config from ${importPath}.`, { type: "success" });
|
|
2219
2326
|
closeAll({ revertThemePreview: false });
|
|
2220
2327
|
})
|
|
2221
2328
|
.catch((error) => {
|
|
2222
|
-
|
|
2329
|
+
notify(error instanceof Error ? error.message : "Import failed.", { type: "error" });
|
|
2223
2330
|
});
|
|
2224
2331
|
return;
|
|
2225
2332
|
}
|
|
@@ -2537,6 +2644,12 @@ export function CommandBar({
|
|
|
2537
2644
|
});
|
|
2538
2645
|
}
|
|
2539
2646
|
|
|
2647
|
+
if (rootShortcutIntent.source === "plugin-command") {
|
|
2648
|
+
return createPluginCommandItem(rootShortcutIntent.command, {
|
|
2649
|
+
shortcutArg: rootShortcutIntent.argText,
|
|
2650
|
+
});
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2540
2653
|
const { command } = rootShortcutIntent;
|
|
2541
2654
|
if (command.id === "search-ticker") {
|
|
2542
2655
|
const inferredSymbol = normalizeTickerInput(activeTickerSymbol, undefined);
|
|
@@ -2567,12 +2680,22 @@ export function CommandBar({
|
|
|
2567
2680
|
: manualPortfolios.length === 1
|
|
2568
2681
|
? manualPortfolios[0]!.id
|
|
2569
2682
|
: null)
|
|
2570
|
-
:
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2683
|
+
: (
|
|
2684
|
+
resolvePreferredCollectionTarget(
|
|
2685
|
+
state,
|
|
2686
|
+
kind,
|
|
2687
|
+
activeCollectionId,
|
|
2688
|
+
action,
|
|
2689
|
+
localTicker,
|
|
2690
|
+
)
|
|
2691
|
+
?? (commandId === "add-watchlist"
|
|
2692
|
+
? resolveSoleCollectionTarget(
|
|
2693
|
+
state,
|
|
2694
|
+
kind,
|
|
2695
|
+
action,
|
|
2696
|
+
localTicker,
|
|
2697
|
+
)
|
|
2698
|
+
: null)
|
|
2576
2699
|
);
|
|
2577
2700
|
const preferredTargetName = preferredTargetId
|
|
2578
2701
|
? (kind === "watchlist"
|
|
@@ -2605,7 +2728,11 @@ export function CommandBar({
|
|
|
2605
2728
|
let initialIdx = 0;
|
|
2606
2729
|
const shortcutItem = buildRootShortcutItem();
|
|
2607
2730
|
|
|
2608
|
-
if (
|
|
2731
|
+
if (
|
|
2732
|
+
rootShortcutIntent.kind !== "none"
|
|
2733
|
+
&& (rootShortcutIntent.source === "pane-template" || rootShortcutIntent.source === "plugin-command")
|
|
2734
|
+
&& shortcutItem
|
|
2735
|
+
) {
|
|
2609
2736
|
items.push(shortcutItem);
|
|
2610
2737
|
} else if (match && match.command.id === "plugins") {
|
|
2611
2738
|
const disabledPlugins = state.config.disabledPlugins || [];
|
|
@@ -2842,6 +2969,7 @@ export function CommandBar({
|
|
|
2842
2969
|
action: () => {
|
|
2843
2970
|
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
2844
2971
|
persistLayoutChange(gridlockAllPanes(currentLayout, { x: 0, y: 0, width, height }));
|
|
2972
|
+
notifyGridlockRevert();
|
|
2845
2973
|
closeAll({ revertThemePreview: false });
|
|
2846
2974
|
},
|
|
2847
2975
|
});
|
|
@@ -2991,12 +3119,14 @@ export function CommandBar({
|
|
|
2991
3119
|
activeTickerData,
|
|
2992
3120
|
activeTickerSymbol,
|
|
2993
3121
|
closeAll,
|
|
3122
|
+
createPluginCommandItem,
|
|
2994
3123
|
currentRoute,
|
|
2995
3124
|
dispatch,
|
|
2996
3125
|
duplicatePane,
|
|
2997
3126
|
executeCollectionCommand,
|
|
2998
3127
|
localTickerSearchResultItems,
|
|
2999
3128
|
nonShortcutPaneTemplateItems,
|
|
3129
|
+
notifyGridlockRevert,
|
|
3000
3130
|
openBuiltInWorkflow,
|
|
3001
3131
|
openPickerRoute,
|
|
3002
3132
|
paneShortcutItems,
|
|
@@ -3166,6 +3296,12 @@ export function CommandBar({
|
|
|
3166
3296
|
return `Shortcut: ${rootShortcutIntent.label}`;
|
|
3167
3297
|
}
|
|
3168
3298
|
|
|
3299
|
+
if (rootShortcutIntent.source === "plugin-command") {
|
|
3300
|
+
return rootShortcutIntent.argText
|
|
3301
|
+
? `Shortcut: ${rootShortcutIntent.label} · ${rootShortcutIntent.argText}`
|
|
3302
|
+
: `Shortcut: ${rootShortcutIntent.label}`;
|
|
3303
|
+
}
|
|
3304
|
+
|
|
3169
3305
|
if (rootShortcutIntent.command.id === "search-ticker") {
|
|
3170
3306
|
const symbol = normalizeTickerInput(activeTickerSymbol, rootShortcutIntent.argText);
|
|
3171
3307
|
if (symbol) {
|
|
@@ -3188,7 +3324,14 @@ export function CommandBar({
|
|
|
3188
3324
|
activeCollectionId,
|
|
3189
3325
|
action,
|
|
3190
3326
|
localTicker,
|
|
3191
|
-
)
|
|
3327
|
+
) ?? (commandId === "add-watchlist"
|
|
3328
|
+
? resolveSoleCollectionTarget(
|
|
3329
|
+
state,
|
|
3330
|
+
kind,
|
|
3331
|
+
action,
|
|
3332
|
+
localTicker,
|
|
3333
|
+
)
|
|
3334
|
+
: null);
|
|
3192
3335
|
const preferredTargetName = preferredTargetId
|
|
3193
3336
|
? (kind === "watchlist"
|
|
3194
3337
|
? state.config.watchlists.find((entry) => entry.id === preferredTargetId)?.name
|
|
@@ -3209,6 +3352,7 @@ export function CommandBar({
|
|
|
3209
3352
|
const intent = parseRootShortcutIntent({
|
|
3210
3353
|
query: rootQueryRef.current,
|
|
3211
3354
|
commands,
|
|
3355
|
+
pluginCommands: getAvailablePluginCommands(),
|
|
3212
3356
|
paneTemplates: getAvailablePaneShortcutTemplates(rootQueryRef.current),
|
|
3213
3357
|
activeTicker: activeTickerSymbol,
|
|
3214
3358
|
});
|
|
@@ -3233,6 +3377,10 @@ export function CommandBar({
|
|
|
3233
3377
|
return false;
|
|
3234
3378
|
}
|
|
3235
3379
|
|
|
3380
|
+
if (intent.source === "plugin-command") {
|
|
3381
|
+
return false;
|
|
3382
|
+
}
|
|
3383
|
+
|
|
3236
3384
|
if (intent.command.id === "search-ticker") {
|
|
3237
3385
|
openModeRoute("ticker-search", intent.argText);
|
|
3238
3386
|
return true;
|
|
@@ -3247,6 +3395,7 @@ export function CommandBar({
|
|
|
3247
3395
|
return false;
|
|
3248
3396
|
}, [
|
|
3249
3397
|
activeTickerSymbol,
|
|
3398
|
+
getAvailablePluginCommands,
|
|
3250
3399
|
getAvailablePaneShortcutTemplates,
|
|
3251
3400
|
openModeRoute,
|
|
3252
3401
|
openPaneTemplateWorkflow,
|
|
@@ -3257,6 +3406,7 @@ export function CommandBar({
|
|
|
3257
3406
|
const intent = parseRootShortcutIntent({
|
|
3258
3407
|
query,
|
|
3259
3408
|
commands,
|
|
3409
|
+
pluginCommands: getAvailablePluginCommands(),
|
|
3260
3410
|
paneTemplates: getAvailablePaneShortcutTemplates(query),
|
|
3261
3411
|
activeTicker: activeTickerSymbol,
|
|
3262
3412
|
});
|
|
@@ -3269,6 +3419,12 @@ export function CommandBar({
|
|
|
3269
3419
|
});
|
|
3270
3420
|
}
|
|
3271
3421
|
|
|
3422
|
+
if (intent.kind !== "none" && intent.source === "plugin-command") {
|
|
3423
|
+
return createPluginCommandItem(intent.command, {
|
|
3424
|
+
shortcutArg: intent.argText,
|
|
3425
|
+
});
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3272
3428
|
const match = matchPrefix(query);
|
|
3273
3429
|
if (!match) {
|
|
3274
3430
|
return null;
|
|
@@ -3376,8 +3532,10 @@ export function CommandBar({
|
|
|
3376
3532
|
return null;
|
|
3377
3533
|
}, [
|
|
3378
3534
|
createPaneTemplateItem,
|
|
3535
|
+
createPluginCommandItem,
|
|
3379
3536
|
activeTickerSymbol,
|
|
3380
3537
|
executeCollectionCommand,
|
|
3538
|
+
getAvailablePluginCommands,
|
|
3381
3539
|
getAvailablePaneShortcutTemplates,
|
|
3382
3540
|
openModeRoute,
|
|
3383
3541
|
paneTemplateItems,
|
|
@@ -3679,6 +3837,7 @@ export function CommandBar({
|
|
|
3679
3837
|
action: () => {
|
|
3680
3838
|
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
3681
3839
|
persistLayoutChange(gridlockAllPanes(currentLayout, { x: 0, y: 0, width, height }));
|
|
3840
|
+
notifyGridlockRevert();
|
|
3682
3841
|
closeAll({ revertThemePreview: false });
|
|
3683
3842
|
},
|
|
3684
3843
|
});
|
|
@@ -3867,6 +4026,7 @@ export function CommandBar({
|
|
|
3867
4026
|
localTickerSearchResultItems,
|
|
3868
4027
|
mapTickerSearchCandidateToResultItem,
|
|
3869
4028
|
nonShortcutPaneTemplateItems,
|
|
4029
|
+
notifyGridlockRevert,
|
|
3870
4030
|
openBuiltInWorkflow,
|
|
3871
4031
|
openInlineConfirm,
|
|
3872
4032
|
openPaneSettingsRoute,
|
|
@@ -3999,20 +4159,31 @@ export function CommandBar({
|
|
|
3999
4159
|
}, [moveListSelection, setHoveredIndex]);
|
|
4000
4160
|
|
|
4001
4161
|
const updateWorkflowValue = useCallback((fieldId: string, value: CommandBarFieldValue) => {
|
|
4002
|
-
|
|
4003
|
-
|
|
4162
|
+
setRouteStack((current) => {
|
|
4163
|
+
let workflowIndex = -1;
|
|
4164
|
+
for (let index = current.length - 1; index >= 0; index -= 1) {
|
|
4165
|
+
if (current[index]?.kind === "workflow") {
|
|
4166
|
+
workflowIndex = index;
|
|
4167
|
+
break;
|
|
4168
|
+
}
|
|
4169
|
+
}
|
|
4170
|
+
if (workflowIndex < 0) return current;
|
|
4171
|
+
const next = [...current];
|
|
4172
|
+
const route = next[workflowIndex]!;
|
|
4173
|
+
if (route.kind !== "workflow") return current;
|
|
4004
4174
|
const nextValues = { ...route.values, [fieldId]: value };
|
|
4005
4175
|
const nextActiveFieldId = route.activeFieldId && getVisibleWorkflowFields(route.fields, nextValues).some((field) => field.id === route.activeFieldId)
|
|
4006
4176
|
? route.activeFieldId
|
|
4007
4177
|
: getFirstVisibleFieldId(route.fields, nextValues);
|
|
4008
|
-
|
|
4178
|
+
next[workflowIndex] = {
|
|
4009
4179
|
...route,
|
|
4010
4180
|
values: nextValues,
|
|
4011
4181
|
activeFieldId: nextActiveFieldId,
|
|
4012
4182
|
error: null,
|
|
4013
4183
|
};
|
|
4184
|
+
return next;
|
|
4014
4185
|
});
|
|
4015
|
-
}, [
|
|
4186
|
+
}, []);
|
|
4016
4187
|
|
|
4017
4188
|
const moveWorkflowFocus = useCallback((delta: number) => {
|
|
4018
4189
|
if (currentRoute?.kind !== "workflow") return;
|
|
@@ -4875,6 +5046,7 @@ export function CommandBar({
|
|
|
4875
5046
|
placeholderColor={paletteSubtleText}
|
|
4876
5047
|
backgroundColor={colors.panel}
|
|
4877
5048
|
flexGrow={1}
|
|
5049
|
+
wrapText
|
|
4878
5050
|
/>
|
|
4879
5051
|
) : (
|
|
4880
5052
|
<box flexDirection="column" paddingX={1} paddingY={0}>
|