gloomberb 0.11.1 → 0.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -9
- package/README.zh-CN.md +6 -6
- package/package.json +1 -1
- package/src/api-client/data.ts +12 -0
- package/src/api-client/index.test.ts +47 -0
- package/src/api-client/index.ts +66 -0
- package/src/api-client/paths.ts +16 -0
- package/src/api-client/types.ts +59 -0
- package/src/app/pane-runtime/index.tsx +4 -1
- package/src/app/pane-runtime/plugin-bindings.test.ts +117 -0
- package/src/app/pane-runtime/plugin-bindings.ts +25 -4
- package/src/app/pane-runtime/ticker-inspector-runtime.ts +25 -14
- package/src/app/runtime/desktop-deeplink.test.ts +16 -0
- package/src/app/runtime/desktop-deeplink.ts +66 -2
- package/src/app/runtime/desktop-menu.ts +3 -0
- package/src/app/runtime/plugin-bindings.ts +30 -28
- package/src/app.tsx +9 -2
- package/src/capabilities/chart-series.ts +3 -2
- package/src/cli/commands/automation.test.ts +9 -0
- package/src/cli/commands/automation.ts +22 -73
- package/src/cli/commands/market.ts +2 -2
- package/src/cli/index.ts +0 -2
- package/src/cli/pane-functions/capabilities.ts +3 -3
- package/src/cli/registry.ts +0 -22
- package/src/cli/result.ts +7 -10
- package/src/components/chart/composite/composite-chart.test.tsx +45 -2
- package/src/components/chart/composite/composite-chart.tsx +12 -9
- package/src/components/chart/composite/scene.ts +0 -21
- package/src/components/chart/composite/tools.ts +49 -0
- package/src/components/command-bar/commands/direct/index.ts +4 -0
- package/src/components/command-bar/commands/registry.ts +8 -1
- package/src/components/command-bar/layout-items/current-layout.ts +1 -1
- package/src/components/command-bar/pane-templates/workflow.ts +4 -0
- package/src/components/command-bar/routes/root/shortcut-feedback.ts +3 -0
- package/src/components/command-bar/routes/root/shortcuts.test.ts +19 -0
- package/src/components/command-bar/routes/root/shortcuts.ts +3 -1
- package/src/components/command-bar/surface/index.test.tsx +6 -6
- package/src/components/command-bar/surface/pane.test.tsx +66 -2
- package/src/components/command-bar/view-model.test.ts +2 -1
- package/src/components/command-bar/workflow/ops.test.ts +54 -0
- package/src/components/command-bar/workflow/ops.ts +1 -1
- package/src/components/data-table/export.test.ts +21 -0
- package/src/components/data-table/export.ts +21 -0
- package/src/components/layout/detached-pane-shell.tsx +74 -10
- package/src/components/layout/pane/title.ts +31 -3
- package/src/components/layout/shell/active-drag.ts +4 -11
- package/src/components/layout/shell/drag/index.ts +4 -20
- package/src/components/layout/shell/drag/runtime.ts +3 -4
- package/src/components/layout/shell/index.test.tsx +17 -3
- package/src/components/layout/shell/index.tsx +86 -30
- package/src/components/layout/shell/menu.test.ts +37 -1
- package/src/components/layout/shell/menu.ts +14 -0
- package/src/components/layout/shell/native/pointer-runtime.ts +22 -8
- package/src/components/layout/shell/pane/actions.ts +0 -5
- package/src/components/layout/shell/pane/management-shortcuts.ts +9 -4
- package/src/components/layout/shell/shortcuts.ts +6 -3
- package/src/components/layout/shell/ticker-link-menu.test.ts +82 -0
- package/src/components/layout/shell/ticker-link-menu.ts +78 -0
- package/src/components/layout/status-bar.test.tsx +62 -61
- package/src/components/layout/status-bar.tsx +53 -75
- package/src/components/ui/data-table/index.tsx +19 -2
- package/src/core/state/app/layout-reducer.ts +36 -0
- package/src/core/state/app/layout.ts +10 -5
- package/src/core/state/app/state.ts +0 -13
- package/src/core/state/app/types.ts +6 -4
- package/src/data/config/chart-settings.ts +5 -5
- package/src/data/config/layout.ts +2 -0
- package/src/data/config/store/index.test.ts +27 -6
- package/src/data/config/store/normalize.ts +3 -5
- package/src/data/plugin-state-store.ts +11 -0
- package/src/data/sqlite/cache.test.ts +10 -0
- package/src/i18n/es.ts +3 -4
- package/src/i18n/ja.ts +3 -4
- package/src/i18n/ko.ts +3 -4
- package/src/i18n/zh-cn.ts +3 -4
- package/src/i18n/zh-tw.ts +3 -4
- package/src/layout-marketplace/api.test.ts +54 -0
- package/src/layout-marketplace/api.ts +65 -0
- package/src/layout-marketplace/gallery-desktop.test.tsx +262 -0
- package/src/layout-marketplace/gallery-desktop.tsx +375 -0
- package/src/layout-marketplace/gallery-terminal.tsx +406 -0
- package/src/layout-marketplace/gallery.test.tsx +148 -0
- package/src/layout-marketplace/gallery.tsx +323 -0
- package/src/layout-marketplace/geometry.test.ts +47 -0
- package/src/layout-marketplace/geometry.ts +80 -0
- package/src/layout-marketplace/mini-workspace.test.tsx +122 -0
- package/src/layout-marketplace/mini-workspace.tsx +428 -0
- package/src/layout-marketplace/model.test.ts +118 -0
- package/src/layout-marketplace/model.ts +171 -0
- package/src/layout-marketplace/name-dialog.tsx +60 -0
- package/src/layout-marketplace/pane-imagery.ts +85 -0
- package/src/layout-marketplace/pane.tsx +20 -0
- package/src/layout-marketplace/payload.test.ts +246 -0
- package/src/layout-marketplace/payload.ts +624 -0
- package/src/layout-marketplace/use-marketplace.ts +63 -0
- package/src/market-data/hooks.tsx +0 -41
- package/src/plugins/broker-sync/broker-sync.test.ts +29 -0
- package/src/plugins/broker-sync/normalize.ts +9 -2
- package/src/plugins/builtin/ai/index.tsx +18 -2
- package/src/plugins/builtin/ai/screener/pane.test.tsx +47 -4
- package/src/plugins/builtin/ai/screener/pane.tsx +35 -12
- package/src/plugins/builtin/alerts/alert-engine.test.ts +16 -1
- package/src/plugins/builtin/alerts/alert-engine.ts +4 -0
- package/src/plugins/builtin/alerts/index.tsx +4 -0
- package/src/plugins/builtin/alerts/pane.tsx +2 -8
- package/src/plugins/builtin/analytics/index.tsx +3 -0
- package/src/plugins/builtin/broker-manager/index.tsx +3 -0
- package/src/plugins/builtin/cds/client.test.ts +83 -0
- package/src/plugins/builtin/cds/client.ts +67 -0
- package/src/plugins/builtin/cds/index.tsx +41 -0
- package/src/plugins/builtin/cds/model.test.ts +232 -0
- package/src/plugins/builtin/cds/model.ts +401 -0
- package/src/plugins/builtin/cds/pane.test.tsx +132 -0
- package/src/plugins/builtin/cds/pane.tsx +358 -0
- package/src/plugins/builtin/chart-composer/chart-spec.ts +31 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +12 -9
- package/src/plugins/builtin/chart-composer/index.tsx +88 -2
- package/src/plugins/builtin/chart-composer/pane-share.test.ts +71 -0
- package/src/plugins/builtin/chart-composer/pane.tsx +81 -14
- package/src/plugins/builtin/chart-composer/settings.ts +2 -13
- package/src/plugins/builtin/chat/content/index.tsx +4 -1
- package/src/plugins/builtin/chat/controller/index.ts +8 -1
- package/src/plugins/builtin/chat/controller/notifications.ts +13 -3
- package/src/plugins/builtin/chat/controller/session-runtime.ts +14 -2
- package/src/plugins/builtin/chat/controller/utils.ts +4 -6
- package/src/plugins/builtin/chat/controller.test.ts +64 -14
- package/src/plugins/builtin/chat/message/desktop.test.tsx +88 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +3 -1
- package/src/plugins/builtin/chat/message/profile-popover.test.tsx +1 -29
- package/src/plugins/builtin/chat/profile-popover.ts +53 -22
- package/src/plugins/builtin/chat/sidebar.test.tsx +81 -0
- package/src/plugins/builtin/chat/sidebar.tsx +63 -0
- package/src/plugins/builtin/chat/test-harness.tsx +29 -1
- package/src/plugins/builtin/cloud/auth-commands.ts +0 -4
- package/src/plugins/builtin/cloud/auth-model.ts +4 -1
- package/src/plugins/builtin/cloud/plugin.tsx +11 -1
- package/src/plugins/builtin/cloud-tweets/pane-share.test.ts +56 -0
- package/src/plugins/builtin/cloud-tweets/pane.tsx +20 -5
- package/src/plugins/builtin/cloud-tweets/registration.ts +44 -5
- package/src/plugins/builtin/composite-plugins.ts +5 -1
- package/src/plugins/builtin/connections/index.ts +3 -0
- package/src/plugins/builtin/earnings/index.tsx +1 -0
- package/src/plugins/builtin/econ/calendar-model.ts +0 -5
- package/src/plugins/builtin/econ/index.tsx +1 -0
- package/src/plugins/builtin/fear-greed/charts.tsx +12 -8
- package/src/plugins/builtin/futures/index.tsx +1 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +1 -0
- package/src/plugins/builtin/help/index.tsx +2 -2
- package/src/plugins/builtin/ipo-calendar/index.tsx +1 -0
- package/src/plugins/builtin/kelly-sizer/index.tsx +4 -1
- package/src/plugins/builtin/layout-manager/index.tsx +17 -4
- package/src/plugins/builtin/market-halts/index.tsx +1 -0
- package/src/plugins/builtin/market-movers/index.tsx +1 -0
- package/src/plugins/builtin/news/index.tsx +1 -0
- package/src/plugins/builtin/news/wire/feed-config.test.ts +15 -1
- package/src/plugins/builtin/news/wire/feed-config.ts +25 -5
- package/src/plugins/builtin/news/wire/index.tsx +2 -2
- package/src/plugins/builtin/options/analytics.test.ts +83 -0
- package/src/plugins/builtin/options/analytics.ts +110 -0
- package/src/plugins/builtin/options/index.tsx +18 -2
- package/src/plugins/builtin/options/table.ts +121 -51
- package/src/plugins/builtin/options/types.ts +22 -15
- package/src/plugins/builtin/options/view.test.tsx +49 -3
- package/src/plugins/builtin/options/view.tsx +92 -19
- package/src/plugins/builtin/plugin-module.test.ts +2 -4
- package/src/plugins/builtin/plugin-module.ts +0 -7
- package/src/plugins/builtin/portfolio-list/account-metrics.test.ts +2 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +1 -1
- package/src/plugins/builtin/portfolio-list/cli/render.ts +5 -3
- package/src/plugins/builtin/portfolio-list/column-values.ts +15 -4
- package/src/plugins/builtin/portfolio-list/header.tsx +13 -3
- package/src/plugins/builtin/portfolio-list/index.tsx +9 -0
- package/src/plugins/builtin/portfolio-list/metrics.test.ts +31 -1
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +10 -7
- package/src/plugins/builtin/portfolio-list/position-metrics.ts +25 -6
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +21 -16
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +20 -6
- package/src/plugins/builtin/research/equity-diagnostic-pane.test.tsx +33 -7
- package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +109 -12
- package/src/plugins/builtin/research/index.tsx +4 -0
- package/src/plugins/builtin/scanner/index.ts +2 -0
- package/src/plugins/builtin/sectors/index.tsx +1 -0
- package/src/plugins/builtin/shared/public-pane.test.ts +12 -0
- package/src/plugins/builtin/shared/public-pane.ts +9 -0
- package/src/plugins/builtin/shared/ticker-source.ts +32 -0
- package/src/plugins/builtin/shared/ticker-surface.test.ts +27 -0
- package/src/plugins/builtin/shared/ticker-surface.ts +17 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +3 -5
- package/src/plugins/builtin/ticker-detail/index.tsx +12 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +3 -0
- package/src/plugins/builtin/world-indices/index.tsx +3 -0
- package/src/plugins/builtin/world-venue-map/index.ts +27 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +337 -0
- package/src/plugins/builtin/world-venue-map/model.test.ts +53 -0
- package/src/plugins/builtin/world-venue-map/model.ts +147 -0
- package/src/plugins/builtin/world-venue-map/pane.tsx +300 -0
- package/src/plugins/builtin/world-venue-map/world-outlines.ts +2 -0
- package/src/plugins/catalog-backend.ts +6 -44
- package/src/plugins/catalog-browser.test.ts +10 -0
- package/src/plugins/catalog-browser.ts +5 -1
- package/src/plugins/gridlock-notification.ts +2 -1
- package/src/plugins/pane-manager/gridlock.ts +87 -3
- package/src/plugins/pane-manager.test.ts +30 -0
- package/src/plugins/pane-manager.ts +6 -1
- package/src/plugins/prediction-markets/chart.tsx +2 -2
- package/src/plugins/prediction-markets/controller/index.ts +5 -1
- package/src/plugins/prediction-markets/controller/keyboard.ts +10 -14
- package/src/plugins/prediction-markets/detail/overview.tsx +21 -0
- package/src/plugins/prediction-markets/detail/pane.tsx +43 -34
- package/src/plugins/prediction-markets/detail/trades.tsx +34 -9
- package/src/plugins/prediction-markets/detail.test.tsx +29 -2
- package/src/plugins/prediction-markets/index.tsx +8 -4
- package/src/plugins/prediction-markets/navigation.ts +0 -1
- package/src/plugins/prediction-markets/pane.test.tsx +77 -0
- package/src/plugins/prediction-markets/pane.tsx +4 -0
- package/src/plugins/prediction-markets/plugin.test.tsx +36 -0
- package/src/plugins/prediction-markets/test-helpers.tsx +9 -0
- package/src/plugins/registry/index.ts +2 -0
- package/src/plugins/registry/pane-settings.test.ts +53 -0
- package/src/plugins/registry/pane-settings.ts +36 -3
- package/src/plugins/ticker-navigation.test.ts +24 -0
- package/src/plugins/ticker-navigation.ts +40 -0
- package/src/remote/schema.ts +1 -1
- package/src/renderers/browser/cloud-transport.test.ts +36 -0
- package/src/renderers/browser/cloud-transport.ts +19 -0
- package/src/renderers/browser/config-host.ts +0 -4
- package/src/renderers/browser/deeplink-bridge.test.ts +41 -0
- package/src/renderers/browser/deeplink-bridge.ts +22 -0
- package/src/renderers/browser/main.tsx +3 -0
- package/src/renderers/browser/ui-host.tsx +11 -0
- package/src/renderers/cloudflare/worker.test.ts +8 -5
- package/src/renderers/cloudflare/worker.ts +6 -5
- package/src/renderers/electrobun/bun/application-menu/index.ts +3 -2
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -2
- package/src/renderers/electrobun/bun/desktop/initialization.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/plugin-state.ts +7 -1
- package/src/renderers/electrobun/bun/index.ts +1 -0
- package/src/renderers/electrobun/shared/protocol.ts +4 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/host/native.ts +0 -5
- package/src/renderers/electrobun/view/remote/persistence.ts +5 -0
- package/src/renderers/electrobun/view/styles.css +107 -7
- package/src/renderers/electrobun/view/toast-host.test.tsx +78 -0
- package/src/renderers/electrobun/view/toast-host.tsx +69 -43
- package/src/renderers/electrobun/view/ui-host.tsx +7 -1
- package/src/renderers/opentui/host.tsx +4 -0
- package/src/renderers/share/layout-view.test.tsx +58 -0
- package/src/renderers/share/layout-view.tsx +133 -0
- package/src/renderers/share/main.tsx +54 -6
- package/src/renderers/share/styles.css +35 -0
- package/src/renderers/share/view.test.tsx +78 -0
- package/src/renderers/share/view.tsx +71 -0
- package/src/shares/api.test.ts +52 -3
- package/src/shares/api.ts +39 -15
- package/src/shares/location.test.ts +19 -0
- package/src/shares/location.ts +11 -0
- package/src/shares/pane.test.ts +110 -0
- package/src/shares/pane.ts +54 -0
- package/src/shares/payload.ts +74 -1
- package/src/state/app/bootstrap.ts +6 -7
- package/src/state/app/context/index.test.ts +41 -1
- package/src/state/app/context/index.test.tsx +34 -16
- package/src/state/pane-table-export-registry.ts +31 -0
- package/src/state/selectors-ui.ts +0 -8
- package/src/sync/controller.test.ts +57 -1
- package/src/sync/controller.ts +22 -11
- package/src/sync/react.ts +13 -0
- package/src/theme/font-scale.ts +0 -11
- package/src/time-series/range.ts +12 -0
- package/src/time-series/resolution.ts +3 -15
- package/src/time-series/spec.ts +5 -5
- package/src/types/config.ts +35 -5
- package/src/types/desktop-menu.ts +1 -0
- package/src/types/plugin.ts +38 -20
- package/src/ui/context-menu.tsx +1 -1
- package/src/ui/host.tsx +7 -0
- package/src/ui/toast.tsx +2 -0
- package/src/utils/back-navigation.ts +0 -4
- package/src/utils/csv.test.ts +13 -0
- package/src/utils/csv.ts +37 -0
- package/src/utils/save-text-file.test.ts +24 -0
- package/src/utils/save-text-file.ts +36 -0
|
@@ -13,38 +13,26 @@ import type { CliCommandDef } from "../../types/plugin";
|
|
|
13
13
|
import { withCliServices, withConfigData } from "../context";
|
|
14
14
|
import { requireArg, takeOption } from "./command-utils";
|
|
15
15
|
|
|
16
|
-
function deferredResult(command: string, code: "auth_required" | "not_implemented", message: string) {
|
|
17
|
-
return {
|
|
18
|
-
command,
|
|
19
|
-
status: code,
|
|
20
|
-
code,
|
|
21
|
-
message,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
16
|
export const brokerCliCommand: CliCommandDef = {
|
|
26
17
|
name: "broker",
|
|
27
18
|
aliases: ["brokers"],
|
|
28
|
-
description: "Inspect broker profiles
|
|
29
|
-
help: { usage: ["broker list", "broker status"
|
|
19
|
+
description: "Inspect broker profiles",
|
|
20
|
+
help: { usage: ["broker list", "broker status"] },
|
|
30
21
|
execute: async (args, ctx) => {
|
|
31
22
|
const action = args[0] ?? "list";
|
|
23
|
+
if (action !== "list" && action !== "status") {
|
|
24
|
+
ctx.fail("Usage: gloomberb broker list|status");
|
|
25
|
+
}
|
|
32
26
|
await withCliServices(ctx, async (services) => {
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
ctx.printResult({
|
|
28
|
+
data: services.config.brokerInstances.map((instance) => ({
|
|
35
29
|
id: instance.id,
|
|
36
30
|
type: instance.brokerType,
|
|
37
31
|
label: instance.label,
|
|
38
32
|
enabled: instance.enabled !== false,
|
|
39
33
|
connectionMode: instance.connectionMode ?? "",
|
|
40
34
|
lastSyncedAt: instance.lastSyncedAt ? new Date(instance.lastSyncedAt).toISOString() : "",
|
|
41
|
-
}))
|
|
42
|
-
ctx.printResult({ data: rows });
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
ctx.printResult({
|
|
47
|
-
data: deferredResult("broker", "not_implemented", "Broker mutation and connection commands need shared non-UI broker service extraction before they can safely run headless."),
|
|
35
|
+
})),
|
|
48
36
|
});
|
|
49
37
|
});
|
|
50
38
|
},
|
|
@@ -52,36 +40,24 @@ export const brokerCliCommand: CliCommandDef = {
|
|
|
52
40
|
|
|
53
41
|
export const ibkrCliCommand: CliCommandDef = {
|
|
54
42
|
name: "ibkr",
|
|
55
|
-
description: "
|
|
56
|
-
help: { usage: ["ibkr accounts
|
|
43
|
+
description: "Inspect IBKR profiles",
|
|
44
|
+
help: { usage: ["ibkr accounts", "ibkr status"] },
|
|
57
45
|
execute: async (args, ctx) => {
|
|
58
46
|
const action = args[0] ?? "status";
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if ((action === "place" || action === "cancel") && (!profile || !account || !ctx.cliOptions.yes)) {
|
|
62
|
-
ctx.fail(`ibkr ${action} requires --profile, --account, and --yes.`);
|
|
47
|
+
if (action !== "accounts" && action !== "status") {
|
|
48
|
+
ctx.fail("Usage: gloomberb ibkr accounts|status");
|
|
63
49
|
}
|
|
64
50
|
await withCliServices(ctx, async (services) => {
|
|
65
|
-
const ibkrProfiles = services.config.brokerInstances.filter((instance) => instance.brokerType === "ibkr");
|
|
66
|
-
if (action === "accounts" || action === "status") {
|
|
67
|
-
ctx.printResult({ data: ibkrProfiles.map((instance) => ({
|
|
68
|
-
id: instance.id,
|
|
69
|
-
label: instance.label,
|
|
70
|
-
enabled: instance.enabled !== false,
|
|
71
|
-
connectionMode: instance.connectionMode ?? "",
|
|
72
|
-
lastSyncedAt: instance.lastSyncedAt ? new Date(instance.lastSyncedAt).toISOString() : "",
|
|
73
|
-
})) });
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
51
|
ctx.printResult({
|
|
77
|
-
data:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
52
|
+
data: services.config.brokerInstances
|
|
53
|
+
.filter((instance) => instance.brokerType === "ibkr")
|
|
54
|
+
.map((instance) => ({
|
|
55
|
+
id: instance.id,
|
|
56
|
+
label: instance.label,
|
|
57
|
+
enabled: instance.enabled !== false,
|
|
58
|
+
connectionMode: instance.connectionMode ?? "",
|
|
59
|
+
lastSyncedAt: instance.lastSyncedAt ? new Date(instance.lastSyncedAt).toISOString() : "",
|
|
60
|
+
})),
|
|
85
61
|
});
|
|
86
62
|
});
|
|
87
63
|
},
|
|
@@ -133,19 +109,12 @@ export function createAiCliCommand(options: CreateAiCliCommandOptions = {}): Cli
|
|
|
133
109
|
usage: [
|
|
134
110
|
"ai providers",
|
|
135
111
|
"ai ask [--provider id] [--model id] <prompt>",
|
|
136
|
-
"ai screen list|show|delete|refresh|export",
|
|
137
112
|
],
|
|
138
113
|
},
|
|
139
114
|
execute: async (args, ctx) => {
|
|
140
115
|
const action = args[0] ?? "providers";
|
|
141
|
-
if (action === "screen") {
|
|
142
|
-
ctx.printResult({
|
|
143
|
-
data: deferredResult("ai screen", "not_implemented", "AI screener tabs are still pane-state backed; expose them through a shared non-UI screener store before headless mutation."),
|
|
144
|
-
});
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
116
|
if (action !== "providers" && action !== "ask") {
|
|
148
|
-
ctx.fail("Usage: gloomberb ai providers|ask
|
|
117
|
+
ctx.fail("Usage: gloomberb ai providers|ask");
|
|
149
118
|
}
|
|
150
119
|
|
|
151
120
|
const rawArgs = args.slice(1);
|
|
@@ -258,23 +227,3 @@ export const rssCliCommand: CliCommandDef = {
|
|
|
258
227
|
})) });
|
|
259
228
|
},
|
|
260
229
|
};
|
|
261
|
-
|
|
262
|
-
function cloudCommand(name: string, description: string): CliCommandDef {
|
|
263
|
-
return {
|
|
264
|
-
name,
|
|
265
|
-
description,
|
|
266
|
-
execute: (_args, ctx) => {
|
|
267
|
-
ctx.printResult({
|
|
268
|
-
data: deferredResult(name, "auth_required", "This command needs an existing cloud/session token. New auth/account/chat CLI workflows are deferred in this pass."),
|
|
269
|
-
});
|
|
270
|
-
},
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
export const cloudCliCommands: CliCommandDef[] = [
|
|
275
|
-
cloudCommand("buildout", "Fetch Buildout company data through an existing cloud session"),
|
|
276
|
-
cloudCommand("congress", "Fetch congressional trade data through an existing cloud session"),
|
|
277
|
-
cloudCommand("substack", "Fetch Substack reader data through an existing session"),
|
|
278
|
-
cloudCommand("x-feed", "Fetch X/Twitter feed data through an existing cloud session"),
|
|
279
|
-
cloudCommand("tweets", "Alias for x-feed"),
|
|
280
|
-
];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CliCommandDef } from "../../types/plugin";
|
|
2
|
-
import type
|
|
2
|
+
import { TIME_RANGES, type TimeRange } from "../../time-series/range";
|
|
3
3
|
import type { EarningsEvent, QuoteBatchResult, SecFilingItem } from "../../types/data-provider";
|
|
4
4
|
import type { NewsArticle, NewsFeed, NewsQuery } from "../../news/types";
|
|
5
5
|
import type {
|
|
@@ -15,7 +15,7 @@ import { formatCompact } from "../../utils/format";
|
|
|
15
15
|
import { withCliServices, withMarketData } from "../context";
|
|
16
16
|
import { isoDate, parsePositiveInt, requireArg, takeOption } from "./command-utils";
|
|
17
17
|
|
|
18
|
-
const VALID_RANGES = new Set<TimeRange>(
|
|
18
|
+
const VALID_RANGES = new Set<TimeRange>(TIME_RANGES);
|
|
19
19
|
const VALID_NEWS_FEEDS = new Set<NewsFeed>(["latest", "top", "breaking", "ticker", "sector", "topic"]);
|
|
20
20
|
|
|
21
21
|
type QuoteCliRecord = Omit<QuoteBatchResult, "error"> & { error: string | null };
|
package/src/cli/index.ts
CHANGED
|
@@ -23,7 +23,6 @@ import { createSystemCliCommands } from "./commands/system";
|
|
|
23
23
|
import {
|
|
24
24
|
aiCliCommand,
|
|
25
25
|
brokerCliCommand,
|
|
26
|
-
cloudCliCommands,
|
|
27
26
|
ibkrCliCommand,
|
|
28
27
|
rssCliCommand,
|
|
29
28
|
} from "./commands/automation";
|
|
@@ -198,7 +197,6 @@ function createCoreCliCommands(
|
|
|
198
197
|
ibkrCliCommand,
|
|
199
198
|
aiCliCommand,
|
|
200
199
|
rssCliCommand,
|
|
201
|
-
...cloudCliCommands,
|
|
202
200
|
];
|
|
203
201
|
return commands;
|
|
204
202
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CHART_RESOLUTIONS } from "../../time-series/range";
|
|
1
2
|
import type { PaneDef, PaneTemplateDef } from "../../types/plugin";
|
|
2
3
|
|
|
3
4
|
export type PaneFunctionReadiness = "ready" | "partial" | "unsupported";
|
|
@@ -128,8 +129,7 @@ const CAPABILITIES: Record<string, PaneFunctionCapability> = {
|
|
|
128
129
|
description: "Market-price sampling resolution.",
|
|
129
130
|
type: "enum",
|
|
130
131
|
aliases: ["resolution"],
|
|
131
|
-
values:
|
|
132
|
-
.map((value) => ({ value })),
|
|
132
|
+
values: CHART_RESOLUTIONS.map((value) => ({ value })),
|
|
133
133
|
},
|
|
134
134
|
],
|
|
135
135
|
},
|
|
@@ -251,7 +251,7 @@ const CAPABILITIES: Record<string, PaneFunctionCapability> = {
|
|
|
251
251
|
description: "Price sampling resolution.",
|
|
252
252
|
type: "enum",
|
|
253
253
|
aliases: ["resolution"],
|
|
254
|
-
values:
|
|
254
|
+
values: CHART_RESOLUTIONS.filter((value) => value !== "auto").map((value) => ({ value })),
|
|
255
255
|
defaultValue: "1d",
|
|
256
256
|
},
|
|
257
257
|
],
|
package/src/cli/registry.ts
CHANGED
|
@@ -4,7 +4,6 @@ import type {
|
|
|
4
4
|
CliCommandDef,
|
|
5
5
|
CliDispatchResult,
|
|
6
6
|
GloomPlugin,
|
|
7
|
-
PluginCliCommandDescriptor,
|
|
8
7
|
} from "../types/plugin";
|
|
9
8
|
import type { LoadedExternalPlugin } from "../plugins/loader";
|
|
10
9
|
import { getPluginCatalog } from "../plugins/catalog";
|
|
@@ -35,7 +34,6 @@ export interface CliCommandRegistry {
|
|
|
35
34
|
config: AppConfig | null;
|
|
36
35
|
plugins: GloomPlugin[];
|
|
37
36
|
externalPlugins: LoadedExternalPlugin[];
|
|
38
|
-
descriptors: Array<{ pluginId: string; descriptor: PluginCliCommandDescriptor }>;
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
interface BuildCliCommandRegistryOptions {
|
|
@@ -71,19 +69,6 @@ function getCommandUsageLabel(command: CliCommandDef): string {
|
|
|
71
69
|
return command.help?.usage?.[0] ?? command.name;
|
|
72
70
|
}
|
|
73
71
|
|
|
74
|
-
function descriptorToCommand(descriptor: PluginCliCommandDescriptor): CliCommandDef | null {
|
|
75
|
-
if (!descriptor.execute) return null;
|
|
76
|
-
return {
|
|
77
|
-
name: descriptor.name,
|
|
78
|
-
aliases: descriptor.aliases,
|
|
79
|
-
description: descriptor.summary,
|
|
80
|
-
help: descriptor.examples?.length
|
|
81
|
-
? { usage: descriptor.examples }
|
|
82
|
-
: undefined,
|
|
83
|
-
execute: descriptor.execute,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
72
|
function renderHelpSections(registry: CliCommandRegistry): string[] {
|
|
88
73
|
const lines: string[] = [];
|
|
89
74
|
|
|
@@ -172,7 +157,6 @@ export function buildCliCommandRegistry({
|
|
|
172
157
|
}
|
|
173
158
|
|
|
174
159
|
const loadablePlugins: GloomPlugin[] = [];
|
|
175
|
-
const descriptors: Array<{ pluginId: string; descriptor: PluginCliCommandDescriptor }> = [];
|
|
176
160
|
for (const entry of catalog) {
|
|
177
161
|
if (entry.error) {
|
|
178
162
|
registryLog.warn(`Skipping external plugin "${entry.plugin.id}" for CLI registration.`, {
|
|
@@ -185,11 +169,6 @@ export function buildCliCommandRegistry({
|
|
|
185
169
|
for (const command of entry.plugin.cliCommands ?? []) {
|
|
186
170
|
registerCommand(command, entry.plugin.id, "plugin");
|
|
187
171
|
}
|
|
188
|
-
for (const descriptor of entry.plugin.cli?.commands ?? []) {
|
|
189
|
-
descriptors.push({ pluginId: entry.plugin.id, descriptor });
|
|
190
|
-
const command = descriptorToCommand(descriptor);
|
|
191
|
-
if (command) registerCommand(command, entry.plugin.id, "plugin");
|
|
192
|
-
}
|
|
193
172
|
}
|
|
194
173
|
|
|
195
174
|
const commands = allCommands.filter((entry) => (
|
|
@@ -210,7 +189,6 @@ export function buildCliCommandRegistry({
|
|
|
210
189
|
config,
|
|
211
190
|
plugins: loadablePlugins.filter((plugin) => !disabledPlugins.has(plugin.id)),
|
|
212
191
|
externalPlugins,
|
|
213
|
-
descriptors,
|
|
214
192
|
};
|
|
215
193
|
}
|
|
216
194
|
|
package/src/cli/result.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CliGlobalOptions } from "./options";
|
|
2
2
|
import { renderTable, type CliTableColumn } from "../utils/cli-output";
|
|
3
|
+
import { serializeCsv } from "../utils/csv";
|
|
3
4
|
|
|
4
5
|
export interface CliResult<T = unknown> {
|
|
5
6
|
data: T;
|
|
@@ -57,11 +58,6 @@ function inferColumns(rows: Record<string, unknown>[]): CliResultColumn<Record<s
|
|
|
57
58
|
return [...keys].map((key) => ({ key, header: key }));
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
function csvEscape(value: string): string {
|
|
61
|
-
if (!/[",\n\r]/.test(value)) return value;
|
|
62
|
-
return `"${value.replace(/"/g, '""')}"`;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
61
|
function serializeColumns<Row>(columns?: CliResultColumn<Row>[]) {
|
|
66
62
|
return columns?.map((column) => ({
|
|
67
63
|
key: column.key,
|
|
@@ -78,11 +74,12 @@ function renderCsv<Row extends Record<string, unknown>>(
|
|
|
78
74
|
const resolvedColumns = columns && columns.length > 0
|
|
79
75
|
? columns
|
|
80
76
|
: inferColumns(rows as Record<string, unknown>[]) as CliResultColumn<Row>[];
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
.map((
|
|
84
|
-
|
|
85
|
-
|
|
77
|
+
return serializeCsv([
|
|
78
|
+
resolvedColumns.map((column) => column.header),
|
|
79
|
+
...rows.map((row) => resolvedColumns.map((column) => (
|
|
80
|
+
column.value ? column.value(row) : row[column.key]
|
|
81
|
+
))),
|
|
82
|
+
]);
|
|
86
83
|
}
|
|
87
84
|
|
|
88
85
|
function renderTextTable<Row extends Record<string, unknown>>(
|
|
@@ -31,6 +31,9 @@ import {
|
|
|
31
31
|
resolveCompositeMinimumSpanMs,
|
|
32
32
|
zoomCompositeViewport,
|
|
33
33
|
} from "./interactions";
|
|
34
|
+
import { getThemeColors, syncTheme } from "../../../theme/colors";
|
|
35
|
+
import { ThemeProvider } from "../../../theme/theme-context";
|
|
36
|
+
import { DEFAULT_THEME } from "../../../theme/themes";
|
|
34
37
|
|
|
35
38
|
let testSetup: Awaited<ReturnType<typeof testRender>> | undefined;
|
|
36
39
|
let chartShortcut: ((event: KeyEventLike) => void) | null = null;
|
|
@@ -131,12 +134,12 @@ function keyEvent(name: string, shift = false): KeyEventLike {
|
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
afterEach(async () => {
|
|
134
|
-
if (
|
|
135
|
-
await act(async () => testSetup!.renderer.destroy());
|
|
137
|
+
if (testSetup) await act(async () => testSetup!.renderer.destroy());
|
|
136
138
|
testSetup = undefined;
|
|
137
139
|
chartShortcut = null;
|
|
138
140
|
capturedSurfaceProps = null;
|
|
139
141
|
capturedSurfaceNode = null;
|
|
142
|
+
syncTheme(DEFAULT_THEME);
|
|
140
143
|
});
|
|
141
144
|
|
|
142
145
|
function point(date: string, value: number): TimeSeriesPoint {
|
|
@@ -204,6 +207,46 @@ function twoSessionCandles(): ResolvedSeries {
|
|
|
204
207
|
}
|
|
205
208
|
|
|
206
209
|
describe("CompositeChart", () => {
|
|
210
|
+
test("repaints cached chart rasters when the theme changes", async () => {
|
|
211
|
+
let setThemeId: ((themeId: string) => void) | null = null;
|
|
212
|
+
const chartSeries = [series("price", "main", "left", "USD", [100, 101, 102])];
|
|
213
|
+
|
|
214
|
+
function ThemedChart() {
|
|
215
|
+
const [themeId, setTheme] = useState(DEFAULT_THEME);
|
|
216
|
+
setThemeId = setTheme;
|
|
217
|
+
return (
|
|
218
|
+
<ThemeProvider themeId={themeId}>
|
|
219
|
+
<CaptureChartSurfaceProvider>
|
|
220
|
+
<CompositeChart
|
|
221
|
+
width={60}
|
|
222
|
+
height={12}
|
|
223
|
+
series={chartSeries}
|
|
224
|
+
panels={[{ id: "main" }]}
|
|
225
|
+
/>
|
|
226
|
+
</CaptureChartSurfaceProvider>
|
|
227
|
+
</ThemeProvider>
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
testSetup = await testRender(<ThemedChart />, { width: 62, height: 14 });
|
|
232
|
+
await act(async () => {
|
|
233
|
+
await testSetup!.renderOnce();
|
|
234
|
+
await testSetup!.renderOnce();
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const nextThemeId = "github-light";
|
|
238
|
+
await act(async () => {
|
|
239
|
+
setThemeId?.(nextThemeId);
|
|
240
|
+
await testSetup!.renderOnce();
|
|
241
|
+
await testSetup!.renderOnce();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const bitmap = capturedSurfaceProps!.bitmaps[0] as { pixels: Uint8Array };
|
|
245
|
+
const expectedBackground = getThemeColors(nextThemeId).bg;
|
|
246
|
+
const expectedRgb = [1, 3, 5].map((offset) => parseInt(expectedBackground.slice(offset, offset + 2), 16));
|
|
247
|
+
expect(Array.from(bitmap.pixels.slice(0, 3))).toEqual(expectedRgb);
|
|
248
|
+
});
|
|
249
|
+
|
|
207
250
|
test("shows the regular-session move beside the latest intraday value", async () => {
|
|
208
251
|
testSetup = await testRender(
|
|
209
252
|
<CompositeChart
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { useShortcut } from "../../../react/input";
|
|
15
15
|
import { useOptionalPaneInstanceId, usePaneSettingValue } from "../../../state/app/context";
|
|
16
16
|
import { colors as themeColors, hoverBg } from "../../../theme/colors";
|
|
17
|
+
import { useThemeColors } from "../../../theme/theme-context";
|
|
17
18
|
import { formatPercentRaw } from "../../../utils/format";
|
|
18
19
|
import { isPlainKey } from "../../../utils/keyboard";
|
|
19
20
|
import { truncateWithEllipsis } from "../../../utils/text-wrap";
|
|
@@ -62,6 +63,7 @@ import { renderCompositePanelBitmap } from "./rasterizer";
|
|
|
62
63
|
import {
|
|
63
64
|
buildChartToolVectors,
|
|
64
65
|
CHART_DRAWING_COLORS,
|
|
66
|
+
CHART_DRAWINGS_SETTING_KEY,
|
|
65
67
|
countMeasureBars,
|
|
66
68
|
drawChartToolOverlay,
|
|
67
69
|
resolveChartToolKind,
|
|
@@ -70,6 +72,7 @@ import {
|
|
|
70
72
|
hitTestDrawings,
|
|
71
73
|
isDrawingTool,
|
|
72
74
|
nextDrawingColor,
|
|
75
|
+
parseChartDrawings,
|
|
73
76
|
resolveDrawingFromDrag,
|
|
74
77
|
resolveZoomBoxRange,
|
|
75
78
|
shiftDrawing,
|
|
@@ -1371,7 +1374,6 @@ function CompositeLegend({
|
|
|
1371
1374
|
);
|
|
1372
1375
|
}
|
|
1373
1376
|
|
|
1374
|
-
const CHART_DRAWINGS_SETTING_KEY = "chartDrawings";
|
|
1375
1377
|
const NO_DRAWINGS: readonly ChartDrawing[] = [];
|
|
1376
1378
|
/** Coalesces a drag into one write instead of one per pointer move. */
|
|
1377
1379
|
const DRAWING_PERSIST_DELAY_MS = 400;
|
|
@@ -1397,7 +1399,7 @@ function ChartDrawingStore({
|
|
|
1397
1399
|
);
|
|
1398
1400
|
const restoredRef = useRef<readonly ChartDrawing[] | null>(null);
|
|
1399
1401
|
if (restoredRef.current === null) {
|
|
1400
|
-
restoredRef.current =
|
|
1402
|
+
restoredRef.current = parseChartDrawings(stored);
|
|
1401
1403
|
}
|
|
1402
1404
|
|
|
1403
1405
|
useEffect(() => {
|
|
@@ -1445,6 +1447,7 @@ export function CompositeChart({
|
|
|
1445
1447
|
onToggleSeries,
|
|
1446
1448
|
isSeriesToggleable,
|
|
1447
1449
|
}: CompositeChartProps) {
|
|
1450
|
+
const activeThemeColors = useThemeColors();
|
|
1448
1451
|
const { cellWidthPx = 8, pixelRatio = 1 } = useUiCapabilities();
|
|
1449
1452
|
const isDesktopWeb = useUiHost().kind === "desktop-web";
|
|
1450
1453
|
const showTextFallback = useShowChartTextFallback();
|
|
@@ -1692,13 +1695,13 @@ export function CompositeChart({
|
|
|
1692
1695
|
}).join("|");
|
|
1693
1696
|
const plotHeight = Math.max(panelCount, totalHeight - legendRows - timeAxisRows);
|
|
1694
1697
|
const resolvedColors = useMemo<CompositeChartColors>(() => ({
|
|
1695
|
-
background: colors?.background ??
|
|
1696
|
-
grid: colors?.grid ??
|
|
1697
|
-
crosshair: colors?.crosshair ??
|
|
1698
|
-
text: colors?.text ??
|
|
1699
|
-
textDim: colors?.textDim ??
|
|
1700
|
-
negative: colors?.negative ??
|
|
1701
|
-
}), [colors]);
|
|
1698
|
+
background: colors?.background ?? activeThemeColors.bg,
|
|
1699
|
+
grid: colors?.grid ?? activeThemeColors.border,
|
|
1700
|
+
crosshair: colors?.crosshair ?? activeThemeColors.borderFocused,
|
|
1701
|
+
text: colors?.text ?? activeThemeColors.text,
|
|
1702
|
+
textDim: colors?.textDim ?? activeThemeColors.textDim,
|
|
1703
|
+
negative: colors?.negative ?? activeThemeColors.negative,
|
|
1704
|
+
}), [activeThemeColors, colors]);
|
|
1702
1705
|
const projectedScene = useMemo(() => {
|
|
1703
1706
|
// lastTickKey busts this memo when a live tick mutates series identity in place.
|
|
1704
1707
|
void lastTickKey;
|
|
@@ -17,7 +17,6 @@ import type {
|
|
|
17
17
|
import {
|
|
18
18
|
buildCompositeTimeScale,
|
|
19
19
|
projectCompositeTimestamp,
|
|
20
|
-
unprojectCompositeTimestamp,
|
|
21
20
|
} from "./time-scale";
|
|
22
21
|
import type { CompositeTimeScale } from "./types";
|
|
23
22
|
|
|
@@ -523,26 +522,6 @@ export function resolveCompositeCursorDate(scene: CompositeChartScene, localX: n
|
|
|
523
522
|
return scene.dates[nearestIndex] ?? null;
|
|
524
523
|
}
|
|
525
524
|
|
|
526
|
-
export function resolveCompositeTimeAxisDate(
|
|
527
|
-
scene: CompositeChartScene,
|
|
528
|
-
ratio: number,
|
|
529
|
-
): Date {
|
|
530
|
-
const safeRatio = Math.max(0, Math.min(1, ratio));
|
|
531
|
-
if (scene.timeScale.kind === "market" && scene.dates.length > 0) {
|
|
532
|
-
let nearestIndex = 0;
|
|
533
|
-
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
534
|
-
scene.dateRatios.forEach((candidate, index) => {
|
|
535
|
-
const distance = Math.abs(candidate - safeRatio);
|
|
536
|
-
if (distance < nearestDistance) {
|
|
537
|
-
nearestIndex = index;
|
|
538
|
-
nearestDistance = distance;
|
|
539
|
-
}
|
|
540
|
-
});
|
|
541
|
-
return scene.dates[nearestIndex] ?? new Date(scene.startTime);
|
|
542
|
-
}
|
|
543
|
-
return new Date(unprojectCompositeTimestamp(scene.timeScale, safeRatio));
|
|
544
|
-
}
|
|
545
|
-
|
|
546
525
|
export function resolveAdjacentCompositeCursorDate(
|
|
547
526
|
scene: CompositeChartScene,
|
|
548
527
|
current: Date | null,
|
|
@@ -27,6 +27,55 @@ export interface ChartDrawing {
|
|
|
27
27
|
color: string;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export const CHART_DRAWINGS_SETTING_KEY = "chartDrawings";
|
|
31
|
+
const MAX_SHARED_CHART_DRAWINGS = 100;
|
|
32
|
+
const MAX_SHARED_DRAWING_POINTS = 5_000;
|
|
33
|
+
|
|
34
|
+
export function parseChartDrawings(value: unknown): ChartDrawing[] {
|
|
35
|
+
if (!Array.isArray(value) || value.length > MAX_SHARED_CHART_DRAWINGS) return [];
|
|
36
|
+
let pointCount = 0;
|
|
37
|
+
const drawings: ChartDrawing[] = [];
|
|
38
|
+
for (const entry of value) {
|
|
39
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
|
|
40
|
+
const drawing = entry as Record<string, unknown>;
|
|
41
|
+
if (!Object.keys(drawing).every((key) => ["id", "panelId", "points", "color"].includes(key))) return [];
|
|
42
|
+
if (
|
|
43
|
+
typeof drawing.id !== "string"
|
|
44
|
+
|| drawing.id.length === 0
|
|
45
|
+
|| drawing.id.length > 160
|
|
46
|
+
|| typeof drawing.panelId !== "string"
|
|
47
|
+
|| drawing.panelId.length === 0
|
|
48
|
+
|| drawing.panelId.length > 120
|
|
49
|
+
|| typeof drawing.color !== "string"
|
|
50
|
+
|| !/^#[0-9a-f]{6}$/i.test(drawing.color)
|
|
51
|
+
|| !Array.isArray(drawing.points)
|
|
52
|
+
|| drawing.points.length < 2
|
|
53
|
+
) return [];
|
|
54
|
+
pointCount += drawing.points.length;
|
|
55
|
+
if (pointCount > MAX_SHARED_DRAWING_POINTS) return [];
|
|
56
|
+
const points: ChartDrawingPoint[] = [];
|
|
57
|
+
for (const point of drawing.points) {
|
|
58
|
+
if (!point || typeof point !== "object" || Array.isArray(point)) return [];
|
|
59
|
+
const raw = point as Record<string, unknown>;
|
|
60
|
+
if (
|
|
61
|
+
!Object.keys(raw).every((key) => key === "time" || key === "value")
|
|
62
|
+
|| typeof raw.time !== "number"
|
|
63
|
+
|| !Number.isFinite(raw.time)
|
|
64
|
+
|| typeof raw.value !== "number"
|
|
65
|
+
|| !Number.isFinite(raw.value)
|
|
66
|
+
) return [];
|
|
67
|
+
points.push({ time: raw.time, value: raw.value });
|
|
68
|
+
}
|
|
69
|
+
drawings.push({
|
|
70
|
+
id: drawing.id,
|
|
71
|
+
panelId: drawing.panelId,
|
|
72
|
+
points,
|
|
73
|
+
color: drawing.color,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return drawings;
|
|
77
|
+
}
|
|
78
|
+
|
|
30
79
|
export interface ChartDrawingHit {
|
|
31
80
|
drawing: ChartDrawing;
|
|
32
81
|
/** Grabbed endpoint, or null when the whole shape was grabbed. */
|
|
@@ -87,6 +87,10 @@ export function runDirectCommandAction(options: {
|
|
|
87
87
|
closeAll({ revertThemePreview: false });
|
|
88
88
|
pluginRegistry.showPane("help");
|
|
89
89
|
return;
|
|
90
|
+
case "layout-marketplace":
|
|
91
|
+
closeAll({ revertThemePreview: false });
|
|
92
|
+
pluginRegistry.showPane("layout-marketplace");
|
|
93
|
+
return;
|
|
90
94
|
case "pane-settings":
|
|
91
95
|
if (state.focusedPaneId) openPaneSettingsRoute(state.focusedPaneId);
|
|
92
96
|
return;
|
|
@@ -138,8 +138,15 @@ export const commands: Command[] = [
|
|
|
138
138
|
category: "Config",
|
|
139
139
|
},
|
|
140
140
|
{
|
|
141
|
-
id: "layout",
|
|
141
|
+
id: "layout-marketplace",
|
|
142
142
|
prefix: "LAY",
|
|
143
|
+
label: "Layouts",
|
|
144
|
+
description: "Open the layout browser to switch, publish, or add layouts",
|
|
145
|
+
category: "Config",
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: "layout",
|
|
149
|
+
prefix: "LMA",
|
|
143
150
|
label: "Layout Actions",
|
|
144
151
|
description: "Organize panes and saved layouts",
|
|
145
152
|
hasArg: true,
|
|
@@ -79,7 +79,7 @@ export function buildCurrentLayoutItems({
|
|
|
79
79
|
},
|
|
80
80
|
{
|
|
81
81
|
id: "layout-gridlock",
|
|
82
|
-
label: "
|
|
82
|
+
label: "Tidy Windows",
|
|
83
83
|
detail: currentLayout.floating.length > 0
|
|
84
84
|
? "Infer a tiled layout from the current window positions"
|
|
85
85
|
: "Retile all panes from their current arrangement",
|
|
@@ -112,6 +112,10 @@ export function useCommandBarPaneTemplateActions({
|
|
|
112
112
|
const trimmedArg = rawArg?.trim() || "";
|
|
113
113
|
const argKind = template.shortcut?.argKind ?? template.shortcut?.argPlaceholder;
|
|
114
114
|
if (argKind === "ticker") {
|
|
115
|
+
if (!trimmedArg && template.shortcut?.argOptional) {
|
|
116
|
+
await openPaneTemplateDirect(template);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
115
119
|
const resolvedTicker = await resolveTickerInput(
|
|
116
120
|
trimmedArg || undefined,
|
|
117
121
|
activeTickerSymbol,
|
|
@@ -34,6 +34,9 @@ export function buildRootShortcutFeedback({
|
|
|
34
34
|
if (rootShortcutIntent.source === "pane-template") {
|
|
35
35
|
const argKind = getPaneTemplateArgKind(rootShortcutIntent.template);
|
|
36
36
|
if (argKind === "ticker") {
|
|
37
|
+
if (rootShortcutIntent.template.shortcut?.argOptional && !rootShortcutIntent.argText) {
|
|
38
|
+
return `Shortcut: ${rootShortcutIntent.label}`;
|
|
39
|
+
}
|
|
37
40
|
const symbol = normalizeTickerInput(activeTickerSymbol, rootShortcutIntent.argText);
|
|
38
41
|
if (symbol) {
|
|
39
42
|
return rootShortcutIntent.kind === "inferred-complete"
|
|
@@ -46,6 +46,13 @@ const paneTemplates: PaneTemplateDef[] = [
|
|
|
46
46
|
description: "Search provider instruments",
|
|
47
47
|
shortcut: { prefix: "SRCH", argPlaceholder: "query", argKind: "text" },
|
|
48
48
|
},
|
|
49
|
+
{
|
|
50
|
+
id: "cds-pane",
|
|
51
|
+
paneId: "cds",
|
|
52
|
+
label: "Single-Name CDS",
|
|
53
|
+
description: "CDS shortcut",
|
|
54
|
+
shortcut: { prefix: "CDS", argPlaceholder: "ticker", argKind: "ticker", argOptional: true },
|
|
55
|
+
},
|
|
49
56
|
];
|
|
50
57
|
|
|
51
58
|
const pluginCommands: CommandDef[] = [{
|
|
@@ -135,6 +142,18 @@ describe("ticker data root shortcuts", () => {
|
|
|
135
142
|
}
|
|
136
143
|
});
|
|
137
144
|
|
|
145
|
+
test("optional ticker shortcuts do not infer the active ticker", () => {
|
|
146
|
+
const bare = parse("CDS", "MSFT");
|
|
147
|
+
expect(bare.kind).toBe("partial");
|
|
148
|
+
if (bare.kind === "none") throw new Error("Expected shortcut intent");
|
|
149
|
+
expect(bare.completionQuery).toBeNull();
|
|
150
|
+
|
|
151
|
+
const scoped = parse("CDS ORCL", "MSFT");
|
|
152
|
+
expect(scoped.kind).toBe("complete");
|
|
153
|
+
if (scoped.kind === "none") throw new Error("Expected shortcut intent");
|
|
154
|
+
expect(scoped.argText).toBe("ORCL");
|
|
155
|
+
});
|
|
156
|
+
|
|
138
157
|
test("EM accepts optional text tickers without active ticker inference", () => {
|
|
139
158
|
const bare = parse("EM", "AAPL");
|
|
140
159
|
expect(bare.kind).toBe("partial");
|
|
@@ -145,7 +145,9 @@ export function parseRootShortcutIntent({
|
|
|
145
145
|
if (!match) return { kind: "none" };
|
|
146
146
|
|
|
147
147
|
const argText = trimmed.slice(match.prefix.length).trim();
|
|
148
|
-
const inferredArg =
|
|
148
|
+
const inferredArg = match.source === "pane-template" && match.template?.shortcut?.argOptional
|
|
149
|
+
? null
|
|
150
|
+
: inferShortcutArg(match.argKind, activeTicker);
|
|
149
151
|
const completionQuery = inferredArg ? `${match.prefix} ${inferredArg}` : null;
|
|
150
152
|
|
|
151
153
|
let kind: Exclude<ShortcutIntentKind, "none">;
|