gloomberb 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -19
- package/bin/gloomberb +3 -0
- package/package.json +5 -4
- package/patches/@opentui%2Fcore@0.1.90.patch +356 -0
- package/src/app.tsx +1330 -0
- package/src/brokers/sync-broker-instance.test.ts +100 -0
- package/src/brokers/sync-broker-instance.ts +341 -0
- package/src/cli/commands/plugins.ts +173 -0
- package/src/cli/commands/portfolio.ts +210 -0
- package/src/cli/commands/search.ts +120 -0
- package/src/cli/commands/ticker.ts +375 -0
- package/src/cli/commands/watchlist.ts +210 -0
- package/src/cli/context.ts +62 -0
- package/src/cli/errors.ts +13 -0
- package/src/cli/helpers.ts +85 -0
- package/src/cli/index.ts +143 -0
- package/src/cli/ticker-resolution.ts +39 -0
- package/src/cli/types.ts +15 -0
- package/src/cli.test.ts +361 -0
- package/src/components/chart/chart-data.ts +239 -0
- package/src/components/chart/chart-renderer.ts +1199 -0
- package/src/components/chart/chart-types.ts +82 -0
- package/src/components/chart/chart-viewport.ts +21 -0
- package/src/components/chart/chart.test.ts +439 -0
- package/src/components/chart/comparison-chart-data.test.ts +109 -0
- package/src/components/chart/comparison-chart-data.ts +316 -0
- package/src/components/chart/comparison-chart-renderer.test.ts +84 -0
- package/src/components/chart/comparison-chart-renderer.ts +354 -0
- package/src/components/chart/comparison-stock-chart.tsx +1486 -0
- package/src/components/chart/cursor-motion.ts +48 -0
- package/src/components/chart/native/chart-rasterizer.ts +732 -0
- package/src/components/chart/native/kitty-adapter.ts +12 -0
- package/src/components/chart/native/kitty-manager.ts +142 -0
- package/src/components/chart/native/kitty-protocol.ts +110 -0
- package/src/components/chart/native/kitty-support.ts +71 -0
- package/src/components/chart/native/kitty.test.ts +620 -0
- package/src/components/chart/native/renderer-selection.ts +24 -0
- package/src/components/chart/native/surface-manager.ts +225 -0
- package/src/components/chart/native/surface-sync.ts +34 -0
- package/src/components/chart/stock-chart-renderer-switch.test.tsx +165 -0
- package/src/components/chart/stock-chart.test.ts +107 -0
- package/src/components/chart/stock-chart.tsx +1556 -0
- package/src/components/command-bar/__snapshots__/command-bar.test.tsx.snap +137 -0
- package/src/components/command-bar/command-bar.test.tsx +1524 -0
- package/src/components/command-bar/command-bar.tsx +4823 -0
- package/src/components/command-bar/command-registry.ts +261 -0
- package/src/components/command-bar/helpers.test.ts +224 -0
- package/src/components/command-bar/helpers.ts +371 -0
- package/src/components/command-bar/root-shortcuts.ts +158 -0
- package/src/components/command-bar/view-model.test.ts +152 -0
- package/src/components/command-bar/view-model.ts +185 -0
- package/src/components/command-bar/workflow-ops.test.ts +70 -0
- package/src/components/command-bar/workflow-ops.ts +433 -0
- package/src/components/command-bar/workflow-types.ts +127 -0
- package/src/components/detail-feed-view.test.tsx +105 -0
- package/src/components/detail-feed-view.tsx +295 -0
- package/src/components/index.ts +32 -0
- package/src/components/layout/floating-pane.tsx +49 -0
- package/src/components/layout/header.tsx +129 -0
- package/src/components/layout/pane-header.tsx +44 -0
- package/src/components/layout/pane.tsx +49 -0
- package/src/components/layout/pixel-mouse-mode.test.ts +122 -0
- package/src/components/layout/pixel-mouse.test.ts +40 -0
- package/src/components/layout/shell.test.tsx +625 -0
- package/src/components/layout/shell.tsx +1200 -0
- package/src/components/layout/status-bar.test.tsx +173 -0
- package/src/components/layout/status-bar.tsx +138 -0
- package/src/components/markdown-editor.tsx +134 -0
- package/src/components/onboarding/onboarding-wizard.test.tsx +184 -0
- package/src/components/onboarding/onboarding-wizard.tsx +1365 -0
- package/src/components/pane-settings-dialog.tsx +468 -0
- package/src/components/pane-template-wizard.test.tsx +52 -0
- package/src/components/pane-template-wizard.tsx +199 -0
- package/src/components/price-selector-dialog.tsx +195 -0
- package/src/components/spinner.tsx +2 -0
- package/src/components/tab-bar.tsx +17 -0
- package/src/components/ticker-badge-text.test.tsx +160 -0
- package/src/components/ticker-badge-text.tsx +58 -0
- package/src/components/ticker-badge.tsx +88 -0
- package/src/components/ticker-list-table.tsx +188 -0
- package/src/components/toggle-list.tsx +74 -0
- package/src/components/ui/button.tsx +77 -0
- package/src/components/ui/data-table.tsx +210 -0
- package/src/components/ui/external-link.tsx +22 -0
- package/src/components/ui/fields.tsx +187 -0
- package/src/components/ui/frame.tsx +73 -0
- package/src/components/ui/index.ts +53 -0
- package/src/components/ui/list-view.tsx +166 -0
- package/src/components/ui/loading.tsx +70 -0
- package/src/components/ui/status.tsx +83 -0
- package/src/components/ui/tabs.tsx +47 -0
- package/src/components/ui/toggle.tsx +164 -0
- package/src/components/ui/ui.test.tsx +134 -0
- package/src/data/app-persistence.ts +25 -0
- package/src/data/config-store.test.ts +310 -0
- package/src/data/config-store.ts +469 -0
- package/src/data/plugin-state-store.ts +102 -0
- package/src/data/resource-store.ts +323 -0
- package/src/data/session-store.ts +52 -0
- package/src/data/sqlite-cache.test.ts +103 -0
- package/src/data/sqlite-database.ts +62 -0
- package/src/data/sqlite-json.ts +19 -0
- package/src/data/ticker-repository.ts +45 -0
- package/src/data/ticker-store.ts +46 -0
- package/src/index.tsx +67 -0
- package/src/market-data/coordinator.test.ts +369 -0
- package/src/market-data/coordinator.ts +580 -0
- package/src/market-data/hooks.tsx +234 -0
- package/src/market-data/query-store.ts +28 -0
- package/src/market-data/request-types.test.ts +64 -0
- package/src/market-data/request-types.ts +70 -0
- package/src/market-data/result-types.ts +56 -0
- package/src/market-data/selectors.ts +166 -0
- package/src/market-data/trace.ts +7 -0
- package/src/pane-settings.test.ts +71 -0
- package/src/pane-settings.ts +73 -0
- package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +338 -0
- package/src/plugins/builtin/ai/gloomberb-cli.ts +70 -0
- package/src/plugins/builtin/ai/index.test.tsx +139 -0
- package/src/plugins/builtin/ai/index.tsx +86 -0
- package/src/plugins/builtin/ai/providers.ts +99 -0
- package/src/plugins/builtin/ai/runner.ts +82 -0
- package/src/plugins/builtin/ai/screener-contract.test.ts +117 -0
- package/src/plugins/builtin/ai/screener-contract.ts +174 -0
- package/src/plugins/builtin/ai/screener-pane.test.tsx +365 -0
- package/src/plugins/builtin/ai/screener-pane.tsx +1161 -0
- package/src/plugins/builtin/ai/settings.ts +79 -0
- package/src/plugins/builtin/ai/ticker-context.ts +64 -0
- package/src/plugins/builtin/ai/utils.ts +71 -0
- package/src/plugins/builtin/ask-ai.test.tsx +258 -0
- package/src/plugins/builtin/ask-ai.tsx +9 -0
- package/src/plugins/builtin/chat-controller.test.ts +217 -0
- package/src/plugins/builtin/chat-controller.ts +340 -0
- package/src/plugins/builtin/chat.test.tsx +451 -0
- package/src/plugins/builtin/chat.tsx +620 -0
- package/src/plugins/builtin/comparison-chart.test.tsx +277 -0
- package/src/plugins/builtin/comparison-chart.tsx +169 -0
- package/src/plugins/builtin/data-providers.ts +16 -0
- package/src/plugins/builtin/debug.tsx +351 -0
- package/src/plugins/builtin/help.test.tsx +110 -0
- package/src/plugins/builtin/help.tsx +281 -0
- package/src/plugins/builtin/layout-manager.tsx +264 -0
- package/src/plugins/builtin/manual-entry.tsx +25 -0
- package/src/plugins/builtin/news.tsx +143 -0
- package/src/plugins/builtin/notes-files.ts +58 -0
- package/src/plugins/builtin/notes.tsx +396 -0
- package/src/plugins/builtin/options-availability.test.tsx +179 -0
- package/src/plugins/builtin/options-availability.ts +158 -0
- package/src/plugins/builtin/options.tsx +364 -0
- package/src/plugins/builtin/portfolio-list/header.tsx +227 -0
- package/src/plugins/builtin/portfolio-list/index.tsx +99 -0
- package/src/plugins/builtin/portfolio-list/metrics.ts +436 -0
- package/src/plugins/builtin/portfolio-list/pane.tsx +533 -0
- package/src/plugins/builtin/portfolio-list/settings.ts +280 -0
- package/src/plugins/builtin/portfolio-list/summary.tsx +270 -0
- package/src/plugins/builtin/portfolio-list/table.tsx +64 -0
- package/src/plugins/builtin/portfolio-list.test.ts +74 -0
- package/src/plugins/builtin/portfolio-list.test.tsx +1164 -0
- package/src/plugins/builtin/portfolio-metrics.test.ts +153 -0
- package/src/plugins/builtin/quote-monitor.test.tsx +114 -0
- package/src/plugins/builtin/sec.tsx +250 -0
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +43 -0
- package/src/plugins/builtin/ticker-detail/financials-tab.tsx +390 -0
- package/src/plugins/builtin/ticker-detail/index.tsx +80 -0
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +242 -0
- package/src/plugins/builtin/ticker-detail/pane.tsx +183 -0
- package/src/plugins/builtin/ticker-detail/quote-monitor.tsx +83 -0
- package/src/plugins/builtin/ticker-detail/settings.ts +132 -0
- package/src/plugins/builtin/ticker-detail-chart-tab-switch.test.tsx +193 -0
- package/src/plugins/builtin/ticker-detail.test.tsx +578 -0
- package/src/plugins/builtin/yahoo.ts +18 -0
- package/src/plugins/event-bus.ts +37 -0
- package/src/plugins/ibkr/__snapshots__/trade-tab.test.tsx.snap +35 -0
- package/src/plugins/ibkr/account-cache.test.ts +101 -0
- package/src/plugins/ibkr/account-cache.ts +137 -0
- package/src/plugins/ibkr/broker-adapter.ts +168 -0
- package/src/plugins/ibkr/config.test.ts +68 -0
- package/src/plugins/ibkr/config.ts +262 -0
- package/src/plugins/ibkr/dialogs.tsx +83 -0
- package/src/plugins/ibkr/flex.test.ts +107 -0
- package/src/plugins/ibkr/flex.ts +267 -0
- package/src/plugins/ibkr/fundamental-parser.ts +149 -0
- package/src/plugins/ibkr/gateway-helpers.ts +28 -0
- package/src/plugins/ibkr/gateway-selection.ts +51 -0
- package/src/plugins/ibkr/gateway-service.test.ts +450 -0
- package/src/plugins/ibkr/gateway-service.ts +2228 -0
- package/src/plugins/ibkr/index.test.tsx +183 -0
- package/src/plugins/ibkr/index.tsx +153 -0
- package/src/plugins/ibkr/instance-selection.test.ts +121 -0
- package/src/plugins/ibkr/instance-selection.ts +36 -0
- package/src/plugins/ibkr/price-normalization.test.ts +38 -0
- package/src/plugins/ibkr/price-normalization.ts +59 -0
- package/src/plugins/ibkr/trade-badge.tsx +27 -0
- package/src/plugins/ibkr/trade-tab.test.tsx +225 -0
- package/src/plugins/ibkr/trade-tab.tsx +1050 -0
- package/src/plugins/ibkr/trade-utils.test.ts +175 -0
- package/src/plugins/ibkr/trade-utils.ts +149 -0
- package/src/plugins/ibkr/trading-pane.tsx +438 -0
- package/src/plugins/ibkr/trading-state.test.ts +109 -0
- package/src/plugins/ibkr/trading-state.ts +339 -0
- package/src/plugins/loader.ts +71 -0
- package/src/plugins/pane-manager.test.ts +103 -0
- package/src/plugins/pane-manager.ts +974 -0
- package/src/plugins/plugin-persistence.ts +85 -0
- package/src/plugins/plugin-runtime.test.tsx +185 -0
- package/src/plugins/plugin-runtime.tsx +175 -0
- package/src/plugins/prediction-markets/cache.ts +114 -0
- package/src/plugins/prediction-markets/categories.ts +223 -0
- package/src/plugins/prediction-markets/chart.tsx +512 -0
- package/src/plugins/prediction-markets/columns.ts +133 -0
- package/src/plugins/prediction-markets/controller-data.ts +614 -0
- package/src/plugins/prediction-markets/controller.ts +725 -0
- package/src/plugins/prediction-markets/detail/book.tsx +118 -0
- package/src/plugins/prediction-markets/detail/outcomes.tsx +102 -0
- package/src/plugins/prediction-markets/detail/overview.tsx +73 -0
- package/src/plugins/prediction-markets/detail/pane.tsx +308 -0
- package/src/plugins/prediction-markets/detail/rules.tsx +16 -0
- package/src/plugins/prediction-markets/detail/shared.tsx +29 -0
- package/src/plugins/prediction-markets/detail/trades.tsx +30 -0
- package/src/plugins/prediction-markets/detail.test.tsx +47 -0
- package/src/plugins/prediction-markets/index.tsx +102 -0
- package/src/plugins/prediction-markets/keyboard.ts +64 -0
- package/src/plugins/prediction-markets/launch.test.ts +126 -0
- package/src/plugins/prediction-markets/launch.ts +240 -0
- package/src/plugins/prediction-markets/metrics.ts +311 -0
- package/src/plugins/prediction-markets/navigation.ts +90 -0
- package/src/plugins/prediction-markets/outcome-order.ts +19 -0
- package/src/plugins/prediction-markets/pane.test.tsx +425 -0
- package/src/plugins/prediction-markets/pane.tsx +294 -0
- package/src/plugins/prediction-markets/plugin.test.tsx +570 -0
- package/src/plugins/prediction-markets/rows.ts +229 -0
- package/src/plugins/prediction-markets/services/fetch.ts +97 -0
- package/src/plugins/prediction-markets/services/kalshi-adapter.ts +555 -0
- package/src/plugins/prediction-markets/services/polymarket-adapter.ts +660 -0
- package/src/plugins/prediction-markets/services/polymarket-ws.ts +108 -0
- package/src/plugins/prediction-markets/settings.ts +164 -0
- package/src/plugins/prediction-markets/status.ts +24 -0
- package/src/plugins/prediction-markets/table.tsx +84 -0
- package/src/plugins/prediction-markets/test-helpers.tsx +636 -0
- package/src/plugins/prediction-markets/types.ts +186 -0
- package/src/plugins/registry.ts +633 -0
- package/src/sources/exchange-rates.ts +18 -0
- package/src/sources/gloomberb-cloud.test.ts +199 -0
- package/src/sources/gloomberb-cloud.ts +326 -0
- package/src/sources/provider-errors.ts +19 -0
- package/src/sources/provider-router.test.ts +1476 -0
- package/src/sources/provider-router.ts +1405 -0
- package/src/sources/sec-edgar.test.ts +286 -0
- package/src/sources/sec-edgar.ts +526 -0
- package/src/sources/yahoo-finance.test.ts +14 -0
- package/src/sources/yahoo-finance.ts +1017 -0
- package/src/state/app-activity.test.ts +41 -0
- package/src/state/app-activity.ts +106 -0
- package/src/state/app-bootstrap.test.ts +471 -0
- package/src/state/app-bootstrap.ts +308 -0
- package/src/state/app-context.test.ts +406 -0
- package/src/state/app-context.test.tsx +132 -0
- package/src/state/app-context.tsx +969 -0
- package/src/state/selectors.ts +67 -0
- package/src/state/session-persistence.test.ts +128 -0
- package/src/state/session-persistence.ts +161 -0
- package/src/state/ticker-refresh-queue.test.ts +46 -0
- package/src/state/ticker-refresh-queue.ts +40 -0
- package/src/state/use-inline-tickers.ts +174 -0
- package/src/state/use-quote-streaming.test.ts +37 -0
- package/src/state/use-quote-streaming.test.tsx +70 -0
- package/src/state/use-quote-streaming.ts +80 -0
- package/src/state/use-session-snapshot.ts +84 -0
- package/src/test-support/data-provider.ts +22 -0
- package/src/theme/color-utils.ts +67 -0
- package/src/theme/colors.ts +128 -0
- package/src/theme/themes.test.ts +45 -0
- package/src/theme/themes.ts +491 -0
- package/src/types/broker.ts +105 -0
- package/src/types/config.ts +434 -0
- package/src/types/data-provider.ts +81 -0
- package/src/types/financials.ts +142 -0
- package/src/types/instrument.ts +29 -0
- package/src/types/persistence.ts +21 -0
- package/src/types/plugin.ts +354 -0
- package/src/types/ticker.ts +58 -0
- package/src/types/trading.ts +85 -0
- package/src/updater.test.ts +162 -0
- package/src/updater.ts +247 -0
- package/src/utils/api-client.ts +666 -0
- package/src/utils/ascii-chart.ts +172 -0
- package/src/utils/broker-instances.ts +47 -0
- package/src/utils/cli-output.ts +83 -0
- package/src/utils/currency-units.test.ts +49 -0
- package/src/utils/currency-units.ts +82 -0
- package/src/utils/debug-log.ts +133 -0
- package/src/utils/exchange-rate-map.ts +8 -0
- package/src/utils/format.ts +158 -0
- package/src/utils/fuzzy-search.ts +34 -0
- package/src/utils/market-status.ts +95 -0
- package/src/utils/options.ts +86 -0
- package/src/utils/price-history.ts +39 -0
- package/src/utils/quote-time.test.ts +22 -0
- package/src/utils/quote-time.ts +50 -0
- package/src/utils/sec.test.ts +55 -0
- package/src/utils/sec.ts +45 -0
- package/src/utils/ticker-list.test.ts +22 -0
- package/src/utils/ticker-list.ts +35 -0
- package/src/utils/ticker-metadata.ts +51 -0
- package/src/utils/ticker-search.test.ts +227 -0
- package/src/utils/ticker-search.ts +764 -0
- package/src/utils/ticker-tokenizer.test.ts +48 -0
- package/src/utils/ticker-tokenizer.ts +72 -0
- package/src/version.ts +1 -0
- package/install.mjs +0 -68
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { TextAttributes } from "@opentui/core";
|
|
2
|
+
import { useKeyboard } from "@opentui/react";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import type { ReactNode } from "react";
|
|
5
|
+
import type { GloomPlugin, PaneProps } from "../../types/plugin";
|
|
6
|
+
import { colors, hoverBg } from "../../theme/colors";
|
|
7
|
+
import { getSharedRegistry } from "../registry";
|
|
8
|
+
|
|
9
|
+
function ShortcutBadge({ label }: { label: string }) {
|
|
10
|
+
return (
|
|
11
|
+
<box backgroundColor={colors.selected}>
|
|
12
|
+
<text fg={colors.selectedText} attributes={TextAttributes.BOLD}>
|
|
13
|
+
{` ${label} `}
|
|
14
|
+
</text>
|
|
15
|
+
</box>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function ShortcutRow({
|
|
20
|
+
badges,
|
|
21
|
+
description,
|
|
22
|
+
compact,
|
|
23
|
+
}: {
|
|
24
|
+
badges: string[];
|
|
25
|
+
description: string;
|
|
26
|
+
compact: boolean;
|
|
27
|
+
}) {
|
|
28
|
+
return (
|
|
29
|
+
<box flexDirection={compact ? "column" : "row"} gap={1}>
|
|
30
|
+
<box flexDirection="row" gap={1} flexShrink={0}>
|
|
31
|
+
{badges.map((badge) => <ShortcutBadge key={badge} label={badge} />)}
|
|
32
|
+
</box>
|
|
33
|
+
<box flexGrow={1}>
|
|
34
|
+
<text fg={colors.text}>{description}</text>
|
|
35
|
+
</box>
|
|
36
|
+
</box>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function HelpSection({ title, children }: { title: string; children: ReactNode }) {
|
|
41
|
+
return (
|
|
42
|
+
<box flexDirection="column">
|
|
43
|
+
<text fg={colors.textBright} attributes={TextAttributes.BOLD}>{title}</text>
|
|
44
|
+
<box height={1} />
|
|
45
|
+
{children}
|
|
46
|
+
</box>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function ActionButton({
|
|
51
|
+
id,
|
|
52
|
+
label,
|
|
53
|
+
hovered,
|
|
54
|
+
onHover,
|
|
55
|
+
onPress,
|
|
56
|
+
}: {
|
|
57
|
+
id: string;
|
|
58
|
+
label: string;
|
|
59
|
+
hovered: boolean;
|
|
60
|
+
onHover: (id: string | null) => void;
|
|
61
|
+
onPress: () => void;
|
|
62
|
+
}) {
|
|
63
|
+
return (
|
|
64
|
+
<box
|
|
65
|
+
backgroundColor={hovered ? hoverBg() : colors.panel}
|
|
66
|
+
onMouseMove={() => onHover(id)}
|
|
67
|
+
onMouseOut={() => onHover(null)}
|
|
68
|
+
onMouseDown={(event: any) => {
|
|
69
|
+
event.stopPropagation?.();
|
|
70
|
+
event.preventDefault?.();
|
|
71
|
+
onPress();
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
<text fg={hovered ? colors.textBright : colors.text}>{` ${label} `}</text>
|
|
75
|
+
</box>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function resolveWindowTemplates(registry: ReturnType<typeof getSharedRegistry>) {
|
|
80
|
+
if (!registry || !registry.paneTemplates) return [];
|
|
81
|
+
|
|
82
|
+
const disabledPlugins = new Set(registry.getConfigFn?.().disabledPlugins ?? []);
|
|
83
|
+
const allPlugins = registry.allPlugins ?? new Map<string, { name?: string }>();
|
|
84
|
+
|
|
85
|
+
return [...registry.paneTemplates.values()]
|
|
86
|
+
.filter((template) => template.shortcut)
|
|
87
|
+
.filter((template) => {
|
|
88
|
+
const pluginId = registry.getPaneTemplatePluginId?.(template.id);
|
|
89
|
+
return !pluginId || !disabledPlugins.has(pluginId);
|
|
90
|
+
})
|
|
91
|
+
.map((template) => {
|
|
92
|
+
const shortcut = template.shortcut!;
|
|
93
|
+
const pluginId = registry.getPaneTemplatePluginId?.(template.id);
|
|
94
|
+
const pluginName = pluginId ? allPlugins.get(pluginId)?.name : null;
|
|
95
|
+
return {
|
|
96
|
+
id: template.id,
|
|
97
|
+
badges: [
|
|
98
|
+
shortcut.prefix,
|
|
99
|
+
shortcut.argPlaceholder ? `<${shortcut.argPlaceholder}>` : null,
|
|
100
|
+
].filter((value): value is string => !!value),
|
|
101
|
+
description: pluginName ? `${template.label} (${pluginName})` : template.label,
|
|
102
|
+
};
|
|
103
|
+
})
|
|
104
|
+
.sort((left, right) => (
|
|
105
|
+
left.badges.join(" ").localeCompare(right.badges.join(" "))
|
|
106
|
+
|| left.description.localeCompare(right.description)
|
|
107
|
+
));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function HelpPane({ focused, width, height, close }: PaneProps) {
|
|
111
|
+
const registry = getSharedRegistry();
|
|
112
|
+
const compact = width < 78;
|
|
113
|
+
const [hoveredAction, setHoveredAction] = useState<string | null>(null);
|
|
114
|
+
const windowTemplates = resolveWindowTemplates(registry);
|
|
115
|
+
|
|
116
|
+
useKeyboard((event) => {
|
|
117
|
+
if (!focused) return;
|
|
118
|
+
if (event.name === "escape") {
|
|
119
|
+
close?.();
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const openDebugLog = () => {
|
|
124
|
+
close?.();
|
|
125
|
+
registry?.showWidget("debug");
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const openLayoutActions = () => {
|
|
129
|
+
close?.();
|
|
130
|
+
registry?.openCommandBarFn("LAY ");
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const openPluginManager = () => {
|
|
134
|
+
close?.();
|
|
135
|
+
registry?.openCommandBarFn("PL ");
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<box flexDirection="column" width={width} height={height}>
|
|
140
|
+
<scrollbox flexGrow={1} scrollY>
|
|
141
|
+
<box flexDirection="column" padding={1}>
|
|
142
|
+
<box flexDirection="column" gap={1}>
|
|
143
|
+
<text fg={colors.textBright} attributes={TextAttributes.BOLD}>How To Use Gloomberb</text>
|
|
144
|
+
<box flexDirection="column">
|
|
145
|
+
<text fg={colors.textDim}>Gloomberb is command-bar first.</text>
|
|
146
|
+
<text fg={colors.textDim}>Use the keyboard for speed, and the mouse for windows.</text>
|
|
147
|
+
</box>
|
|
148
|
+
</box>
|
|
149
|
+
|
|
150
|
+
<box flexDirection={compact ? "column" : "row"} gap={1}>
|
|
151
|
+
<ActionButton
|
|
152
|
+
id="debug"
|
|
153
|
+
label="Open Debug Log"
|
|
154
|
+
hovered={hoveredAction === "debug"}
|
|
155
|
+
onHover={setHoveredAction}
|
|
156
|
+
onPress={openDebugLog}
|
|
157
|
+
/>
|
|
158
|
+
<ActionButton
|
|
159
|
+
id="layout"
|
|
160
|
+
label="Layout Actions"
|
|
161
|
+
hovered={hoveredAction === "layout"}
|
|
162
|
+
onHover={setHoveredAction}
|
|
163
|
+
onPress={openLayoutActions}
|
|
164
|
+
/>
|
|
165
|
+
<ActionButton
|
|
166
|
+
id="plugins"
|
|
167
|
+
label="Manage Plugins"
|
|
168
|
+
hovered={hoveredAction === "plugins"}
|
|
169
|
+
onHover={setHoveredAction}
|
|
170
|
+
onPress={openPluginManager}
|
|
171
|
+
/>
|
|
172
|
+
</box>
|
|
173
|
+
|
|
174
|
+
<HelpSection title="Command Bar">
|
|
175
|
+
<ShortcutRow
|
|
176
|
+
badges={["Ctrl+P", "`"]}
|
|
177
|
+
description="Open the command bar from anywhere."
|
|
178
|
+
compact={compact}
|
|
179
|
+
/>
|
|
180
|
+
<ShortcutRow
|
|
181
|
+
badges={["help"]}
|
|
182
|
+
description="Open this help window directly."
|
|
183
|
+
compact={compact}
|
|
184
|
+
/>
|
|
185
|
+
<ShortcutRow
|
|
186
|
+
badges={["T <ticker>"]}
|
|
187
|
+
description="Search for a ticker or company and open it."
|
|
188
|
+
compact={compact}
|
|
189
|
+
/>
|
|
190
|
+
<ShortcutRow
|
|
191
|
+
badges={["NP", "PS", "PL"]}
|
|
192
|
+
description="Create panes, edit pane settings, and manage plugins."
|
|
193
|
+
compact={compact}
|
|
194
|
+
/>
|
|
195
|
+
</HelpSection>
|
|
196
|
+
|
|
197
|
+
<HelpSection title="Window Templates">
|
|
198
|
+
{windowTemplates.length > 0 ? windowTemplates.map((template) => (
|
|
199
|
+
<ShortcutRow
|
|
200
|
+
key={template.id}
|
|
201
|
+
badges={template.badges}
|
|
202
|
+
description={template.description}
|
|
203
|
+
compact={compact}
|
|
204
|
+
/>
|
|
205
|
+
)) : (
|
|
206
|
+
<text fg={colors.textDim}>No shortcut window templates are currently registered.</text>
|
|
207
|
+
)}
|
|
208
|
+
</HelpSection>
|
|
209
|
+
|
|
210
|
+
<HelpSection title="Move Around">
|
|
211
|
+
<ShortcutRow
|
|
212
|
+
badges={["Tab", "Shift+Tab"]}
|
|
213
|
+
description="Move focus between panes and floating windows."
|
|
214
|
+
compact={compact}
|
|
215
|
+
/>
|
|
216
|
+
<ShortcutRow
|
|
217
|
+
badges={["Ctrl+W"]}
|
|
218
|
+
description="Close the focused pane, docked or floating."
|
|
219
|
+
compact={compact}
|
|
220
|
+
/>
|
|
221
|
+
<ShortcutRow
|
|
222
|
+
badges={["j", "k"]}
|
|
223
|
+
description="Move through lists in most panes. Arrow keys also work."
|
|
224
|
+
compact={compact}
|
|
225
|
+
/>
|
|
226
|
+
<ShortcutRow
|
|
227
|
+
badges={["r", "Shift+R"]}
|
|
228
|
+
description="Refresh the focused ticker or refresh everything."
|
|
229
|
+
compact={compact}
|
|
230
|
+
/>
|
|
231
|
+
</HelpSection>
|
|
232
|
+
|
|
233
|
+
<HelpSection title="Layout System">
|
|
234
|
+
<ShortcutRow
|
|
235
|
+
badges={["LAY"]}
|
|
236
|
+
description="Open layout actions."
|
|
237
|
+
compact={compact}
|
|
238
|
+
/>
|
|
239
|
+
<text fg={colors.text}>Docked panes stay in the saved layout.</text>
|
|
240
|
+
<box flexDirection="column">
|
|
241
|
+
<text fg={colors.text}>Floating panes can be dragged by the title bar</text>
|
|
242
|
+
<text fg={colors.text}>and resized from the lower-right corner.</text>
|
|
243
|
+
</box>
|
|
244
|
+
<text fg={colors.text}>Run Gridlock All Windows if the layout gets messy.</text>
|
|
245
|
+
</HelpSection>
|
|
246
|
+
|
|
247
|
+
<HelpSection title="If There Is A Bug">
|
|
248
|
+
<text fg={colors.text}>Open Debug Log, then run Export Debug Log from the command bar.</text>
|
|
249
|
+
<box flexDirection="column">
|
|
250
|
+
<text fg={colors.text}>The file lands in ~/Downloads. Include steps, ticker or layout, plugin,</text>
|
|
251
|
+
<text fg={colors.text}>and a screenshot if it is visual.</text>
|
|
252
|
+
</box>
|
|
253
|
+
</HelpSection>
|
|
254
|
+
</box>
|
|
255
|
+
</scrollbox>
|
|
256
|
+
|
|
257
|
+
<box height={1} paddingX={1}>
|
|
258
|
+
<text fg={colors.textMuted}>Esc closes this window. Ctrl+W closes the focused pane.</text>
|
|
259
|
+
</box>
|
|
260
|
+
</box>
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export const helpPlugin: GloomPlugin = {
|
|
265
|
+
id: "help",
|
|
266
|
+
name: "Help",
|
|
267
|
+
version: "1.0.0",
|
|
268
|
+
description: "Shortcut and layout help",
|
|
269
|
+
|
|
270
|
+
panes: [
|
|
271
|
+
{
|
|
272
|
+
id: "help",
|
|
273
|
+
name: "Help",
|
|
274
|
+
icon: "?",
|
|
275
|
+
component: HelpPane,
|
|
276
|
+
defaultPosition: "right",
|
|
277
|
+
defaultMode: "floating",
|
|
278
|
+
defaultFloatingSize: { width: 88, height: 32 },
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
};
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { saveConfig } from "../../data/config-store";
|
|
2
|
+
import { findPaneInstance, type LayoutConfig } from "../../types/config";
|
|
3
|
+
import type { GloomPlugin, GloomPluginContext } from "../../types/plugin";
|
|
4
|
+
import type { AppAction } from "../../state/app-context";
|
|
5
|
+
import { getSharedRegistry } from "../registry";
|
|
6
|
+
import {
|
|
7
|
+
dockPane,
|
|
8
|
+
floatPane,
|
|
9
|
+
getDockedPaneIds,
|
|
10
|
+
gridlockAllPanes,
|
|
11
|
+
isPaneInLayout,
|
|
12
|
+
isPaneDocked,
|
|
13
|
+
removePane,
|
|
14
|
+
swapPanes,
|
|
15
|
+
} from "../pane-manager";
|
|
16
|
+
|
|
17
|
+
let dispatchRef: ((action: AppAction) => void) | null = null;
|
|
18
|
+
let getStateRef: (() => { layout: LayoutConfig; termWidth: number; termHeight: number; focusedPaneId: string | null }) | null = null;
|
|
19
|
+
|
|
20
|
+
export function setLayoutManagerDispatch(
|
|
21
|
+
dispatch: (action: AppAction) => void,
|
|
22
|
+
getState: () => { layout: LayoutConfig; termWidth: number; termHeight: number; focusedPaneId: string | null },
|
|
23
|
+
) {
|
|
24
|
+
dispatchRef = dispatch;
|
|
25
|
+
getStateRef = getState;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function persistLayout(layout: LayoutConfig) {
|
|
29
|
+
if (!dispatchRef) return;
|
|
30
|
+
dispatchRef({ type: "PUSH_LAYOUT_HISTORY" });
|
|
31
|
+
dispatchRef({ type: "UPDATE_LAYOUT", layout });
|
|
32
|
+
const registry = getSharedRegistry();
|
|
33
|
+
if (!registry) return;
|
|
34
|
+
const config = registry.getConfigFn();
|
|
35
|
+
const layouts = config.layouts.map((savedLayout, index) => (
|
|
36
|
+
index === config.activeLayoutIndex ? { ...savedLayout, layout } : savedLayout
|
|
37
|
+
));
|
|
38
|
+
saveConfig({ ...config, layout, layouts }).catch(() => {});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getFocusedPane(layout: LayoutConfig, focusedPaneId: string | null) {
|
|
42
|
+
return focusedPaneId ? findPaneInstance(layout, focusedPaneId) ?? null : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const layoutManagerPlugin: GloomPlugin = {
|
|
46
|
+
id: "layout-manager",
|
|
47
|
+
name: "Layout Manager",
|
|
48
|
+
version: "1.0.0",
|
|
49
|
+
description: "Pane layout management commands",
|
|
50
|
+
|
|
51
|
+
setup(ctx) {
|
|
52
|
+
ctx.registerCommand({
|
|
53
|
+
id: "float-pane",
|
|
54
|
+
label: "Float Pane",
|
|
55
|
+
description: "Detach a docked pane into a floating window",
|
|
56
|
+
keywords: ["float", "detach", "undock", "window", "pane"],
|
|
57
|
+
category: "config",
|
|
58
|
+
execute: async () => {
|
|
59
|
+
if (!getStateRef) return;
|
|
60
|
+
const registry = getSharedRegistry();
|
|
61
|
+
if (!registry) return;
|
|
62
|
+
|
|
63
|
+
const { layout, termWidth, termHeight, focusedPaneId } = getStateRef();
|
|
64
|
+
const focusedPane = getFocusedPane(layout, focusedPaneId);
|
|
65
|
+
if (!focusedPane || !isPaneDocked(layout, focusedPane.instanceId)) {
|
|
66
|
+
ctx.showToast("Focus a docked pane to float it", { type: "info" });
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const def = registry.panes.get(focusedPane.paneId);
|
|
71
|
+
const nextLayout = floatPane(layout, focusedPane.instanceId, termWidth, termHeight, def);
|
|
72
|
+
persistLayout(nextLayout);
|
|
73
|
+
dispatchRef?.({ type: "FOCUS_PANE", paneId: focusedPane.instanceId });
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
ctx.registerCommand({
|
|
78
|
+
id: "dock-pane",
|
|
79
|
+
label: "Dock Pane",
|
|
80
|
+
description: "Dock a floating pane back into the layout",
|
|
81
|
+
keywords: ["dock", "attach", "pin", "pane"],
|
|
82
|
+
category: "config",
|
|
83
|
+
execute: async () => {
|
|
84
|
+
if (!getStateRef) return;
|
|
85
|
+
const registry = getSharedRegistry();
|
|
86
|
+
if (!registry) return;
|
|
87
|
+
|
|
88
|
+
const { layout, focusedPaneId } = getStateRef();
|
|
89
|
+
const focusedPane = getFocusedPane(layout, focusedPaneId);
|
|
90
|
+
if (!focusedPane || !layout.floating.some((entry) => entry.instanceId === focusedPane.instanceId)) {
|
|
91
|
+
ctx.showToast("Focus a floating pane to dock it", { type: "info" });
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const nextLayout = dockPane(layout, focusedPane.instanceId);
|
|
96
|
+
persistLayout(nextLayout);
|
|
97
|
+
dispatchRef?.({ type: "FOCUS_PANE", paneId: focusedPane.instanceId });
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
ctx.registerCommand({
|
|
102
|
+
id: "add-pane",
|
|
103
|
+
label: "New Pane",
|
|
104
|
+
description: "Create a new pane from plugin templates",
|
|
105
|
+
keywords: ["new", "add", "pane", "panel", "show"],
|
|
106
|
+
category: "config",
|
|
107
|
+
hidden: () => true,
|
|
108
|
+
execute: async () => {
|
|
109
|
+
ctx.openCommandBar("NP ");
|
|
110
|
+
ctx.showToast("Choose a pane template to create", { type: "info" });
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
ctx.registerCommand({
|
|
115
|
+
id: "gridlock-all",
|
|
116
|
+
label: "Gridlock All Windows",
|
|
117
|
+
description: "Arrange all visible panes into a tiled grid",
|
|
118
|
+
keywords: ["grid", "gridlock", "tile", "arrange", "windows", "layout"],
|
|
119
|
+
category: "config",
|
|
120
|
+
execute: async () => {
|
|
121
|
+
if (!getStateRef) return;
|
|
122
|
+
const { layout, termWidth, termHeight } = getStateRef();
|
|
123
|
+
persistLayout(gridlockAllPanes(layout, { x: 0, y: 0, width: termWidth, height: termHeight }));
|
|
124
|
+
ctx.showToast("Retiled all panes", { type: "success" });
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
ctx.registerCommand({
|
|
129
|
+
id: "remove-pane",
|
|
130
|
+
label: "Remove Pane",
|
|
131
|
+
description: "Remove a pane from the layout",
|
|
132
|
+
keywords: ["remove", "pane", "close", "hide", "panel"],
|
|
133
|
+
category: "config",
|
|
134
|
+
execute: async () => {
|
|
135
|
+
if (!getStateRef) return;
|
|
136
|
+
const { layout, focusedPaneId } = getStateRef();
|
|
137
|
+
const focusedPane = getFocusedPane(layout, focusedPaneId);
|
|
138
|
+
if (!focusedPane || !isPaneInLayout(layout, focusedPane.instanceId)) {
|
|
139
|
+
ctx.showToast("Focus a pane to remove it", { type: "info" });
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
persistLayout(removePane(layout, focusedPane.instanceId));
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
ctx.registerCommand({
|
|
147
|
+
id: "new-layout",
|
|
148
|
+
label: "New Layout",
|
|
149
|
+
description: "Create a new layout",
|
|
150
|
+
keywords: ["new", "create", "add", "layout", "workspace"],
|
|
151
|
+
category: "config",
|
|
152
|
+
wizard: [{ key: "name", label: "Layout name", placeholder: "e.g. Trading, Research, Overview" }],
|
|
153
|
+
execute: async (values) => {
|
|
154
|
+
const name = values?.name?.trim();
|
|
155
|
+
if (!name) {
|
|
156
|
+
ctx.showToast("Layout name is required", { type: "error" });
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
dispatchRef?.({ type: "NEW_LAYOUT", name });
|
|
160
|
+
ctx.showToast(`Layout "${name}" created`, { type: "success" });
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
ctx.registerCommand({
|
|
165
|
+
id: "delete-layout",
|
|
166
|
+
label: "Delete Layout",
|
|
167
|
+
description: "Delete the current layout preset",
|
|
168
|
+
keywords: ["delete", "remove", "layout", "preset"],
|
|
169
|
+
category: "config",
|
|
170
|
+
confirm: () => {
|
|
171
|
+
const registry = getSharedRegistry();
|
|
172
|
+
if (!registry) return null;
|
|
173
|
+
const config = registry.getConfigFn();
|
|
174
|
+
const layout = config.layouts[config.activeLayoutIndex];
|
|
175
|
+
if (!layout) return null;
|
|
176
|
+
return {
|
|
177
|
+
title: "Delete Layout",
|
|
178
|
+
body: [`Delete layout "${layout.name}"? This cannot be undone.`],
|
|
179
|
+
confirmLabel: "Delete Layout",
|
|
180
|
+
cancelLabel: "Back",
|
|
181
|
+
tone: "danger",
|
|
182
|
+
};
|
|
183
|
+
},
|
|
184
|
+
execute: async () => {
|
|
185
|
+
const registry = getSharedRegistry();
|
|
186
|
+
if (!registry) return;
|
|
187
|
+
const config = registry.getConfigFn();
|
|
188
|
+
if (config.layouts.length <= 1) {
|
|
189
|
+
ctx.showToast("Can't delete the only layout", { type: "error" });
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const index = config.activeLayoutIndex;
|
|
193
|
+
const name = config.layouts[index]!.name;
|
|
194
|
+
dispatchRef?.({ type: "DELETE_LAYOUT", index });
|
|
195
|
+
ctx.showToast(`Layout "${name}" deleted`, { type: "success" });
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
ctx.registerCommand({
|
|
200
|
+
id: "rename-layout",
|
|
201
|
+
label: "Rename Layout",
|
|
202
|
+
description: "Rename the current layout preset",
|
|
203
|
+
keywords: ["rename", "layout", "preset"],
|
|
204
|
+
category: "config",
|
|
205
|
+
wizard: [{ key: "name", label: "New name", placeholder: "Layout name" }],
|
|
206
|
+
execute: async (values) => {
|
|
207
|
+
const name = values?.name?.trim();
|
|
208
|
+
if (!name) {
|
|
209
|
+
ctx.showToast("Name is required", { type: "error" });
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const registry = getSharedRegistry();
|
|
213
|
+
if (!registry) return;
|
|
214
|
+
dispatchRef?.({ type: "RENAME_LAYOUT", index: registry.getConfigFn().activeLayoutIndex, name });
|
|
215
|
+
ctx.showToast(`Layout renamed to "${name}"`, { type: "success" });
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
ctx.registerCommand({
|
|
220
|
+
id: "duplicate-layout",
|
|
221
|
+
label: "Duplicate Layout",
|
|
222
|
+
description: "Create a copy of the current layout",
|
|
223
|
+
keywords: ["duplicate", "copy", "clone", "layout"],
|
|
224
|
+
category: "config",
|
|
225
|
+
execute: async () => {
|
|
226
|
+
const registry = getSharedRegistry();
|
|
227
|
+
if (!registry) return;
|
|
228
|
+
dispatchRef?.({ type: "DUPLICATE_LAYOUT", index: registry.getConfigFn().activeLayoutIndex });
|
|
229
|
+
ctx.showToast("Layout duplicated", { type: "success" });
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
ctx.registerCommand({
|
|
234
|
+
id: "swap-panes",
|
|
235
|
+
label: "Swap Panes",
|
|
236
|
+
description: "Swap two pane positions",
|
|
237
|
+
keywords: ["swap", "switch", "pane", "panel"],
|
|
238
|
+
category: "config",
|
|
239
|
+
execute: async () => {
|
|
240
|
+
if (!getStateRef) return;
|
|
241
|
+
const { layout, focusedPaneId } = getStateRef();
|
|
242
|
+
const focusedPane = getFocusedPane(layout, focusedPaneId);
|
|
243
|
+
const dockedPaneIds = getDockedPaneIds(layout);
|
|
244
|
+
if (dockedPaneIds.length < 2) {
|
|
245
|
+
ctx.showToast("Need at least 2 docked panes to swap", { type: "info" });
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (!focusedPane || !isPaneDocked(layout, focusedPane.instanceId)) {
|
|
249
|
+
ctx.showToast("Focus a docked pane to swap it", { type: "info" });
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const others = dockedPaneIds.filter((instanceId) => instanceId !== focusedPane.instanceId);
|
|
254
|
+
if (others.length === 1) {
|
|
255
|
+
persistLayout(swapPanes(layout, focusedPane.instanceId, others[0]!));
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
ctx.openCommandBar("LAY ");
|
|
260
|
+
ctx.showToast("Choose a swap target from layout mode", { type: "info" });
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
},
|
|
264
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { GloomPlugin } from "../../types/plugin";
|
|
2
|
+
import type { BrokerAdapter, BrokerPosition } from "../../types/broker";
|
|
3
|
+
|
|
4
|
+
const manualBroker: BrokerAdapter = {
|
|
5
|
+
id: "manual",
|
|
6
|
+
name: "Manual Entry",
|
|
7
|
+
|
|
8
|
+
async validate() {
|
|
9
|
+
return true; // Always valid
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
async importPositions(): Promise<BrokerPosition[]> {
|
|
13
|
+
// Manual entry doesn't import - positions are created directly
|
|
14
|
+
return [];
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
configSchema: [],
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const manualEntryPlugin: GloomPlugin = {
|
|
21
|
+
id: "manual-entry",
|
|
22
|
+
name: "Manual Entry",
|
|
23
|
+
version: "1.0.0",
|
|
24
|
+
broker: manualBroker,
|
|
25
|
+
};
|