gloomberb 0.10.4 → 0.10.5
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/LICENSE +1 -1
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +3 -5
- package/src/cli/entry.ts +11 -1
- package/src/cli/native-smoke.ts +5 -0
- package/src/components/command-bar/routes/root/provider-search.ts +7 -3
- package/src/components/command-bar/routes/ticker-search/results.test.ts +34 -0
- package/src/components/command-bar/routes/ticker-search/results.ts +4 -6
- package/src/components/command-bar/routes/ticker-search/route.ts +2 -2
- package/src/components/command-bar/routing/list-state.ts +2 -1
- package/src/components/command-bar/surface/index.test.tsx +65 -0
- package/src/components/command-bar/view-model.test.ts +24 -0
- package/src/components/command-bar/view-model.ts +2 -1
- package/src/plugins/builtin/econ/fred-cache.test.ts +8 -1
- package/src/plugins/builtin/help/index.tsx +1 -1
- package/src/plugins/ibkr/gateway/account-loaders.ts +1 -1
- package/src/renderers/electrobun/bun/application-menu/index.ts +1 -1
- package/src/renderers/opentui/dialog-host.tsx +279 -80
- package/src/renderers/opentui/test-utils.tsx +9 -6
- package/src/renderers/opentui/toast-host.tsx +136 -7
- package/src/renderers/opentui/ui-host.tsx +19 -3
- package/src/tickers/search/index.test.ts +284 -2
- package/src/tickers/search/index.ts +55 -18
- package/src/tickers/search/ranking.ts +143 -18
- package/src/tickers/search/types.ts +1 -0
- package/src/updater/github-releases.test.ts +3 -3
- package/src/updater/github-releases.ts +1 -1
package/LICENSE
CHANGED
|
@@ -13,7 +13,7 @@ The above copyright notice and this permission notice shall be included in all
|
|
|
13
13
|
copies or substantial portions of the Software.
|
|
14
14
|
|
|
15
15
|
When using this software in a product or service, attribution to the Gloomberb
|
|
16
|
-
project (https://github.com/
|
|
16
|
+
project (https://github.com/gloom-sh/gloomberb) is appreciated and encouraged,
|
|
17
17
|
though not legally required beyond preserving the above copyright notice.
|
|
18
18
|
|
|
19
19
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
package/README.md
CHANGED
|
@@ -64,7 +64,7 @@ This installs `gloomberb` to `~/.local/bin` by default. A Linux desktop package
|
|
|
64
64
|
|
|
65
65
|
Install the desktop app:
|
|
66
66
|
|
|
67
|
-
- [Download GloomberbSetup.exe for Windows](https://github.com/
|
|
67
|
+
- [Download GloomberbSetup.exe for Windows](https://github.com/gloom-sh/gloomberb/releases/latest/download/stable-win-x64-GloomberbSetup.exe)
|
|
68
68
|
|
|
69
69
|
The installer supports Windows 11 on x64 and ARM64. On ARM64, the desktop app and its bundled `gloomberb` terminal command use Windows' built-in x64 emulation.
|
|
70
70
|
|
package/README.zh-CN.md
CHANGED
|
@@ -66,7 +66,7 @@ curl -fsSL gloomberb.com/install | bash
|
|
|
66
66
|
|
|
67
67
|
安装桌面应用:
|
|
68
68
|
|
|
69
|
-
- [下载 GloomberbSetup.exe(Windows x64)](https://github.com/
|
|
69
|
+
- [下载 GloomberbSetup.exe(Windows x64)](https://github.com/gloom-sh/gloomberb/releases/latest/download/stable-win-x64-GloomberbSetup.exe)
|
|
70
70
|
|
|
71
71
|
安装程序会添加应用本体和 `gloomberb` 终端命令。如果只需要终端版本,安装 Bun 后使用该包即可:
|
|
72
72
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gloomberb",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.5",
|
|
4
4
|
"packageManager": "bun@1.3.11",
|
|
5
5
|
"description": "Market research and portfolio tracker for the terminal",
|
|
6
6
|
"module": "src/index.tsx",
|
|
@@ -30,12 +30,13 @@
|
|
|
30
30
|
"typecheck:electrobun-view": "tsc --project tsconfig.electrobun-view.json",
|
|
31
31
|
"typecheck:scripts": "tsc --project tsconfig.scripts.json",
|
|
32
32
|
"test": "bun test",
|
|
33
|
+
"test:global-upgrade": "bun run scripts/smoke-global-upgrade.ts",
|
|
33
34
|
"release": "./scripts/release.sh"
|
|
34
35
|
},
|
|
35
36
|
"license": "MIT",
|
|
36
37
|
"repository": {
|
|
37
38
|
"type": "git",
|
|
38
|
-
"url": "https://github.com/
|
|
39
|
+
"url": "https://github.com/gloom-sh/gloomberb"
|
|
39
40
|
},
|
|
40
41
|
"keywords": [
|
|
41
42
|
"terminal",
|
|
@@ -59,15 +60,12 @@
|
|
|
59
60
|
"dependencies": {
|
|
60
61
|
"@earendil-works/pi-agent-core": "0.81.1",
|
|
61
62
|
"@earendil-works/pi-ai": "0.81.1",
|
|
62
|
-
"@opentui-ui/dialog": "^0.1.2",
|
|
63
|
-
"@opentui-ui/toast": "^0.0.5",
|
|
64
63
|
"@opentui/core": "^0.3.2",
|
|
65
64
|
"@opentui/react": "^0.3.2",
|
|
66
65
|
"@stoqey/ib": "^1.5.6",
|
|
67
66
|
"@tanstack/react-virtual": "^3.14.2",
|
|
68
67
|
"hls.js": "1.6.13",
|
|
69
68
|
"jimp": "1.6.1",
|
|
70
|
-
"opentui-spinner": "^0.0.6",
|
|
71
69
|
"react": "19.2.7",
|
|
72
70
|
"react-dom": "19.2.7",
|
|
73
71
|
"rxjs": "^7.8.2",
|
package/src/cli/entry.ts
CHANGED
|
@@ -2,7 +2,12 @@ import { dispatchCli } from "./index";
|
|
|
2
2
|
import { fail, inferCliErrorOptions, printCliError } from "./errors";
|
|
3
3
|
import { loadExternalPlugins } from "../plugins/loader";
|
|
4
4
|
import type { CliLaunchRequest } from "../types/plugin";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
OPEN_TUI_NATIVE_SMOKE_COMMAND,
|
|
7
|
+
OPEN_TUI_RUNTIME_SMOKE_COMMAND,
|
|
8
|
+
smokeOpenTuiNative,
|
|
9
|
+
smokeOpenTuiRuntime,
|
|
10
|
+
} from "./native-smoke";
|
|
6
11
|
|
|
7
12
|
async function launchOpenTuiApp(options: {
|
|
8
13
|
externalPlugins: Awaited<ReturnType<typeof loadExternalPlugins>>;
|
|
@@ -26,6 +31,11 @@ export async function runCliEntrypoint(rawArgs = process.argv.slice(2)): Promise
|
|
|
26
31
|
process.exit(0);
|
|
27
32
|
}
|
|
28
33
|
|
|
34
|
+
if (command === OPEN_TUI_RUNTIME_SMOKE_COMMAND) {
|
|
35
|
+
await smokeOpenTuiRuntime();
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
29
39
|
const externalPlugins = await loadExternalPlugins();
|
|
30
40
|
|
|
31
41
|
if (!command) {
|
package/src/cli/native-smoke.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
export const OPEN_TUI_NATIVE_SMOKE_COMMAND = "__gloomberb-smoke-opentui-native";
|
|
2
|
+
export const OPEN_TUI_RUNTIME_SMOKE_COMMAND = "__gloomberb-smoke-opentui-runtime";
|
|
2
3
|
|
|
3
4
|
export async function smokeOpenTuiNative(): Promise<void> {
|
|
4
5
|
await import("../renderers/opentui/native-smoke");
|
|
5
6
|
}
|
|
7
|
+
|
|
8
|
+
export async function smokeOpenTuiRuntime(): Promise<void> {
|
|
9
|
+
await import("../renderers/opentui/start");
|
|
10
|
+
}
|
|
@@ -98,7 +98,7 @@ export function useRootProviderSearch(options: {
|
|
|
98
98
|
);
|
|
99
99
|
const localItems = localTickerSearchResultItems(searchQuery, { limit: 6 });
|
|
100
100
|
setRootProviderResults(cachedCandidates
|
|
101
|
-
? mergeTickerSearchResultItems(searchQuery,
|
|
101
|
+
? mergeTickerSearchResultItems(searchQuery, buildTickerSearchResultItems(cachedCandidates, searchQuery), localItems)
|
|
102
102
|
: null);
|
|
103
103
|
setRootProviderResultsQuery(cachedCandidates ? searchQuery : null);
|
|
104
104
|
|
|
@@ -125,8 +125,8 @@ export function useRootProviderSearch(options: {
|
|
|
125
125
|
);
|
|
126
126
|
setRootProviderResults(mergeTickerSearchResultItems(
|
|
127
127
|
searchQuery,
|
|
128
|
-
localTickerSearchResultItems(searchQuery, { limit: 6 }),
|
|
129
128
|
buildTickerSearchResultItems(combined, searchQuery),
|
|
129
|
+
localTickerSearchResultItems(searchQuery, { limit: 6 }),
|
|
130
130
|
));
|
|
131
131
|
setRootProviderResultsQuery(searchQuery);
|
|
132
132
|
} catch {
|
|
@@ -178,7 +178,11 @@ export function useRootProviderSearch(options: {
|
|
|
178
178
|
rootResultItems,
|
|
179
179
|
rootTickerSearchArg,
|
|
180
180
|
]);
|
|
181
|
-
const rootSectionOrder: CommandBarSectionOrder = rootPlainTickerSearchArg
|
|
181
|
+
const rootSectionOrder: CommandBarSectionOrder = rootPlainTickerSearchArg
|
|
182
|
+
? "app-first"
|
|
183
|
+
: rootTickerSearchArg
|
|
184
|
+
? "ranked"
|
|
185
|
+
: "default";
|
|
182
186
|
const orderedRootResults = useMemo(
|
|
183
187
|
() => orderListResults(rootResults, { sectionOrder: rootSectionOrder }),
|
|
184
188
|
[rootResults, rootSectionOrder],
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import type { ResultItem } from "../../list/model";
|
|
3
|
+
import { mergeTickerSearchResultItems } from "./results";
|
|
4
|
+
|
|
5
|
+
function resultItem(id: string, label: string, right: string, kind: ResultItem["kind"] = "ticker"): ResultItem {
|
|
6
|
+
return {
|
|
7
|
+
id,
|
|
8
|
+
label,
|
|
9
|
+
detail: "Apple Inc.",
|
|
10
|
+
category: kind === "info" ? "Search" : "Saved",
|
|
11
|
+
kind,
|
|
12
|
+
right,
|
|
13
|
+
action: () => {},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
test("keeps completed ticker-search results authoritative over provisional rows", () => {
|
|
18
|
+
const authoritative = [
|
|
19
|
+
resultItem("ranked:AAPL", "AAPL", "Equity NASDAQ"),
|
|
20
|
+
resultItem("ranked:APC", "APC", "Equity XETRA"),
|
|
21
|
+
];
|
|
22
|
+
const provisional = [
|
|
23
|
+
resultItem("local:APC", "APC", "Equity XETRA"),
|
|
24
|
+
resultItem("local:AAPL", "AAPL", "Equity NASDAQ"),
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
expect(mergeTickerSearchResultItems("Apple", authoritative, provisional).map((item) => item.id)).toEqual([
|
|
28
|
+
"ranked:AAPL",
|
|
29
|
+
"ranked:APC",
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
const noResults = resultItem("no-results", "No matches for Apple", "", "info");
|
|
33
|
+
expect(mergeTickerSearchResultItems("Apple", [noResults], [])).toEqual([noResults]);
|
|
34
|
+
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { TickerRecord } from "../../../../types/ticker";
|
|
2
2
|
import {
|
|
3
3
|
createLocalTickerSearchCandidates,
|
|
4
|
-
rankTickerSearchItems,
|
|
5
4
|
type TickerSearchCandidate,
|
|
6
5
|
} from "../../../../tickers/search";
|
|
7
6
|
import type { ResultItem } from "../../list/model";
|
|
@@ -33,7 +32,7 @@ function isExactTickerResultMatch(item: ResultItem, query: string): boolean {
|
|
|
33
32
|
|
|
34
33
|
export function mergeTickerSearchResultItems(
|
|
35
34
|
query: string,
|
|
36
|
-
|
|
35
|
+
rankedItems: ResultItem[],
|
|
37
36
|
fallbackItems: ResultItem[],
|
|
38
37
|
): ResultItem[] {
|
|
39
38
|
const merged: ResultItem[] = [];
|
|
@@ -45,12 +44,11 @@ export function mergeTickerSearchResultItems(
|
|
|
45
44
|
seen.add(key);
|
|
46
45
|
merged.push(item);
|
|
47
46
|
};
|
|
48
|
-
|
|
47
|
+
rankedItems.forEach(addItem);
|
|
49
48
|
fallbackItems.forEach(addItem);
|
|
50
|
-
if (merged.length === 0) return fallbackItems;
|
|
49
|
+
if (merged.length === 0) return rankedItems.length > 0 ? rankedItems : fallbackItems;
|
|
51
50
|
|
|
52
|
-
return
|
|
53
|
-
.map((item) => isExactTickerResultMatch(item, query) && item.category !== "Saved"
|
|
51
|
+
return merged.map((item) => isExactTickerResultMatch(item, query) && item.category !== "Saved"
|
|
54
52
|
? { ...item, category: "Exact Match" }
|
|
55
53
|
: item);
|
|
56
54
|
}
|
|
@@ -80,7 +80,7 @@ export function useTickerSearchRouteResults(options: {
|
|
|
80
80
|
brokerInstanceId,
|
|
81
81
|
);
|
|
82
82
|
setTickerSearchResults(cachedCandidates
|
|
83
|
-
? mergeTickerSearchResultItems(searchQuery,
|
|
83
|
+
? mergeTickerSearchResultItems(searchQuery, buildTickerSearchResultItems(cachedCandidates, searchQuery), localItems)
|
|
84
84
|
: localItems);
|
|
85
85
|
const requestId = ++searchRequestIdRef.current;
|
|
86
86
|
const searchDelay = skipTickerSearchDebounceRef.current ? 0 : 200;
|
|
@@ -109,8 +109,8 @@ export function useTickerSearchRouteResults(options: {
|
|
|
109
109
|
);
|
|
110
110
|
setTickerSearchResults(mergeTickerSearchResultItems(
|
|
111
111
|
searchQuery,
|
|
112
|
-
localTickerSearchResultItems(searchQuery, { limit: 6 }),
|
|
113
112
|
buildTickerSearchResultItems(combined, searchQuery),
|
|
113
|
+
localTickerSearchResultItems(searchQuery, { limit: 6 }),
|
|
114
114
|
));
|
|
115
115
|
} catch {
|
|
116
116
|
if (requestId !== searchRequestIdRef.current) return;
|
|
@@ -135,12 +135,13 @@ function buildRouteListState(options: BuildRouteListStateOptions): ListScreenSta
|
|
|
135
135
|
query: currentRoute.query,
|
|
136
136
|
selectedIdx: currentRoute.selectedIdx,
|
|
137
137
|
hoveredIdx: currentRoute.hoveredIdx,
|
|
138
|
-
results: orderListResults(results),
|
|
138
|
+
results: orderListResults(results, { sectionOrder: "ranked" }),
|
|
139
139
|
searching: tickerSearchPending,
|
|
140
140
|
emptyLabel: emptyState.label,
|
|
141
141
|
emptyDetail: emptyState.detail,
|
|
142
142
|
footerLeft: getScreenFooterLeft(currentRoute),
|
|
143
143
|
footerRight: getScreenFooterRight(currentRoute),
|
|
144
|
+
sectionOrder: "ranked",
|
|
144
145
|
};
|
|
145
146
|
}
|
|
146
147
|
default:
|
|
@@ -926,6 +926,71 @@ describe("CommandBar", () => {
|
|
|
926
926
|
expect(aaplRow).toBeLessThan(appRow);
|
|
927
927
|
});
|
|
928
928
|
|
|
929
|
+
test("keeps the provider-ranked canonical listing ahead of provisional saved order", async () => {
|
|
930
|
+
testSetup = await testRender(
|
|
931
|
+
<CommandBarHarness
|
|
932
|
+
query=""
|
|
933
|
+
extraTickers={[makeTicker("APC", "Apple Inc.", {
|
|
934
|
+
exchange: "XETRA",
|
|
935
|
+
currency: "EUR",
|
|
936
|
+
assetCategory: "STK",
|
|
937
|
+
})]}
|
|
938
|
+
configureState={(state) => ({
|
|
939
|
+
...state,
|
|
940
|
+
commandBarLaunchRequest: {
|
|
941
|
+
kind: "ticker-search",
|
|
942
|
+
query: "Apple",
|
|
943
|
+
sequence: 1,
|
|
944
|
+
},
|
|
945
|
+
tickers: new Map([
|
|
946
|
+
["APC", state.tickers.get("APC")!],
|
|
947
|
+
["AAPL", state.tickers.get("AAPL")!],
|
|
948
|
+
["MSFT", state.tickers.get("MSFT")!],
|
|
949
|
+
]),
|
|
950
|
+
})}
|
|
951
|
+
dataProvider={makeDataProvider(async () => [
|
|
952
|
+
{
|
|
953
|
+
providerId: "yahoo",
|
|
954
|
+
symbol: "AAPL",
|
|
955
|
+
name: "Apple Inc.",
|
|
956
|
+
exchange: "NASDAQ",
|
|
957
|
+
primaryExchange: "NASDAQ",
|
|
958
|
+
type: "EQUITY",
|
|
959
|
+
currency: "USD",
|
|
960
|
+
},
|
|
961
|
+
{
|
|
962
|
+
providerId: "broker",
|
|
963
|
+
symbol: "APC",
|
|
964
|
+
name: "Apple Inc.",
|
|
965
|
+
exchange: "XETRA",
|
|
966
|
+
primaryExchange: "XETRA",
|
|
967
|
+
type: "EQUITY",
|
|
968
|
+
currency: "EUR",
|
|
969
|
+
},
|
|
970
|
+
{
|
|
971
|
+
providerId: "yahoo",
|
|
972
|
+
symbol: "APLY",
|
|
973
|
+
name: "Apple Yield Shares ETF",
|
|
974
|
+
exchange: "NYSE Arca",
|
|
975
|
+
type: "ETF",
|
|
976
|
+
currency: "USD",
|
|
977
|
+
},
|
|
978
|
+
])}
|
|
979
|
+
/>,
|
|
980
|
+
{ width: 100, height: 24 },
|
|
981
|
+
);
|
|
982
|
+
|
|
983
|
+
await testSetup.renderOnce();
|
|
984
|
+
const frame = await waitForFrameToContain("APLY");
|
|
985
|
+
const rows = frame.split("\n");
|
|
986
|
+
const aaplRow = rows.findIndex((line) => line.trimStart().startsWith("AAPL"));
|
|
987
|
+
const apcRow = rows.findIndex((line) => line.trimStart().startsWith("APC"));
|
|
988
|
+
|
|
989
|
+
expect(aaplRow).toBeGreaterThanOrEqual(0);
|
|
990
|
+
expect(apcRow).toBeGreaterThanOrEqual(0);
|
|
991
|
+
expect(aaplRow).toBeLessThan(apcRow);
|
|
992
|
+
});
|
|
993
|
+
|
|
929
994
|
test("renders form-layout wizard fields together on one screen", async () => {
|
|
930
995
|
testSetup = await testRender(
|
|
931
996
|
<CommandBarHarness
|
|
@@ -67,6 +67,30 @@ describe("command bar view model helpers", () => {
|
|
|
67
67
|
]);
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
+
test("orders ticker sections by their best-ranked candidate", () => {
|
|
71
|
+
const sections = buildSections([
|
|
72
|
+
{ id: "primary", category: "Primary Listing" },
|
|
73
|
+
{ id: "saved-alternate", category: "Saved" },
|
|
74
|
+
{ id: "other", category: "Other Listings" },
|
|
75
|
+
{ id: "saved-fallback", category: "Saved" },
|
|
76
|
+
{ id: "fund", category: "Funds & Derivatives" },
|
|
77
|
+
], { sectionOrder: "ranked" });
|
|
78
|
+
|
|
79
|
+
expect(sections.map((section) => section.category)).toEqual([
|
|
80
|
+
"Primary Listing",
|
|
81
|
+
"Saved",
|
|
82
|
+
"Other Listings",
|
|
83
|
+
"Funds & Derivatives",
|
|
84
|
+
]);
|
|
85
|
+
expect(sections.flatMap((section) => section.items.map((item) => item.id))).toEqual([
|
|
86
|
+
"primary",
|
|
87
|
+
"saved-alternate",
|
|
88
|
+
"saved-fallback",
|
|
89
|
+
"other",
|
|
90
|
+
"fund",
|
|
91
|
+
]);
|
|
92
|
+
});
|
|
93
|
+
|
|
70
94
|
test("derives row presentation for toggles and current rows", () => {
|
|
71
95
|
expect(getRowPresentation({
|
|
72
96
|
id: "plugin:news",
|
|
@@ -17,7 +17,7 @@ export interface CommandBarSection<T> {
|
|
|
17
17
|
items: T[];
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export type CommandBarSectionOrder = "default" | "app-first";
|
|
20
|
+
export type CommandBarSectionOrder = "default" | "app-first" | "ranked";
|
|
21
21
|
|
|
22
22
|
export interface CommandBarItemView {
|
|
23
23
|
id: string;
|
|
@@ -138,6 +138,7 @@ export function truncateText(text: string, width: number): string {
|
|
|
138
138
|
|
|
139
139
|
function getCategoryPriority(category: string, sectionOrder: CommandBarSectionOrder = "default"): number {
|
|
140
140
|
const normalized = category.trim().toLowerCase();
|
|
141
|
+
if (sectionOrder === "ranked") return 0;
|
|
141
142
|
// The AI answers the question the user actually typed, so it leads the list.
|
|
142
143
|
if (normalized === "ask ai") return -100;
|
|
143
144
|
if (normalized === "exact match") return -50;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterEach, describe, expect, test } from "bun:test";
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
import { MemoryPluginPersistence } from "../../../test-support/plugin-persistence";
|
|
3
3
|
import {
|
|
4
4
|
attachFredSeriesPersistence,
|
|
@@ -32,6 +32,13 @@ function makeSeries(value: number): FredSeriesData {
|
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
// Plugin activation in other test files attaches a live persistence to this
|
|
36
|
+
// module and never detaches it, so the cache these tests exercise depends on
|
|
37
|
+
// which file ran first. Establish the state instead of inheriting it.
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
resetFredSeriesPersistence();
|
|
40
|
+
});
|
|
41
|
+
|
|
35
42
|
afterEach(() => {
|
|
36
43
|
resetFredSeriesPersistence();
|
|
37
44
|
});
|
|
@@ -31,7 +31,7 @@ const HELP_TABS = [
|
|
|
31
31
|
] as const;
|
|
32
32
|
|
|
33
33
|
type HelpTabId = typeof HELP_TABS[number]["value"];
|
|
34
|
-
const GLOOMBERB_ISSUES_URL = "https://github.com/
|
|
34
|
+
const GLOOMBERB_ISSUES_URL = "https://github.com/gloom-sh/gloomberb/issues";
|
|
35
35
|
|
|
36
36
|
function HelpPane({ focused, width, height }: PaneProps) {
|
|
37
37
|
const registry = getSharedRegistry();
|
|
@@ -135,7 +135,7 @@ export async function loadIbkrPositions({
|
|
|
135
135
|
ticker: position.contract.localSymbol || position.contract.symbol,
|
|
136
136
|
exchange: position.contract.primaryExch || position.contract.exchange || "",
|
|
137
137
|
shares: Math.abs(position.pos),
|
|
138
|
-
avgCost:
|
|
138
|
+
avgCost: portfolioSnapshot?.avgCost ?? position.avgCost,
|
|
139
139
|
currency: position.contract.currency || "USD",
|
|
140
140
|
accountId,
|
|
141
141
|
name: position.contract.description || position.contract.localSymbol || position.contract.symbol,
|
|
@@ -2,7 +2,7 @@ import type { ApplicationMenuItemConfig } from "electrobun/bun";
|
|
|
2
2
|
import type { DesktopApplicationMenuCommand } from "../../../../types/desktop-menu";
|
|
3
3
|
|
|
4
4
|
export const ELECTROBUN_APPLICATION_MENU_ACTION = "gloom.application-menu.select";
|
|
5
|
-
const GITHUB_ISSUE_URL = "https://github.com/
|
|
5
|
+
const GITHUB_ISSUE_URL = "https://github.com/gloom-sh/gloomberb/issues/new/choose";
|
|
6
6
|
|
|
7
7
|
export type ElectrobunApplicationMenuCommand =
|
|
8
8
|
| DesktopApplicationMenuCommand
|