gloomberb 0.4.0 → 0.5.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 +2 -2
- package/package.json +1 -1
- package/src/app.tsx +184 -69
- 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 +296 -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 -1
- package/src/components/layout/header.tsx +5 -4
- package/src/components/layout/pane-sizing.ts +6 -0
- package/src/components/layout/pane.tsx +5 -1
- package/src/components/layout/shell.test.tsx +263 -32
- package/src/components/layout/shell.tsx +10 -6
- 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 +62 -2
- package/src/components/toggle-list.tsx +15 -2
- package/src/components/ui/data-table.tsx +82 -5
- 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 +92 -18
- package/src/plugins/builtin/chat-controller.ts +40 -11
- package/src/plugins/builtin/chat.test.tsx +715 -4
- package/src/plugins/builtin/chat.tsx +420 -119
- 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 +33 -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/gateway-service.test.ts +22 -0
- package/src/plugins/ibkr/gateway-service.ts +6 -0
- package/src/plugins/ibkr/index.test.tsx +1 -1
- package/src/plugins/ibkr/index.tsx +8 -2
- 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 +129 -14
- package/src/plugins/prediction-markets/controller.ts +1 -1
- package/src/plugins/prediction-markets/detail/shared.tsx +7 -6
- package/src/plugins/prediction-markets/index.tsx +5 -1
- package/src/plugins/prediction-markets/pane.test.tsx +78 -0
- package/src/plugins/prediction-markets/pane.tsx +7 -3
- package/src/plugins/prediction-markets/plugin.test.tsx +48 -1
- package/src/plugins/prediction-markets/services/fetch.ts +17 -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 +22 -5
- package/src/plugins/registry.ts +45 -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/updater.test.ts +2 -0
- package/src/utils/api-client.ts +12 -0
- 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
|
}
|
|
@@ -2488,6 +2595,10 @@ export function CommandBar({
|
|
|
2488
2595
|
return name ? `in "${name}"` : command.description;
|
|
2489
2596
|
}
|
|
2490
2597
|
case "check-for-updates":
|
|
2598
|
+
if (state.updateProgress?.phase === "downloading") return `Downloading v${state.updateAvailable?.version}: ${state.updateProgress.percent ?? 0}%`;
|
|
2599
|
+
if (state.updateProgress?.phase === "replacing") return "Installing update";
|
|
2600
|
+
if (state.updateProgress?.phase === "done") return "Update installed - restart to apply";
|
|
2601
|
+
if (state.updateProgress?.phase === "error") return `Update failed: ${state.updateProgress.error}`;
|
|
2491
2602
|
if (state.updateCheckInProgress) return "Checking GitHub releases now";
|
|
2492
2603
|
if (state.updateAvailable) return `Latest available: v${state.updateAvailable.version}`;
|
|
2493
2604
|
if (state.updateNotice) return state.updateNotice;
|
|
@@ -2533,6 +2644,12 @@ export function CommandBar({
|
|
|
2533
2644
|
});
|
|
2534
2645
|
}
|
|
2535
2646
|
|
|
2647
|
+
if (rootShortcutIntent.source === "plugin-command") {
|
|
2648
|
+
return createPluginCommandItem(rootShortcutIntent.command, {
|
|
2649
|
+
shortcutArg: rootShortcutIntent.argText,
|
|
2650
|
+
});
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2536
2653
|
const { command } = rootShortcutIntent;
|
|
2537
2654
|
if (command.id === "search-ticker") {
|
|
2538
2655
|
const inferredSymbol = normalizeTickerInput(activeTickerSymbol, undefined);
|
|
@@ -2563,12 +2680,22 @@ export function CommandBar({
|
|
|
2563
2680
|
: manualPortfolios.length === 1
|
|
2564
2681
|
? manualPortfolios[0]!.id
|
|
2565
2682
|
: null)
|
|
2566
|
-
:
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
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)
|
|
2572
2699
|
);
|
|
2573
2700
|
const preferredTargetName = preferredTargetId
|
|
2574
2701
|
? (kind === "watchlist"
|
|
@@ -2601,7 +2728,11 @@ export function CommandBar({
|
|
|
2601
2728
|
let initialIdx = 0;
|
|
2602
2729
|
const shortcutItem = buildRootShortcutItem();
|
|
2603
2730
|
|
|
2604
|
-
if (
|
|
2731
|
+
if (
|
|
2732
|
+
rootShortcutIntent.kind !== "none"
|
|
2733
|
+
&& (rootShortcutIntent.source === "pane-template" || rootShortcutIntent.source === "plugin-command")
|
|
2734
|
+
&& shortcutItem
|
|
2735
|
+
) {
|
|
2605
2736
|
items.push(shortcutItem);
|
|
2606
2737
|
} else if (match && match.command.id === "plugins") {
|
|
2607
2738
|
const disabledPlugins = state.config.disabledPlugins || [];
|
|
@@ -2838,6 +2969,7 @@ export function CommandBar({
|
|
|
2838
2969
|
action: () => {
|
|
2839
2970
|
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
2840
2971
|
persistLayoutChange(gridlockAllPanes(currentLayout, { x: 0, y: 0, width, height }));
|
|
2972
|
+
notifyGridlockRevert();
|
|
2841
2973
|
closeAll({ revertThemePreview: false });
|
|
2842
2974
|
},
|
|
2843
2975
|
});
|
|
@@ -2987,12 +3119,14 @@ export function CommandBar({
|
|
|
2987
3119
|
activeTickerData,
|
|
2988
3120
|
activeTickerSymbol,
|
|
2989
3121
|
closeAll,
|
|
3122
|
+
createPluginCommandItem,
|
|
2990
3123
|
currentRoute,
|
|
2991
3124
|
dispatch,
|
|
2992
3125
|
duplicatePane,
|
|
2993
3126
|
executeCollectionCommand,
|
|
2994
3127
|
localTickerSearchResultItems,
|
|
2995
3128
|
nonShortcutPaneTemplateItems,
|
|
3129
|
+
notifyGridlockRevert,
|
|
2996
3130
|
openBuiltInWorkflow,
|
|
2997
3131
|
openPickerRoute,
|
|
2998
3132
|
paneShortcutItems,
|
|
@@ -3162,6 +3296,12 @@ export function CommandBar({
|
|
|
3162
3296
|
return `Shortcut: ${rootShortcutIntent.label}`;
|
|
3163
3297
|
}
|
|
3164
3298
|
|
|
3299
|
+
if (rootShortcutIntent.source === "plugin-command") {
|
|
3300
|
+
return rootShortcutIntent.argText
|
|
3301
|
+
? `Shortcut: ${rootShortcutIntent.label} · ${rootShortcutIntent.argText}`
|
|
3302
|
+
: `Shortcut: ${rootShortcutIntent.label}`;
|
|
3303
|
+
}
|
|
3304
|
+
|
|
3165
3305
|
if (rootShortcutIntent.command.id === "search-ticker") {
|
|
3166
3306
|
const symbol = normalizeTickerInput(activeTickerSymbol, rootShortcutIntent.argText);
|
|
3167
3307
|
if (symbol) {
|
|
@@ -3184,7 +3324,14 @@ export function CommandBar({
|
|
|
3184
3324
|
activeCollectionId,
|
|
3185
3325
|
action,
|
|
3186
3326
|
localTicker,
|
|
3187
|
-
)
|
|
3327
|
+
) ?? (commandId === "add-watchlist"
|
|
3328
|
+
? resolveSoleCollectionTarget(
|
|
3329
|
+
state,
|
|
3330
|
+
kind,
|
|
3331
|
+
action,
|
|
3332
|
+
localTicker,
|
|
3333
|
+
)
|
|
3334
|
+
: null);
|
|
3188
3335
|
const preferredTargetName = preferredTargetId
|
|
3189
3336
|
? (kind === "watchlist"
|
|
3190
3337
|
? state.config.watchlists.find((entry) => entry.id === preferredTargetId)?.name
|
|
@@ -3205,6 +3352,7 @@ export function CommandBar({
|
|
|
3205
3352
|
const intent = parseRootShortcutIntent({
|
|
3206
3353
|
query: rootQueryRef.current,
|
|
3207
3354
|
commands,
|
|
3355
|
+
pluginCommands: getAvailablePluginCommands(),
|
|
3208
3356
|
paneTemplates: getAvailablePaneShortcutTemplates(rootQueryRef.current),
|
|
3209
3357
|
activeTicker: activeTickerSymbol,
|
|
3210
3358
|
});
|
|
@@ -3229,6 +3377,10 @@ export function CommandBar({
|
|
|
3229
3377
|
return false;
|
|
3230
3378
|
}
|
|
3231
3379
|
|
|
3380
|
+
if (intent.source === "plugin-command") {
|
|
3381
|
+
return false;
|
|
3382
|
+
}
|
|
3383
|
+
|
|
3232
3384
|
if (intent.command.id === "search-ticker") {
|
|
3233
3385
|
openModeRoute("ticker-search", intent.argText);
|
|
3234
3386
|
return true;
|
|
@@ -3243,6 +3395,7 @@ export function CommandBar({
|
|
|
3243
3395
|
return false;
|
|
3244
3396
|
}, [
|
|
3245
3397
|
activeTickerSymbol,
|
|
3398
|
+
getAvailablePluginCommands,
|
|
3246
3399
|
getAvailablePaneShortcutTemplates,
|
|
3247
3400
|
openModeRoute,
|
|
3248
3401
|
openPaneTemplateWorkflow,
|
|
@@ -3253,6 +3406,7 @@ export function CommandBar({
|
|
|
3253
3406
|
const intent = parseRootShortcutIntent({
|
|
3254
3407
|
query,
|
|
3255
3408
|
commands,
|
|
3409
|
+
pluginCommands: getAvailablePluginCommands(),
|
|
3256
3410
|
paneTemplates: getAvailablePaneShortcutTemplates(query),
|
|
3257
3411
|
activeTicker: activeTickerSymbol,
|
|
3258
3412
|
});
|
|
@@ -3265,6 +3419,12 @@ export function CommandBar({
|
|
|
3265
3419
|
});
|
|
3266
3420
|
}
|
|
3267
3421
|
|
|
3422
|
+
if (intent.kind !== "none" && intent.source === "plugin-command") {
|
|
3423
|
+
return createPluginCommandItem(intent.command, {
|
|
3424
|
+
shortcutArg: intent.argText,
|
|
3425
|
+
});
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3268
3428
|
const match = matchPrefix(query);
|
|
3269
3429
|
if (!match) {
|
|
3270
3430
|
return null;
|
|
@@ -3372,8 +3532,10 @@ export function CommandBar({
|
|
|
3372
3532
|
return null;
|
|
3373
3533
|
}, [
|
|
3374
3534
|
createPaneTemplateItem,
|
|
3535
|
+
createPluginCommandItem,
|
|
3375
3536
|
activeTickerSymbol,
|
|
3376
3537
|
executeCollectionCommand,
|
|
3538
|
+
getAvailablePluginCommands,
|
|
3377
3539
|
getAvailablePaneShortcutTemplates,
|
|
3378
3540
|
openModeRoute,
|
|
3379
3541
|
paneTemplateItems,
|
|
@@ -3675,6 +3837,7 @@ export function CommandBar({
|
|
|
3675
3837
|
action: () => {
|
|
3676
3838
|
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
3677
3839
|
persistLayoutChange(gridlockAllPanes(currentLayout, { x: 0, y: 0, width, height }));
|
|
3840
|
+
notifyGridlockRevert();
|
|
3678
3841
|
closeAll({ revertThemePreview: false });
|
|
3679
3842
|
},
|
|
3680
3843
|
});
|
|
@@ -3863,6 +4026,7 @@ export function CommandBar({
|
|
|
3863
4026
|
localTickerSearchResultItems,
|
|
3864
4027
|
mapTickerSearchCandidateToResultItem,
|
|
3865
4028
|
nonShortcutPaneTemplateItems,
|
|
4029
|
+
notifyGridlockRevert,
|
|
3866
4030
|
openBuiltInWorkflow,
|
|
3867
4031
|
openInlineConfirm,
|
|
3868
4032
|
openPaneSettingsRoute,
|
|
@@ -3995,20 +4159,31 @@ export function CommandBar({
|
|
|
3995
4159
|
}, [moveListSelection, setHoveredIndex]);
|
|
3996
4160
|
|
|
3997
4161
|
const updateWorkflowValue = useCallback((fieldId: string, value: CommandBarFieldValue) => {
|
|
3998
|
-
|
|
3999
|
-
|
|
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;
|
|
4000
4174
|
const nextValues = { ...route.values, [fieldId]: value };
|
|
4001
4175
|
const nextActiveFieldId = route.activeFieldId && getVisibleWorkflowFields(route.fields, nextValues).some((field) => field.id === route.activeFieldId)
|
|
4002
4176
|
? route.activeFieldId
|
|
4003
4177
|
: getFirstVisibleFieldId(route.fields, nextValues);
|
|
4004
|
-
|
|
4178
|
+
next[workflowIndex] = {
|
|
4005
4179
|
...route,
|
|
4006
4180
|
values: nextValues,
|
|
4007
4181
|
activeFieldId: nextActiveFieldId,
|
|
4008
4182
|
error: null,
|
|
4009
4183
|
};
|
|
4184
|
+
return next;
|
|
4010
4185
|
});
|
|
4011
|
-
}, [
|
|
4186
|
+
}, []);
|
|
4012
4187
|
|
|
4013
4188
|
const moveWorkflowFocus = useCallback((delta: number) => {
|
|
4014
4189
|
if (currentRoute?.kind !== "workflow") return;
|
|
@@ -4871,6 +5046,7 @@ export function CommandBar({
|
|
|
4871
5046
|
placeholderColor={paletteSubtleText}
|
|
4872
5047
|
backgroundColor={colors.panel}
|
|
4873
5048
|
flexGrow={1}
|
|
5049
|
+
wrapText
|
|
4874
5050
|
/>
|
|
4875
5051
|
) : (
|
|
4876
5052
|
<box flexDirection="column" paddingX={1} paddingY={0}>
|