gloomberb 0.9.7 → 0.9.9
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/package.json +1 -1
- package/src/api-client/auth.ts +20 -0
- package/src/api-client/index.test.ts +3 -3
- package/src/api-client/index.ts +48 -6
- package/src/api-client/request.test.ts +63 -0
- package/src/api-client/request.ts +53 -5
- package/src/api-client/types.ts +45 -2
- package/src/app/pane-runtime/layout-placement.ts +0 -1
- package/src/app/runtime/desktop-deeplink.test.ts +87 -0
- package/src/app/runtime/desktop-deeplink.ts +496 -0
- package/src/app.tsx +34 -0
- package/src/brokers/account-cache.ts +2 -10
- package/src/brokers/broker-portfolio-sync.ts +0 -1
- package/src/brokers/broker-ticker-sync.ts +20 -16
- package/src/brokers/remote-broker-adapter.ts +3 -0
- package/src/capabilities/registry.test.ts +25 -0
- package/src/capabilities/registry.ts +22 -5
- package/src/cli/commands/automation.ts +1 -3
- package/src/cli/commands/command-utils.ts +0 -7
- package/src/cli/commands/overview.ts +2 -2
- package/src/cli/commands/plugins.ts +34 -17
- package/src/cli/index.ts +2 -2
- package/src/cli.test.ts +0 -157
- package/src/components/chart/bar-chart-renderer.test.ts +87 -12
- package/src/components/chart/bar-chart-renderer.ts +111 -55
- package/src/components/chart/comparison/renderer.ts +0 -1
- package/src/components/chart/comparison-stock-chart/render-output.ts +3 -8
- package/src/components/chart/comparison-stock-chart/view.tsx +0 -2
- package/src/components/chart/core/index.test.ts +0 -24
- package/src/components/chart/core/renderer.ts +0 -4
- package/src/components/chart/core/resolution.test.ts +0 -17
- package/src/components/chart/core/resolution.ts +0 -26
- package/src/components/chart/core/scroll.test.ts +0 -6
- package/src/components/chart/core/scroll.ts +0 -22
- package/src/components/chart/core/viewport.test.ts +2 -23
- package/src/components/chart/core/viewport.ts +0 -27
- package/src/components/chart/market-session.ts +1 -1
- package/src/components/chart/native/surface/hooks.ts +10 -3
- package/src/components/chart/static/bar-chart-surface.tsx +104 -20
- package/src/components/chart/stock-chart/data-runtime.ts +0 -1
- package/src/components/chart/stock-chart/footer.test.ts +65 -0
- package/src/components/chart/stock-chart/footer.ts +68 -13
- package/src/components/chart/stock-chart/resolved.tsx +18 -4
- package/src/components/chart/stock-chart/viewport/effects.ts +0 -3
- package/src/components/command-bar/helpers.test.ts +1 -80
- package/src/components/command-bar/helpers.ts +1 -36
- package/src/components/command-bar/pane-settings.ts +1 -1
- package/src/components/command-bar/pane-templates/items.ts +11 -3
- package/src/components/command-bar/panel/runtime.ts +0 -2
- package/src/components/command-bar/routes/root/results.ts +1 -1
- package/src/components/command-bar/routes/ticker-search/results.ts +0 -11
- package/src/components/command-bar/surface/index.test.tsx +52 -16
- package/src/components/command-bar/surface/index.tsx +0 -1
- package/src/components/command-bar/workflow/ops.ts +81 -20
- package/src/components/feed-data-table/stack-view.test.tsx +36 -130
- package/src/components/feed-data-table/stack-view.tsx +0 -1
- package/src/components/index.ts +1 -0
- package/src/components/layout/shell/index.test.tsx +2 -113
- package/src/components/layout/shell/index.tsx +0 -1
- package/src/components/layout/shell/menu.ts +0 -1
- package/src/components/layout/shell/terminal-pointer-runtime.ts +0 -1
- package/src/components/onboarding/onboarding-wizard.tsx +3 -3
- package/src/components/onboarding/wizard-keyboard.ts +2 -23
- package/src/components/price-selector-dialog.tsx +3 -3
- package/src/components/ticker/badge/text.tsx +0 -1
- package/src/components/toggle-list.tsx +29 -73
- package/src/components/ui/button.tsx +4 -1
- package/src/components/ui/checkbox.tsx +76 -0
- package/src/components/ui/choice-dialog.tsx +15 -3
- package/src/components/ui/frame.tsx +1 -1
- package/src/components/ui/index.ts +2 -0
- package/src/components/ui/message-composer.tsx +4 -0
- package/src/components/ui/native-select.tsx +4 -2
- package/src/components/ui/page-stack-view.tsx +1 -1
- package/src/components/ui/ui.test.tsx +0 -78
- package/src/core/state/app/layout-reducer.ts +0 -1
- package/src/data/config/layout.ts +3 -3
- package/src/data/resource-store.ts +6 -0
- package/src/data/sqlite/cache.test.ts +5 -0
- package/src/data/ticker-store.ts +0 -7
- package/src/market-data/coordinator/auxiliary.ts +1 -44
- package/src/market-data/coordinator/entries.ts +0 -1
- package/src/market-data/coordinator/events.test.ts +49 -0
- package/src/market-data/coordinator/events.ts +25 -4
- package/src/market-data/coordinator/index.test.ts +223 -10
- package/src/market-data/coordinator/index.ts +41 -21
- package/src/market-data/coordinator/quotes.ts +13 -11
- package/src/market-data/coordinator/subscriptions.test.ts +1 -0
- package/src/market-data/hooks.tsx +17 -13
- package/src/market-data/market/freshness.ts +58 -1
- package/src/market-data/market/status.ts +19 -1
- package/src/market-data/query-store.ts +0 -4
- package/src/market-data/quotes/freshness.test.ts +49 -1
- package/src/market-data/quotes/freshness.ts +8 -0
- package/src/market-data/quotes/resolution.test.ts +185 -0
- package/src/market-data/quotes/resolution.ts +96 -10
- package/src/market-data/request-types.test.ts +61 -1
- package/src/market-data/request-types.ts +42 -8
- package/src/market-data/result-types.ts +0 -11
- package/src/market-data/selectors.ts +1 -5
- package/src/news/aggregator.ts +5 -1
- package/src/pane-settings.test.ts +0 -18
- package/src/plugins/builtin/account-management/footer.ts +2 -5
- package/src/plugins/builtin/account-management/form-components.tsx +175 -59
- package/src/plugins/builtin/account-management/keyboard.ts +32 -2
- package/src/plugins/builtin/account-management/model.test.ts +99 -4
- package/src/plugins/builtin/account-management/model.ts +259 -23
- package/src/plugins/builtin/account-management/pane.tsx +784 -155
- package/src/plugins/builtin/broker-manager/index.test.tsx +0 -32
- package/src/plugins/builtin/buildout/cells.tsx +1 -1
- package/src/plugins/builtin/buildout/detail/satellite-images.tsx +1 -1
- package/src/plugins/builtin/buildout/detail/ui.tsx +0 -1
- package/src/plugins/builtin/buildout/format.ts +2 -11
- package/src/plugins/builtin/chat/chat.test.tsx +127 -1
- package/src/plugins/builtin/chat/content/channel-navigation.ts +1 -21
- package/src/plugins/builtin/chat/content/composer-cursor.ts +29 -0
- package/src/plugins/builtin/chat/content/composer-runtime.ts +22 -24
- package/src/plugins/builtin/chat/content/composer.tsx +84 -1
- package/src/plugins/builtin/chat/content/index.tsx +153 -16
- package/src/plugins/builtin/chat/content/layout-metrics.ts +10 -1
- package/src/plugins/builtin/chat/content/mentions.test.ts +74 -0
- package/src/plugins/builtin/chat/content/mentions.ts +88 -0
- package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +264 -0
- package/src/plugins/builtin/chat/content/shortcuts.ts +47 -1
- package/src/plugins/builtin/chat/content/snapshot.ts +7 -0
- package/src/plugins/builtin/chat/content/transcript.tsx +0 -5
- package/src/plugins/builtin/chat/controller/index.ts +0 -10
- package/src/plugins/builtin/chat/controller.test.ts +5 -5
- package/src/plugins/builtin/chat/message/profile-popover.test.tsx +36 -0
- package/src/plugins/builtin/chat/message/profile-popover.tsx +124 -20
- package/src/plugins/builtin/chat/sidebar.test.tsx +83 -6
- package/src/plugins/builtin/chat/sidebar.tsx +26 -2
- package/src/plugins/builtin/chat/test-harness.tsx +22 -12
- package/src/plugins/builtin/cloud/plugin.tsx +8 -1
- package/src/plugins/builtin/cloud-tweets/pane.tsx +0 -1
- package/src/plugins/builtin/congress-trades/model.ts +2 -7
- package/src/plugins/builtin/congress-trades/pane.tsx +0 -1
- package/src/plugins/builtin/correlation/index.tsx +4 -1
- package/src/plugins/builtin/correlation/relationship/controls.tsx +8 -2
- package/src/plugins/builtin/correlation/relationship/model.ts +22 -2
- package/src/plugins/builtin/correlation/relationship/pane.tsx +9 -3
- package/src/plugins/builtin/earnings/index.tsx +0 -9
- package/src/plugins/builtin/econ/index.tsx +1 -5
- package/src/plugins/builtin/help/index.tsx +2 -2
- package/src/plugins/builtin/holders/pane.tsx +0 -5
- package/src/plugins/builtin/insider/index.tsx +1 -8
- package/src/plugins/builtin/kelly-sizer/index.test.tsx +0 -26
- package/src/plugins/builtin/kelly-sizer/index.tsx +0 -2
- package/src/plugins/builtin/market-movers/index.tsx +0 -2
- package/src/plugins/builtin/news/index.tsx +0 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +0 -11
- package/src/plugins/builtin/options/view.test.tsx +1 -38
- package/src/plugins/builtin/plugin-groups.ts +17 -3
- package/src/plugins/builtin/portfolio-list/column-values.ts +15 -8
- package/src/plugins/builtin/portfolio-list/index.test.ts +52 -25
- package/src/plugins/builtin/portfolio-list/metrics.test.ts +44 -7
- package/src/plugins/builtin/portfolio-list/pane/data.test.ts +54 -0
- package/src/plugins/builtin/portfolio-list/pane/data.ts +50 -3
- package/src/plugins/builtin/portfolio-list/pane/index.test.tsx +314 -16
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +11 -4
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +83 -24
- package/src/plugins/builtin/sec/filing-documents.ts +0 -11
- package/src/plugins/builtin/sec/index.tsx +0 -2
- package/src/plugins/builtin/substack/article-detail.tsx +1 -1
- package/src/plugins/builtin/thirteenf/api.ts +0 -15
- package/src/plugins/builtin/thirteenf/pane.tsx +1 -3
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/content.tsx +42 -6
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/model.ts +415 -64
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/settings.ts +23 -2
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/types.ts +8 -1
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph.tsx +3 -1
- package/src/plugins/builtin/ticker-detail/data-panes.test.ts +95 -1
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +0 -7
- package/src/plugins/builtin/ticker-detail/index.tsx +2 -0
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.test.tsx +5 -34
- package/src/plugins/builtin/world-indices/index.tsx +0 -4
- package/src/plugins/builtin/world-indices/model.ts +0 -7
- package/src/plugins/ibkr/account-cache.ts +1 -1
- package/src/plugins/ibkr/gateway/market-data.ts +12 -5
- package/src/plugins/ibkr/gateway/service/index.test.ts +31 -0
- package/src/plugins/ibkr/portfolio-performance.ts +1 -1
- package/src/plugins/ibkr/trade/utils.ts +1 -7
- package/src/plugins/loader.ts +2 -1
- package/src/plugins/prediction-markets/detail.test.tsx +0 -34
- package/src/plugins/registry/context.ts +15 -0
- package/src/plugins/registry/contributions.ts +22 -11
- package/src/plugins/registry/index.test.ts +44 -0
- package/src/plugins/registry/index.ts +75 -26
- package/src/remote/controller.ts +1 -7
- package/src/remote/json-patch.ts +15 -1
- package/src/remote/server.ts +16 -6
- package/src/renderers/electrobun/bun/application-menu/index.ts +1 -1
- package/src/renderers/electrobun/bun/core-capabilities.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/http-fetch.test.ts +14 -0
- package/src/renderers/electrobun/bun/desktop/http-fetch.ts +8 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -1
- package/src/renderers/electrobun/bun/index.ts +42 -0
- package/src/renderers/electrobun/shared/protocol.ts +5 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +29 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -0
- package/src/renderers/electrobun/view/data-table/dom.ts +0 -1
- package/src/renderers/electrobun/view/desktop/controls.tsx +135 -4
- package/src/renderers/electrobun/view/desktop/list-view.tsx +38 -25
- package/src/renderers/electrobun/view/desktop-deeplink-bridge.ts +10 -0
- package/src/renderers/electrobun/view/host/box.tsx +0 -2
- package/src/renderers/electrobun/view/host/mouse.ts +0 -11
- package/src/renderers/electrobun/view/host/scroll-box.tsx +1 -2
- package/src/renderers/electrobun/view/host/text.tsx +1 -2
- package/src/renderers/electrobun/view/http-fetch.ts +15 -2
- package/src/renderers/electrobun/view/main.tsx +3 -0
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +9 -18
- package/src/renderers/electrobun/view/remote/capability-invoker.test.ts +50 -0
- package/src/renderers/electrobun/view/remote/capability-invoker.ts +36 -0
- package/src/renderers/electrobun/view/ui-host.tsx +2 -0
- package/src/renderers/opentui/host.tsx +11 -3
- package/src/renderers/opentui/input-host.tsx +5 -1
- package/src/sources/gloomberb-cloud/index.test.ts +0 -72
- package/src/sources/gloomberb-cloud/index.ts +1 -30
- package/src/sources/gloomberb-cloud/news.ts +0 -12
- package/src/sources/provider-router/batches.ts +11 -3
- package/src/sources/provider-router/cached-financials.test.ts +123 -0
- package/src/sources/provider-router/financial-routes.ts +77 -5
- package/src/sources/provider-router/financials.test.ts +49 -0
- package/src/sources/provider-router/financials.ts +40 -3
- package/src/sources/provider-router/index.test.ts +314 -0
- package/src/sources/provider-router/primary.ts +17 -3
- package/src/sources/provider-router/search.ts +5 -0
- package/src/sources/provider-router/streaming.ts +6 -1
- package/src/sources/provider-router/supplemental.ts +4 -3
- package/src/sources/sec-edgar.test.ts +42 -0
- package/src/sources/sec-edgar.ts +1 -2
- package/src/sources/yahoo-finance/history.ts +5 -1
- package/src/sources/yahoo-finance/mappers.test.ts +29 -0
- package/src/sources/yahoo-finance/mappers.ts +2 -1
- package/src/sources/yahoo-finance/options.ts +59 -14
- package/src/sources/yahoo-finance/quote-summary.ts +4 -1
- package/src/sources/yahoo-finance/requests.ts +2 -1
- package/src/sources/yahoo-finance/snapshots.ts +13 -2
- package/src/sources/yahoo-finance.test.ts +40 -21
- package/src/sources/yahoo-finance.ts +19 -10
- package/src/state/app/context/index.test.tsx +0 -81
- package/src/state/app/context/selector.test.tsx +6 -2
- package/src/state/hooks/quote-streaming.test.ts +14 -1
- package/src/state/hooks/quote-streaming.ts +5 -4
- package/src/sync/controller.test.ts +31 -0
- package/src/sync/controller.ts +300 -0
- package/src/sync/core-contributors.test.ts +251 -0
- package/src/sync/core-contributors.ts +400 -0
- package/src/sync/profile-analytics.ts +39 -0
- package/src/sync/react.ts +53 -0
- package/src/sync/types.ts +77 -0
- package/src/test-support/data-provider.ts +0 -1
- package/src/types/desktop-deeplink.ts +7 -0
- package/src/types/plugin.ts +3 -0
- package/src/ui/host.tsx +15 -1
- package/src/ui/index.tsx +2 -0
- package/src/updater.test.ts +0 -21
- package/src/updater.ts +20 -16
- package/src/utils/async-deadline.ts +26 -0
- package/src/utils/exchanges.test.ts +11 -1
- package/src/utils/exchanges.ts +5 -1
- package/src/utils/selection-clipboard.ts +2 -2
- package/src/version.ts +1 -1
- package/src/components/layout/pane/header.test.tsx +0 -39
- package/src/plugins/builtin/chat/channels.test.ts +0 -28
- package/src/plugins/builtin/chat/index.tsx +0 -4
- package/src/plugins/builtin/cloud-tweets/index.ts +0 -1
- package/src/plugins/builtin/earnings/index.test.ts +0 -12
- package/src/plugins/builtin/help/index.test.tsx +0 -242
- package/src/plugins/builtin/substack/article-detail.test.tsx +0 -10
- package/src/renderers/electrobun/bun/application-menu/index.test.ts +0 -21
- package/src/renderers/electrobun/bun/window/focus.test.ts +0 -35
- package/src/renderers/electrobun/view/host/mouse.test.ts +0 -37
- package/src/renderers/electrobun/view/host/text.test.ts +0 -22
- package/src/renderers/opentui/dialog-host.test.tsx +0 -44
- /package/src/{plugins/ibkr → utils}/hash.ts +0 -0
package/src/updater.ts
CHANGED
|
@@ -91,10 +91,6 @@ function normalizePath(value: string): string {
|
|
|
91
91
|
return value.replace(/\\/g, "/").toLowerCase();
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
function tryRealpath(value: string): string {
|
|
95
|
-
return value;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
94
|
function basename(value: string): string {
|
|
99
95
|
const normalized = value.replace(/\\/g, "/");
|
|
100
96
|
return normalized.slice(normalized.lastIndexOf("/") + 1);
|
|
@@ -124,7 +120,7 @@ const runtimeExecutables = new Set([
|
|
|
124
120
|
]);
|
|
125
121
|
|
|
126
122
|
function resolveEntrypointPath(argv = process.argv): string {
|
|
127
|
-
return
|
|
123
|
+
return argv[1] ?? "";
|
|
128
124
|
}
|
|
129
125
|
|
|
130
126
|
function isSourceEntrypoint(entrypoint: string): boolean {
|
|
@@ -138,7 +134,7 @@ function isMacAppBundleExecutable(execPath: string): boolean {
|
|
|
138
134
|
}
|
|
139
135
|
|
|
140
136
|
function isBundledDesktopTuiRuntime(execPath: string, argv: string[]): boolean {
|
|
141
|
-
const normalizedExecPath = normalizePath(
|
|
137
|
+
const normalizedExecPath = normalizePath(execPath);
|
|
142
138
|
const entrypoint = normalizePath(resolveEntrypointPath(argv));
|
|
143
139
|
if (!entrypoint.endsWith("/resources/gloomberb-tui/tui-entry.js")) return false;
|
|
144
140
|
return normalizedExecPath.endsWith("/contents/macos/bun")
|
|
@@ -150,7 +146,7 @@ export function resolveSelfUpdateTargetPath(
|
|
|
150
146
|
execPath = getRuntimeProcess()?.execPath ?? "",
|
|
151
147
|
argv = getRuntimeProcess()?.argv ?? [],
|
|
152
148
|
): string | null {
|
|
153
|
-
const resolvedExecPath =
|
|
149
|
+
const resolvedExecPath = execPath;
|
|
154
150
|
const normalizedExecPath = normalizePath(resolvedExecPath);
|
|
155
151
|
if (isMacAppBundleExecutable(resolvedExecPath)) return null;
|
|
156
152
|
|
|
@@ -171,7 +167,7 @@ export function detectUpdateAction(
|
|
|
171
167
|
if (isMacAppBundleExecutable(execPath)) return null;
|
|
172
168
|
if (isBundledDesktopTuiRuntime(execPath, argv)) return null;
|
|
173
169
|
|
|
174
|
-
const normalizedExecPath = normalizePath(
|
|
170
|
+
const normalizedExecPath = normalizePath(execPath);
|
|
175
171
|
const execBase = basename(normalizedExecPath);
|
|
176
172
|
const entrypoint = normalizePath(resolveEntrypointPath(argv));
|
|
177
173
|
if (execBase === "gloomberb.exe") return null;
|
|
@@ -344,7 +340,6 @@ export async function performUpdate(
|
|
|
344
340
|
const fsModulePath = "fs";
|
|
345
341
|
const zlibModulePath = "zlib";
|
|
346
342
|
const {
|
|
347
|
-
writeFileSync,
|
|
348
343
|
renameSync,
|
|
349
344
|
unlinkSync,
|
|
350
345
|
chmodSync,
|
|
@@ -377,12 +372,16 @@ export async function performUpdate(
|
|
|
377
372
|
}
|
|
378
373
|
}
|
|
379
374
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
375
|
+
const downloaded = new Uint8Array(received);
|
|
376
|
+
let offset = 0;
|
|
377
|
+
for (const chunk of chunks) {
|
|
378
|
+
downloaded.set(chunk, offset);
|
|
379
|
+
offset += chunk.byteLength;
|
|
380
|
+
}
|
|
381
|
+
const nextBinary = release.compressed
|
|
382
|
+
? new Uint8Array(gunzipSync(downloaded))
|
|
383
|
+
: downloaded;
|
|
384
|
+
await Bun.write(updatePath, nextBinary);
|
|
386
385
|
chmodSync(updatePath, 0o755);
|
|
387
386
|
|
|
388
387
|
// Swap binaries
|
|
@@ -391,7 +390,12 @@ export async function performUpdate(
|
|
|
391
390
|
unlinkSync(oldPath);
|
|
392
391
|
} catch {}
|
|
393
392
|
renameSync(execPath, oldPath);
|
|
394
|
-
|
|
393
|
+
try {
|
|
394
|
+
renameSync(updatePath, execPath);
|
|
395
|
+
} catch (error) {
|
|
396
|
+
renameSync(oldPath, execPath);
|
|
397
|
+
throw error;
|
|
398
|
+
}
|
|
395
399
|
try {
|
|
396
400
|
unlinkSync(oldPath);
|
|
397
401
|
} catch {}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export class OperationTimeoutError extends Error {
|
|
2
|
+
constructor(message: string) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "TimeoutError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function withDeadline<T>(
|
|
9
|
+
promise: Promise<T>,
|
|
10
|
+
timeoutMs: number,
|
|
11
|
+
message: string,
|
|
12
|
+
onTimeout?: (error: OperationTimeoutError) => void,
|
|
13
|
+
): Promise<T> {
|
|
14
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
15
|
+
const timeout = new Promise<never>((_resolve, reject) => {
|
|
16
|
+
timer = setTimeout(() => {
|
|
17
|
+
const error = new OperationTimeoutError(message);
|
|
18
|
+
reject(error);
|
|
19
|
+
onTimeout?.(error);
|
|
20
|
+
}, timeoutMs);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return Promise.race([promise, timeout]).finally(() => {
|
|
24
|
+
if (timer) clearTimeout(timer);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { CANONICAL_EXCHANGE_ALIASES, EXCHANGE_TIME_ZONES } from "./exchanges";
|
|
2
|
+
import { canonicalExchange, CANONICAL_EXCHANGE_ALIASES, EXCHANGE_TIME_ZONES, resolveExchangeTimeZone } from "./exchanges";
|
|
3
3
|
|
|
4
4
|
describe("exchange metadata", () => {
|
|
5
5
|
test("has a valid timezone for every canonical exchange", () => {
|
|
@@ -13,4 +13,14 @@ describe("exchange metadata", () => {
|
|
|
13
13
|
})).not.toThrow();
|
|
14
14
|
}
|
|
15
15
|
});
|
|
16
|
+
|
|
17
|
+
test("normalizes broker venue aliases used by portfolio rows", () => {
|
|
18
|
+
expect(canonicalExchange("LSEETF")).toBe("LSE");
|
|
19
|
+
expect(canonicalExchange("TSE")).toBe("TSX");
|
|
20
|
+
expect(canonicalExchange("AEB")).toBe("AMS");
|
|
21
|
+
expect(canonicalExchange("EURONEXT")).toBe("AMS");
|
|
22
|
+
expect(resolveExchangeTimeZone("LSEETF")).toBe("Europe/London");
|
|
23
|
+
expect(resolveExchangeTimeZone("TSE")).toBe("America/Toronto");
|
|
24
|
+
expect(resolveExchangeTimeZone("AEB")).toBe("Europe/Amsterdam");
|
|
25
|
+
});
|
|
16
26
|
});
|
package/src/utils/exchanges.ts
CHANGED
|
@@ -17,7 +17,7 @@ export const CANONICAL_EXCHANGE_ALIASES: Record<string, string> = {
|
|
|
17
17
|
ARCX: "ARCA",
|
|
18
18
|
BATS: "BATS",
|
|
19
19
|
TYO: "JPX",
|
|
20
|
-
TSE: "
|
|
20
|
+
TSE: "TSX",
|
|
21
21
|
TSEJ: "JPX",
|
|
22
22
|
JPX: "JPX",
|
|
23
23
|
HKG: "HKEX",
|
|
@@ -26,6 +26,7 @@ export const CANONICAL_EXCHANGE_ALIASES: Record<string, string> = {
|
|
|
26
26
|
XHKG: "HKEX",
|
|
27
27
|
LON: "LSE",
|
|
28
28
|
LSE: "LSE",
|
|
29
|
+
LSEETF: "LSE",
|
|
29
30
|
XLON: "LSE",
|
|
30
31
|
TOR: "TSX",
|
|
31
32
|
TSX: "TSX",
|
|
@@ -56,8 +57,11 @@ export const CANONICAL_EXCHANGE_ALIASES: Record<string, string> = {
|
|
|
56
57
|
EPA: "EPA",
|
|
57
58
|
XPAR: "EPA",
|
|
58
59
|
PAR: "EPA",
|
|
60
|
+
SBF: "EPA",
|
|
59
61
|
AMS: "AMS",
|
|
60
62
|
XAMS: "AMS",
|
|
63
|
+
AEB: "AMS",
|
|
64
|
+
EURONEXT: "AMS",
|
|
61
65
|
BRU: "BRU",
|
|
62
66
|
XBRU: "BRU",
|
|
63
67
|
LIS: "LIS",
|
|
@@ -65,7 +65,7 @@ export function copyActiveSelection(
|
|
|
65
65
|
export function isCopyShortcut(
|
|
66
66
|
event: Pick<KeyEvent, "name" | "super" | "meta" | "ctrl" | "shift">,
|
|
67
67
|
): boolean {
|
|
68
|
-
const keyName = event.name
|
|
68
|
+
const keyName = event.name?.toLowerCase() ?? "";
|
|
69
69
|
return keyName === "c" && (
|
|
70
70
|
event.super === true
|
|
71
71
|
|| event.meta === true
|
|
@@ -76,7 +76,7 @@ export function isCopyShortcut(
|
|
|
76
76
|
export function isPasteShortcut(
|
|
77
77
|
event: Pick<KeyEvent, "name" | "super" | "meta" | "ctrl" | "shift">,
|
|
78
78
|
): boolean {
|
|
79
|
-
const keyName = event.name
|
|
79
|
+
const keyName = event.name?.toLowerCase() ?? "";
|
|
80
80
|
return keyName === "v" && (
|
|
81
81
|
event.super === true
|
|
82
82
|
|| event.meta === true
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.9.
|
|
1
|
+
export const VERSION = "0.9.9";
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import { act } from "react";
|
|
3
|
-
import { testRender } from "../../../renderers/opentui/test-utils";
|
|
4
|
-
import { PaneHeader } from "./header";
|
|
5
|
-
|
|
6
|
-
let testSetup: Awaited<ReturnType<typeof testRender>> | undefined;
|
|
7
|
-
|
|
8
|
-
afterEach(() => {
|
|
9
|
-
if (testSetup) {
|
|
10
|
-
testSetup.renderer.destroy();
|
|
11
|
-
testSetup = undefined;
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
describe("PaneHeader", () => {
|
|
16
|
-
test("fires the terminal action handler from the visible action text", async () => {
|
|
17
|
-
let actions = 0;
|
|
18
|
-
testSetup = await testRender(
|
|
19
|
-
<PaneHeader
|
|
20
|
-
title="Portfolio"
|
|
21
|
-
width={40}
|
|
22
|
-
focused
|
|
23
|
-
showActions
|
|
24
|
-
onActionMouseDown={() => { actions += 1; }}
|
|
25
|
-
/>,
|
|
26
|
-
{ width: 40, height: 3 },
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
await testSetup.renderOnce();
|
|
30
|
-
const actionCol = testSetup.captureCharFrame().split("\n")[0]?.indexOf("...");
|
|
31
|
-
expect(actionCol).toBeGreaterThanOrEqual(0);
|
|
32
|
-
|
|
33
|
-
await act(async () => {
|
|
34
|
-
await testSetup!.mockMouse.click(actionCol! + 1, 0);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
expect(actions).toBe(1);
|
|
38
|
-
});
|
|
39
|
-
});
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import type { ChatChannel } from "../../../api-client";
|
|
3
|
-
import {
|
|
4
|
-
channelPrefix,
|
|
5
|
-
formatChannelLabel,
|
|
6
|
-
formatChatPaneTitle,
|
|
7
|
-
} from "./channels";
|
|
8
|
-
|
|
9
|
-
describe("chat channel labels", () => {
|
|
10
|
-
test("formats direct-message labels and titles with one mention prefix", () => {
|
|
11
|
-
const channel: ChatChannel = {
|
|
12
|
-
id: "dm:c7f89ce26f9da83d6be08a4838738074",
|
|
13
|
-
name: "@risto",
|
|
14
|
-
kind: "direct",
|
|
15
|
-
created_at: "2026-05-27T10:30:03.712Z",
|
|
16
|
-
dmUser: { id: "u2", username: "@risto", displayName: "Risto" },
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
expect(formatChannelLabel(channel, channel.id)).toBe("@risto");
|
|
20
|
-
expect(channelPrefix(channel, true)).toBe(" ");
|
|
21
|
-
expect(formatChatPaneTitle(channel, channel.id)).toBe("@risto");
|
|
22
|
-
expect(formatChatPaneTitle(undefined, channel.id)).toBe("DM");
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test("uses a neutral group title before private channel metadata loads", () => {
|
|
26
|
-
expect(formatChatPaneTitle(undefined, "grp:93f14c6b9827c30f")).toBe("Group");
|
|
27
|
-
});
|
|
28
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { registerTwitterFeedFeature } from "./registration";
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { resolveEarningsMonitorSymbols } from "./model";
|
|
3
|
-
|
|
4
|
-
describe("earnings monitor scope", () => {
|
|
5
|
-
test("uses explicit EM tickers before collection or app fallback tickers", () => {
|
|
6
|
-
expect(resolveEarningsMonitorSymbols(["AAPL", "MSFT"], ["NVDA"])).toEqual(["AAPL", "MSFT"]);
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
test("falls back to existing earnings monitor ticker universe when EM has no tickers", () => {
|
|
10
|
-
expect(resolveEarningsMonitorSymbols([], ["NVDA", "AMD"])).toEqual(["NVDA", "AMD"]);
|
|
11
|
-
});
|
|
12
|
-
});
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import { act } from "react";
|
|
3
|
-
import { testRender } from "../../../renderers/opentui/test-utils";
|
|
4
|
-
import { createTestPluginRuntime } from "../../../test-support/plugin-runtime";
|
|
5
|
-
import { setSharedRegistryForTests } from "../../registry";
|
|
6
|
-
import { PluginRenderProvider } from "../../runtime";
|
|
7
|
-
import { HelpPane } from ".";
|
|
8
|
-
|
|
9
|
-
let testSetup: Awaited<ReturnType<typeof testRender>> | undefined;
|
|
10
|
-
|
|
11
|
-
afterEach(() => {
|
|
12
|
-
if (testSetup) {
|
|
13
|
-
testSetup.renderer.destroy();
|
|
14
|
-
testSetup = undefined;
|
|
15
|
-
}
|
|
16
|
-
setSharedRegistryForTests(undefined);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
async function renderHelpPane(
|
|
20
|
-
runtime = createTestPluginRuntime(),
|
|
21
|
-
size = { width: 88, height: 36 },
|
|
22
|
-
) {
|
|
23
|
-
testSetup = await testRender(
|
|
24
|
-
<PluginRenderProvider pluginId="help" runtime={runtime}>
|
|
25
|
-
<HelpPane
|
|
26
|
-
paneId="help:main"
|
|
27
|
-
paneType="help"
|
|
28
|
-
focused
|
|
29
|
-
width={size.width}
|
|
30
|
-
height={size.height}
|
|
31
|
-
close={() => {}}
|
|
32
|
-
/>
|
|
33
|
-
</PluginRenderProvider>,
|
|
34
|
-
size,
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
await testSetup.renderOnce();
|
|
38
|
-
await testSetup.renderOnce();
|
|
39
|
-
return testSetup;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function moveHelpTabRight(count = 1) {
|
|
43
|
-
await act(async () => {
|
|
44
|
-
for (let index = 0; index < count; index += 1) {
|
|
45
|
-
testSetup!.mockInput.pressArrow("right");
|
|
46
|
-
}
|
|
47
|
-
await testSetup!.renderOnce();
|
|
48
|
-
await testSetup!.renderOnce();
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async function resetRenderedHelpPane(
|
|
53
|
-
runtime = createTestPluginRuntime(),
|
|
54
|
-
size = { width: 88, height: 36 },
|
|
55
|
-
) {
|
|
56
|
-
if (testSetup) {
|
|
57
|
-
act(() => {
|
|
58
|
-
testSetup!.renderer.destroy();
|
|
59
|
-
});
|
|
60
|
-
testSetup = undefined;
|
|
61
|
-
}
|
|
62
|
-
return renderHelpPane(runtime, size);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function withoutScrollbar(line: string) {
|
|
66
|
-
const scrollThumb = String.fromCharCode(0x2588);
|
|
67
|
-
return (line.endsWith(scrollThumb) ? line.slice(0, -1) : line).trimEnd();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
describe("HelpPane", () => {
|
|
71
|
-
test("starts on basics and puts section spacing before headings instead of after them", async () => {
|
|
72
|
-
await renderHelpPane(createTestPluginRuntime(), { width: 88, height: 80 });
|
|
73
|
-
|
|
74
|
-
const lines = testSetup!.captureCharFrame().split("\n").map(withoutScrollbar);
|
|
75
|
-
const commandBarRow = lines.findIndex((line) => line.includes("Command Bar"));
|
|
76
|
-
const layoutRow = lines.findIndex((line) => line.includes("Layout Basics"));
|
|
77
|
-
|
|
78
|
-
expect(lines[0]).toContain("Basics");
|
|
79
|
-
expect(testSetup!.captureCharFrame()).toContain("Basics");
|
|
80
|
-
expect(testSetup!.captureCharFrame()).toContain("Functions");
|
|
81
|
-
expect(testSetup!.captureCharFrame()).toContain("Issues");
|
|
82
|
-
expect(testSetup!.captureCharFrame()).not.toContain("Issues/Debug");
|
|
83
|
-
expect(testSetup!.captureCharFrame()).not.toContain("Window Templates");
|
|
84
|
-
expect(testSetup!.captureCharFrame()).not.toContain("Search for a ticker or open the best matching security.");
|
|
85
|
-
expect(commandBarRow).toBeGreaterThan(0);
|
|
86
|
-
expect(layoutRow).toBeGreaterThan(commandBarRow);
|
|
87
|
-
expect(lines[commandBarRow - 1]?.trim()).toBe("");
|
|
88
|
-
expect(lines[commandBarRow + 1]).toContain("Open command mode");
|
|
89
|
-
expect(testSetup!.captureCharFrame()).toContain("Open ticker search directly.");
|
|
90
|
-
expect(testSetup!.captureCharFrame()).toContain("DES");
|
|
91
|
-
expect(testSetup!.captureCharFrame()).toContain("Clear command text.");
|
|
92
|
-
expect(testSetup!.captureCharFrame()).toContain("Delete the previous word");
|
|
93
|
-
expect(testSetup!.captureCharFrame()).toContain("Submit multiline command forms.");
|
|
94
|
-
expect(testSetup!.captureCharFrame()).toContain("close all floating panes");
|
|
95
|
-
expect(lines[layoutRow - 1]?.trim()).toBe("");
|
|
96
|
-
expect(lines[layoutRow + 1]?.trim()).not.toBe("");
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test("separates functions from keyboard shortcuts", async () => {
|
|
100
|
-
setSharedRegistryForTests({
|
|
101
|
-
commands: new Map([[
|
|
102
|
-
"set-alert",
|
|
103
|
-
{
|
|
104
|
-
id: "set-alert",
|
|
105
|
-
label: "Add Alert",
|
|
106
|
-
description: "Create a price alert",
|
|
107
|
-
keywords: [],
|
|
108
|
-
category: "data",
|
|
109
|
-
shortcut: "SA",
|
|
110
|
-
shortcutArg: { placeholder: "symbol condition price" },
|
|
111
|
-
execute: () => {},
|
|
112
|
-
},
|
|
113
|
-
]]),
|
|
114
|
-
paneTemplates: new Map([[
|
|
115
|
-
"quote",
|
|
116
|
-
{
|
|
117
|
-
id: "quote",
|
|
118
|
-
paneId: "ticker-detail",
|
|
119
|
-
label: "Quote Monitor",
|
|
120
|
-
description: "Open ticker detail",
|
|
121
|
-
shortcut: { prefix: "QQ", argPlaceholder: "tickers", argKind: "ticker-list" },
|
|
122
|
-
},
|
|
123
|
-
]]),
|
|
124
|
-
shortcuts: new Map([[
|
|
125
|
-
"sync-data",
|
|
126
|
-
{
|
|
127
|
-
id: "sync-data",
|
|
128
|
-
key: "x",
|
|
129
|
-
shift: true,
|
|
130
|
-
description: "Sync data",
|
|
131
|
-
execute: () => {},
|
|
132
|
-
},
|
|
133
|
-
]]),
|
|
134
|
-
allPlugins: new Map([
|
|
135
|
-
["alerts", { name: "Alerts" }],
|
|
136
|
-
["ticker-research", { name: "Ticker Research" }],
|
|
137
|
-
["gloomberb-cloud", { name: "Gloom Cloud" }],
|
|
138
|
-
]),
|
|
139
|
-
getConfigFn: () => ({ disabledPlugins: [] }),
|
|
140
|
-
getCommandPluginId: () => "alerts",
|
|
141
|
-
getPaneTemplatePluginId: () => "ticker-research",
|
|
142
|
-
getShortcutPluginId: () => "gloomberb-cloud",
|
|
143
|
-
} as any);
|
|
144
|
-
|
|
145
|
-
await renderHelpPane(createTestPluginRuntime(), { width: 88, height: 80 });
|
|
146
|
-
await moveHelpTabRight();
|
|
147
|
-
|
|
148
|
-
const functionsFrame = testSetup!.captureCharFrame();
|
|
149
|
-
expect(functionsFrame).toContain("Manage Plugins");
|
|
150
|
-
expect(functionsFrame).toMatch(/AW\s+<ticker>/);
|
|
151
|
-
expect(functionsFrame).toMatch(/SA\s+<symbol condition price>/);
|
|
152
|
-
expect(functionsFrame).toMatch(/QQ\s+<tickers>/);
|
|
153
|
-
expect(functionsFrame).toContain("Alerts");
|
|
154
|
-
expect(functionsFrame).toContain("Add Alert");
|
|
155
|
-
expect(functionsFrame).toContain("Shift+X");
|
|
156
|
-
expect(functionsFrame).toContain("Gloom Cloud");
|
|
157
|
-
expect(functionsFrame).toContain("Sync data");
|
|
158
|
-
expect(functionsFrame).toContain("Ticker Research");
|
|
159
|
-
expect(functionsFrame).toContain("Quote Monitor");
|
|
160
|
-
expect(functionsFrame).not.toContain("Add Alert (Alerts)");
|
|
161
|
-
expect(functionsFrame).not.toContain("Sync data (Gloom Cloud)");
|
|
162
|
-
expect(functionsFrame).not.toContain("Quote Monitor (Ticker Research)");
|
|
163
|
-
expect(functionsFrame).not.toContain("Ctrl+W");
|
|
164
|
-
|
|
165
|
-
await resetRenderedHelpPane(createTestPluginRuntime(), { width: 88, height: 80 });
|
|
166
|
-
await moveHelpTabRight(2);
|
|
167
|
-
|
|
168
|
-
const shortcutsFrame = testSetup!.captureCharFrame();
|
|
169
|
-
expect(shortcutsFrame).toContain("Ctrl+W");
|
|
170
|
-
expect(shortcutsFrame).toContain("Ctrl+Alt+W");
|
|
171
|
-
expect(shortcutsFrame).toContain("Ctrl+Shift+M");
|
|
172
|
-
expect(shortcutsFrame).toContain("Ctrl+Shift+R");
|
|
173
|
-
expect(shortcutsFrame).toContain("Ctrl+,");
|
|
174
|
-
expect(shortcutsFrame).toContain("Navigation");
|
|
175
|
-
expect(shortcutsFrame).toContain("Up/Down");
|
|
176
|
-
expect(shortcutsFrame).toContain("j/k");
|
|
177
|
-
expect(shortcutsFrame).toContain("Left/Right");
|
|
178
|
-
expect(shortcutsFrame).toContain("h/l");
|
|
179
|
-
expect(shortcutsFrame).toContain("PageUp/PageDown");
|
|
180
|
-
expect(shortcutsFrame).toContain("Window Mode");
|
|
181
|
-
expect(shortcutsFrame).toContain("h/j/k/l");
|
|
182
|
-
expect(shortcutsFrame).toContain("Close all floating panes.");
|
|
183
|
-
expect(shortcutsFrame).not.toContain("AW");
|
|
184
|
-
expect(shortcutsFrame).not.toContain("Ctrl+Shift+O");
|
|
185
|
-
expect(shortcutsFrame).not.toContain("Pop the focused pane");
|
|
186
|
-
expect(shortcutsFrame).not.toContain("Cmd+W");
|
|
187
|
-
expect(shortcutsFrame).not.toContain("Cmd+,");
|
|
188
|
-
expect(shortcutsFrame).not.toContain("Cmd+K");
|
|
189
|
-
expect(shortcutsFrame).not.toContain("Cmd/Ctrl");
|
|
190
|
-
expect(shortcutsFrame).not.toContain("CmdOrCtrl");
|
|
191
|
-
expect(shortcutsFrame).not.toContain("Open ticker actions for the focused ticker.");
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
test("keeps shortcuts and descriptions on the same row when narrow", async () => {
|
|
195
|
-
await renderHelpPane(createTestPluginRuntime(), { width: 70, height: 80 });
|
|
196
|
-
|
|
197
|
-
const lines = testSetup!.captureCharFrame().split("\n").map(withoutScrollbar);
|
|
198
|
-
const closeCommandRow = lines.findIndex((line) => line.includes("Close the command bar."));
|
|
199
|
-
|
|
200
|
-
expect(closeCommandRow).toBeGreaterThanOrEqual(0);
|
|
201
|
-
expect(lines[closeCommandRow]).toContain("Esc");
|
|
202
|
-
expect(lines[closeCommandRow]).toContain("`");
|
|
203
|
-
|
|
204
|
-
await moveHelpTabRight();
|
|
205
|
-
|
|
206
|
-
const functionLines = testSetup!.captureCharFrame().split("\n").map(withoutScrollbar);
|
|
207
|
-
const securityDetailsRow = functionLines.findIndex((line) => line.includes("Open security details"));
|
|
208
|
-
expect(securityDetailsRow).toBeGreaterThanOrEqual(0);
|
|
209
|
-
expect(functionLines[securityDetailsRow]).toContain("DES");
|
|
210
|
-
expect(functionLines[securityDetailsRow]).toContain("<ticker>");
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
test("opens the debug log from the mouse action", async () => {
|
|
214
|
-
const calls: string[] = [];
|
|
215
|
-
const runtime = createTestPluginRuntime({
|
|
216
|
-
openCommandBar: (query?: string) => calls.push(`command:${query ?? ""}`),
|
|
217
|
-
showPane: (paneId: string) => calls.push(`pane:${paneId}`),
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
await renderHelpPane(runtime);
|
|
221
|
-
await moveHelpTabRight(3);
|
|
222
|
-
|
|
223
|
-
const frame = testSetup!.captureCharFrame();
|
|
224
|
-
expect(frame).toContain("GitHub Issues");
|
|
225
|
-
expect(frame).not.toContain("Issues/Debug");
|
|
226
|
-
expect(frame).not.toContain("plugin,\n");
|
|
227
|
-
|
|
228
|
-
const lines = frame.split("\n");
|
|
229
|
-
const row = lines.findIndex((line) => line.includes("Open Debug Log"));
|
|
230
|
-
const col = lines[row]?.indexOf("Open Debug Log") ?? -1;
|
|
231
|
-
|
|
232
|
-
expect(row).toBeGreaterThanOrEqual(0);
|
|
233
|
-
expect(col).toBeGreaterThanOrEqual(0);
|
|
234
|
-
|
|
235
|
-
await act(async () => {
|
|
236
|
-
await testSetup!.mockMouse.click(col + 1, row);
|
|
237
|
-
await testSetup!.renderOnce();
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
expect(calls).toEqual(["pane:debug"]);
|
|
241
|
-
});
|
|
242
|
-
});
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { resolveArticleImageSize } from "./article-detail";
|
|
3
|
-
|
|
4
|
-
describe("Substack article image sizing", () => {
|
|
5
|
-
test("uses extra detail pane room without shrinking compact panes", () => {
|
|
6
|
-
expect(resolveArticleImageSize(40)).toEqual({ width: 40, height: 12 });
|
|
7
|
-
expect(resolveArticleImageSize(86)).toEqual({ width: 86, height: 15 });
|
|
8
|
-
expect(resolveArticleImageSize(220)).toEqual({ width: 152, height: 26 });
|
|
9
|
-
});
|
|
10
|
-
});
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { buildApplicationMenu, buildDesktopApplicationMenu } from "./index";
|
|
3
|
-
|
|
4
|
-
describe("desktop application menu", () => {
|
|
5
|
-
test("hides the application menu on Windows to preserve vertical space", () => {
|
|
6
|
-
expect(buildDesktopApplicationMenu("win32")).toEqual([]);
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
test("keeps the native application menu on macOS", () => {
|
|
10
|
-
expect(buildDesktopApplicationMenu("darwin")).toEqual(buildApplicationMenu());
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test("routes macOS quit through an explicit command item", () => {
|
|
14
|
-
const appMenu = buildApplicationMenu()[0]?.submenu;
|
|
15
|
-
expect(appMenu?.at(-1)).toMatchObject({
|
|
16
|
-
label: "Quit Gloomberb",
|
|
17
|
-
accelerator: "CmdOrCtrl+Q",
|
|
18
|
-
data: { type: "quit" },
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
});
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { detachedRpcKey, focusWindowForRpcKey, MAIN_WINDOW_RPC_KEY, type FocusableElectrobunWindow } from "./focus";
|
|
3
|
-
|
|
4
|
-
describe("Electrobun window focus routing", () => {
|
|
5
|
-
test("uses activate for the main window", () => {
|
|
6
|
-
const calls: string[] = [];
|
|
7
|
-
const mainWindow: FocusableElectrobunWindow = {
|
|
8
|
-
activate: () => calls.push("activate"),
|
|
9
|
-
focus: () => calls.push("focus"),
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
expect(focusWindowForRpcKey(MAIN_WINDOW_RPC_KEY, mainWindow, new Map())).toBe(true);
|
|
13
|
-
expect(calls).toEqual(["activate"]);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test("uses activate for detached windows", () => {
|
|
17
|
-
const calls: string[] = [];
|
|
18
|
-
const detachedWindows = new Map<string, FocusableElectrobunWindow>([
|
|
19
|
-
["pane:1", { activate: () => calls.push("detached") }],
|
|
20
|
-
]);
|
|
21
|
-
|
|
22
|
-
expect(focusWindowForRpcKey(detachedRpcKey("pane:1"), null, detachedWindows)).toBe(true);
|
|
23
|
-
expect(calls).toEqual(["detached"]);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test("falls back to focus for legacy window objects", () => {
|
|
27
|
-
const calls: string[] = [];
|
|
28
|
-
const mainWindow: FocusableElectrobunWindow = {
|
|
29
|
-
focus: () => calls.push("focus"),
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
expect(focusWindowForRpcKey(MAIN_WINDOW_RPC_KEY, mainWindow, new Map())).toBe(true);
|
|
33
|
-
expect(calls).toEqual(["focus"]);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { hasDirectMouseHandler, mouseHandlers } from "./mouse";
|
|
3
|
-
|
|
4
|
-
function fakeMouseEvent() {
|
|
5
|
-
return {
|
|
6
|
-
clientX: 16,
|
|
7
|
-
clientY: 36,
|
|
8
|
-
button: 0,
|
|
9
|
-
detail: 0,
|
|
10
|
-
timeStamp: 1,
|
|
11
|
-
shiftKey: false,
|
|
12
|
-
altKey: false,
|
|
13
|
-
ctrlKey: false,
|
|
14
|
-
metaKey: false,
|
|
15
|
-
preventDefault() {},
|
|
16
|
-
stopPropagation() {},
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
describe("desktop web mouse host", () => {
|
|
21
|
-
test("forwards mouse over handlers", () => {
|
|
22
|
-
let cellX = -1;
|
|
23
|
-
let cellY = -1;
|
|
24
|
-
const handlers = mouseHandlers({
|
|
25
|
-
onMouseOver: (event: { x: number; y: number }) => {
|
|
26
|
-
cellX = event.x;
|
|
27
|
-
cellY = event.y;
|
|
28
|
-
},
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
handlers.onMouseOver?.(fakeMouseEvent() as never);
|
|
32
|
-
|
|
33
|
-
expect(cellX).toBe(2);
|
|
34
|
-
expect(cellY).toBe(2);
|
|
35
|
-
expect(hasDirectMouseHandler({ onMouseOver: () => {} })).toBe(true);
|
|
36
|
-
});
|
|
37
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { renderAsciiText } from "../../../../ui/ascii-font";
|
|
3
|
-
import { webAsciiTextLines, webAsciiTextWordmarkVariant } from "./ascii-text";
|
|
4
|
-
|
|
5
|
-
describe("desktop web ASCII text", () => {
|
|
6
|
-
test("keeps the legacy Gloomberb wordmark on macOS", () => {
|
|
7
|
-
expect(webAsciiTextWordmarkVariant("Gloomberb", "wordmark", "darwin")).toBe("legacy");
|
|
8
|
-
expect(webAsciiTextWordmarkVariant("Gloomberb", "wordmark", "MacIntel")).toBe("legacy");
|
|
9
|
-
expect(webAsciiTextLines("Gloomberb", "wordmark", "darwin")).toEqual(
|
|
10
|
-
renderAsciiText("Gloomberb", "wordmark"),
|
|
11
|
-
);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
test("uses the web-safe Gloomberb wordmark off macOS", () => {
|
|
15
|
-
const lines = webAsciiTextLines("Gloomberb", "wordmark", "win32");
|
|
16
|
-
|
|
17
|
-
expect(webAsciiTextWordmarkVariant("Gloomberb", "wordmark", "win32")).toBe("compat");
|
|
18
|
-
expect(lines).toHaveLength(5);
|
|
19
|
-
expect(lines[0]).toContain("____");
|
|
20
|
-
expect(lines).not.toEqual(renderAsciiText("Gloomberb", "wordmark"));
|
|
21
|
-
});
|
|
22
|
-
});
|