gloomberb 0.4.1 β 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/app.tsx +168 -65
- package/src/cli/commands/ticker.ts +45 -15
- package/src/cli/helpers.ts +4 -2
- package/src/cli/registry.test.ts +42 -35
- package/src/cli.test.ts +4 -2
- package/src/components/chart/chart-control-hint.tsx +39 -0
- package/src/components/chart/chart-controller.test.ts +110 -0
- package/src/components/chart/chart-controller.ts +364 -2
- package/src/components/chart/chart-data.ts +177 -29
- package/src/components/chart/chart-indicator-selector.tsx +53 -0
- package/src/components/chart/chart-pane-settings.ts +20 -0
- package/src/components/chart/chart-renderer.ts +220 -30
- package/src/components/chart/chart-resolution.test.ts +67 -0
- package/src/components/chart/chart-resolution.ts +74 -4
- package/src/components/chart/chart-scroll.test.ts +69 -0
- package/src/components/chart/chart-scroll.ts +104 -0
- package/src/components/chart/chart-types.ts +12 -0
- package/src/components/chart/chart-viewport.test.ts +5 -1
- package/src/components/chart/chart-viewport.ts +1 -1
- package/src/components/chart/chart.test.ts +249 -10
- package/src/components/chart/comparison-chart-renderer.test.ts +71 -1
- package/src/components/chart/comparison-chart-renderer.ts +40 -3
- package/src/components/chart/comparison-stock-chart.tsx +218 -123
- package/src/components/chart/indicators/bands.test.ts +50 -0
- package/src/components/chart/indicators/bands.ts +29 -0
- package/src/components/chart/indicators/moving-averages.test.ts +54 -0
- package/src/components/chart/indicators/moving-averages.ts +42 -0
- package/src/components/chart/indicators/options.test.ts +32 -0
- package/src/components/chart/indicators/options.ts +107 -0
- package/src/components/chart/indicators/oscillators.test.ts +79 -0
- package/src/components/chart/indicators/oscillators.ts +92 -0
- package/src/components/chart/indicators/types.ts +36 -0
- package/src/components/chart/native/chart-rasterizer.ts +51 -0
- package/src/components/chart/native/kitty.test.ts +68 -0
- package/src/components/chart/stock-chart-renderer-switch.test.tsx +740 -9
- package/src/components/chart/stock-chart.test.ts +239 -0
- package/src/components/chart/stock-chart.tsx +2257 -372
- package/src/components/command-bar/command-bar.test.tsx +337 -5
- package/src/components/command-bar/command-bar.tsx +292 -120
- package/src/components/command-bar/helpers.ts +1 -1
- package/src/components/command-bar/root-shortcuts.ts +33 -4
- package/src/components/command-bar/view-model.test.ts +3 -3
- package/src/components/command-bar/view-model.ts +4 -4
- package/src/components/command-bar/workflow-ops.ts +10 -0
- package/src/components/layout/floating-pane.tsx +3 -2
- package/src/components/layout/header.tsx +2 -1
- package/src/components/layout/pane-sizing.ts +10 -0
- package/src/components/layout/pane.tsx +9 -2
- package/src/components/layout/shell.test.tsx +282 -28
- package/src/components/layout/shell.tsx +10 -7
- package/src/components/layout/status-bar.test.tsx +16 -7
- package/src/components/layout/status-bar.tsx +4 -1
- package/src/components/markdown-editor.tsx +1 -0
- package/src/components/pane-settings-dialog.tsx +13 -152
- package/src/components/pane-template-wizard.tsx +1 -0
- package/src/components/price-selector-dialog.tsx +12 -7
- package/src/components/ticker-badge-text.test.tsx +50 -0
- package/src/components/ticker-badge-text.tsx +17 -2
- package/src/components/ticker-badge.tsx +2 -2
- package/src/components/ticker-list-table.tsx +54 -1
- package/src/components/toggle-list.tsx +15 -2
- package/src/components/ui/data-table.tsx +60 -3
- package/src/components/ui/external-link.tsx +48 -11
- package/src/components/ui/fields.tsx +2 -1
- package/src/components/ui/index.ts +8 -1
- package/src/components/ui/multi-select-chips.tsx +69 -0
- package/src/components/ui/multi-select-dialog.tsx +267 -0
- package/src/components/ui/multi-select.ts +78 -0
- package/src/components/ui/ui.test.tsx +343 -1
- package/src/data/plugin-state-store.ts +30 -19
- package/src/data/resource-store.ts +133 -103
- package/src/data/session-store.ts +19 -12
- package/src/data/sqlite-cache.test.ts +37 -2
- package/src/data/sqlite-database.ts +8 -2
- package/src/data/sqlite-retry.test.ts +63 -0
- package/src/data/sqlite-retry.ts +66 -0
- package/src/data/ticker-store.ts +29 -18
- package/src/index.tsx +6 -1
- package/src/market-data/coordinator.test.ts +114 -5
- package/src/market-data/coordinator.ts +70 -1
- package/src/market-data/hooks.test.tsx +73 -2
- package/src/market-data/hooks.tsx +16 -4
- package/src/notifications/app-notifier.test.ts +177 -0
- package/src/notifications/app-notifier.ts +241 -0
- package/src/pane-settings.test.ts +2 -0
- package/src/plugins/builtin/ai/index.test.tsx +1 -1
- package/src/plugins/builtin/ai/screener-pane.test.tsx +2 -0
- package/src/plugins/builtin/ai/screener-pane.tsx +1 -0
- package/src/plugins/builtin/ai/ticker-context.ts +3 -2
- package/src/plugins/builtin/alerts/alert-engine.test.ts +90 -0
- package/src/plugins/builtin/alerts/alert-engine.ts +55 -0
- package/src/plugins/builtin/alerts/index.test.tsx +357 -0
- package/src/plugins/builtin/alerts/index.tsx +519 -0
- package/src/plugins/builtin/alerts/types.ts +14 -0
- package/src/plugins/builtin/analytics/index.test.tsx +300 -0
- package/src/plugins/builtin/analytics/index.tsx +775 -0
- package/src/plugins/builtin/analytics/metrics.test.ts +125 -0
- package/src/plugins/builtin/analytics/metrics.ts +155 -0
- package/src/plugins/builtin/ask-ai.test.tsx +2 -0
- package/src/plugins/builtin/chat-controller.test.ts +83 -18
- package/src/plugins/builtin/chat-controller.ts +26 -14
- package/src/plugins/builtin/chat.test.tsx +715 -4
- package/src/plugins/builtin/chat.tsx +424 -120
- package/src/plugins/builtin/comparison-chart.test.tsx +97 -57
- package/src/plugins/builtin/comparison-chart.tsx +1 -2
- package/src/plugins/builtin/correlation/compute.test.ts +147 -0
- package/src/plugins/builtin/correlation/compute.ts +115 -0
- package/src/plugins/builtin/correlation/index.tsx +385 -0
- package/src/plugins/builtin/correlation/plugin.test.ts +44 -0
- package/src/plugins/builtin/correlation/settings.test.ts +34 -0
- package/src/plugins/builtin/correlation/settings.ts +95 -0
- package/src/plugins/builtin/debug.tsx +36 -50
- package/src/plugins/builtin/earnings/earnings-cache.test.ts +181 -0
- package/src/plugins/builtin/earnings/earnings-cache.ts +136 -0
- package/src/plugins/builtin/earnings/earnings-data.test.ts +59 -0
- package/src/plugins/builtin/earnings/earnings-data.ts +50 -0
- package/src/plugins/builtin/earnings/index.tsx +337 -0
- package/src/plugins/builtin/econ/calendar-source.test.ts +219 -0
- package/src/plugins/builtin/econ/calendar-source.ts +121 -0
- package/src/plugins/builtin/econ/fred-client.test.ts +63 -0
- package/src/plugins/builtin/econ/fred-client.ts +79 -0
- package/src/plugins/builtin/econ/fred-series-map.ts +88 -0
- package/src/plugins/builtin/econ/index.tsx +999 -0
- package/src/plugins/builtin/econ/types.ts +13 -0
- package/src/plugins/builtin/fred-settings.ts +10 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +178 -0
- package/src/plugins/builtin/fx-matrix/pairs.ts +30 -0
- package/src/plugins/builtin/help.test.tsx +1 -5
- package/src/plugins/builtin/help.tsx +1 -16
- package/src/plugins/builtin/insider/index.tsx +417 -0
- package/src/plugins/builtin/insider/insider-data.test.ts +55 -0
- package/src/plugins/builtin/insider/insider-data.ts +49 -0
- package/src/plugins/builtin/layout-manager.tsx +24 -16
- package/src/plugins/builtin/market-movers/index.tsx +573 -0
- package/src/plugins/builtin/market-movers/screener.test.ts +143 -0
- package/src/plugins/builtin/market-movers/screener.ts +105 -0
- package/src/plugins/builtin/options.tsx +4 -3
- package/src/plugins/builtin/portfolio-list/cli/commands.ts +11 -6
- package/src/plugins/builtin/portfolio-list/command-bar.ts +6 -3
- package/src/plugins/builtin/portfolio-list/header.tsx +3 -2
- package/src/plugins/builtin/portfolio-list/metrics.ts +22 -11
- package/src/plugins/builtin/portfolio-list/pane.tsx +3 -9
- package/src/plugins/builtin/portfolio-list/settings.ts +9 -0
- package/src/plugins/builtin/portfolio-list/table.tsx +3 -0
- package/src/plugins/builtin/portfolio-list.test.tsx +3 -1
- package/src/plugins/builtin/portfolio-metrics.test.ts +13 -1
- package/src/plugins/builtin/sec.tsx +114 -7
- package/src/plugins/builtin/sectors/index.tsx +359 -0
- package/src/plugins/builtin/sectors/sector-data.ts +18 -0
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +75 -13
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +316 -80
- package/src/plugins/builtin/ticker-detail/quote-monitor.tsx +4 -6
- package/src/plugins/builtin/ticker-detail-chart-tab-switch.test.tsx +17 -12
- package/src/plugins/builtin/ticker-detail.test.tsx +3 -3
- package/src/plugins/builtin/world-indices/index.tsx +410 -0
- package/src/plugins/builtin/world-indices/indices.ts +52 -0
- package/src/plugins/builtin/yield-curve/index.tsx +242 -0
- package/src/plugins/builtin/yield-curve/treasury-data.test.ts +212 -0
- package/src/plugins/builtin/yield-curve/treasury-data.ts +107 -0
- package/src/plugins/catalog.ts +22 -0
- package/src/plugins/gridlock-notification.ts +15 -0
- package/src/plugins/ibkr/__snapshots__/trade-tab.test.tsx.snap +5 -5
- package/src/plugins/ibkr/index.test.tsx +1 -1
- package/src/plugins/ibkr/index.tsx +1 -1
- package/src/plugins/ibkr/trade-tab.tsx +23 -5
- package/src/plugins/ibkr/trade-utils.test.ts +1 -1
- package/src/plugins/ibkr/trade-utils.ts +7 -6
- package/src/plugins/ibkr/trading-pane.tsx +8 -7
- package/src/plugins/plugin-runtime.test.tsx +2 -0
- package/src/plugins/plugin-runtime.tsx +10 -0
- package/src/plugins/prediction-markets/controller-data.ts +8 -0
- package/src/plugins/prediction-markets/detail/shared.tsx +7 -6
- package/src/plugins/prediction-markets/plugin.test.tsx +48 -1
- package/src/plugins/prediction-markets/services/fetch.ts +13 -7
- package/src/plugins/prediction-markets/services/polymarket-adapter.ts +29 -11
- package/src/plugins/prediction-markets/services/polymarket-ws.ts +20 -0
- package/src/plugins/prediction-markets/test-helpers.tsx +2 -0
- package/src/plugins/registry.ts +22 -13
- package/src/sources/provider-router.test.ts +3 -2
- package/src/sources/sec-edgar.ts +13 -1
- package/src/sources/yahoo-finance.ts +75 -1
- package/src/state/app-context-selector.test.tsx +17 -1
- package/src/state/app-context.test.tsx +24 -0
- package/src/state/app-context.tsx +29 -6
- package/src/theme/colors.ts +5 -0
- package/src/theme/themes.ts +25 -0
- package/src/types/config.test.ts +107 -0
- package/src/types/config.ts +30 -1
- package/src/types/data-provider.ts +13 -0
- package/src/types/plugin.ts +35 -1
- package/src/utils/api-client.test.ts +90 -0
- package/src/utils/api-client.ts +56 -16
- package/src/utils/format.test.ts +11 -1
- package/src/utils/format.ts +86 -8
- package/src/utils/inline-content-tokenizer.test.ts +22 -0
- package/src/utils/inline-content-tokenizer.ts +76 -0
- package/src/utils/link-tokenizer.test.ts +27 -0
- package/src/utils/link-tokenizer.ts +93 -0
- package/src/utils/market-format.test.ts +109 -0
- package/src/utils/market-format.ts +299 -0
- package/src/utils/market-status.ts +3 -2
- package/src/utils/selection-clipboard.test.ts +43 -0
- package/src/utils/selection-clipboard.ts +88 -0
- package/src/utils/terminal-input-reset.test.ts +50 -0
- package/src/utils/terminal-input-reset.ts +24 -0
- package/src/utils/throttled-fetch.test.ts +145 -0
- package/src/utils/throttled-fetch.ts +212 -0
- package/src/utils/ticker-tokenizer.test.ts +31 -0
- package/src/utils/ticker-tokenizer.ts +7 -42
- package/src/version.ts +1 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type EconImpact = "high" | "medium" | "low";
|
|
2
|
+
|
|
3
|
+
export interface EconEvent {
|
|
4
|
+
id: string;
|
|
5
|
+
date: Date;
|
|
6
|
+
time: string; // "08:30" or "All Day"
|
|
7
|
+
country: string; // "US", "GB", etc.
|
|
8
|
+
event: string;
|
|
9
|
+
actual: string | null;
|
|
10
|
+
forecast: string | null;
|
|
11
|
+
prior: string | null;
|
|
12
|
+
impact: EconImpact;
|
|
13
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AppConfig } from "../../types/config";
|
|
2
|
+
|
|
3
|
+
export const FRED_API_KEY_CONFIG_PLUGIN_ID = "econ-calendar";
|
|
4
|
+
export const FRED_API_KEY_CONFIG_KEY = "fredApiKey";
|
|
5
|
+
export const FRED_API_KEY_COMMAND_LABEL = "Set FRED API Key";
|
|
6
|
+
|
|
7
|
+
export function getSharedFredApiKey(config: Pick<AppConfig, "pluginConfig">): string {
|
|
8
|
+
const value = config.pluginConfig?.[FRED_API_KEY_CONFIG_PLUGIN_ID]?.[FRED_API_KEY_CONFIG_KEY];
|
|
9
|
+
return typeof value === "string" ? value.trim() : "";
|
|
10
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { TextAttributes } from "@opentui/core";
|
|
3
|
+
import { useKeyboard } from "@opentui/react";
|
|
4
|
+
import type { GloomPlugin, PaneProps } from "../../../types/plugin";
|
|
5
|
+
import { colors, blendHex } from "../../../theme/colors";
|
|
6
|
+
import { getSharedDataProvider } from "../../registry";
|
|
7
|
+
import { MAJOR_CURRENCIES, CURRENCY_FLAGS, formatRate, type MajorCurrency } from "./pairs";
|
|
8
|
+
|
|
9
|
+
const REFRESH_INTERVAL_MS = 60_000;
|
|
10
|
+
|
|
11
|
+
function formatAge(ms: number): string {
|
|
12
|
+
const secs = Math.floor(ms / 1000);
|
|
13
|
+
if (secs < 60) return `${secs}s ago`;
|
|
14
|
+
const mins = Math.floor(secs / 60);
|
|
15
|
+
if (mins < 60) return `${mins}m ago`;
|
|
16
|
+
return `${Math.floor(mins / 60)}h ago`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function FxMatrixPane({ focused, width, height }: PaneProps) {
|
|
20
|
+
const [rates, setRates] = useState<Map<MajorCurrency, number>>(new Map());
|
|
21
|
+
const [loading, setLoading] = useState(true);
|
|
22
|
+
const [lastRefreshed, setLastRefreshed] = useState<number | null>(null);
|
|
23
|
+
const [now, setNow] = useState(Date.now());
|
|
24
|
+
const fetchGenRef = useRef(0);
|
|
25
|
+
|
|
26
|
+
const fetchRates = async () => {
|
|
27
|
+
const provider = getSharedDataProvider();
|
|
28
|
+
if (!provider) return;
|
|
29
|
+
|
|
30
|
+
fetchGenRef.current += 1;
|
|
31
|
+
const gen = fetchGenRef.current;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const results = await Promise.allSettled(
|
|
35
|
+
MAJOR_CURRENCIES.map(async (currency) => {
|
|
36
|
+
if (currency === "USD") return { currency, rate: 1 };
|
|
37
|
+
const rate = await provider.getExchangeRate(currency);
|
|
38
|
+
return { currency, rate };
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (fetchGenRef.current !== gen) return;
|
|
43
|
+
|
|
44
|
+
const newRates = new Map<MajorCurrency, number>();
|
|
45
|
+
for (const result of results) {
|
|
46
|
+
if (result.status === "fulfilled") {
|
|
47
|
+
newRates.set(result.value.currency as MajorCurrency, result.value.rate);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
setRates(newRates);
|
|
52
|
+
setLastRefreshed(Date.now());
|
|
53
|
+
} finally {
|
|
54
|
+
if (fetchGenRef.current === gen) setLoading(false);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
fetchRates();
|
|
60
|
+
const interval = setInterval(fetchRates, REFRESH_INTERVAL_MS);
|
|
61
|
+
return () => clearInterval(interval);
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
const interval = setInterval(() => setNow(Date.now()), 5_000);
|
|
66
|
+
return () => clearInterval(interval);
|
|
67
|
+
}, []);
|
|
68
|
+
|
|
69
|
+
useKeyboard((event) => {
|
|
70
|
+
if (!focused) return;
|
|
71
|
+
if (event.name === "r") {
|
|
72
|
+
fetchRates();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const headerBg = blendHex(colors.bg, colors.border, 0.3);
|
|
77
|
+
|
|
78
|
+
function crossRate(row: MajorCurrency, col: MajorCurrency): number | null {
|
|
79
|
+
if (row === col) return 1;
|
|
80
|
+
const rowUsd = rates.get(row);
|
|
81
|
+
const colUsd = rates.get(col);
|
|
82
|
+
if (rowUsd == null || colUsd == null) return null;
|
|
83
|
+
return rowUsd / colUsd;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const ageText = lastRefreshed ? `updated ${formatAge(now - lastRefreshed)}` : loading ? "loadingβ¦" : "";
|
|
87
|
+
|
|
88
|
+
// Row header: just the 3-letter code, no emoji (keeps width predictable)
|
|
89
|
+
// Rates use flexGrow so they fill available space dynamically
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<box flexDirection="column" width={width} height={height}>
|
|
93
|
+
{/* Header */}
|
|
94
|
+
<box flexDirection="row" height={1} paddingX={1}>
|
|
95
|
+
<text fg={colors.textMuted}>{ageText}</text>
|
|
96
|
+
</box>
|
|
97
|
+
|
|
98
|
+
{/* Column header row */}
|
|
99
|
+
<box flexDirection="row" paddingX={1} height={1} backgroundColor={headerBg}>
|
|
100
|
+
<box width={5} flexShrink={0} />
|
|
101
|
+
{MAJOR_CURRENCIES.map((col) => (
|
|
102
|
+
<box key={col} flexGrow={1} justifyContent="flex-end" paddingRight={1}>
|
|
103
|
+
<text fg={colors.textDim} attributes={TextAttributes.BOLD}>{col}</text>
|
|
104
|
+
</box>
|
|
105
|
+
))}
|
|
106
|
+
</box>
|
|
107
|
+
|
|
108
|
+
{/* Matrix rows */}
|
|
109
|
+
<scrollbox flexGrow={1} scrollY focusable={false}>
|
|
110
|
+
<box flexDirection="column">
|
|
111
|
+
{loading && rates.size === 0 ? (
|
|
112
|
+
<box paddingX={1} paddingY={1}>
|
|
113
|
+
<text fg={colors.textMuted}>Fetching ratesβ¦</text>
|
|
114
|
+
</box>
|
|
115
|
+
) : (
|
|
116
|
+
MAJOR_CURRENCIES.map((row) => (
|
|
117
|
+
<box key={row} flexDirection="row" paddingX={1}>
|
|
118
|
+
<box width={5} flexShrink={0}>
|
|
119
|
+
<text fg={colors.textBright} attributes={TextAttributes.BOLD}>{row}</text>
|
|
120
|
+
</box>
|
|
121
|
+
{MAJOR_CURRENCIES.map((col) => {
|
|
122
|
+
const rate = crossRate(row, col);
|
|
123
|
+
const isDiag = row === col;
|
|
124
|
+
return (
|
|
125
|
+
<box key={col} flexGrow={1} justifyContent="flex-end" paddingRight={1}>
|
|
126
|
+
{rate == null ? (
|
|
127
|
+
<text fg={colors.textDim}>β</text>
|
|
128
|
+
) : (
|
|
129
|
+
<text fg={isDiag ? colors.textDim : colors.text}>
|
|
130
|
+
{formatRate(rate, col)}
|
|
131
|
+
</text>
|
|
132
|
+
)}
|
|
133
|
+
</box>
|
|
134
|
+
);
|
|
135
|
+
})}
|
|
136
|
+
</box>
|
|
137
|
+
))
|
|
138
|
+
)}
|
|
139
|
+
</box>
|
|
140
|
+
</scrollbox>
|
|
141
|
+
|
|
142
|
+
<box height={1} paddingX={1}>
|
|
143
|
+
<text fg={colors.textMuted}>[r]efresh</text>
|
|
144
|
+
</box>
|
|
145
|
+
</box>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const fxMatrixPlugin: GloomPlugin = {
|
|
150
|
+
id: "fx-matrix",
|
|
151
|
+
name: "FX Cross Rates",
|
|
152
|
+
version: "1.0.0",
|
|
153
|
+
description: "Currency cross-rate matrix for major FX pairs",
|
|
154
|
+
toggleable: true,
|
|
155
|
+
|
|
156
|
+
panes: [
|
|
157
|
+
{
|
|
158
|
+
id: "fx-matrix",
|
|
159
|
+
name: "FX Cross Rates",
|
|
160
|
+
icon: "F",
|
|
161
|
+
component: FxMatrixPane,
|
|
162
|
+
defaultPosition: "right",
|
|
163
|
+
defaultMode: "floating",
|
|
164
|
+
defaultFloatingSize: { width: 105, height: 14 },
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
|
|
168
|
+
paneTemplates: [
|
|
169
|
+
{
|
|
170
|
+
id: "fx-matrix-pane",
|
|
171
|
+
paneId: "fx-matrix",
|
|
172
|
+
label: "FX Cross Rates",
|
|
173
|
+
description: "Currency cross-rate matrix for major FX pairs.",
|
|
174
|
+
keywords: ["fx", "forex", "currency", "exchange", "rates", "cross", "matrix"],
|
|
175
|
+
shortcut: { prefix: "FXC" },
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const MAJOR_CURRENCIES = ["USD", "EUR", "GBP", "JPY", "CHF", "CAD", "AUD", "NZD"] as const;
|
|
2
|
+
export type MajorCurrency = typeof MAJOR_CURRENCIES[number];
|
|
3
|
+
|
|
4
|
+
export const CURRENCY_LABELS: Record<MajorCurrency, string> = {
|
|
5
|
+
USD: "US Dollar",
|
|
6
|
+
EUR: "Euro",
|
|
7
|
+
GBP: "British Pound",
|
|
8
|
+
JPY: "Japanese Yen",
|
|
9
|
+
CHF: "Swiss Franc",
|
|
10
|
+
CAD: "Canadian Dollar",
|
|
11
|
+
AUD: "Australian Dollar",
|
|
12
|
+
NZD: "New Zealand Dollar",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const CURRENCY_FLAGS: Record<MajorCurrency, string> = {
|
|
16
|
+
USD: "πΊπΈ",
|
|
17
|
+
EUR: "πͺπΊ",
|
|
18
|
+
GBP: "π¬π§",
|
|
19
|
+
JPY: "π―π΅",
|
|
20
|
+
CHF: "π¨π",
|
|
21
|
+
CAD: "π¨π¦",
|
|
22
|
+
AUD: "π¦πΊ",
|
|
23
|
+
NZD: "π³πΏ",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** Format rate with appropriate precision β JPY pairs get 2 decimals, others get 4 */
|
|
27
|
+
export function formatRate(rate: number, toCurrency: MajorCurrency): string {
|
|
28
|
+
const decimals = toCurrency === "JPY" ? 2 : 4;
|
|
29
|
+
return rate.toFixed(decimals);
|
|
30
|
+
}
|
|
@@ -69,7 +69,6 @@ describe("HelpPane", () => {
|
|
|
69
69
|
|
|
70
70
|
test("opens the debug log from the mouse action", async () => {
|
|
71
71
|
const calls: string[] = [];
|
|
72
|
-
let closed = 0;
|
|
73
72
|
|
|
74
73
|
setSharedRegistryForTests({
|
|
75
74
|
showWidget: (paneId: string) => calls.push(`widget:${paneId}`),
|
|
@@ -83,9 +82,7 @@ describe("HelpPane", () => {
|
|
|
83
82
|
focused
|
|
84
83
|
width={88}
|
|
85
84
|
height={36}
|
|
86
|
-
close={() => {
|
|
87
|
-
closed += 1;
|
|
88
|
-
}}
|
|
85
|
+
close={() => {}}
|
|
89
86
|
/>,
|
|
90
87
|
{ width: 88, height: 36 },
|
|
91
88
|
);
|
|
@@ -104,7 +101,6 @@ describe("HelpPane", () => {
|
|
|
104
101
|
await testSetup!.renderOnce();
|
|
105
102
|
});
|
|
106
103
|
|
|
107
|
-
expect(closed).toBe(1);
|
|
108
104
|
expect(calls).toEqual(["widget:debug"]);
|
|
109
105
|
});
|
|
110
106
|
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { TextAttributes } from "@opentui/core";
|
|
2
|
-
import { useKeyboard } from "@opentui/react";
|
|
3
2
|
import { useState } from "react";
|
|
4
3
|
import type { ReactNode } from "react";
|
|
5
4
|
import type { GloomPlugin, PaneProps } from "../../types/plugin";
|
|
@@ -107,31 +106,21 @@ function resolveWindowTemplates(registry: ReturnType<typeof getSharedRegistry>)
|
|
|
107
106
|
));
|
|
108
107
|
}
|
|
109
108
|
|
|
110
|
-
export function HelpPane({
|
|
109
|
+
export function HelpPane({ width, height }: PaneProps) {
|
|
111
110
|
const registry = getSharedRegistry();
|
|
112
111
|
const compact = width < 78;
|
|
113
112
|
const [hoveredAction, setHoveredAction] = useState<string | null>(null);
|
|
114
113
|
const windowTemplates = resolveWindowTemplates(registry);
|
|
115
114
|
|
|
116
|
-
useKeyboard((event) => {
|
|
117
|
-
if (!focused) return;
|
|
118
|
-
if (event.name === "escape") {
|
|
119
|
-
close?.();
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
|
|
123
115
|
const openDebugLog = () => {
|
|
124
|
-
close?.();
|
|
125
116
|
registry?.showWidget("debug");
|
|
126
117
|
};
|
|
127
118
|
|
|
128
119
|
const openLayoutActions = () => {
|
|
129
|
-
close?.();
|
|
130
120
|
registry?.openCommandBarFn("LAY ");
|
|
131
121
|
};
|
|
132
122
|
|
|
133
123
|
const openPluginManager = () => {
|
|
134
|
-
close?.();
|
|
135
124
|
registry?.openCommandBarFn("PL ");
|
|
136
125
|
};
|
|
137
126
|
|
|
@@ -253,10 +242,6 @@ export function HelpPane({ focused, width, height, close }: PaneProps) {
|
|
|
253
242
|
</HelpSection>
|
|
254
243
|
</box>
|
|
255
244
|
</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
245
|
</box>
|
|
261
246
|
);
|
|
262
247
|
}
|